PageRenderTime 49ms CodeModel.GetById 29ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/code/ryzom/tools/server/www/login/email/htmlMimeMail.php

https://bitbucket.org/mattraykowski/ryzomcore_demoshard
PHP | 777 lines | 451 code | 94 blank | 232 comment | 68 complexity | ea8deb1c4ca1affe5f571ac7f876973e MD5 | raw file
  1<?php
  2/**
  3* Filename.......: class.html.mime.mail.inc
  4* Project........: HTML Mime mail class
  5* Last Modified..: $Date: 2007/06/19 15:29:18 $
  6* CVS Revision...: $Revision: 1.1 $
  7* Copyright......: 2001, 2002 Richard Heyes
  8*/
  9
 10require_once(dirname(__FILE__) . '/mimePart.php');
 11
 12class htmlMimeMail
 13{
 14	/**
 15	* The html part of the message
 16    * @var string
 17    */
 18	var $html;
 19
 20	/**
 21	* The text part of the message(only used in TEXT only messages)
 22	* @var string
 23	*/
 24	var $text;
 25
 26	/**
 27	* The main body of the message after building
 28	* @var string
 29	*/
 30	var $output;
 31
 32	/**
 33	* The alternative text to the HTML part (only used in HTML messages)
 34	* @var string
 35	*/
 36	var $html_text;
 37
 38	/**
 39	* An array of embedded images/objects
 40	* @var array
 41	*/
 42	var $html_images;
 43
 44	/**
 45	* An array of recognised image types for the findHtmlImages() method
 46	* @var array
 47	*/
 48	var $image_types;
 49
 50	/**
 51	* Parameters that affect the build process
 52	* @var array
 53	*/
 54	var $build_params;
 55
 56	/**
 57	* Array of attachments
 58	* @var array
 59	*/
 60	var $attachments;
 61
 62	/**
 63	* The main message headers
 64	* @var array
 65	*/
 66	var $headers;
 67
 68	/**
 69	* Whether the message has been built or not
 70	* @var boolean
 71	*/
 72	var $is_built;
 73
 74	/**
 75    * The return path address. If not set the From:
 76	* address is used instead
 77	* @var string
 78    */
 79	var $return_path;
 80
 81	/**
 82    * Array of information needed for smtp sending
 83	* @var array
 84    */
 85	var $smtp_params;
 86
 87/**
 88* Constructor function. Sets the headers
 89* if supplied.
 90*/
 91
 92	function htmlMimeMail()
 93	{
 94		/**
 95        * Initialise some variables.
 96        */
 97		$this->html_images = array();
 98		$this->headers     = array();
 99		$this->is_built    = false;
100
101		/**
102        * If you want the auto load functionality
103		* to find other image/file types, add the
104		* extension and content type here.
105        */
106		$this->image_types = array(
107									'gif'	=> 'image/gif',
108									'jpg'	=> 'image/jpeg',
109									'jpeg'	=> 'image/jpeg',
110									'jpe'	=> 'image/jpeg',
111									'bmp'	=> 'image/bmp',
112									'png'	=> 'image/png',
113									'tif'	=> 'image/tiff',
114									'tiff'	=> 'image/tiff',
115									'swf'	=> 'application/x-shockwave-flash'
116								  );
117
118		/**
119        * Set these up
120        */
121		$this->build_params['html_encoding'] = 'quoted-printable';
122		$this->build_params['text_encoding'] = '7bit';
123		$this->build_params['html_charset']  = 'UTF-8';
124		$this->build_params['text_charset']  = 'UTF-8';
125		//$this->build_params['head_charset']  = 'ISO-8859-1';
126		$this->build_params['head_charset']  = 'UTF-8';
127		$this->build_params['text_wrap']     = 998;
128
129		/**
130        * Defaults for smtp sending
131        */
132		if (!empty($GLOBALS['HTTP_SERVER_VARS']['HTTP_HOST'])) {
133			$helo = $GLOBALS['HTTP_SERVER_VARS']['HTTP_HOST'];
134		} elseif (!empty($GLOBALS['HTTP_SERVER_VARS']['SERVER_NAME'])) {
135			$helo = $GLOBALS['HTTP_SERVER_VARS']['SERVER_NAME'];
136		} else {
137			$helo = 'localhost';
138		}
139
140		$this->smtp_params['host'] = 'localhost';
141		$this->smtp_params['port'] = 25;
142		$this->smtp_params['helo'] = $helo;
143		$this->smtp_params['auth'] = false;
144		$this->smtp_params['user'] = '';
145		$this->smtp_params['pass'] = '';
146
147		/**
148        * Make sure the MIME version header is first.
149        */
150		$this->headers['MIME-Version'] = '1.0';
151	}
152
153/**
154* This function will read a file in
155* from a supplied filename and return
156* it. This can then be given as the first
157* argument of the the functions
158* add_html_image() or add_attachment().
159*/
160	function getFile($filename)
161	{
162		$return = '';
163		if ($fp = fopen($filename, 'rb')) {
164			while (!feof($fp)) {
165				$return .= fread($fp, 1024);
166			}
167			fclose($fp);
168			return $return;
169
170		} else {
171			return false;
172		}
173	}
174
175/**
176* Accessor to set the CRLF style
177*/
178	function setCrlf($crlf = "\n")
179	{
180		if (!defined('CRLF')) {
181			define('CRLF', $crlf, true);
182		}
183
184		if (!defined('MAIL_MIMEPART_CRLF')) {
185			define('MAIL_MIMEPART_CRLF', $crlf, true);
186		}
187	}
188
189/**
190* Accessor to set the SMTP parameters
191*/
192	function setSMTPParams($host = null, $port = null, $helo = null, $auth = null, $user = null, $pass = null)
193	{
194		if (!is_null($host)) $this->smtp_params['host'] = $host;
195		if (!is_null($port)) $this->smtp_params['port'] = $port;
196		if (!is_null($helo)) $this->smtp_params['helo'] = $helo;
197		if (!is_null($auth)) $this->smtp_params['auth'] = $auth;
198		if (!is_null($user)) $this->smtp_params['user'] = $user;
199		if (!is_null($pass)) $this->smtp_params['pass'] = $pass;
200	}
201
202/**
203* Accessor function to set the text encoding
204*/
205	function setTextEncoding($encoding = '7bit')
206	{
207		$this->build_params['text_encoding'] = $encoding;
208	}
209
210/**
211* Accessor function to set the HTML encoding
212*/
213	function setHtmlEncoding($encoding = 'quoted-printable')
214	{
215		$this->build_params['html_encoding'] = $encoding;
216	}
217
218/**
219* Accessor function to set the text charset
220*/
221	function setTextCharset($charset = 'ISO-8859-1')
222	{
223		$this->build_params['text_charset'] = $charset;
224	}
225
226/**
227* Accessor function to set the HTML charset
228*/
229	function setHtmlCharset($charset = 'ISO-8859-1')
230	{
231		$this->build_params['html_charset'] = $charset;
232	}
233
234/**
235* Accessor function to set the header encoding charset
236*/
237	function setHeadCharset($charset = 'ISO-8859-1')
238	{
239		$this->build_params['head_charset'] = $charset;
240	}
241
242/**
243* Accessor function to set the text wrap count
244*/
245	function setTextWrap($count = 998)
246	{
247		$this->build_params['text_wrap'] = $count;
248	}
249
250/**
251* Accessor to set a header
252*/
253	function setHeader($name, $value)
254	{
255		$this->headers[$name] = $value;
256	}
257
258/**
259* Accessor to add a Subject: header
260*/
261	function setSubject($subject)
262	{
263		$this->headers['Subject'] = $subject;
264	}
265
266/**
267* Accessor to add a From: header
268*/
269	function setFrom($from)
270	{
271		$this->headers['From'] = $from;
272	}
273
274/**
275* Accessor to set the return path
276*/
277	function setReturnPath($return_path)
278	{
279		$this->return_path = $return_path;
280	}
281
282/**
283* Accessor to add a Cc: header
284*/
285	function setCc($cc)
286	{
287		$this->headers['Cc'] = $cc;
288	}
289
290/**
291* Accessor to add a Bcc: header
292*/
293	function setBcc($bcc)
294	{
295		$this->headers['Bcc'] = $bcc;
296	}
297
298/**
299* Adds plain text. Use this function
300* when NOT sending html email
301*/
302	function setText($text = '')
303	{
304		$this->text = $text;
305	}
306
307/**
308* Adds a html part to the mail.
309* Also replaces image names with
310* content-id's.
311*/
312	function setHtml($html, $text = null, $images_dir = null)
313	{
314		$this->html      = $html;
315		$this->html_text = $text;
316
317		if (isset($images_dir)) {
318			$this->_findHtmlImages($images_dir);
319		}
320	}
321
322/**
323* Function for extracting images from
324* html source. This function will look
325* through the html code supplied by add_html()
326* and find any file that ends in one of the
327* extensions defined in $obj->image_types.
328* If the file exists it will read it in and
329* embed it, (not an attachment).
330*
331* @author Dan Allen
332*/
333	function _findHtmlImages($images_dir)
334	{
335		// Build the list of image extensions
336		while (list($key,) = each($this->image_types)) {
337			$extensions[] = $key;
338		}
339
340		preg_match_all('/(?:"|\')([^"\']+\.('.implode('|', $extensions).'))(?:"|\')/Ui', $this->html, $images);
341
342		for ($i=0; $i<count($images[1]); $i++) {
343			if (file_exists($images_dir . $images[1][$i])) {
344				$html_images[] = $images[1][$i];
345				$this->html = str_replace($images[1][$i], basename($images[1][$i]), $this->html);
346			}
347		}
348
349		if (!empty($html_images)) {
350
351			// If duplicate images are embedded, they may show up as attachments, so remove them.
352			$html_images = array_unique($html_images);
353			sort($html_images);
354
355			for ($i=0; $i<count($html_images); $i++) {
356				if ($image = $this->getFile($images_dir.$html_images[$i])) {
357					$ext = substr($html_images[$i], strrpos($html_images[$i], '.') + 1);
358					$content_type = $this->image_types[strtolower($ext)];
359					$this->addHtmlImage($image, basename($html_images[$i]), $content_type);
360				}
361			}
362		}
363	}
364
365/**
366* Adds an image to the list of embedded
367* images.
368*/
369	function addHtmlImage($file, $name = '', $c_type='application/octet-stream')
370	{
371		$this->html_images[] = array(
372										'body'   => $file,
373										'name'   => $name,
374										'c_type' => $c_type,
375										'cid'    => md5(uniqid(time()))
376									);
377	}
378
379
380/**
381* Adds a file to the list of attachments.
382*/
383	function addAttachment($file, $name = '', $c_type='application/octet-stream', $encoding = 'base64')
384	{
385		$this->attachments[] = array(
386									'body'		=> $file,
387									'name'		=> $name,
388									'c_type'	=> $c_type,
389									'encoding'	=> $encoding
390								  );
391	}
392
393/**
394* Adds a text subpart to a mime_part object
395*/
396	function &_addTextPart(&$obj, $text)
397	{
398		$params['content_type'] = 'text/plain';
399		$params['encoding']     = $this->build_params['text_encoding'];
400		$params['charset']      = $this->build_params['text_charset'];
401		if (is_object($obj)) {
402			return $obj->addSubpart($text, $params);
403		} else {
404			return new Mail_mimePart($text, $params);
405		}
406	}
407
408/**
409* Adds a html subpart to a mime_part object
410*/
411	function &_addHtmlPart(&$obj)
412	{
413		$params['content_type'] = 'text/html';
414		$params['encoding']     = $this->build_params['html_encoding'];
415		$params['charset']      = $this->build_params['html_charset'];
416		if (is_object($obj)) {
417			return $obj->addSubpart($this->html, $params);
418		} else {
419			return new Mail_mimePart($this->html, $params);
420		}
421	}
422
423/**
424* Starts a message with a mixed part
425*/
426	function &_addMixedPart()
427	{
428		$params['content_type'] = 'multipart/mixed';
429		return new Mail_mimePart('', $params);
430	}
431
432/**
433* Adds an alternative part to a mime_part object
434*/
435	function &_addAlternativePart(&$obj)
436	{
437		$params['content_type'] = 'multipart/alternative';
438		if (is_object($obj)) {
439			return $obj->addSubpart('', $params);
440		} else {
441			return new Mail_mimePart('', $params);
442		}
443	}
444
445/**
446* Adds a html subpart to a mime_part object
447*/
448	function &_addRelatedPart(&$obj)
449	{
450		$params['content_type'] = 'multipart/related';
451		if (is_object($obj)) {
452			return $obj->addSubpart('', $params);
453		} else {
454			return new Mail_mimePart('', $params);
455		}
456	}
457
458/**
459* Adds an html image subpart to a mime_part object
460*/
461	function &_addHtmlImagePart(&$obj, $value)
462	{
463		$params['content_type'] = $value['c_type'];
464		$params['encoding']     = 'base64';
465		$params['disposition']  = 'inline';
466		$params['dfilename']    = $value['name'];
467		$params['cid']          = $value['cid'];
468		$obj->addSubpart($value['body'], $params);
469	}
470
471/**
472* Adds an attachment subpart to a mime_part object
473*/
474	function &_addAttachmentPart(&$obj, $value)
475	{
476		$params['content_type'] = $value['c_type'];
477		$params['encoding']     = $value['encoding'];
478		$params['disposition']  = 'attachment';
479		$params['dfilename']    = $value['name'];
480		$obj->addSubpart($value['body'], $params);
481	}
482
483/**
484* Builds the multipart message from the
485* list ($this->_parts). $params is an
486* array of parameters that shape the building
487* of the message. Currently supported are:
488*
489* $params['html_encoding'] - The type of encoding to use on html. Valid options are
490*                            "7bit", "quoted-printable" or "base64" (all without quotes).
491*                            7bit is EXPRESSLY NOT RECOMMENDED. Default is quoted-printable
492* $params['text_encoding'] - The type of encoding to use on plain text Valid options are
493*                            "7bit", "quoted-printable" or "base64" (all without quotes).
494*                            Default is 7bit
495* $params['text_wrap']     - The character count at which to wrap 7bit encoded data.
496*                            Default this is 998.
497* $params['html_charset']  - The character set to use for a html section.
498*                            Default is ISO-8859-1
499* $params['text_charset']  - The character set to use for a text section.
500*                          - Default is ISO-8859-1
501* $params['head_charset']  - The character set to use for header encoding should it be needed.
502*                          - Default is ISO-8859-1
503*/
504	function buildMessage($params = array())
505	{
506		if (!empty($params)) {
507			while (list($key, $value) = each($params)) {
508				$this->build_params[$key] = $value;
509			}
510		}
511
512		if (!empty($this->html_images)) {
513			foreach ($this->html_images as $value) {
514				$this->html = str_replace($value['name'], 'cid:'.$value['cid'], $this->html);
515			}
516		}
517
518		$null        = null;
519		$attachments = !empty($this->attachments) ? true : false;
520		$html_images = !empty($this->html_images) ? true : false;
521		$html        = !empty($this->html)        ? true : false;
522		$text        = isset($this->text)         ? true : false;
523
524		switch (true) {
525			case $text AND !$attachments:
526				$message = &$this->_addTextPart($null, $this->text);
527				break;
528
529			case !$text AND $attachments AND !$html:
530				$message = &$this->_addMixedPart();
531
532				for ($i=0; $i<count($this->attachments); $i++) {
533					$this->_addAttachmentPart($message, $this->attachments[$i]);
534				}
535				break;
536
537			case $text AND $attachments:
538				$message = &$this->_addMixedPart();
539				$this->_addTextPart($message, $this->text);
540
541				for ($i=0; $i<count($this->attachments); $i++) {
542					$this->_addAttachmentPart($message, $this->attachments[$i]);
543				}
544				break;
545
546			case $html AND !$attachments AND !$html_images:
547				if (!is_null($this->html_text)) {
548					$message = &$this->_addAlternativePart($null);
549					$this->_addTextPart($message, $this->html_text);
550					$this->_addHtmlPart($message);
551				} else {
552					$message = &$this->_addHtmlPart($null);
553				}
554				break;
555
556			case $html AND !$attachments AND $html_images:
557				if (!is_null($this->html_text)) {
558					$message = &$this->_addAlternativePart($null);
559					$this->_addTextPart($message, $this->html_text);
560					$related = &$this->_addRelatedPart($message);
561				} else {
562					$message = &$this->_addRelatedPart($null);
563					$related = &$message;
564				}
565				$this->_addHtmlPart($related);
566				for ($i=0; $i<count($this->html_images); $i++) {
567					$this->_addHtmlImagePart($related, $this->html_images[$i]);
568				}
569				break;
570
571			case $html AND $attachments AND !$html_images:
572				$message = &$this->_addMixedPart();
573				if (!is_null($this->html_text)) {
574					$alt = &$this->_addAlternativePart($message);
575					$this->_addTextPart($alt, $this->html_text);
576					$this->_addHtmlPart($alt);
577				} else {
578					$this->_addHtmlPart($message);
579				}
580				for ($i=0; $i<count($this->attachments); $i++) {
581					$this->_addAttachmentPart($message, $this->attachments[$i]);
582				}
583				break;
584
585			case $html AND $attachments AND $html_images:
586				$message = &$this->_addMixedPart();
587				if (!is_null($this->html_text)) {
588					$alt = &$this->_addAlternativePart($message);
589					$this->_addTextPart($alt, $this->html_text);
590					$rel = &$this->_addRelatedPart($alt);
591				} else {
592					$rel = &$this->_addRelatedPart($message);
593				}
594				$this->_addHtmlPart($rel);
595				for ($i=0; $i<count($this->html_images); $i++) {
596					$this->_addHtmlImagePart($rel, $this->html_images[$i]);
597				}
598				for ($i=0; $i<count($this->attachments); $i++) {
599					$this->_addAttachmentPart($message, $this->attachments[$i]);
600				}
601				break;
602
603		}
604
605		if (isset($message)) {
606			$output = $message->encode();
607			$this->output   = $output['body'];
608			$this->headers  = array_merge($this->headers, $output['headers']);
609
610			// Add message ID header
611			srand((double)microtime()*10000000);
612			//$message_id = sprintf('<%s.%s@%s>', base_convert(time(), 10, 36), base_convert(rand(), 10, 36), !empty($GLOBALS['HTTP_SERVER_VARS']['HTTP_HOST']) ? $GLOBALS['HTTP_SERVER_VARS']['HTTP_HOST'] : $GLOBALS['HTTP_SERVER_VARS']['SERVER_NAME']);
613			// *** don't want to show atrium.ryzom.com in headers, so forcing to www.ryzom.com
614			$message_id = sprintf('<%s.%s@%s>', base_convert(time(), 10, 36), base_convert(rand(), 10, 36), 'www.ryzom.com');
615			$this->headers['Message-ID'] = $message_id;
616
617			$this->is_built = true;
618			return true;
619		} else {
620			return false;
621		}
622	}
623
624/**
625* Function to encode a header if necessary
626* according to RFC2047
627*/
628	function _encodeHeader($input, $charset = 'ISO-8859-1')
629	{
630		preg_match_all('/(\w*[\x80-\xFF]+\w*)/', $input, $matches);
631		foreach ($matches[1] as $value) {
632			$replacement = preg_replace('/([\x80-\xFF])/e', '"=" . strtoupper(dechex(ord("\1")))', $value);
633			$input = str_replace($value, '=?' . $charset . '?Q?' . $replacement . '?=', $input);
634		}
635
636		return $input;
637	}
638
639/**
640* Sends the mail.
641*
642* @param  array  $recipients
643* @param  string $type OPTIONAL
644* @return mixed
645*/
646	function send($recipients, $type = 'mail')
647	{
648		if (!defined('CRLF')) {
649			$this->setCrlf($type == 'mail' ? "\n" : "\r\n");
650		}
651
652		if (!$this->is_built) {
653			$this->buildMessage();
654		}
655
656		switch ($type) {
657			case 'mail':
658				$subject = '';
659				if (!empty($this->headers['Subject'])) {
660					$subject = $this->_encodeHeader($this->headers['Subject'], $this->build_params['head_charset']);
661					unset($this->headers['Subject']);
662				}
663
664				// Get flat representation of headers
665				foreach ($this->headers as $name => $value) {
666					$headers[] = $name . ': ' . $this->_encodeHeader($value, $this->build_params['head_charset']);
667				}
668
669				$to = $this->_encodeHeader(implode(', ', $recipients), $this->build_params['head_charset']);
670
671				if (!empty($this->return_path)) {
672					$result = mail($to, $subject, $this->output, implode(CRLF, $headers), '-f' . $this->return_path);
673				} else {
674					$result = mail($to, $subject, $this->output, implode(CRLF, $headers));
675				}
676
677				// Reset the subject in case mail is resent
678				if ($subject !== '') {
679					$this->headers['Subject'] = $subject;
680				}
681
682				// Return
683				return $result;
684				break;
685
686			case 'smtp':
687				require_once(dirname(__FILE__) . '/smtp.php');
688				require_once(dirname(__FILE__) . '/RFC822.php');
689				$smtp = &smtp::connect($this->smtp_params);
690
691				// Parse recipients argument for internet addresses
692				foreach ($recipients as $recipient) {
693					$addresses = Mail_RFC822::parseAddressList($recipient, $this->smtp_params['helo'], null, false);
694					foreach ($addresses as $address) {
695						$smtp_recipients[] = sprintf('%s@%s', $address->mailbox, $address->host);
696					}
697				}
698				unset($addresses); // These are reused
699				unset($address);   // These are reused
700
701				// Get flat representation of headers, parsing
702				// Cc and Bcc as we go
703				foreach ($this->headers as $name => $value) {
704					if ($name == 'Cc' OR $name == 'Bcc') {
705						$addresses = Mail_RFC822::parseAddressList($value, $this->smtp_params['helo'], null, false);
706						foreach ($addresses as $address) {
707							$smtp_recipients[] = sprintf('%s@%s', $address->mailbox, $address->host);
708						}
709					}
710					if ($name == 'Bcc') {
711						continue;
712					}
713					$headers[] = $name . ': ' . $this->_encodeHeader($value, $this->build_params['head_charset']);
714				}
715				// Add To header based on $recipients argument
716				$headers[] = 'To: ' . $this->_encodeHeader(implode(', ', $recipients), $this->build_params['head_charset']);
717
718				// Add headers to send_params
719				$send_params['headers']    = $headers;
720				$send_params['recipients'] = array_values(array_unique($smtp_recipients));
721				$send_params['body']       = $this->output;
722
723				// Setup return path
724				if (isset($this->return_path)) {
725					$send_params['from'] = $this->return_path;
726				} elseif (!empty($this->headers['From'])) {
727					$from = Mail_RFC822::parseAddressList($this->headers['From']);
728					$send_params['from'] = sprintf('%s@%s', $from[0]->mailbox, $from[0]->host);
729				} else {
730					$send_params['from'] = 'postmaster@' . $this->smtp_params['helo'];
731				}
732
733				// Send it
734				if (!$smtp->send($send_params)) {
735					$this->errors = $smtp->errors;
736					return false;
737				}
738				return true;
739				break;
740		}
741	}
742
743/**
744* Use this method to return the email
745* in message/rfc822 format. Useful for
746* adding an email to another email as
747* an attachment. there's a commented
748* out example in example.php.
749*/
750	function getRFC822($recipients)
751	{
752		// Make up the date header as according to RFC822
753		$this->setHeader('Date', date('D, d M y H:i:s O'));
754
755		if (!defined('CRLF')) {
756			$this->setCrlf($type == 'mail' ? "\n" : "\r\n");
757		}
758
759		if (!$this->is_built) {
760			$this->buildMessage();
761		}
762
763		// Return path ?
764		if (isset($this->return_path)) {
765			$headers[] = 'Return-Path: ' . $this->return_path;
766		}
767
768		// Get flat representation of headers
769		foreach ($this->headers as $name => $value) {
770			$headers[] = $name . ': ' . $value;
771		}
772		$headers[] = 'To: ' . implode(', ', $recipients);
773
774		return implode(CRLF, $headers) . CRLF . CRLF . $this->output;
775	}
776} // End of class.
777?>