Merge branch 'main/rendor-staging' into main/atys-live
[ryzomcore.git] / web / public_php / login / email / htmlMimeMail.php
blob4026bb985cb71c9964a666a5ebfed11c1b8d929f
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 */
10 require_once(dirname(__FILE__) . '/mimePart.php');
12 class htmlMimeMail
14 /**
15 * The html part of the message
16 * @var string
18 var $html;
20 /**
21 * The text part of the message(only used in TEXT only messages)
22 * @var string
24 var $text;
26 /**
27 * The main body of the message after building
28 * @var string
30 var $output;
32 /**
33 * The alternative text to the HTML part (only used in HTML messages)
34 * @var string
36 var $html_text;
38 /**
39 * An array of embedded images/objects
40 * @var array
42 var $html_images;
44 /**
45 * An array of recognised image types for the findHtmlImages() method
46 * @var array
48 var $image_types;
50 /**
51 * Parameters that affect the build process
52 * @var array
54 var $build_params;
56 /**
57 * Array of attachments
58 * @var array
60 var $attachments;
62 /**
63 * The main message headers
64 * @var array
66 var $headers;
68 /**
69 * Whether the message has been built or not
70 * @var boolean
72 var $is_built;
74 /**
75 * The return path address. If not set the From:
76 * address is used instead
77 * @var string
79 var $return_path;
81 /**
82 * Array of information needed for smtp sending
83 * @var array
85 var $smtp_params;
87 /**
88 * Constructor function. Sets the headers
89 * if supplied.
92 function htmlMimeMail()
94 /**
95 * Initialise some variables.
97 $this->html_images = array();
98 $this->headers = array();
99 $this->is_built = false;
102 * If you want the auto load functionality
103 * to find other image/file types, add the
104 * extension and content type here.
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'
119 * Set these up
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;
130 * Defaults for smtp sending
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';
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'] = '';
148 * Make sure the MIME version header is first.
150 $this->headers['MIME-Version'] = '1.0';
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().
160 function getFile($filename)
162 $return = '';
163 if ($fp = fopen($filename, 'rb')) {
164 while (!feof($fp)) {
165 $return .= fread($fp, 1024);
167 fclose($fp);
168 return $return;
170 } else {
171 return false;
176 * Accessor to set the CRLF style
178 function setCrlf($crlf = "\n")
180 if (!defined('CRLF')) {
181 define('CRLF', $crlf, true);
184 if (!defined('MAIL_MIMEPART_CRLF')) {
185 define('MAIL_MIMEPART_CRLF', $crlf, true);
190 * Accessor to set the SMTP parameters
192 function setSMTPParams($host = null, $port = null, $helo = null, $auth = null, $user = null, $pass = null)
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;
203 * Accessor function to set the text encoding
205 function setTextEncoding($encoding = '7bit')
207 $this->build_params['text_encoding'] = $encoding;
211 * Accessor function to set the HTML encoding
213 function setHtmlEncoding($encoding = 'quoted-printable')
215 $this->build_params['html_encoding'] = $encoding;
219 * Accessor function to set the text charset
221 function setTextCharset($charset = 'ISO-8859-1')
223 $this->build_params['text_charset'] = $charset;
227 * Accessor function to set the HTML charset
229 function setHtmlCharset($charset = 'ISO-8859-1')
231 $this->build_params['html_charset'] = $charset;
235 * Accessor function to set the header encoding charset
237 function setHeadCharset($charset = 'ISO-8859-1')
239 $this->build_params['head_charset'] = $charset;
243 * Accessor function to set the text wrap count
245 function setTextWrap($count = 998)
247 $this->build_params['text_wrap'] = $count;
251 * Accessor to set a header
253 function setHeader($name, $value)
255 $this->headers[$name] = $value;
259 * Accessor to add a Subject: header
261 function setSubject($subject)
263 $this->headers['Subject'] = $subject;
267 * Accessor to add a From: header
269 function setFrom($from)
271 $this->headers['From'] = $from;
275 * Accessor to set the return path
277 function setReturnPath($return_path)
279 $this->return_path = $return_path;
283 * Accessor to add a Cc: header
285 function setCc($cc)
287 $this->headers['Cc'] = $cc;
291 * Accessor to add a Bcc: header
293 function setBcc($bcc)
295 $this->headers['Bcc'] = $bcc;
299 * Adds plain text. Use this function
300 * when NOT sending html email
302 function setText($text = '')
304 $this->text = $text;
308 * Adds a html part to the mail.
309 * Also replaces image names with
310 * content-id's.
312 function setHtml($html, $text = null, $images_dir = null)
314 $this->html = $html;
315 $this->html_text = $text;
317 if (isset($images_dir)) {
318 $this->_findHtmlImages($images_dir);
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).
331 * @author Dan Allen
333 function _findHtmlImages($images_dir)
335 // Build the list of image extensions
336 while (list($key,) = each($this->image_types)) {
337 $extensions[] = $key;
340 preg_match_all('/(?:"|\')([^"\']+\.('.implode('|', $extensions).'))(?:"|\')/Ui', $this->html, $images);
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);
349 if (!empty($html_images)) {
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);
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);
366 * Adds an image to the list of embedded
367 * images.
369 function addHtmlImage($file, $name = '', $c_type='application/octet-stream')
371 $this->html_images[] = array(
372 'body' => $file,
373 'name' => $name,
374 'c_type' => $c_type,
375 'cid' => md5(uniqid(time()))
381 * Adds a file to the list of attachments.
383 function addAttachment($file, $name = '', $c_type='application/octet-stream', $encoding = 'base64')
385 $this->attachments[] = array(
386 'body' => $file,
387 'name' => $name,
388 'c_type' => $c_type,
389 'encoding' => $encoding
394 * Adds a text subpart to a mime_part object
396 function &_addTextPart(&$obj, $text)
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);
409 * Adds a html subpart to a mime_part object
411 function &_addHtmlPart(&$obj)
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);
424 * Starts a message with a mixed part
426 function &_addMixedPart()
428 $params['content_type'] = 'multipart/mixed';
429 return new Mail_mimePart('', $params);
433 * Adds an alternative part to a mime_part object
435 function &_addAlternativePart(&$obj)
437 $params['content_type'] = 'multipart/alternative';
438 if (is_object($obj)) {
439 return $obj->addSubpart('', $params);
440 } else {
441 return new Mail_mimePart('', $params);
446 * Adds a html subpart to a mime_part object
448 function &_addRelatedPart(&$obj)
450 $params['content_type'] = 'multipart/related';
451 if (is_object($obj)) {
452 return $obj->addSubpart('', $params);
453 } else {
454 return new Mail_mimePart('', $params);
459 * Adds an html image subpart to a mime_part object
461 function &_addHtmlImagePart(&$obj, $value)
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);
472 * Adds an attachment subpart to a mime_part object
474 function &_addAttachmentPart(&$obj, $value)
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);
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:
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
504 function buildMessage($params = array())
506 if (!empty($params)) {
507 while (list($key, $value) = each($params)) {
508 $this->build_params[$key] = $value;
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);
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;
524 switch (true) {
525 case $text AND !$attachments:
526 $message = &$this->_addTextPart($null, $this->text);
527 break;
529 case !$text AND $attachments AND !$html:
530 $message = &$this->_addMixedPart();
532 for ($i=0; $i<count($this->attachments); $i++) {
533 $this->_addAttachmentPart($message, $this->attachments[$i]);
535 break;
537 case $text AND $attachments:
538 $message = &$this->_addMixedPart();
539 $this->_addTextPart($message, $this->text);
541 for ($i=0; $i<count($this->attachments); $i++) {
542 $this->_addAttachmentPart($message, $this->attachments[$i]);
544 break;
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);
554 break;
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;
565 $this->_addHtmlPart($related);
566 for ($i=0; $i<count($this->html_images); $i++) {
567 $this->_addHtmlImagePart($related, $this->html_images[$i]);
569 break;
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);
580 for ($i=0; $i<count($this->attachments); $i++) {
581 $this->_addAttachmentPart($message, $this->attachments[$i]);
583 break;
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);
594 $this->_addHtmlPart($rel);
595 for ($i=0; $i<count($this->html_images); $i++) {
596 $this->_addHtmlImagePart($rel, $this->html_images[$i]);
598 for ($i=0; $i<count($this->attachments); $i++) {
599 $this->_addAttachmentPart($message, $this->attachments[$i]);
601 break;
605 if (isset($message)) {
606 $output = $message->encode();
607 $this->output = $output['body'];
608 $this->headers = array_merge($this->headers, $output['headers']);
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;
617 $this->is_built = true;
618 return true;
619 } else {
620 return false;
625 * Function to encode a header if necessary
626 * according to RFC2047
628 function _encodeHeader($input, $charset = 'ISO-8859-1')
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);
636 return $input;
640 * Sends the mail.
642 * @param array $recipients
643 * @param string $type OPTIONAL
644 * @return mixed
646 function send($recipients, $type = 'mail')
648 if (!defined('CRLF')) {
649 $this->setCrlf($type == 'mail' ? "\n" : "\r\n");
652 if (!$this->is_built) {
653 $this->buildMessage();
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']);
664 // Get flat representation of headers
665 foreach ($this->headers as $name => $value) {
666 $headers[] = $name . ': ' . $this->_encodeHeader($value, $this->build_params['head_charset']);
669 $to = $this->_encodeHeader(implode(', ', $recipients), $this->build_params['head_charset']);
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));
677 // Reset the subject in case mail is resent
678 if ($subject !== '') {
679 $this->headers['Subject'] = $subject;
682 // Return
683 return $result;
684 break;
686 case 'smtp':
687 require_once(dirname(__FILE__) . '/smtp.php');
688 require_once(dirname(__FILE__) . '/RFC822.php');
689 $smtp = &smtp::connect($this->smtp_params);
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);
698 unset($addresses); // These are reused
699 unset($address); // These are reused
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);
710 if ($name == 'Bcc') {
711 continue;
713 $headers[] = $name . ': ' . $this->_encodeHeader($value, $this->build_params['head_charset']);
715 // Add To header based on $recipients argument
716 $headers[] = 'To: ' . $this->_encodeHeader(implode(', ', $recipients), $this->build_params['head_charset']);
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;
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'];
733 // Send it
734 if (!$smtp->send($send_params)) {
735 $this->errors = $smtp->errors;
736 return false;
738 return true;
739 break;
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.
750 function getRFC822($recipients)
752 // Make up the date header as according to RFC822
753 $this->setHeader('Date', date('D, d M y H:i:s O'));
755 if (!defined('CRLF')) {
756 $this->setCrlf($type == 'mail' ? "\n" : "\r\n");
759 if (!$this->is_built) {
760 $this->buildMessage();
763 // Return path ?
764 if (isset($this->return_path)) {
765 $headers[] = 'Return-Path: ' . $this->return_path;
768 // Get flat representation of headers
769 foreach ($this->headers as $name => $value) {
770 $headers[] = $name . ': ' . $value;
772 $headers[] = 'To: ' . implode(', ', $recipients);
774 return implode(CRLF, $headers) . CRLF . CRLF . $this->output;
776 } // End of class.