/framework/Mail/lib/Horde/Mail/Transport/Smtp.php
PHP | 355 lines | 135 code | 40 blank | 180 comment | 15 complexity | 7806bbb120edff71aa2b8411fbe24cb9 MD5 | raw file
- <?php
- /**
- * SMTP implementation.
- * Requires the Net_SMTP class.
- *
- * LICENSE:
- *
- * Copyright (c) 2010, Chuck Hagenbuch
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * o Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * o Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * o The names of the authors may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * @category Horde
- * @package Mail
- * @author Jon Parise <jon@php.net>
- * @author Chuck Hagenbuch <chuck@horde.org>
- * @copyright 2010 Chuck Hagenbuch
- * @license http://opensource.org/licenses/bsd-license.php New BSD License
- */
- /**
- * SMTP implementation.
- *
- * @category Horde
- * @package Mail
- */
- class Horde_Mail_Transport_Smtp extends Horde_Mail_Transport
- {
- /* Error: Failed to create a Net_SMTP object */
- const ERROR_CREATE = 10000;
- /* Error: Failed to connect to SMTP server */
- const ERROR_CONNECT = 10001;
- /* Error: SMTP authentication failure */
- const ERROR_AUTH = 10002;
- /* Error: No From: address has been provided */
- const ERROR_FROM = 10003;
- /* Error: Failed to set sender */
- const ERROR_SENDER = 10004;
- /* Error: Failed to add recipient */
- const ERROR_RECIPIENT = 10005;
- /* Error: Failed to send data */
- const ERROR_DATA = 10006;
- /**
- * The SMTP greeting.
- *
- * @var string
- */
- public $greeting = null;
- /**
- * The SMTP queued response.
- *
- * @var string
- */
- public $queuedAs = null;
- /**
- * SMTP connection object.
- *
- * @var Net_SMTP
- */
- protected $_smtp = null;
- /**
- * The list of service extension parameters to pass to the Net_SMTP
- * mailFrom() command.
- *
- * @var array
- */
- protected $_extparams = array();
- /**
- * Constructor.
- *
- * @param array $params Additional parameters:
- * <pre>
- * 'auth' - (mixed) SMTP authentication.
- * This value may be set to true, false or the name of a specific
- * authentication method.
- * If the value is set to true, the Net_SMTP package will attempt
- * to use the best authentication method advertised by the remote
- * SMTP server.
- * DEFAULT: false.
- * 'debug' - (boolean) Activate SMTP debug mode?
- * DEFAULT: false
- * 'host' - (string) The server to connect to.
- * DEFAULT: localhost
- * 'localhost' - (string) Hostname or domain that will be sent to the
- * remote SMTP server in the HELO / EHLO message.
- * DEFAULT: localhost
- * 'password' - (string) The password to use for SMTP auth.
- * DEFAULT: NONE
- * 'persist' - (boolean) Should the SMTP connection persist?
- * DEFAULT: false
- * 'pipelining' - (boolean) Use SMTP command pipelining.
- * Use SMTP command pipelining (specified in RFC 2920) if
- * the SMTP server supports it. This speeds up delivery
- * over high-latency connections.
- * DEFAULT: false (use default value from Net_SMTP)
- * 'port' - (integer) The port to connect to.
- * DEFAULT: 25
- * 'timeout' - (integer) The SMTP connection timeout.
- * DEFAULT: NONE
- * 'username' - (string) The username to use for SMTP auth.
- * DEFAULT: NONE
- * </pre>
- */
- public function __construct(array $params = array())
- {
- $this->_params = array_merge(array(
- 'auth' => false,
- 'debug' => false,
- 'host' => 'localhost',
- 'localhost' => 'localhost',
- 'password' => '',
- 'persist' => false,
- 'pipelining' => false,
- 'port' => 25,
- 'timeout' => null,
- 'username' => ''
- ), $params);
- /* Destructor implementation to ensure that we disconnect from any
- * potentially-alive persistent SMTP connections. */
- register_shutdown_function(array($this, 'disconnect'));
- }
- /**
- * Send a message.
- *
- * @param mixed $recipients Either a comma-seperated list of recipients
- * (RFC822 compliant), or an array of
- * recipients, each RFC822 valid. This may
- * contain recipients not specified in the
- * headers, for Bcc:, resending messages, etc.
- * @param array $headers The headers to send with the mail, in an
- * associative array, where the array key is the
- * header name (ie, 'Subject'), and the array
- * value is the header value (ie, 'test'). The
- * header produced from those values would be
- * 'Subject: test'.
- * If the '_raw' key exists, the value of this
- * key will be used as the exact text for
- * sending the message.
- * @param mixed $body The full text of the message body, including
- * any Mime parts, etc. Either a string or a
- * stream resource.
- *
- * @throws Horde_Mail_Exception
- */
- public function send($recipients, array $headers, $body)
- {
- /* If we don't already have an SMTP object, create one. */
- $this->getSMTPObject();
- $headers = $this->_sanitizeHeaders($headers);
- try {
- list($from, $textHeaders) = $this->prepareHeaders($headers);
- } catch (Horde_Mail_Exception $e) {
- $this->_smtp->rset();
- throw $e;
- }
- /* Since few MTAs are going to allow this header to be forged unless
- * it's in the MAIL FROM: exchange, we'll use Return-Path instead of
- * From: if it's set. */
- foreach (array_keys($headers) as $hdr) {
- if (strcasecmp($hdr, 'Return-Path') === 0) {
- $from = $headers[$hdr];
- break;
- }
- }
- if (!strlen($from)) {
- $this->_smtp->rset();
- throw new Horde_Mail_Exception('No From: address has been provided', self::ERROR_FROM);
- }
- $params = '';
- foreach ($this->_extparams as $key => $val) {
- $params .= ' ' . $key . (is_null($val) ? '' : '=' . $val);
- }
- $res = $this->_smtp->mailFrom($from, ltrim($params));
- if ($res instanceof PEAR_Error) {
- $this->_smtp->rset();
- $this->_error("Failed to set sender: $from", $res, self::ERROR_SENDER);
- }
- try {
- $recipients = $this->parseRecipients($recipients);
- } catch (Horde_Mail_Exception $e) {
- $this->_smtp->rset();
- throw $e;
- }
- foreach ($recipients as $recipient) {
- $res = $this->_smtp->rcptTo($recipient);
- if ($res instanceof PEAR_Error) {
- $this->_smtp->rset();
- $this->_error("Failed to add recipient: $recipient", $res, self::ERROR_RECIPIENT);
- }
- }
- /* Send the message's headers and the body as SMTP data. */
- $res = $this->_smtp->data($body, $textHeaders);
- list(,$args) = $this->_smtp->getResponse();
- if (preg_match("/Ok: queued as (.*)/", $args, $queued)) {
- $this->queuedAs = $queued[1];
- }
- /* We need the greeting; from it we can extract the authorative name
- * of the mail server we've really connected to. Ideal if we're
- * connecting to a round-robin of relay servers and need to track
- * which exact one took the email */
- $this->greeting = $this->_smtp->getGreeting();
- if ($res instanceof PEAR_Error) {
- $this->_smtp->rset();
- $this->_error('Failed to send data', $res, self::ERROR_DATA);
- }
- /* If persistent connections are disabled, destroy our SMTP object. */
- if ($this->_params['persist']) {
- $this->disconnect();
- }
- }
- /**
- * Connect to the SMTP server by instantiating a Net_SMTP object.
- *
- * @return Net_SMTP The SMTP object.
- * @throws Horde_Mail_Exception
- */
- public function getSMTPObject()
- {
- if ($this->_smtp) {
- return $this->_smtp;
- }
- $this->_smtp = new Net_SMTP(
- $this->_params['host'],
- $this->_params['port'],
- $this->_params['localhost']
- );
- /* If we still don't have an SMTP object at this point, fail. */
- if (!($this->_smtp instanceof Net_SMTP)) {
- throw new Horde_Mail_Exception('Failed to create a Net_SMTP object', self::ERROR_CREATE);
- }
- /* Configure the SMTP connection. */
- if ($this->_params['debug']) {
- $this->_smtp->setDebug(true);
- }
- /* Attempt to connect to the configured SMTP server. */
- $res = $this->_smtp->connect($this->_params['timeout']);
- if ($res instanceof PEAR_Error) {
- $this->_error('Failed to connect to ' . $this->_params['host'] . ':' . $this->_params['port'], $res, self::ERROR_CONNECT);
- }
- /* Attempt to authenticate if authentication has been enabled. */
- if ($this->_params['auth']) {
- $method = is_string($this->_params['auth'])
- ? $this->_params['auth']
- : '';
- $res = $this->_smtp->auth($this->_params['username'], $this->_params['password'], $method);
- if ($res instanceof PEAR_Error) {
- $this->_smtp->rset();
- $this->_error("$method authentication failure", $res, self::ERROR_AUTH);
- }
- }
- return $this->_smtp;
- }
- /**
- * Add parameter associated with a SMTP service extension.
- *
- * @param string $keyword Extension keyword.
- * @param string $value Any value the keyword needs.
- */
- public function addServiceExtensionParameter($keyword, $value = null)
- {
- $this->_extparams[$keyword] = $value;
- }
- /**
- * Disconnect and destroy the current SMTP connection.
- *
- * @return boolean True if the SMTP connection no longer exists.
- */
- public function disconnect()
- {
- /* If we have an SMTP object, disconnect and destroy it. */
- if (is_object($this->_smtp) && $this->_smtp->disconnect()) {
- $this->_smtp = null;
- }
- /* We are disconnected if we no longer have an SMTP object. */
- return ($this->_smtp === null);
- }
- /**
- * Build a standardized string describing the current SMTP error.
- *
- * @param string $text Custom string describing the error context.
- * @param PEAR_Error $error PEAR_Error object.
- * @param integer $e_code Error code.
- *
- * @throws Horde_Mail_Exception
- */
- protected function _error($text, $error, $e_code)
- {
- /* Split the SMTP response into a code and a response string. */
- list($code, $response) = $this->_smtp->getResponse();
- /* Build our standardized error string. */
- throw new Horde_Mail_Exception($text . ' [SMTP: ' . $error->getMessage() . " (code: $code, response: $response)]", $e_code);
- }
- }