Вход Регистрация
Файл: framework/email/Mailer.php
Строк: 861
<?php

/**
 * Mailer objects are responsible for actually sending emails.
 * The default Mailer class will use PHP's mail() function.
 * 
 * @package framework
 * @subpackage email
 */
class Mailer extends Object {

    
/**
     * Default encoding type for messages. Available options are:
     * - quoted-printable
     * - base64
     *
     * @var string
     * @config
     */
    
private static $default_message_encoding 'quoted-printable';

    
/**
     * Encoding type currently set
     *
     * @var string
     */
    
protected $messageEncoding null;

    
/**
     * Email used for bounces
     *
     * @var string
     * @config
     */
    
private static $default_bounce_email null;

    
/**
     * Email used for bounces
     *
     * @var string
     */
    
protected $bounceEmail null;

    
/**
     * Email used for bounces
     *
     * @return string
     */
    
public function getBounceEmail() {
        return 
$this->bounceEmail
            
?: (defined('BOUNCE_EMAIL') ? BOUNCE_EMAIL null)
            ?: 
self::config()->default_bounce_email;
    }

    
/**
     * Set the email used for bounces
     *
     * @param string $email
     */
    
public function setBounceEmail($email) {
        
$this->bounceEmail $email;
    }

    
/**
     * Get the encoding type used for plain text messages
     *
     * @return string
     */
    
public function getMessageEncoding() {
        return 
$this->messageEncoding ?: static::config()->default_message_encoding;
    }

    
/**
     * Sets encoding type for messages. Available options are:
     * - quoted-printable
     * - base64
     *
     * @param string $encoding
     */
    
public function setMessageEncoding($encoding) {
        
$this->messageEncoding $encoding;
    }

    
/**
     * Encode a message using the given encoding mechanism
     *
     * @param string $message
     * @param string $encoding
     * @return string Encoded $message
     */
    
protected function encodeMessage($message$encoding) {
        switch(
$encoding) {
            case 
'base64':
                return 
chunk_split(base64_encode($message), 60);
            case 
'quoted-printable':
                return 
quoted_printable_encode($message);
            default:
                return 
$message;
        }
    }

    
/**
     * Merge custom headers with default ones
     *
     * @param array $headers Default headers
     * @param array $customHeaders Custom headers
     * @return array Resulting message headers
     */
    
protected function mergeCustomHeaders($headers$customHeaders) {
        
$headers["X-Mailer"] = X_MAILER;
        if(!isset(
$customHeaders["X-Priority"])) {
            
$headers["X-Priority"]    = 3;
        }

        
// Merge!
        
$headers array_merge($headers$customHeaders);

        
// Headers 'Cc' and 'Bcc' need to have the correct case
        
foreach(array('Bcc''Cc') as $correctKey) {
            foreach(
$headers as $key => $value) {
                if(
strcmp($key$correctKey) !== && strcasecmp($key$correctKey) === 0) {
                    
$headers[$correctKey] = $value;
                    unset(
$headers[$key]);
                }
            }
        }

        return 
$headers;
    }

    
/**
     * Send a plain-text email.
     *  
     * @param string $to Email recipient
     * @param string $from Email from
     * @param string $subject Subject text
     * @param string $plainContent Plain text content
     * @param array $attachedFiles List of attached files
     * @param array $customHeaders List of custom headers
     * @return mixed Return false if failure, or list of arguments if success
     */
    
public function sendPlain($to$from$subject$plainContent$attachedFiles = array(), $customHeaders = array()) {
        
// Prepare plain text body
        
$fullBody $this->encodeMessage($plainContent$this->getMessageEncoding());
        
$headers["Content-Type"] = "text/plain; charset=utf-8";
        
$headers["Content-Transfer-Encoding"] = $this->getMessageEncoding();

        
// Send prepared message
        
return $this->sendPreparedMessage($to$from$subject$attachedFiles$customHeaders$fullBody$headers);
    }


    
/**
     * Sends an email as a both HTML and plaintext
     *
     * @param string $to Email recipient
     * @param string $from Email from
     * @param string $subject Subject text
     * @param string $htmlContent HTML Content
     * @param array $attachedFiles List of attachments
     * @param array $customHeaders User specified headers
     * @param string $plainContent Plain text content. If omitted, will be generated from $htmlContent
     * @return mixed Return false if failure, or list of arguments if success
     */
    
public function sendHTML($to$from$subject$htmlContent,
        
$attachedFiles = array(), $customHeaders = array(), $plainContent ''
    
) {
        
// Prepare both Plain and HTML components and merge
        
$plainPart $this->preparePlainSubmessage($plainContent$htmlContent);
        
$htmlPart $this->prepareHTMLSubmessage($htmlContent);
        list(
$fullBody$headers) = $this->encodeMultipart(
            array(
$plainPart$htmlPart),
            
"multipart/alternative"
        
);

        
// Send prepared message
        
return $this->sendPreparedMessage($to$from$subject$attachedFiles$customHeaders$fullBody$headers);
    }

    
/**
     * Send an email of an arbitrary format
     * 
     * @param string $to To
     * @param string $from From
     * @param string $subject Subject
     * @param array $attachedFiles List of attachments
     * @param array $customHeaders User specified headers
     * @param string $fullBody Prepared message
     * @param array $headers Prepared headers
     * @return mixed Return false if failure, or list of arguments if success
     */
    
protected function sendPreparedMessage($to$from$subject$attachedFiles$customHeaders$fullBody$headers) {
        
// If the subject line contains extended characters, we must encode the
        
$subjectEncoded "=?UTF-8?B?" base64_encode($subject) . "?=";
        
$to $this->validEmailAddress($to);
        
$from $this->validEmailAddress($from);

        
// Messages with attachments are handled differently
        
if($attachedFiles) {
            list(
$fullBody$headers) = $this->encodeAttachments($attachedFiles$headers$fullBody);
        }
        
        
// Get bounce email
        
$bounceAddress $this->getBounceEmail() ?: $from;
        if(
preg_match('/^([^<>]*)<([^<>]+)> *$/'$bounceAddress$parts)) $bounceAddress $parts[2];

        
// Get headers
        
$headers["From"] = $from;
        
$headers $this->mergeCustomHeaders($headers$customHeaders);
        
$headersEncoded $this->processHeaders($headers);

        return 
$this->email($to$subjectEncoded$fullBody$headersEncoded$bounceAddress);
    }

    
/**
     * Send the actual email
     * 
     * @param string $to
     * @param string $subjectEncoded
     * @param string $fullBody
     * @param string $headersEncoded
     * @param string $bounceAddress
     * @return mixed Return false if failure, or list of arguments if success
     */
    
protected function email($to$subjectEncoded$fullBody$headersEncoded$bounceAddress) {
        
// Try it without the -f option if it fails
        
$result = @mail($to$subjectEncoded$fullBody$headersEncodedescapeshellarg("-f$bounceAddress"));
        if(!
$result) {
            
$result mail($to$subjectEncoded$fullBody$headersEncoded);
        }
        
        if(
$result) {
            return array(
$to$subjectEncoded$fullBody$headersEncoded$bounceAddress);
        }
            
        return 
false;
    }

    
/**
     * Encode attachments into a message
     *
     * @param array $attachments
     * @param array $headers
     * @param string $body
     * @return array Array containing completed body followed by headers
     */
    
protected function encodeAttachments($attachments$headers$body) {
        
// The first part is the message itself
        
$fullMessage $this->processHeaders($headers$body);
        
$messageParts = array($fullMessage);

        
// Include any specified attachments as additional parts
        
foreach($attachments as $file) {
            if(isset(
$file['tmp_name']) && isset($file['name'])) {
                
$messageParts[] = $this->encodeFileForEmail($file['tmp_name'], $file['name']);
            } else {
                
$messageParts[] = $this->encodeFileForEmail($file);
            }
        }

        
// We further wrap all of this into another multipart block
        
return $this->encodeMultipart($messageParts"multipart/mixed");
    }

    
/**
     * Generate the plainPart of a html message
     *
     * @param string $plainContent Plain body
     * @param string $htmlContent HTML message
     * @return string Encoded headers / message in a single block
     */
    
protected function preparePlainSubmessage($plainContent$htmlContent) {
        
$plainEncoding $this->getMessageEncoding();
        
        
// Generate plain text version if not explicitly given
        
if(!$plainContent$plainContent Convert::xml2raw($htmlContent);

        
// Make the plain text part
        
$headers["Content-Type"] = "text/plain; charset=utf-8";
        
$headers["Content-Transfer-Encoding"] = $plainEncoding;
        
$plainContentEncoded $this->encodeMessage($plainContent$plainEncoding);

        
// Merge with headers
        
return $this->processHeaders($headers$plainContentEncoded);
    }

    
/**
     * Generate the html part of a html message
     *
     * @param string $htmlContent HTML message
     * @return string Encoded headers / message in a single block
     */
    
protected function prepareHTMLSubmessage($htmlContent) {
        
// Add basic wrapper tags if the body tag hasn't been given
        
if(stripos($htmlContent'<body') === false) {
            
$htmlContent =
                
"<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">n" .
                
"<HTML><HEAD>n" .
                
"<META http-equiv="Content-Type" content="text/htmlcharset=utf-8">n" .
                
"<STYLE type="text/css"></STYLE>nn".
                
"</HEAD>n" .
                
"<BODY bgColor="#ffffff">n" .
                    
$htmlContent .
                
"n</BODY>n" .
                
"</HTML>";
        }
        
        
// Make the HTML part
        
$headers["Content-Type"] = "text/html; charset=utf-8";
        
$headers["Content-Transfer-Encoding"] = $this->getMessageEncoding();
        
$htmlContentEncoded $this->encodeMessage($htmlContent$this->getMessageEncoding());

        
// Merge with headers
        
return $this->processHeaders($headers$htmlContentEncoded);
    }

    
/**
     * Encode an array of parts using multipart
     *
     * @param array $parts List of parts
     * @param string $contentType Content-type of parts
     * @param array $headers Existing headers to include in response
     * @return array Array with two items, the body followed by headers
     */
    
protected function encodeMultipart($parts$contentType$headers = array()) {
        
$separator "----=_NextPart_" preg_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.";
        } else {
            
// $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--$separatorn";
        
$body "$baseMessagen.
            
$separator implode("n".$separator$parts) . "n" trim($separator) . "--";

        return array(
$body$headers);
    }


    
/**
     * Add headers to the start of the message
     *
     * @param array $headers
     * @param string $body
     * @return string Resulting message body
     */
    
protected function processHeaders($headers$body '') {
        
$result '';
        foreach(
$headers as $key => $value) {
            
$result .= "$key$valuen";
        }
        if(
$body$result .= "n$body";

        return 
$result;
    }

    
/**
     * Encode the contents of a file for emailing, including headers
     * 
     * $file can be an array, in which case it expects these members:
     *   'filename'        - the filename of the file
     *   'contents'        - the raw binary contents of the file as a string
     *  and can optionally include these members:
     *   'mimetype'        - the mimetype of the file (calculated from filename if missing)
     *   'contentLocation' - the 'Content-Location' header value for the file
     *   
     * $file can also be a string, in which case it is assumed to be the filename
     * 
     * h5. contentLocation
     * 
     * Content Location is one of the two methods allowed for embedding images into an html email. 
     * It's also the simplest, and best supported.
     * 
     * Assume we have an email with this in the body:
     * 
     *   <img src="http://example.com/image.gif" />
     * 
     * To display the image, an email viewer would have to download the image from the web every time 
     * it is displayed. Due to privacy issues, most viewers will not display any images unless 
     * the user clicks 'Show images in this email'. Not optimal.
     * 
     * However, we can also include a copy of this image as an attached file in the email. 
     * By giving it a contentLocation of "http://example.com/image.gif" most email viewers 
     * will use this attached copy instead of downloading it. Better,
     * most viewers will show it without a 'Show images in this email' conformation.
     * 
     * Here is an example of passing this information through Email.php:
     * 
     *   $email = new Email();
     *   $email->attachments[] = array(
     *     'filename' => BASE_PATH . "/themes/mytheme/images/header.gif",
     *     'contents' => file_get_contents(BASE_PATH . "/themes/mytheme/images/header.gif"),
     *     'mimetype' => 'image/gif',
     *     'contentLocation' => Director::absoluteBaseURL() . "/themes/mytheme/images/header.gif"
     *   );
     */
    
protected function encodeFileForEmail($file$destFileName false$disposition NULL$extraHeaders "") {
        if(!
$file) {
            
user_error("encodeFileForEmail: not passed a filename and/or data"E_USER_WARNING);
            return;
        }
        
        if (
is_string($file)) {
            
$file = array('filename' => $file);
            
$fh fopen($file['filename'], "rb");
            if (
$fh) {
                
$file['contents'] = "";
                while(!
feof($fh)) $file['contents'] .= fread($fh10000);    
                
fclose($fh);
            }
        }

        
// Build headers, including content type
        
if(!$destFileName$base basename($file['filename']);
        else 
$base $destFileName;

        
$mimeType = !empty($file['mimetype']) ? $file['mimetype'] : HTTP::get_mime_type($file['filename']);
        if(!
$mimeType$mimeType "application/unknown";
        if (empty(
$disposition)) $disposition = isset($file['contentLocation']) ? 'inline' 'attachment';
        
        
// Encode for emailing
        
if (substr($mimeType04) != 'text') {
            
$encoding "base64";
            
$file['contents'] = chunk_split(base64_encode($file['contents']));
        } else {
            
// This mime type is needed, otherwise some clients will show it as an inline attachment
            
$mimeType 'application/octet-stream';
            
$encoding "quoted-printable";        
            
$file['contents'] = quoted_printable_encode($file['contents']);
        }

        
$headers =    "Content-type: $mimeType;ntname="$base"n".
                    
"Content-Transfer-Encoding: $encodingn".
                    
"Content-Disposition: $disposition;ntfilename="$base"n";
        
        if ( isset(
$file['contentLocation']) ) $headers .= 'Content-Location: ' $file['contentLocation'] . "n" ;
        
        
$headers .= $extraHeaders "n";

        
// Return completed packet
        
return $headers $file['contents'];
    }

    
/**
     * @deprecated since version 4.0
     */
    
public function validEmailAddr($emailAddress) {
        
Deprecation::notice('4.0''This method will be removed in 4.0. Use protected method Mailer->validEmailAddress().');
        return 
$this->validEmailAddress($emailAddress);
    }

    
/**
     * Cleans up emails which may be in 'Name <email@silverstripe.com>' format
     *
     * @param string $emailAddress
     * @return string
     */
    
protected function validEmailAddress($emailAddress) {
        
$emailAddress trim($emailAddress);
        
$openBracket strpos($emailAddress'<');
        
$closeBracket strpos($emailAddress'>');

        
// Unwrap email contained by braces
        
if($openBracket === && $closeBracket !== false) {
            return 
substr($emailAddress1$closeBracket 1);
        }

        
// Ensure name component cannot be mistaken for an email address
        
if($openBracket) {
            
$emailAddress str_replace('@'''substr($emailAddress0$openBracket))
                . 
substr($emailAddress$openBracket);
        }
        
        return 
$emailAddress;
    }

    
/**
     * @deprecated since version 4.0
     */
    
public function wrapImagesInline($htmlContent) {
        
Deprecation::notice('4.0''wrapImagesInline is deprecated');
    }

    
/**
     * @deprecated since version 4.0
     */
    
public function wrapImagesInline_rewriter($url) {
        
Deprecation::notice('4.0''wrapImagesInline_rewriter is deprecated');
    }
}

/**
 * @package framework
 * @subpackage email
 * @deprecated 3.1
 */
function htmlEmail($to$from$subject$htmlContent$attachedFiles false$customheaders false,
    
$plainContent false) {

    
Deprecation::notice('4.0''Use Email->sendHTML() instead');
    
    
$mailer Injector::inst()->create('Mailer');
    return 
$mailer->sendHTML($to$from$subject$plainContent$attachedFiles$customheaders false);
}

/**
 * @package framework
 * @subpackage email
 * @deprecated 3.1
 */
function plaintextEmail($to$from$subject$plainContent$attachedFiles$customheaders false) {
    
Deprecation::notice('4.0''Use Email->sendPlain() instead');

    
$mailer Injector::inst()->create('Mailer');
    return 
$mailer->sendPlain($to$from$subject$plainContent$attachedFiles$customheaders false);
}

/**
 * @package framework
 * @subpackage email
 * @deprecated 3.1
 */
function encodeMultipart($parts$contentType$headers false) {
    
Deprecation::notice('4.0''Use Email->$this->encodeMultipart() instead');

    
$mailer Injector::inst()->create('Mailer');
    return 
$mailer->encodeMultipart($parts$contentType$headers false);
}

/**
 * @package framework
 * @subpackage email
 * @deprecated 3.1
 */
function wrapImagesInline($htmlContent) {
    
Deprecation::notice('4.0''Functionality removed from core');

    
$mailer Injector::inst()->create('Mailer');
    return 
$mailer->wrapImagesInline($htmlContent);
}

/**
 * @package framework
 * @subpackage email
 * @deprecated 3.1
 */
function wrapImagesInline_rewriter($url) {
    
Deprecation::notice('4.0''Functionality removed from core');

    
$mailer Injector::inst()->create('Mailer');
    return 
$mailer->wrapImagesInline_rewriter($url);
    
}

/**
 * @package framework
 * @subpackage email
 * @deprecated 3.1
 */
function processHeaders($headers$body false) {
    
Deprecation::notice('4.0''Set headers through Email->addCustomHeader()');

    
$mailer Injector::inst()->create('Mailer');
    return 
$mailer->processHeaders($headers$url);
}

/**
 * @package framework
 * @subpackage email
 * @deprecated 3.1
 */
function encodeFileForEmail($file$destFileName false$disposition NULL$extraHeaders "") {
    
Deprecation::notice('4.0''Please add files through Email->attachFile()');

    
$mailer Injector::inst()->create('Mailer');
    return 
$mailer->encodeFileForEmail($file$destFileName$disposition$extraHeaders);    
}

/**
 * @package framework
 * @subpackage email
 * @deprecated 3.1
 */
function QuotedPrintable_encode($quotprint) {
    
Deprecation::notice('4.0''No longer available, handled internally');

    
$mailer Injector::inst()->create('Mailer');
    return 
$mailer->QuotedPrintable_encode($quotprint);    
}

/**
 * @package framework
 * @subpackage email
 * @deprecated 3.1
 */
function validEmailAddr($emailAddress) {
    
Deprecation::notice('4.0''This method will be removed in 4.0. Use protected method Mailer->validEmailAddress().');

    
$mailer Injector::inst()->create('Mailer');
    return 
$mailer->validEmailAddr($emailAddress);
}
Онлайн: 0
Реклама