diff options
Diffstat (limited to 'lib/Zend/Mail/Transport')
-rw-r--r-- | lib/Zend/Mail/Transport/Abstract.php | 350 | ||||
-rw-r--r-- | lib/Zend/Mail/Transport/Exception.php | 39 | ||||
-rw-r--r-- | lib/Zend/Mail/Transport/Sendmail.php | 170 | ||||
-rw-r--r-- | lib/Zend/Mail/Transport/Smtp.php | 241 |
4 files changed, 800 insertions, 0 deletions
diff --git a/lib/Zend/Mail/Transport/Abstract.php b/lib/Zend/Mail/Transport/Abstract.php new file mode 100644 index 0000000..603411a --- /dev/null +++ b/lib/Zend/Mail/Transport/Abstract.php @@ -0,0 +1,350 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @category Zend + * @package Zend_Mail + * @subpackage Transport + * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id: Abstract.php 8064 2008-02-16 10:58:39Z thomas $ + */ + + +/** + * @see Zend_Mime + */ +require_once 'Zend/Mime.php'; + + +/** + * Abstract for sending eMails through different + * ways of transport + * + * @category Zend + * @package Zend_Mail + * @subpackage Transport + * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +abstract class Zend_Mail_Transport_Abstract +{ + /** + * Mail body + * @var string + * @access public + */ + public $body = ''; + + /** + * MIME boundary + * @var string + * @access public + */ + public $boundary = ''; + + /** + * Mail header string + * @var string + * @access public + */ + public $header = ''; + + /** + * Array of message headers + * @var array + * @access protected + */ + protected $_headers = array(); + + /** + * Message is a multipart message + * @var boolean + * @access protected + */ + protected $_isMultipart = false; + + /** + * Zend_Mail object + * @var false|Zend_Mail + * @access protected + */ + protected $_mail = false; + + /** + * Array of message parts + * @var array + * @access protected + */ + protected $_parts = array(); + + /** + * Recipients string + * @var string + * @access public + */ + public $recipients = ''; + + /** + * EOL character string used by transport + * @var string + * @access public + */ + public $EOL = "\r\n"; + + /** + * Send an email independent from the used transport + * + * The requisite information for the email will be found in the following + * properties: + * + * - {@link $recipients} - list of recipients (string) + * - {@link $header} - message header + * - {@link $body} - message body + */ + abstract protected function _sendMail(); + + /** + * Return all mail headers as an array + * + * If a boundary is given, a multipart header is generated with a + * Content-Type of either multipart/alternative or multipart/mixed depending + * on the mail parts present in the {@link $_mail Zend_Mail object} present. + * + * @param string $boundary + * @return array + */ + protected function _getHeaders($boundary) + { + if (null !== $boundary) { + // Build multipart mail + $type = $this->_mail->getType(); + if (!$type) { + if ($this->_mail->hasAttachments) { + $type = Zend_Mime::MULTIPART_MIXED; + } elseif ($this->_mail->getBodyText() && $this->_mail->getBodyHtml()) { + $type = Zend_Mime::MULTIPART_ALTERNATIVE; + } else { + $type = Zend_Mime::MULTIPART_MIXED; + } + } + + $this->_headers['Content-Type'] = array( + $type . '; charset="' . $this->_mail->getCharset() . '";' + . $this->EOL + . " " . 'boundary="' . $boundary . '"' + ); + $this->_headers['MIME-Version'] = array('1.0'); + + $this->boundary = $boundary; + } + + return $this->_headers; + } + + /** + * Prepend header name to header value + * + * @param string $item + * @param string $key + * @param string $prefix + * @static + * @access protected + * @return void + */ + protected static function _formatHeader(&$item, $key, $prefix) + { + $item = $prefix . ': ' . $item; + } + + /** + * Prepare header string for use in transport + * + * Prepares and generates {@link $header} based on the headers provided. + * + * @param mixed $headers + * @access protected + * @return void + * @throws Zend_Mail_Transport_Exception if any header lines exceed 998 + * characters + */ + protected function _prepareHeaders($headers) + { + if (!$this->_mail) { + /** + * @see Zend_Mail_Transport_Exception + */ + require_once 'Zend/Mail/Transport/Exception.php'; + throw new Zend_Mail_Transport_Exception('Missing Zend_Mail object in _mail property'); + } + + $this->header = ''; + + foreach ($headers as $header => $content) { + if (isset($content['append'])) { + unset($content['append']); + $value = implode(',' . $this->EOL . ' ', $content); + $this->header .= $header . ': ' . $value . $this->EOL; + } else { + array_walk($content, array(get_class($this), '_formatHeader'), $header); + $this->header .= implode($this->EOL, $content) . $this->EOL; + } + } + + // Sanity check on headers -- should not be > 998 characters + $sane = true; + foreach (explode($this->EOL, $this->header) as $line) { + if (strlen(trim($line)) > 998) { + $sane = false; + break; + } + } + if (!$sane) { + /** + * @see Zend_Mail_Transport_Exception + */ + require_once 'Zend/Mail/Transport/Exception.php'; + throw new Zend_Mail_Exception('At least one mail header line is too long'); + } + } + + /** + * Generate MIME compliant message from the current configuration + * + * If both a text and HTML body are present, generates a + * multipart/alternative Zend_Mime_Part containing the headers and contents + * of each. Otherwise, uses whichever of the text or HTML parts present. + * + * The content part is then prepended to the list of Zend_Mime_Parts for + * this message. + * + * @return void + */ + protected function _buildBody() + { + if (($text = $this->_mail->getBodyText()) + && ($html = $this->_mail->getBodyHtml())) + { + // Generate unique boundary for multipart/alternative + $mime = new Zend_Mime(null); + $boundaryLine = $mime->boundaryLine($this->EOL); + $boundaryEnd = $mime->mimeEnd($this->EOL); + + $text->disposition = false; + $html->disposition = false; + + $body = $boundaryLine + . $text->getHeaders($this->EOL) + . $this->EOL + . $text->getContent($this->EOL) + . $this->EOL + . $boundaryLine + . $html->getHeaders($this->EOL) + . $this->EOL + . $html->getContent($this->EOL) + . $this->EOL + . $boundaryEnd; + + $mp = new Zend_Mime_Part($body); + $mp->type = Zend_Mime::MULTIPART_ALTERNATIVE; + $mp->boundary = $mime->boundary(); + + $this->_isMultipart = true; + + // Ensure first part contains text alternatives + array_unshift($this->_parts, $mp); + + // Get headers + $this->_headers = $this->_mail->getHeaders(); + return; + } + + // If not multipart, then get the body + if (false !== ($body = $this->_mail->getBodyHtml())) { + array_unshift($this->_parts, $body); + } elseif (false !== ($body = $this->_mail->getBodyText())) { + array_unshift($this->_parts, $body); + } + + if (!$body) { + /** + * @see Zend_Mail_Transport_Exception + */ + require_once 'Zend/Mail/Transport/Exception.php'; + throw new Zend_Mail_Transport_Exception('No body specified'); + } + + // Get headers + $this->_headers = $this->_mail->getHeaders(); + $headers = $body->getHeadersArray($this->EOL); + foreach ($headers as $header) { + // Headers in Zend_Mime_Part are kept as arrays with two elements, a + // key and a value + $this->_headers[$header[0]] = array($header[1]); + } + } + + /** + * Send a mail using this transport + * + * @param Zend_Mail $mail + * @access public + * @return void + * @throws Zend_Mail_Transport_Exception if mail is empty + */ + public function send(Zend_Mail $mail) + { + $this->_isMultipart = false; + $this->_mail = $mail; + $this->_parts = $mail->getParts(); + $mime = $mail->getMime(); + + // Build body content + $this->_buildBody(); + + // Determine number of parts and boundary + $count = count($this->_parts); + $boundary = null; + if ($count < 1) { + /** + * @see Zend_Mail_Transport_Exception + */ + require_once 'Zend/Mail/Transport/Exception.php'; + throw new Zend_Mail_Transport_Exception('Empty mail cannot be sent'); + } + + if ($count > 1) { + // Multipart message; create new MIME object and boundary + $mime = new Zend_Mime($this->_mail->getMimeBoundary()); + $boundary = $mime->boundary(); + } elseif ($this->_isMultipart) { + // multipart/alternative -- grab boundary + $boundary = $this->_parts[0]->boundary; + } + + // Determine recipients, and prepare headers + $this->recipients = implode(',', $mail->getRecipients()); + $this->_prepareHeaders($this->_getHeaders($boundary)); + + // Create message body + // This is done so that the same Zend_Mail object can be used in + // multiple transports + $message = new Zend_Mime_Message(); + $message->setParts($this->_parts); + $message->setMime($mime); + $this->body = $message->generateMessage($this->EOL); + + // Send to transport! + $this->_sendMail(); + } +} diff --git a/lib/Zend/Mail/Transport/Exception.php b/lib/Zend/Mail/Transport/Exception.php new file mode 100644 index 0000000..16ef240 --- /dev/null +++ b/lib/Zend/Mail/Transport/Exception.php @@ -0,0 +1,39 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @category Zend + * @package Zend_Mail + * @subpackage Transport + * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id: Exception.php 8064 2008-02-16 10:58:39Z thomas $ + */ + + +/** + * @see Zend_Mail_Exception + */ +require_once 'Zend/Mail/Exception.php'; + + +/** + * @category Zend + * @package Zend_Mail + * @subpackage Transport + * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Zend_Mail_Transport_Exception extends Zend_Mail_Exception +{} + diff --git a/lib/Zend/Mail/Transport/Sendmail.php b/lib/Zend/Mail/Transport/Sendmail.php new file mode 100644 index 0000000..651df3c --- /dev/null +++ b/lib/Zend/Mail/Transport/Sendmail.php @@ -0,0 +1,170 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @category Zend + * @package Zend_Mail + * @subpackage Transport + * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id: Sendmail.php 8064 2008-02-16 10:58:39Z thomas $ + */ + + +/** + * @see Zend_Mail_Transport_Abstract + */ +require_once 'Zend/Mail/Transport/Abstract.php'; + + +/** + * Class for sending eMails via the PHP internal mail() function + * + * @category Zend + * @package Zend_Mail + * @subpackage Transport + * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Zend_Mail_Transport_Sendmail extends Zend_Mail_Transport_Abstract +{ + /** + * Subject + * @var string + * @access public + */ + public $subject = null; + + + /** + * Config options for sendmail parameters + * + * @var string + */ + public $parameters; + + + /** + * EOL character string + * @var string + * @access public + */ + public $EOL = PHP_EOL; + + + /** + * Constructor. + * + * @param string $parameters OPTIONAL (Default: null) + * @return void + */ + public function __construct($parameters = null) + { + $this->parameters = $parameters; + } + + + /** + * Send mail using PHP native mail() + * + * @access public + * @return void + * @throws Zend_Mail_Transport_Exception on mail() failure + */ + public function _sendMail() + { + if ($this->parameters === null) { + $result = mail( + $this->recipients, + $this->_mail->getSubject(), + $this->body, + $this->header); + } else { + $result = mail( + $this->recipients, + $this->_mail->getSubject(), + $this->body, + $this->header, + $this->parameters); + } + if (!$result) { + /** + * @see Zend_Mail_Transport_Exception + */ + require_once 'Zend/Mail/Transport/Exception.php'; + throw new Zend_Mail_Transport_Exception('Unable to send mail'); + } + } + + + /** + * Format and fix headers + * + * mail() uses its $to and $subject arguments to set the To: and Subject: + * headers, respectively. This method strips those out as a sanity check to + * prevent duplicate header entries. + * + * @access protected + * @param array $headers + * @return void + * @throws Zend_Mail_Transport_Exception + */ + protected function _prepareHeaders($headers) + { + if (!$this->_mail) { + /** + * @see Zend_Mail_Transport_Exception + */ + require_once 'Zend/Mail/Transport/Exception.php'; + throw new Zend_Mail_Transport_Exception('_prepareHeaders requires a registered Zend_Mail object'); + } + + // mail() uses its $to parameter to set the To: header, and the $subject + // parameter to set the Subject: header. We need to strip them out. + if (0 === strpos(PHP_OS, 'WIN')) { + // If the current recipients list is empty, throw an error + if (empty($this->recipients)) { + /** + * @see Zend_Mail_Transport_Exception + */ + require_once 'Zend/Mail/Transport/Exception.php'; + throw new Zend_Mail_Transport_Exception('Missing To addresses'); + } + } else { + // All others, simply grab the recipients and unset the To: header + if (!isset($headers['To'])) { + /** + * @see Zend_Mail_Transport_Exception + */ + require_once 'Zend/Mail/Transport/Exception.php'; + throw new Zend_Mail_Transport_Exception('Missing To header'); + } + + unset($headers['To']['append']); + $this->recipients = implode(',', $headers['To']); + } + + // Remove recipient header + unset($headers['To']); + + // Remove subject header, if present + if (isset($headers['Subject'])) { + unset($headers['Subject']); + } + + // Prepare headers + parent::_prepareHeaders($headers); + } + +} + diff --git a/lib/Zend/Mail/Transport/Smtp.php b/lib/Zend/Mail/Transport/Smtp.php new file mode 100644 index 0000000..714266e --- /dev/null +++ b/lib/Zend/Mail/Transport/Smtp.php @@ -0,0 +1,241 @@ +<?php +/** + * Zend Framework + * + * LICENSE + * + * This source file is subject to the new BSD license that is bundled + * with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://framework.zend.com/license/new-bsd + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@zend.com so we can send you a copy immediately. + * + * @category Zend + * @package Zend_Mail + * @subpackage Transport + * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + * @version $Id: Smtp.php 12519 2008-11-10 18:41:24Z alexander $ + */ + + +/** + * @see Zend_Loader + */ +require_once 'Zend/Loader.php'; + +/** + * @see Zend_Mime + */ +require_once 'Zend/Mime.php'; + +/** + * @see Zend_Mail_Protocol_Smtp + */ +require_once 'Zend/Mail/Protocol/Smtp.php'; + +/** + * @see Zend_Mail_Transport_Abstract + */ +require_once 'Zend/Mail/Transport/Abstract.php'; + + +/** + * SMTP connection object + * + * Loads an instance of Zend_Mail_Protocol_Smtp and forwards smtp transactions + * + * @category Zend + * @package Zend_Mail + * @subpackage Transport + * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Zend_Mail_Transport_Smtp extends Zend_Mail_Transport_Abstract +{ + /** + * EOL character string used by transport + * @var string + * @access public + */ + public $EOL = "\n"; + + /** + * Remote smtp hostname or i.p. + * + * @var string + */ + protected $_host; + + + /** + * Port number + * + * @var integer|null + */ + protected $_port; + + + /** + * Local client hostname or i.p. + * + * @var string + */ + protected $_name = 'localhost'; + + + /** + * Authentication type OPTIONAL + * + * @var string + */ + protected $_auth; + + + /** + * Config options for authentication + * + * @var array + */ + protected $_config; + + + /** + * Instance of Zend_Mail_Protocol_Smtp + * + * @var Zend_Mail_Protocol_Smtp + */ + protected $_connection; + + + /** + * Constructor. + * + * @param string $host OPTIONAL (Default: 127.0.0.1) + * @param array|null $config OPTIONAL (Default: null) + * @return void + */ + public function __construct($host = '127.0.0.1', Array $config = array()) + { + if (isset($config['name'])) { + $this->_name = $config['name']; + } + if (isset($config['port'])) { + $this->_port = $config['port']; + } + if (isset($config['auth'])) { + $this->_auth = $config['auth']; + } + + $this->_host = $host; + $this->_config = $config; + } + + + /** + * Class destructor to ensure all open connections are closed + * + * @return void + */ + public function __destruct() + { + if ($this->_connection instanceof Zend_Mail_Protocol_Smtp) { + try { + $this->_connection->quit(); + } catch (Zend_Mail_Protocol_Exception $e) { + // ignore + } + $this->_connection->disconnect(); + } + } + + + /** + * Sets the connection protocol instance + * + * @param Zend_Mail_Protocol_Abstract $client + * + * @return void + */ + public function setConnection(Zend_Mail_Protocol_Abstract $connection) + { + $this->_connection = $connection; + } + + + /** + * Gets the connection protocol instance + * + * @return Zend_Mail_Protocol|null + */ + public function getConnection() + { + return $this->_connection; + } + + /** + * Send an email via the SMTP connection protocol + * + * The connection via the protocol adapter is made just-in-time to allow a + * developer to add a custom adapter if required before mail is sent. + * + * @return void + */ + public function _sendMail() + { + // If sending multiple messages per session use existing adapter + if (!($this->_connection instanceof Zend_Mail_Protocol_Smtp)) { + // Check if authentication is required and determine required class + $connectionClass = 'Zend_Mail_Protocol_Smtp'; + if ($this->_auth) { + $connectionClass .= '_Auth_' . ucwords($this->_auth); + } + Zend_Loader::loadClass($connectionClass); + $this->setConnection(new $connectionClass($this->_host, $this->_port, $this->_config)); + $this->_connection->connect(); + $this->_connection->helo($this->_name); + } else { + // Reset connection to ensure reliable transaction + $this->_connection->rset(); + } + + // Set mail return path from sender email address + $this->_connection->mail($this->_mail->getReturnPath()); + + // Set recipient forward paths + foreach ($this->_mail->getRecipients() as $recipient) { + $this->_connection->rcpt($recipient); + } + + // Issue DATA command to client + $this->_connection->data($this->header . Zend_Mime::LINEEND . $this->body); + } + + /** + * Format and fix headers + * + * Some SMTP servers do not strip BCC headers. Most clients do it themselves as do we. + * + * @access protected + * @param array $headers + * @return void + * @throws Zend_Transport_Exception + */ + protected function _prepareHeaders($headers) + { + if (!$this->_mail) { + /** + * @see Zend_Mail_Transport_Exception + */ + require_once 'Zend/Mail/Transport/Exception.php'; + throw new Zend_Mail_Transport_Exception('_prepareHeaders requires a registered Zend_Mail object'); + } + + unset($headers['Bcc']); + + // Prepare headers + parent::_prepareHeaders($headers); + } +} |