Source for file Mailer.php
Documentation is available at Mailer.php
* Mailer objects are responsible for actually sending emails.
* The default Mailer class will use PHP's mail() fun
* One Email::set_mailer();
* Send a plain-text email
function sendPlain($to, $from, $subject, $plainContent, $attachedFiles = false, $customheaders = false) {
return plaintextEmail($to, $from, $subject, $htmlContent, $attachedFiles, $customheaders);
* Send a multi-part HTML email
function sendHTML($to, $from, $subject, $htmlContent, $attachedFiles = false, $customheaders = false, $plainContent = false, $inlineImages = false) {
return htmlEmail($to, $from, $subject, $htmlContent, $attachedFiles, $customheaders, $plainContent, $inlineImages);
// TO DO: Clean this code up, make it more OO.
// For now, we've just put a clean interface around this dirty code :)
* Sends an email as a both HTML and plaintext
* $attachedFiles should be an array of file names
* - if you pass the entire $_FILES entry, the user-uploaded filename will be preserved
* use $plainContent to override default plain-content generation
function htmlEmail($to, $from, $subject, $htmlContent, $attachedFiles = false, $customheaders = false, $plainContent = false, $inlineImages = false) {
if ($customheaders && is_array($customheaders) == false) {
echo "htmlEmail($to, $from, $subject, ...) could not send mail: improper \$customheaders passed:<BR>";
$subjectIsUnicode = (strpos($subject,"&#") !== false);
$bodyIsUnicode = (strpos($htmlContent,"&#") !== false);
// We generate plaintext content by default, but you can pass custom stuff
if(isset ($bodyIsUnicode) && $bodyIsUnicode) $plainEncoding = "base64";
// If the subject line contains extended characters, we must encode the
if(isset ($subjectIsUnicode) && $subjectIsUnicode)
// Make the plain text part
$headers["Content-Type"] = "text/plain; charset=\"utf-8\"";
$headers["Content-Transfer-Encoding"] = $plainEncoding ? $plainEncoding : "quoted-printable";
$headers["Content-Type"] = "text/html; charset=\"utf-8\"";
// Add basic wrapper tags if the body tag hasn't been given
if(stripos($htmlContent, '<body') === false) {
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n" .
"<META http-equiv=Content-Type content=\"text/html; charset=utf-8\">\n" .
"<STYLE type=3Dtext/css></STYLE>\n\n".
"<BODY bgColor=#ffffff>\n" .
$headers["Content-Transfer-Encoding"] = "quoted-printable";
list ($messageBody, $messageHeaders) = encodeMultipart(array($plainPart,$htmlPart), "multipart/alternative");
// Messages with attachments are handled differently
if($attachedFiles && is_array($attachedFiles)) {
// The first part is the message itself
$messageParts = array($fullMessage);
// Include any specified attachments as additional parts
foreach($attachedFiles as $file) {
if($file['tmp_name'] && $file['name']) {
// We further wrap all of this into another multipart block
list ($fullBody, $headers) = encodeMultipart($messageParts, "multipart/mixed");
// Messages without attachments do not require such treatment
$headers = $messageHeaders;
$fullBody = $messageBody;
// Messages with the X-SilverStripeMessageID header can be tracked
if(isset ($customheaders["X-SilverStripeMessageID"]) && defined('BOUNCE_EMAIL')) {
$bounceAddress = BOUNCE_EMAIL;
// Get the human name from the from address, if there is one
if(ereg('^([^<>]+)<([^<>])> *$', $from, $parts))
$bounceAddress = "$parts[1]<$bounceAddress>";
// $headers["Sender"] = $from;
if (!isset ($customheaders["X-Priority"])) $headers["X-Priority"] = 3;
$headers = array_merge((array) $headers, (array) $customheaders);
// the carbon copy header has to be 'Cc', not 'CC' or 'cc' -- ensure this.
if (isset ($headers['CC'])) { $headers['Cc'] = $headers['CC']; unset ($headers['CC']); }
if (isset ($headers['cc'])) { $headers['Cc'] = $headers['cc']; unset ($headers['cc']); }
// the carbon copy header has to be 'Bcc', not 'BCC' or 'bcc' -- ensure this.
if (isset ($headers['BCC'])) {$headers['Bcc']= $headers['BCC']; unset ($headers['BCC']); }
if (isset ($headers['bcc'])) {$headers['Bcc']= $headers['bcc']; unset ($headers['bcc']); }
// Try it without the -f option if it fails
if(!($result = @mail($to, $subject, $fullBody, $headers, "-f$bounceAddress"))) {
$result = mail($to, $subject, $fullBody, $headers);
* Send a plain text e-mail
function plaintextEmail($to, $from, $subject, $plainContent, $attachedFiles, $customheaders = false) {
$subjectIsUnicode = false;
$plainEncoding = false; // Not ensurely where this is supposed to be set, but defined it false for now to remove php notices
if ($customheaders && is_array($customheaders) == false) {
echo "htmlEmail($to, $from, $subject, ...) could not send mail: improper \$customheaders passed:<BR>";
if(strpos($subject,"&#") !== false) $subjectIsUnicode = true;
// If the subject line contains extended characters, we must encode it
// Make the plain text part
$headers["Content-Type"] = "text/plain; charset=\"utf-8\"";
$headers["Content-Transfer-Encoding"] = $plainEncoding ? $plainEncoding : "quoted-printable";
// Messages with attachments are handled differently
// The first part is the message itself
$messageParts = array($fullMessage);
// Include any specified attachments as additional parts
foreach($attachedFiles as $file) {
if($file['tmp_name'] && $file['name']) {
// We further wrap all of this into another multipart block
list ($fullBody, $headers) = encodeMultipart($messageParts, "multipart/mixed");
// Messages without attachments do not require such treatment
$fullBody = $plainContent;
// Messages with the X-SilverStripeMessageID header can be tracked
if(isset ($customheaders["X-SilverStripeMessageID"]) && defined('BOUNCE_EMAIL')) {
$bounceAddress = BOUNCE_EMAIL;
// Get the human name from the from address, if there is one
if(ereg('^([^<>]+)<([^<>])> *$', $from, $parts))
$bounceAddress = "$parts[1]<$bounceAddress>";
// $headers["Sender"] = $from;
if(!isset ($customheaders["X-Priority"])) {
$headers["X-Priority"] = 3;
$headers = array_merge((array) $headers, (array) $customheaders);
// the carbon copy header has to be 'Cc', not 'CC' or 'cc' -- ensure this.
if (isset ($headers['CC'])) { $headers['Cc'] = $headers['CC']; unset ($headers['CC']); }
if (isset ($headers['cc'])) { $headers['Cc'] = $headers['cc']; unset ($headers['cc']); }
// Try it without the -f option if it fails
if(!$result = @mail($to, $subject, $fullBody, $headers, "-f$bounceAddress"))
$result = mail($to, $subject, $fullBody, $headers);
return array($to,$subject,$fullBody,$headers);
$separator = "----=_NextPart_" . ereg_replace('[^0-9]','',rand() * 10000000000);
$headers["MIME-Version"] = "1.0";
$headers["Content-Type"] = "$contentType; boundary=\"$separator\"";
$headers["Content-Transfer-Encoding"] = "7bit";
if($contentType == "multipart/alternative") {
//$baseMessage = "This is an encoded HTML message. There are two parts: a plain text and an HTML message, open whatever suits you better.";
$baseMessage = "\nThis is a multi-part message in MIME format.";
//$baseMessage = "This is a message containing attachments. The e-mail body is contained in the first attachment";
$baseMessage = "\nThis is a multi-part message in MIME format.";
$separator = "\n--$separator\n";
$body = "$baseMessage\n" .
$separator . implode("\n". $separator, $parts) . "\n" . trim($separator) . "--";
return array($body, $headers);
* Return a multipart/related e-mail chunk for the given HTML message and its linked images
* Decodes absolute URLs, accessing the appropriate local images
$replacedContent = imageRewriter($htmlContent, 'wrapImagesInline_rewriter($URL)');
$headers["Content-Type"] = "text/html; charset=\"utf-8\"";
$headers["Content-Transfer-Encoding"] = "quoted-printable";
// Make all the image parts
foreach($_INLINED_IMAGES as $url => $cid) {
// Merge together in a multipart
$url = relativiseURL($url);
if(!$_INLINED_IMAGES[$url]) {
$identifier = "automatedmessage." . rand(1000,1000000000) . "@silverstripe.com";
$_INLINED_IMAGES[$url] = $identifier;
return "cid:" . $_INLINED_IMAGES[$url];
* Combine headers w/ the body into a single string
if($body) $res .= "\n$body";
* Encode the contents of a file for emailing, including headers
function encodeFileForEmail($file, $destFileName = false, $disposition = "attachment", $extraHeaders = "") {
user_error("encodeFileForEmail: not passed a filename and/or data", E_USER_WARNING);
$file = array('filename' => $file);
$fh = fopen($file['filename'], "rb");
while(!feof($fh)) $file['contents'] .= fread($fh, 10000);
// Build headers, including content type
if(!$destFileName) $base = basename($file['filename']);
else $base = $destFileName;
$mimeType = $file['mimetype'] ? $file['mimetype'] : getMimeType($file['filename']);
if(!$mimeType) $mimeType = "application/unknown";
if (substr($file['mimetype'], 0, 4) != 'text') {
// This mime type is needed, otherwise some clients will show it as an inline attachment
$mimeType = 'application/octet-stream';
$encoding = "quoted-printable";
$headers = "Content-type: $mimeType;\n\tname=\"$base\"\n".
"Content-Transfer-Encoding: $encoding\n".
"Content-Disposition: $disposition;\n\tfilename=\"$base\"\n" . $extraHeaders . "\n";
// Return completed packet
return $headers . $file['contents'];
$quotprint = (string) preg_replace("~([\x01-\x1F\x3D\x7F-\xFF])~e", "sprintf('=%02X', ord('\\1'))", $quotprint);
//$quotprint = (string) str_replace('\=0D=0A',"=0D=0A",$quotprint);
$quotprint = (string) str_replace('=0D=0A',"\n",$quotprint);
$quotprint = (string) str_replace('=0A=0D',"\n",$quotprint);
$quotprint = (string) str_replace('=0D',"\n",$quotprint);
$quotprint = (string) str_replace('=0A',"\n",$quotprint);
return (string) $quotprint;
$emailAddress = trim($emailAddress);
$angBrack = strpos($emailAddress, '<');
$emailAddress = substr($emailAddress, 1, strpos($emailAddress,'>')- 1);
. substr($emailAddress, $angBrack);
* Get mime type based on extension
global $global_mimetypes;
return $global_mimetypes[$ext];
* Load the mime-type data from the system file
$mimetypePathCustom = '/etc/mime.types';
$mimeTypes = file_exists($mimetypePathGeneric) ? file($mimetypePathGeneric) : file($mimetypePathCustom);
foreach($mimeTypes as $typeSpec) {
if(($typeSpec = trim($typeSpec)) && substr($typeSpec,0,1) != "#") {
$parts = split("[ \t\r\n]+", $typeSpec);
foreach($parts as $ext) {
$mimeData[$ext] = $mimeType;
global $global_mimetypes;
$global_mimetypes = $mimeData;
|