00001 <?php
00015 require_once(dirname(__FILE__) . '/mimePart.php');
00016
00017 class Rmail
00018 {
00023 private $html;
00024
00029 private $text;
00030
00035 private $output;
00036
00041 private $html_images;
00042
00047 private $image_types;
00048
00053 private $build_params;
00054
00059 private $attachments;
00060
00065 private $headers;
00066
00072 private $return_path;
00073
00078 private $smtp_params;
00079
00084 private $sendmail_path;
00085
00089 public function __construct()
00090 {
00094 $this->attachments = array();
00095 $this->html_images = array();
00096 $this->headers = array();
00097 $this->text = '';
00098 $this->sendmail_path = '/usr/lib/sendmail -ti';
00099
00105 $this->image_types = array('gif' => 'image/gif',
00106 'jpg' => 'image/jpeg',
00107 'jpeg' => 'image/jpeg',
00108 'jpe' => 'image/jpeg',
00109 'bmp' => 'image/bmp',
00110 'png' => 'image/png',
00111 'tif' => 'image/tiff',
00112 'tiff' => 'image/tiff',
00113 'swf' => 'application/x-shockwave-flash');
00114
00118 $this->build_params['html_encoding'] = new QPrintEncoding();
00119 $this->build_params['text_encoding'] = new SevenBitEncoding();
00120 $this->build_params['html_charset'] = 'ISO-8859-1';
00121 $this->build_params['text_charset'] = 'ISO-8859-1';
00122 $this->build_params['head_charset'] = 'ISO-8859-1';
00123 $this->build_params['text_wrap'] = 998;
00124
00128 if (!empty($_SERVER['HTTP_HOST'])) {
00129 $helo = $_SERVER['HTTP_HOST'];
00130
00131 } elseif (!empty($_SERVER['SERVER_NAME'])) {
00132 $helo = $_SERVER['SERVER_NAME'];
00133
00134 } else {
00135 $helo = 'localhost';
00136 }
00137
00138 $this->smtp_params['host'] = 'localhost';
00139 $this->smtp_params['port'] = 25;
00140 $this->smtp_params['helo'] = $helo;
00141 $this->smtp_params['auth'] = false;
00142 $this->smtp_params['user'] = '';
00143 $this->smtp_params['pass'] = '';
00144
00148 $this->headers['MIME-Version'] = '1.0';
00149 $this->headers['X-Mailer'] = 'Rmail <http://www.phpguru.org/>';
00150 }
00151
00159 public function setCRLF($crlf = "\n")
00160 {
00161 if (!defined('CRLF')) {
00162 define('CRLF', $crlf, true);
00163 }
00164
00165 if (!defined('MAIL_MIMEPART_CRLF')) {
00166 define('MAIL_MIMEPART_CRLF', $crlf, true);
00167 }
00168 }
00169
00180 public function setSMTPParams($host = null, $port = null, $helo = null, $auth = null, $user = null, $pass = null)
00181 {
00182 if (!is_null($host)) $this->smtp_params['host'] = $host;
00183 if (!is_null($port)) $this->smtp_params['port'] = $port;
00184 if (!is_null($helo)) $this->smtp_params['helo'] = $helo;
00185 if (!is_null($auth)) $this->smtp_params['auth'] = $auth;
00186 if (!is_null($user)) $this->smtp_params['user'] = $user;
00187 if (!is_null($pass)) $this->smtp_params['pass'] = $pass;
00188 }
00189
00195 public function setSendmailPath($path)
00196 {
00197 $this->sendmail_path = $path;
00198 }
00199
00205 public function setTextEncoding(iEncoding $encoding)
00206 {
00207 $this->build_params['text_encoding'] = $encoding;
00208 }
00209
00215 public function setHTMLEncoding(iEncoding $encoding)
00216 {
00217 $this->build_params['html_encoding'] = $encoding;
00218 }
00219
00225 public function setTextCharset($charset = 'ISO-8859-1')
00226 {
00227 $this->build_params['text_charset'] = $charset;
00228 }
00229
00235 public function setHTMLCharset($charset = 'ISO-8859-1')
00236 {
00237 $this->build_params['html_charset'] = $charset;
00238 }
00239
00245 public function setHeadCharset($charset = 'ISO-8859-1')
00246 {
00247 $this->build_params['head_charset'] = $charset;
00248 }
00249
00255 public function setTextWrap($count = 998)
00256 {
00257 $this->build_params['text_wrap'] = $count;
00258 }
00259
00266 public function setHeader($name, $value)
00267 {
00268 $this->headers[$name] = $value;
00269 }
00270
00278 public function setReceipt($email)
00279 {
00280 $this->headers['Disposition-Notification-To'] = $email;
00281 }
00282
00288 public function setSubject($subject)
00289 {
00290 $this->headers['Subject'] = $subject;
00291 }
00292
00298 public function setFrom($from)
00299 {
00300 $this->headers['From'] = $from;
00301 }
00302
00310 public function setPriority($priority = 'normal')
00311 {
00312 switch (strtolower($priority)) {
00313 case 'high':
00314 case '1':
00315 $this->headers['X-Priority'] = '1';
00316 $this->headers['X-MSMail-Priority'] = 'High';
00317 break;
00318
00319 case 'normal':
00320 case '3':
00321 $this->headers['X-Priority'] = '3';
00322 $this->headers['X-MSMail-Priority'] = 'Normal';
00323 break;
00324
00325 case 'low':
00326 case '5':
00327 $this->headers['X-Priority'] = '5';
00328 $this->headers['X-MSMail-Priority'] = 'Low';
00329 break;
00330 }
00331 }
00332
00338 public function setReturnPath($return_path)
00339 {
00340 $this->return_path = $return_path;
00341 }
00342
00348 public function setCc($cc)
00349 {
00350 $this->headers['Cc'] = $cc;
00351 }
00352
00358 public function setBcc($bcc)
00359 {
00360 $this->headers['Bcc'] = $bcc;
00361 }
00362
00369 public function setText($text)
00370 {
00371 $this->text = $text;
00372 }
00373
00382 function setHTML($html, $images_dir = null)
00383 {
00384 $this->html = $html;
00385
00386 if (!empty($images_dir)) {
00387 $this->findHtmlImages($images_dir);
00388 }
00389 }
00390
00402 private function findHtmlImages($images_dir)
00403 {
00404
00405 $extensions = array_keys($this->image_types);
00406
00407 preg_match_all('/(?:"|\')([^"\']+\.('.implode('|', $extensions).'))(?:"|\')/Ui', $this->html, $matches);
00408
00409 foreach ($matches[1] as $m) {
00410 if (file_exists($images_dir . $m)) {
00411 $html_images[] = $m;
00412 $this->html = str_replace($m, basename($m), $this->html);
00413 }
00414 }
00415
00419 if (!empty($html_images)) {
00420
00421
00422 $html_images = array_unique($html_images);
00423 sort($html_images);
00424
00425 foreach ($html_images as $img) {
00426 if ($image = file_get_contents($images_dir . $img)) {
00427 $ext = preg_replace('#^.*\.(\w{3,4})$#e', 'strtolower("$1")', $img);
00428 $content_type = $this->image_types[$ext];
00429 $this->addEmbeddedImage(new stringEmbeddedImage($image, basename($img), $content_type));
00430 }
00431 }
00432 }
00433 }
00434
00441 public function addEmbeddedImage($embeddedImage)
00442 {
00443 $embeddedImage->cid = md5(uniqid(time()));
00444
00445 $this->html_images[] = $embeddedImage;
00446 }
00447
00448
00454 public function addAttachment($attachment)
00455 {
00456 $this->attachments[] = $attachment;
00457 }
00458
00465 private function addTextPart(&$message)
00466 {
00467 $params['content_type'] = 'text/plain';
00468 $params['encoding'] = $this->build_params['text_encoding']->getType();
00469 $params['charset'] = $this->build_params['text_charset'];
00470
00471 if (!empty($message)) {
00472 $message->addSubpart($this->text, $params);
00473 } else {
00474 $message = new Mail_mimePart($this->text, $params);
00475 }
00476 }
00477
00484 private function addHtmlPart(&$message)
00485 {
00486 $params['content_type'] = 'text/html';
00487 $params['encoding'] = $this->build_params['html_encoding']->getType();
00488 $params['charset'] = $this->build_params['html_charset'];
00489
00490 if (!empty($message)) {
00491 $message->addSubpart($this->html, $params);
00492 } else {
00493 $message = new Mail_mimePart($this->html, $params);
00494 }
00495 }
00496
00502 private function addMixedPart(&$message)
00503 {
00504 $params['content_type'] = 'multipart/mixed';
00505
00506 $message = new Mail_mimePart('', $params);
00507 }
00508
00515 private function addAlternativePart(&$message)
00516 {
00517 $params['content_type'] = 'multipart/alternative';
00518
00519 if (!empty($message)) {
00520 return $message->addSubpart('', $params);
00521 } else {
00522 $message = new Mail_mimePart('', $params);
00523 }
00524 }
00525
00532 private function addRelatedPart(&$message)
00533 {
00534 $params['content_type'] = 'multipart/related';
00535
00536 if (!empty($message)) {
00537 return $message->addSubpart('', $params);
00538 } else {
00539 $message = new Mail_mimePart('', $params);
00540 }
00541 }
00542
00548 private function addHtmlImageParts(&$message)
00549 {
00550 foreach ($this->html_images as $value) {
00551 $params['content_type'] = $value->contentType;
00552 $params['encoding'] = $value->encoding->getType();
00553 $params['disposition'] = 'inline';
00554 $params['dfilename'] = $value->name;
00555 $params['cid'] = $value->cid;
00556
00557 $message->addSubpart($value->data, $params);
00558 }
00559 }
00560
00566 private function addAttachmentParts(&$message)
00567 {
00568 foreach ($this->attachments as $value) {
00569 $params['content_type'] = $value->contentType;
00570 $params['encoding'] = $value->encoding->getType();
00571 $params['disposition'] = 'attachment';
00572 $params['dfilename'] = $value->name;
00573
00574 $message->addSubpart($value->data, $params);
00575 }
00576 }
00577
00581 private function build()
00582 {
00583 if (!empty($this->html_images)) {
00584 foreach ($this->html_images as $value) {
00585 $quoted = preg_quote($value->name);
00586 $cid = preg_quote($value->cid);
00587
00588 $this->html = preg_replace("#src=\"$quoted\"|src='$quoted'#", "src=\"cid:$cid\"", $this->html);
00589 $this->html = preg_replace("#background=\"$quoted\"|background='$quoted'#", "background=\"cid:$cid\"", $this->html);
00590 }
00591 }
00592
00593 $message = null;
00594 $attachments = !empty($this->attachments);
00595 $html_images = !empty($this->html_images);
00596 $html = !empty($this->html);
00597 $text = !$html;
00598
00599 switch (true) {
00600 case $text:
00601 $message = null;
00602 if ($attachments) {
00603 $this->addMixedPart($message);
00604 }
00605
00606 $this->addTextPart($message);
00607
00608
00609 $this->addAttachmentParts($message);
00610 break;
00611
00612 case $html AND !$attachments AND !$html_images:
00613 $this->addAlternativePart($message);
00614
00615 $this->addTextPart($message);
00616 $this->addHtmlPart($message);
00617 break;
00618
00619 case $html AND !$attachments AND $html_images:
00620 $this->addRelatedPart($message);
00621 $alt = $this->addAlternativePart($message);
00622
00623 $this->addTextPart($alt);
00624 $this->addHtmlPart($alt);
00625
00626
00627 $this->addHtmlImageParts($message);
00628 break;
00629
00630 case $html AND $attachments AND !$html_images:
00631 $this->addMixedPart($message);
00632 $alt = $this->addAlternativePart($message);
00633
00634 $this->addTextPart($alt);
00635 $this->addHtmlPart($alt);
00636
00637
00638 $this->addAttachmentParts($message);
00639 break;
00640
00641 case $html AND $attachments AND $html_images:
00642 $this->addMixedPart($message);
00643 $rel = $this->addRelatedPart($message);
00644 $alt = $this->addAlternativePart($rel);
00645
00646 $this->addTextPart($alt);
00647 $this->addHtmlPart($alt);
00648
00649
00650 $this->addHtmlImageParts($rel);
00651
00652
00653 $this->addAttachmentParts($message);
00654 break;
00655
00656 }
00657
00658 if (isset($message)) {
00659 $output = $message->encode();
00660 $this->output = $output['body'];
00661 $this->headers = array_merge($this->headers, $output['headers']);
00662
00663
00664 if (!empty($_SERVER['HTTP_HOST'])) {
00665 $hostname = $_SERVER['HTTP_HOST'];
00666
00667 } else if (!empty($_SERVER['SERVER_NAME'])) {
00668 $hostname = $_SERVER['SERVER_NAME'];
00669
00670 } else if (!empty($_ENV['HOSTNAME'])) {
00671 $hostname = $_ENV['HOSTNAME'];
00672
00673 } else {
00674 $hostname = 'localhost';
00675 }
00676
00677 $message_id = sprintf('<%s.%s@%s>', base_convert(time(), 10, 36), base_convert(rand(), 10, 36), $hostname);
00678 $this->headers['Message-ID'] = $message_id;
00679
00680 return true;
00681 } else {
00682 return false;
00683 }
00684 }
00685
00694 private function encodeHeader($input, $charset = 'ISO-8859-1')
00695 {
00696 preg_match_all('/(\w*[\x80-\xFF]+\w*)/', $input, $matches);
00697 foreach ($matches[1] as $value) {
00698 $replacement = preg_replace('/([\x80-\xFF])/e', '"=" . strtoupper(dechex(ord("\1")))', $value);
00699 $input = str_replace($value, '=?' . $charset . '?Q?' . $replacement . '?=', $input);
00700 }
00701
00702 return $input;
00703 }
00704
00712 public function send($recipients, $type = 'mail')
00713 {
00714 if (!defined('CRLF')) {
00715 $this->setCRLF( ($type == 'mail' OR $type == 'sendmail') ? "\n" : "\r\n");
00716 }
00717
00718 $this->build();
00719
00720 switch ($type) {
00721 case 'mail':
00722 $subject = '';
00723 if (!empty($this->headers['Subject'])) {
00724 $subject = $this->encodeHeader($this->headers['Subject'], $this->build_params['head_charset']);
00725 unset($this->headers['Subject']);
00726 }
00727
00728
00729 foreach ($this->headers as $name => $value) {
00730 $headers[] = $name . ': ' . $this->encodeHeader($value, $this->build_params['head_charset']);
00731 }
00732
00733 $to = $this->encodeHeader(implode(', ', $recipients), $this->build_params['head_charset']);
00734
00735 if (!empty($this->return_path)) {
00736 $result = mail($to, $subject, $this->output, implode(CRLF, $headers), '-f' . $this->return_path);
00737 } else {
00738 $result = mail($to, $subject, $this->output, implode(CRLF, $headers));
00739 }
00740
00741
00742 if ($subject !== '') {
00743 $this->headers['Subject'] = $subject;
00744 }
00745
00746
00747
00748 return $result;
00749 break;
00750
00751 case 'sendmail':
00752
00753 foreach ($this->headers as $name => $value) {
00754 $headers[] = $name . ': ' . $this->encodeHeader($value, $this->build_params['head_charset']);
00755 }
00756
00757
00758 $headers[] = 'To: ' . $this->encodeHeader(implode(', ', $recipients), $this->build_params['head_charset']);
00759
00760
00761 $returnPath = '';
00762 if (!empty($this->return_path)) {
00763 $returnPath = '-f' . $this->return_path;
00764 }
00765
00766 $pipe = popen($this->sendmail_path . " " . $returnPath, 'w');
00767 $bytes = fputs($pipe, implode(CRLF, $headers) . CRLF . CRLF . $this->output);
00768 $r = pclose($pipe);
00769
00770 return $r;
00771 break;
00772
00773 case 'smtp':
00774 require_once(dirname(__FILE__) . '/smtp.php');
00775 require_once(dirname(__FILE__) . '/RFC822.php');
00776 $smtp = &smtp::connect($this->smtp_params);
00777
00778
00779 foreach ($recipients as $recipient) {
00780 $addresses = Mail_RFC822::parseAddressList($recipient, $this->smtp_params['helo'], null, false);
00781 foreach ($addresses as $address) {
00782 $smtp_recipients[] = sprintf('%s@%s', $address->mailbox, $address->host);
00783 }
00784 }
00785 unset($addresses);
00786 unset($address);
00787
00788
00789
00790 foreach ($this->headers as $name => $value) {
00791 if ($name == 'Cc' OR $name == 'Bcc') {
00792 $addresses = Mail_RFC822::parseAddressList($value, $this->smtp_params['helo'], null, false);
00793 foreach ($addresses as $address) {
00794 $smtp_recipients[] = sprintf('%s@%s', $address->mailbox, $address->host);
00795 }
00796 }
00797 if ($name == 'Bcc') {
00798 continue;
00799 }
00800 $headers[] = $name . ': ' . $this->encodeHeader($value, $this->build_params['head_charset']);
00801 }
00802
00803 $headers[] = 'To: ' . $this->encodeHeader(implode(', ', $recipients), $this->build_params['head_charset']);
00804
00805
00806 $send_params['headers'] = $headers;
00807 $send_params['recipients'] = array_values(array_unique($smtp_recipients));
00808 $send_params['body'] = $this->output;
00809
00810
00811 if (isset($this->return_path)) {
00812 $send_params['from'] = $this->return_path;
00813 } elseif (!empty($this->headers['From'])) {
00814 $from = Mail_RFC822::parseAddressList($this->headers['From']);
00815 $send_params['from'] = sprintf('%s@%s', $from[0]->mailbox, $from[0]->host);
00816 } else {
00817 $send_params['from'] = 'postmaster@' . $this->smtp_params['helo'];
00818 }
00819
00820
00821 if (!$smtp->send($send_params)) {
00822 $this->errors = $smtp->getErrors();
00823 return false;
00824 }
00825 return true;
00826 break;
00827 }
00828 }
00829
00840 public function getRFC822($recipients, $type = 'mail')
00841 {
00842
00843 $this->setHeader('Date', date('D, d M y H:i:s O'));
00844
00845 if (!defined('CRLF')) {
00846 $this->setCRLF($type == 'mail' ? "\n" : "\r\n");
00847 }
00848
00849 $this->build();
00850
00851
00852 if (isset($this->return_path)) {
00853 $headers[] = 'Return-Path: ' . $this->return_path;
00854 }
00855
00856
00857 foreach ($this->headers as $name => $value) {
00858 $headers[] = $name . ': ' . $value;
00859 }
00860 $headers[] = 'To: ' . implode(', ', $recipients);
00861
00862 return implode(CRLF, $headers) . CRLF . CRLF . $this->output;
00863 }
00864 }
00865
00866
00870 class attachment
00871 {
00876 public $data;
00877
00882 public $name;
00883
00888 public $contentType;
00889
00894 public $encoding;
00895
00904 public function __construct($data, $name, $contentType, iEncoding $encoding)
00905 {
00906 $this->data = $data;
00907 $this->name = $name;
00908 $this->contentType = $contentType;
00909 $this->encoding = $encoding;
00910 }
00911 }
00912
00913
00917 class fileAttachment extends attachment
00918 {
00926 public function __construct($filename, $contentType = 'application/octet-stream', $encoding = null)
00927 {
00928 $encoding = is_null($encoding) ? new Base64Encoding() : $encoding;
00929
00930 parent::__construct(file_get_contents($filename), basename($filename), $contentType, $encoding);
00931 }
00932 }
00933
00934
00939 class stringAttachment extends attachment
00940 {
00949 public function __construct($data, $name = '', $contentType = 'application/octet-stream', $encoding = null)
00950 {
00951 $encoding = is_null($encoding) ? new Base64Encoding() : $encoding;
00952
00953 parent::__construct($data, $name, $contentType, $encoding);
00954 }
00955 }
00956
00957
00961 class fileEmbeddedImage extends fileAttachment
00962 {
00963 }
00964
00965
00969 class stringEmbeddedImage extends stringAttachment
00970 {
00971 }
00972
00973
00980 interface iEncoding
00981 {
00982 public function encode($input);
00983 public function getType();
00984 }
00985
00986
00990 class Base64Encoding implements iEncoding
00991 {
00992
00993
00994
00995
00996
00997
00998 public function encode($input)
00999 {
01000 return rtrim(chunk_split(base64_encode($input), 76, defined('MAIL_MIME_PART_CRLF') ? MAIL_MIME_PART_CRLF : "\r\n"));
01001 }
01002
01006 public function getType()
01007 {
01008 return 'base64';
01009 }
01010 }
01011
01012
01016 class QPrintEncoding implements iEncoding
01017 {
01018
01019
01020
01021
01022
01023
01024 public function encode($input)
01025 {
01026
01027 $input = preg_replace('/([^\x20\x21-\x3C\x3E-\x7E\x0A\x0D])/e', 'sprintf("=%02X", ord("\1"))', $input);
01028 $inputLen = strlen($input);
01029 $outLines = array();
01030 $output = '';
01031
01032 $lines = preg_split('/\r?\n/', $input);
01033
01034
01035 for ($i=0; $i<count($lines); $i++) {
01036
01037 if (strlen($lines[$i]) > $lineMax) {
01038 $outLines[] = substr($lines[$i], 0, $lineMax - 1) . "=";
01039 $lines[$i] = substr($lines[$i], $lineMax - 1);
01040 $i--;
01041 } else {
01042 $outLines[] = $lines[$i];
01043 }
01044 }
01045
01046
01047 $output = preg_replace('/(\x20+)$/me', 'str_replace(" ", "=20", "\1")', $outLines);
01048
01049 return implode("\r\n", $output);
01050 }
01051
01055 public function getType()
01056 {
01057 return 'quoted-printable';
01058 }
01059 }
01060
01061
01065 class SevenBitEncoding implements iEncoding
01066 {
01067
01068
01069
01070
01071
01072
01073 public function encode($input)
01074 {
01075 return $input;
01076 }
01077
01081 public function getType()
01082 {
01083 return '7bit';
01084 }
01085 }
01086
01087
01091 class EightBitEncoding implements iEncoding
01092 {
01093
01094
01095
01096
01097
01098
01099 public function encode($input)
01100 {
01101 return $input;
01102 }
01103
01107 public function getType()
01108 {
01109 return '8bit';
01110 }
01111 }
01112 ?>