default
[ class tree: default ] [ index: default ] [ all elements ]

Source for file Mailer.php

Documentation is available at Mailer.php

  1. <?php
  2.  
  3. /**
  4.  * Mailer objects are responsible for actually sending emails.
  5.  * The default Mailer class will use PHP's mail() fun
  6.  * However, it
  7.  * One Email::set_mailer();
  8.  */
  9. class Mailer extends Object {
  10.     /**
  11.      * Send a plain-text email
  12.      */
  13.     function sendPlain($to$from$subject$plainContent$attachedFiles false$customheaders false{
  14.         return plaintextEmail($to$from$subject$htmlContent$attachedFiles$customheaders);
  15.     }
  16.     
  17.     /**
  18.      * Send a multi-part HTML email
  19.      */
  20.     function sendHTML($to$from$subject$htmlContent$attachedFiles false$customheaders false$plainContent false$inlineImages false{
  21.         return htmlEmail($to$from$subject$htmlContent$attachedFiles$customheaders$plainContent$inlineImages);
  22.     }
  23. }
  24.  
  25. // TO DO: Clean this code up, make it more OO.
  26. // For now, we've just put a clean interface around this dirty code :)
  27.  
  28. /*
  29.  * Sends an email as a both HTML and plaintext
  30.  *   $attachedFiles should be an array of file names
  31.  *    - if you pass the entire $_FILES entry, the user-uploaded filename will be preserved
  32.  *   use $plainContent to override default plain-content generation
  33.  */
  34. function htmlEmail($to$from$subject$htmlContent$attachedFiles false$customheaders false$plainContent false$inlineImages false{
  35.     if ($customheaders && is_array($customheaders== false{
  36.         echo "htmlEmail($to$from$subject, ...) could not send mail: improper \$customheaders passed:<BR>";
  37.         dieprintr($headers);
  38.     }
  39.  
  40.     
  41.     $subjectIsUnicode (strpos($subject,"&#"!== false);
  42.     $bodyIsUnicode (strpos($htmlContent,"&#"!== false);
  43.     $plainEncoding "";
  44.     
  45.     // We generate plaintext content by default, but you can pass custom stuff
  46.     $plainEncoding '';
  47.     if(!$plainContent{
  48.         $plainContent Convert::xml2raw($htmlContent);
  49.         if(isset($bodyIsUnicode&& $bodyIsUnicode$plainEncoding "base64";
  50.     }
  51.  
  52.  
  53.     // If the subject line contains extended characters, we must encode the 
  54.     $subject Convert::xml2raw($subject);
  55.     if(isset($subjectIsUnicode&& $subjectIsUnicode)
  56.         $subject "=?UTF-8?B?" base64_encode($subject"?=";
  57.  
  58.  
  59.     // Make the plain text part
  60.     $headers["Content-Type""text/plain; charset=\"utf-8\"";
  61.     $headers["Content-Transfer-Encoding"$plainEncoding $plainEncoding "quoted-printable";
  62.  
  63.     $plainPart processHeaders($headers($plainEncoding == "base64"chunk_split(base64_encode($plainContent),60wordwrap($plainContent,120));
  64.  
  65.     // Make the HTML part
  66.     $headers["Content-Type""text/html; charset=\"utf-8\"";
  67.         
  68.     
  69.     // Add basic wrapper tags if the body tag hasn't been given
  70.     if(stripos($htmlContent'<body'=== false{
  71.         $htmlContent =
  72.             "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n" .
  73.             "<HTML><HEAD>\n" .
  74.             "<META http-equiv=Content-Type content=\"text/html; charset=utf-8\">\n" .
  75.             "<STYLE type=3Dtext/css></STYLE>\n\n".
  76.             "</HEAD>\n" .
  77.             "<BODY bgColor=#ffffff>\n" .
  78.                 $htmlContent .
  79.             "\n</BODY>\n" .
  80.             "</HTML>";
  81.     }
  82.  
  83.     if($inlineImages{
  84.         $htmlPart wrapImagesInline($htmlContent);
  85.     else {
  86.         $headers["Content-Transfer-Encoding""quoted-printable";
  87.         $htmlPart processHeaders($headerswordwrap(QuotedPrintable_encode($htmlContent),120));
  88.     }
  89.     
  90.     list($messageBody$messageHeadersencodeMultipart(array($plainPart,$htmlPart)"multipart/alternative");
  91.  
  92.     // Messages with attachments are handled differently
  93.     if($attachedFiles && is_array($attachedFiles)) {
  94.         
  95.         // The first part is the message itself
  96.         $fullMessage processHeaders($messageHeaders$messageBody);
  97.         $messageParts array($fullMessage);
  98.  
  99.         // Include any specified attachments as additional parts
  100.         foreach($attachedFiles as $file{
  101.             if($file['tmp_name'&& $file['name']{
  102.                 $messageParts[encodeFileForEmail($file['tmp_name']$file['name']);
  103.             else {
  104.                 $messageParts[encodeFileForEmail($file);
  105.             }
  106.         }
  107.             
  108.         // We further wrap all of this into another multipart block
  109.         list($fullBody$headersencodeMultipart($messageParts"multipart/mixed");
  110.  
  111.     // Messages without attachments do not require such treatment
  112.     else {
  113.         $headers $messageHeaders;
  114.         $fullBody $messageBody;
  115.     }
  116.  
  117.     // Email headers
  118.     $headers["From"]         validEmailAddr($from);
  119.  
  120.     // Messages with the X-SilverStripeMessageID header can be tracked
  121.     if(isset($customheaders["X-SilverStripeMessageID"]&& defined('BOUNCE_EMAIL')) {
  122.         $bounceAddress BOUNCE_EMAIL;
  123.         // Get the human name from the from address, if there is one
  124.         if(ereg('^([^<>]+)<([^<>])> *$'$from$parts))
  125.             $bounceAddress "$parts[1]<$bounceAddress>";
  126.     else {
  127.         $bounceAddress $from;
  128.     }
  129.     
  130.     // $headers["Sender"]         = $from;
  131.     $headers["X-Mailer"]    X_MAILER;
  132.     if (!isset($customheaders["X-Priority"])) $headers["X-Priority"]    3;
  133.     
  134.     $headers array_merge((array)$headers(array)$customheaders);
  135.  
  136.     // the carbon copy header has to be 'Cc', not 'CC' or 'cc' -- ensure this.
  137.     if (isset($headers['CC'])) $headers['Cc'$headers['CC']unset($headers['CC'])}
  138.     if (isset($headers['cc'])) $headers['Cc'$headers['cc']unset($headers['cc'])}
  139.     
  140.     // the carbon copy header has to be 'Bcc', not 'BCC' or 'bcc' -- ensure this.
  141.     if (isset($headers['BCC'])) {$headers['Bcc']=$headers['BCC']unset($headers['BCC'])}
  142.     if (isset($headers['bcc'])) {$headers['Bcc']=$headers['bcc']unset($headers['bcc'])}
  143.         
  144.     
  145.     // Send the email
  146.     $headers processHeaders($headers);
  147.     $to validEmailAddr($to);
  148.     
  149.     // Try it without the -f option if it fails
  150.     if(!($result @mail($to$subject$fullBody$headers"-f$bounceAddress"))) {
  151.         $result mail($to$subject$fullBody$headers);
  152.     }
  153.     
  154.     return $result;
  155. }
  156.  
  157. /*
  158.  * Send a plain text e-mail
  159.  */
  160. function plaintextEmail($to$from$subject$plainContent$attachedFiles$customheaders false{
  161.     $subjectIsUnicode false;    
  162.     $plainEncoding false// Not ensurely where this is supposed to be set, but defined it false for now to remove php notices
  163.  
  164.     if ($customheaders && is_array($customheaders== false{
  165.         echo "htmlEmail($to$from$subject, ...) could not send mail: improper \$customheaders passed:<BR>";
  166.         dieprintr($headers);
  167.     }
  168.  
  169.     if(strpos($subject,"&#"!== false$subjectIsUnicode true;
  170.  
  171.     // If the subject line contains extended characters, we must encode it
  172.     $subject Convert::xml2raw($subject);
  173.     if($subjectIsUnicode)
  174.         $subject "=?UTF-8?B?" base64_encode($subject"?=";
  175.  
  176.  
  177.     // Make the plain text part
  178.     $headers["Content-Type""text/plain; charset=\"utf-8\"";
  179.     $headers["Content-Transfer-Encoding"$plainEncoding $plainEncoding "quoted-printable";
  180.  
  181.     $plainContent ($plainEncoding == "base64"chunk_split(base64_encode($plainContent),60QuotedPrintable_encode($plainContent);
  182.  
  183.     // Messages with attachments are handled differently
  184.     if(is_array($attachedFiles)) {
  185.         // The first part is the message itself
  186.         $fullMessage processHeaders($headers$plainContent);
  187.         $messageParts array($fullMessage);
  188.  
  189.         // Include any specified attachments as additional parts
  190.         foreach($attachedFiles as $file{
  191.             if($file['tmp_name'&& $file['name']{
  192.                 $messageParts[encodeFileForEmail($file['tmp_name']$file['name']);
  193.             else {
  194.                 $messageParts[encodeFileForEmail($file);
  195.             }
  196.         }
  197.         
  198.  
  199.         // We further wrap all of this into another multipart block
  200.         list($fullBody$headersencodeMultipart($messageParts"multipart/mixed");
  201.  
  202.     // Messages without attachments do not require such treatment
  203.     else {
  204.         $fullBody $plainContent;
  205.     }
  206.  
  207.     // Email headers
  208.     $headers["From"]         validEmailAddr($from);
  209.  
  210.     // Messages with the X-SilverStripeMessageID header can be tracked
  211.     if(isset($customheaders["X-SilverStripeMessageID"]&& defined('BOUNCE_EMAIL')) {        
  212.         $bounceAddress BOUNCE_EMAIL;
  213.         // Get the human name from the from address, if there is one
  214.         if(ereg('^([^<>]+)<([^<>])> *$'$from$parts))
  215.             $bounceAddress "$parts[1]<$bounceAddress>";
  216.     else {
  217.         $bounceAddress $from;
  218.     }
  219.     
  220.     // $headers["Sender"]         = $from;
  221.     $headers["X-Mailer"]    X_MAILER;
  222.     if(!isset($customheaders["X-Priority"])) {
  223.         $headers["X-Priority"]    3;
  224.     }
  225.     
  226.     $headers array_merge((array)$headers(array)$customheaders);
  227.  
  228.     // the carbon copy header has to be 'Cc', not 'CC' or 'cc' -- ensure this.
  229.     if (isset($headers['CC'])) $headers['Cc'$headers['CC']unset($headers['CC'])}
  230.     if (isset($headers['cc'])) $headers['Cc'$headers['cc']unset($headers['cc'])}
  231.         
  232.     // Send the email
  233.     $headers processHeaders($headers);
  234.     $to validEmailAddr($to);
  235.  
  236.     // Try it without the -f option if it fails
  237.     if(!$result @mail($to$subject$fullBody$headers"-f$bounceAddress"))
  238.         $result mail($to$subject$fullBody$headers);
  239.     
  240.     if($result)
  241.         return array($to,$subject,$fullBody,$headers);
  242.         
  243.     return false;
  244. }
  245.  
  246.  
  247. function encodeMultipart($parts$contentType$headers false{
  248.     $separator "----=_NextPart_" ereg_replace('[^0-9]','',rand(10000000000);
  249.  
  250.  
  251.     $headers["MIME-Version""1.0";
  252.     $headers["Content-Type""$contentType; boundary=\"$separator\"";
  253.     $headers["Content-Transfer-Encoding""7bit";
  254.  
  255.     if($contentType == "multipart/alternative"{
  256.         //$baseMessage = "This is an encoded HTML message.  There are two parts: a plain text and an HTML message, open whatever suits you better.";
  257.         $baseMessage "\nThis is a multi-part message in MIME format.";
  258.     else {
  259.         //$baseMessage = "This is a message containing attachments.  The e-mail body is contained in the first attachment";
  260.         $baseMessage "\nThis is a multi-part message in MIME format.";
  261.     }
  262.  
  263.  
  264.     $separator "\n--$separator\n";
  265.     $body "$baseMessage\n.
  266.         $separator implode("\n".$separator$parts"\n" trim($separator"--";
  267.  
  268.     return array($body$headers);
  269. }
  270.  
  271. /*
  272.  * Return a multipart/related e-mail chunk for the given HTML message and its linked images
  273.  * Decodes absolute URLs, accessing the appropriate local images
  274.  */
  275. function wrapImagesInline($htmlContent{
  276.     global $_INLINED_IMAGES;
  277.     $_INLINED_IMAGES null;
  278.     
  279.     $replacedContent imageRewriter($htmlContent'wrapImagesInline_rewriter($URL)');
  280.     
  281.     
  282.     // Make the HTML part
  283.     $headers["Content-Type""text/html; charset=\"utf-8\"";
  284.     $headers["Content-Transfer-Encoding""quoted-printable";
  285.     $multiparts[processHeaders($headersQuotedPrintable_encode($replacedContent));
  286.     
  287.     // Make all the image parts        
  288.     global $_INLINED_IMAGES;
  289.     foreach($_INLINED_IMAGES as $url => $cid{
  290.         $multiparts[encodeFileForEmail($urlfalse"inline""Content-ID: <$cid>\n");        
  291.     }
  292.  
  293.     // Merge together in a multipart
  294.     list($body$headersencodeMultipart($multiparts"multipart/related");
  295.     return processHeaders($headers$body);
  296. }
  297. function wrapImagesInline_rewriter($url{
  298.     $url relativiseURL($url);
  299.     
  300.     global $_INLINED_IMAGES;
  301.     if(!$_INLINED_IMAGES[$url]{
  302.         $identifier "automatedmessage." rand(1000,1000000000"@silverstripe.com";
  303.         $_INLINED_IMAGES[$url$identifier;
  304.     }
  305.     return "cid:" $_INLINED_IMAGES[$url];
  306.     
  307. }
  308.  
  309. /*
  310.  * Combine headers w/ the body into a single string
  311.  */
  312. function processHeaders($headers$body false{
  313.     $res '';
  314.     if(is_array($headers)) while(list($k$veach($headers))
  315.         $res .= "$k$v\n";
  316.     if($body$res .= "\n$body";
  317.     return $res;
  318. }
  319.  
  320. /*
  321.  * Encode the contents of a file for emailing, including headers
  322.  */
  323. function encodeFileForEmail($file$destFileName false$disposition "attachment"$extraHeaders ""{    
  324.     if(!$file{
  325.         user_error("encodeFileForEmail: not passed a filename and/or data"E_USER_WARNING);
  326.         return;
  327.     }
  328.     
  329.     if (is_string($file)) {
  330.         $file array('filename' => $file);
  331.         $fh fopen($file['filename']"rb");
  332.         if ($fh{
  333.             while(!feof($fh)) $file['contents'.= fread($fh10000);    
  334.             fclose($fh);
  335.         }
  336.     }
  337.  
  338.     // Build headers, including content type
  339.     if(!$destFileName$base basename($file['filename']);
  340.     else $base $destFileName;
  341.  
  342.     $mimeType $file['mimetype'$file['mimetype'getMimeType($file['filename']);
  343.     if(!$mimeType$mimeType "application/unknown";
  344.         
  345.     // Encode for emailing
  346.     if (substr($file['mimetype']04!= 'text'{
  347.         $encoding "base64";
  348.         $file['contents'chunk_split(base64_encode($file['contents']));
  349.     else {
  350.         // This mime type is needed, otherwise some clients will show it as an inline attachment
  351.         $mimeType 'application/octet-stream';
  352.         $encoding "quoted-printable";        
  353.         $file['contents'QuotedPrintable_encode($file['contents']);        
  354.     }
  355.  
  356.     $headers "Content-type: $mimeType;\n\tname=\"$base\"\n".
  357.                          "Content-Transfer-Encoding: $encoding\n".
  358.                          "Content-Disposition: $disposition;\n\tfilename=\"$base\"\n$extraHeaders "\n";
  359.  
  360.     // Return completed packet
  361.     return $headers $file['contents'];
  362. }
  363.  
  364. function QuotedPrintable_encode($quotprint{        
  365.         $quotprint = (string) str_replace('\r\n',chr(13).chr(10),$quotprint);
  366.         $quotprint = (string) str_replace('\n',  chr(13).chr(10),$quotprint);
  367.         $quotprint = (string) preg_replace("~([\x01-\x1F\x3D\x7F-\xFF])~e""sprintf('=%02X', ord('\\1'))"$quotprint);
  368.         //$quotprint = (string) str_replace('\=0D=0A',"=0D=0A",$quotprint);
  369.         $quotprint = (string) str_replace('=0D=0A',"\n",$quotprint);    
  370.         $quotprint = (string) str_replace('=0A=0D',"\n",$quotprint);    
  371.         $quotprint = (string) str_replace('=0D',"\n",$quotprint);    
  372.         $quotprint = (string) str_replace('=0A',"\n",$quotprint);    
  373.         return (string) $quotprint;
  374. }
  375.  
  376. function validEmailAddr($emailAddress{
  377.     $emailAddress trim($emailAddress);
  378.     $angBrack strpos($emailAddress'<');
  379.     
  380.     if($angBrack === 0{
  381.         $emailAddress substr($emailAddress1strpos($emailAddress,'>')-1);
  382.         
  383.     else if($angBrack{        
  384.         $emailAddress str_replace('@'''substr($emailAddress0$angBrack))
  385.                             .substr($emailAddress$angBrack);
  386.     }
  387.     
  388.     return $emailAddress;
  389. }
  390.  
  391. /*
  392.  * Get mime type based on extension
  393.  */
  394. function getMimeType($filename{
  395.     global $global_mimetypes;
  396.     if(!$global_mimetypesloadMimeTypes();
  397.     $ext strtolower(substr($filename,strrpos($filename,'.')+1));
  398.     return $global_mimetypes[$ext];
  399. }
  400.  
  401. /*
  402.  * Load the mime-type data from the system file
  403.  */
  404. function loadMimeTypes({
  405.     $mimetypePathCustom '/etc/mime.types';
  406.     $mimetypePathGeneric Director::baseFolder('/sapphire/email/mime.types';
  407.     $mimeTypes file_exists($mimetypePathGeneric?  file($mimetypePathGenericfile($mimetypePathCustom);
  408.     foreach($mimeTypes as $typeSpec{
  409.         if(($typeSpec trim($typeSpec)) && substr($typeSpec,0,1!= "#"{
  410.             $parts split("[ \t\r\n]+"$typeSpec);
  411.             if(sizeof($parts1{
  412.                 $mimeType array_shift($parts);
  413.                 foreach($parts as $ext{
  414.                     $ext strtolower($ext);
  415.                     $mimeData[$ext$mimeType;
  416.                 }
  417.             }
  418.         }
  419.     }
  420.  
  421.     global $global_mimetypes;
  422.     $global_mimetypes $mimeData;
  423.     return $mimeData;
  424. }

blog comments powered by Disqus
Documentation generated on Tue, 13 May 2008 06:35:04 +1200 by phpDocumentor 1.3.2