protected $_identity;
/**
- * A Horde_Mail_Driver object.
+ * A Horde_Mail_Transport object.
*
- * @var Horde_Mail_Driver
+ * @var Horde_Mail_Transport
*/
protected $_mail;
* Required parameter:
* - identity: An identity factory that implements
* getIdentity().
- * - mail: A Horde_Mail_Driver instance.
+ * - mail: A Horde_Mail_Transport instance.
* - charset: The charset of the messages.
*/
public function __construct(array $params = null)
if (!method_exists($params['identity'], 'getIdentity')) {
throw new Horde_Alarm_Exception('Parameter \'identity\' does not implement getIdentity().');
}
- $r = new ReflectionObject($params['mail']);
- if (!($params['mail'] instanceof Horde_Mail_Driver)) {
- throw new Horde_Alarm_Exception('Parameter \'mail\' is not a Horde_Mail_Driver object.');
+ if (!($params['mail'] instanceof Horde_Mail_Transport)) {
+ throw new Horde_Alarm_Exception('Parameter \'mail\' is not a Horde_Mail_Transport object.');
}
$this->_identity = $params['identity'];
$this->_mail = $params['mail'];
{
public function create(Horde_Injector $injector)
{
- $driver = isset($GLOBALS['conf']['mailer']['type'])
+ $transport = isset($GLOBALS['conf']['mailer']['type'])
? $GLOBALS['conf']['mailer']['type']
: 'null';
$params = isset($GLOBALS['conf']['mailer']['params'])
? $GLOBALS['conf']['mailer']['params']
: array();
- if (($driver == 'smtp') &&
+ if (($transport == 'smtp') &&
$params['auth'] &&
empty($params['username'])) {
$params['username'] = $GLOBALS['registry']->getAuth();
$params['password'] = $GLOBALS['registry']->getAuthCredential('password');
}
- try {
- return Horde_Mail::factory($driver, $params);
- } catch (Horde_Mime_Exception $e) {
- throw new Horde_Exception($e);
+ $class = 'Horde_Mail_Transport_' . ucfirst($transport);
+ if (class_exists($class)) {
+ return new $class($params);
}
+ throw new Horde_Exception('Unable to find class for transport ' . $transport);
}
public function equals(Horde_Injector_Binder $binder)
class Horde_Mail
{
/**
- * Returns a Horde_Mail_Driver:: object.
+ * Returns a Horde_Mail_Transport:: object.
*
- * @param string $driver The driver to instantiate.
- * @param array $params The parameters to pass to the object.
+ * @param string $transport The transport to instantiate.
+ * @param array $params The parameters to pass to the transport.
*
- * @return Horde_Mail_Driver The driver instance.
+ * @return Horde_Mail_Transport The transport instance.
* @throws Horde_Mail_Exception
+ * @deprecated
*/
- static public function factory($driver, $params = array())
+ static public function factory($transport, $params = array())
{
- $class = __CLASS__ . '_' . ucfirst($driver);
-
+ $class = 'Horde_Mail_Transport_' . ucfirst($transport);
if (class_exists($class)) {
return new $class($params);
}
- throw new Horde_Mail_Exception('Unable to find class for driver ' . $driver);
+ throw new Horde_Mail_Exception('Unable to find class for transport ' . $transport);
}
-
}
+++ /dev/null
-<?php
-/**
- * Mail driver base class.
- *
- * LICENSE:
- *
- * Copyright (c) 2002-2007, Richard Heyes
- * 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 Mail
- * @package Mail
- * @author Chuck Hagenbuch <chuck@horde.org>
- * @author Michael Slusarz <slusarz@horde.org>
- * @copyright 1997-2010 Chuck Hagenbuch
- * @copyright 2010 Michael Slusarz
- * @license http://opensource.org/licenses/bsd-license.php New BSD License
- */
-
-/**
- * Mail driver base class.
- *
- * @access public
- * @version $Revision: 294747 $
- * @package Mail
- */
-abstract class Horde_Mail_Driver
-{
- /**
- * Line terminator used for separating header lines.
- *
- * @var string
- */
- public $sep = "\r\n";
-
- /**
- * Configuration parameters.
- *
- * @var array
- */
- protected $_params = array();
-
- /**
- * 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
- */
- abstract public function send($recipients, array $headers, $body);
-
- /**
- * Take an array of mail headers and return a string containing text
- * usable in sending a message.
- *
- * @param array $headers The array of headers to prepare, 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.
- *
- * @return mixed Returns false if it encounters a bad address; otherwise
- * returns an array containing two elements: Any From:
- * address found in the headers, and the plain text version
- * of the headers.
- * @throws Horde_Mail_Exception
- */
- public function prepareHeaders(array $headers)
- {
- $lines = array();
- $from = null;
-
- $parser = new Horde_Mail_Rfc822();
-
- foreach ($headers as $key => $value) {
- if (strcasecmp($key, 'From') === 0) {
- $addresses = $parser->parseAddressList($value, array(
- 'nest_groups' => false,
- ));
- $from = $addresses[0]->mailbox . '@' . $addresses[0]->host;
-
- // Reject envelope From: addresses with spaces.
- if (strstr($from, ' ')) {
- return false;
- }
-
- $lines[] = $key . ': ' . $value;
- } elseif (strcasecmp($key, 'Received') === 0) {
- $received = array();
- if (!is_array($value)) {
- $value = array($value);
- }
-
- foreach ($value as $line) {
- $received[] = $key . ': ' . $line;
- }
-
- // Put Received: headers at the top. Spam detectors often
- // flag messages with Received: headers after the Subject:
- // as spam.
- $lines = array_merge($received, $lines);
- } else {
- // If $value is an array (i.e., a list of addresses), convert
- // it to a comma-delimited string of its elements (addresses).
- if (is_array($value)) {
- $value = implode(', ', $value);
- }
- $lines[] = $key . ': ' . $value;
- }
- }
-
- return array($from, isset($headers['_raw']) ? $headers['_raw'] : join($this->sep, $lines));
- }
-
- /**
- * Take a set of recipients and parse them, returning an array of bare
- * addresses (forward paths) that can be passed to sendmail or an SMTP
- * server with the 'RCPT TO:' command.
- *
- * @param mixed Either a comma-separated list of recipients (RFC822
- * compliant), or an array of recipients, each RFC822 valid.
- *
- * @return array Forward paths (bare addresses).
- * @throws Horde_Mail_Exception
- */
- public function parseRecipients($recipients)
- {
- // if we're passed an array, assume addresses are valid and
- // implode them before parsing.
- if (is_array($recipients)) {
- $recipients = implode(', ', $recipients);
- }
-
- // Parse recipients, leaving out all personal info. This is
- // for smtp recipients, etc. All relevant personal information
- // should already be in the headers.
- $parser = new Horde_Mail_Rfc822();
- $addresses = $parser->parseAddressList($recipients, array(
- 'nest_groups' => false
- ));
-
- $recipients = array();
- if (is_array($addresses)) {
- foreach ($addresses as $ob) {
- $recipients[] = $ob->mailbox . '@' . $ob->host;
- }
- }
-
- return $recipients;
- }
-
- /**
- * Sanitize an array of mail headers by removing any additional header
- * strings present in a legitimate header's value. The goal of this
- * filter is to prevent mail injection attacks.
- *
- * @param array $headers The associative array of headers to sanitize.
- *
- * @return array The sanitized headers.
- */
- protected function _sanitizeHeaders($headers)
- {
- foreach (array_keys($headers) as $key) {
- $headers[$key] = preg_replace('=((<CR>|<LF>|0x0A/%0A|0x0D/%0D|\\n|\\r)\S).*=i', null, $headers[$key]);
- }
-
- return $headers;
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Internal PHP-mail() interface.
- *
- * 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 Chuck Hagenbuch <chuck@horde.org>
- * @author Michael Slusarz <slusarz@horde.org>
- * @copyright 2010 Chuck Hagenbuch
- * @copyright 2010 Michael Slusarz
- * @license http://opensource.org/licenses/bsd-license.php New BSD License
- */
-
-/**
- * Internal PHP-mail() interface.
- *
- * @category Horde
- * @package Mail
- */
-class Horde_Mail_Mail extends Horde_Mail_Driver
-{
- /**
- * Constructor.
- *
- * @param array $params Additional parameters:
- * <pre>
- * 'args' - (string) Extra arguments for the mail() function.
- * </pre>
- */
- public function __construct(array $params = array())
- {
- $this->_params = array_merge($this->_params, $params);
-
- /* Because the mail() function may pass headers as command
- * line arguments, we can't guarantee the use of the standard
- * "\r\n" separator. Instead, we use the system's native line
- * separator. */
- $this->sep = defined('PHP_EOL')
- ? PHP_EOL
- : (strpos(PHP_OS, 'WIN') === false) ? "\n" : "\r\n";
- }
-
- /**
- * 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)
- {
- $headers = $this->_sanitizeHeaders($headers);
-
- // If we're passed an array of recipients, implode it.
- if (is_array($recipients)) {
- $recipients = array_map('trim', implode(',', $recipients));
- }
-
- $subject = '';
-
- foreach (array_keys($headers) as $hdr) {
- if (strcasecmp($hdr, 'Subject') === 0) {
- // Get the Subject out of the headers array so that we can
- // pass it as a separate argument to mail().
- $subject = $headers[$hdr];
- unset($headers[$hdr]);
- } elseif (strcasecmp($hdr, 'To') === 0) {
- // Remove the To: header. The mail() function will add its
- // own To: header based on the contents of $recipients.
- unset($headers[$hdr]);
- }
- }
-
- // Flatten the headers out.
- list(, $text_headers) = $this->prepareHeaders($headers);
-
- // mail() requires a string for $body. If resource, need to convert
- // to a string.
- if (is_resource($body)) {
- $body_str = '';
- rewind($body);
- while (!feof($body)) {
- $body_str .= fread($body, 8192);
- }
- $body = $body_str;
- }
-
- // We only use mail()'s optional fifth parameter if the additional
- // parameters have been provided and we're not running in safe mode.
- if (empty($this->_params) || ini_get('safe_mode')) {
- $result = mail($recipients, $subject, $body, $text_headers);
- } else {
- $result = mail($recipients, $subject, $body, $text_headers, isset($this->_params['args']) ? $this->_params['args'] : '');
- }
-
- // If the mail() function returned failure, we need to create an
- // Exception and return it instead of the boolean result.
- if ($result === false) {
- throw new Horde_Mail_Exception('mail() returned failure.');
- }
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Mock mail driver.
- *
- * 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 Chuck Hagenbuch <chuck@horde.org>
- * @copyright 2010 Chuck Hagenbuch
- * @license http://opensource.org/licenses/bsd-license.php New BSD License
- */
-
-/**
- * Mock implementation, for testing.
- *
- * @category Horde
- * @package Mail
- */
-class Horde_Mail_Mock extends Horde_Mail_Driver
-{
- /**
- * Array of messages that have been sent with the mock.
- *
- * @var array
- */
- public $sentMessages = array();
-
- /**
- * Callback before sending mail.
- *
- * @var callback
- */
- protected $_preSendCallback;
-
- /**
- * Callback after sending mai.
- *
- * @var callback
- */
- protected $_postSendCallback;
-
- /**
- * Constructor.
- *
- * @param array Optional parameters:
- * <pre>
- * 'preSendCallback' - (callback) Called before an email would be sent.
- * 'postSendCallback' - (callback) Called after an email would have been
- * sent.
- * </pre>
- */
- public function __construct(array $params = array())
- {
- if (isset($params['preSendCallback']) &&
- is_callable($params['preSendCallback'])) {
- $this->_preSendCallback = $params['preSendCallback'];
- }
-
- if (isset($params['postSendCallback']) &&
- is_callable($params['postSendCallback'])) {
- $this->_postSendCallback = $params['postSendCallback'];
- }
- }
-
- /**
- * Send a message. Silently discards all mail.
- *
- * @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 ($this->_preSendCallback) {
- call_user_func_array($this->_preSendCallback, array($this, $recipients, $headers, $body));
- }
-
- $headers = $this->_sanitizeHeaders($headers);
- list(, $text_headers) = $this->prepareHeaders($headers);
-
- $this->sentMessages[] = array(
- 'body' => $body,
- 'headers' => $headers,
- 'header_text' => $text_headers,
- 'recipients' => $recipients
- );
-
- if ($this->_postSendCallback) {
- call_user_func_array($this->_postSendCallback, array($this, $recipients, $headers, $body));
- }
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Null implementation of the mail interface.
- *
- * LICENSE:
- *
- * Copyright (c) 2010 Phil Kernick
- * 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 Phil Kernick <philk@rotfl.com.au>
- * @copyright 2010 Phil Kernick
- * @license http://opensource.org/licenses/bsd-license.php New BSD License
- */
-
-/**
- * Null implementation of the mail interface.
- *
- * @category Horde
- * @package Mail
- */
-class Horde_Mail_Null extends Horde_Mail_Driver
-{
- /**
- * 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)
- {
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Sendmail interface.
- *
- * 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 Chuck Hagenbuch <chuck@horde.org>
- * @author Michael Slusarz <slusarz@horde.org>
- * @copyright 2010 Chuck Hagenbuch
- * @copyright 2010 Michael Slusarz
- * @license http://opensource.org/licenses/bsd-license.php New BSD License
- */
-
-/**
- * Sendmail interface.
- *
- * @category Horde
- * @package Mail
- */
-class Horde_Mail_Sendmail extends Horde_Mail_Driver
-{
- /**
- * Any extra command-line parameters to pass to the sendmail or
- * sendmail wrapper binary.
- *
- * @var string
- */
- protected $_sendmailArgs = '-i';
-
- /**
- * The location of the sendmail or sendmail wrapper binary on the
- * filesystem.
- *
- * @var string
- */
- protected $_sendmailPath = '/usr/sbin/sendmail';
-
- /**
- * Constructor.
- *
- * @param array $params Additional parameters:
- * <pre>
- * 'sendmail_args' - (string) Any extra parameters to pass to the sendmail
- * or sendmail wrapper binary.
- * DEFAULT: -i
- * 'sendmail_path' - (string) The location of the sendmail binary on the
- * filesystem.
- * DEFAULT: /usr/sbin/sendmail
- * </pre>
- */
- public function __construct(array $params = array())
- {
- if (isset($params['sendmail_args'])) {
- $this->_sendmailArgs = $params['sendmail_args'];
- }
-
- if (isset($params['sendmail_path'])) {
- $this->_sendmailPath = $params['sendmail_path'];
- }
-
- /* Because we need to pass message headers to the sendmail program on
- * the commandline, we can't guarantee the use of the standard "\r\n"
- * separator. Instead, we use the system's native line separator. */
- $this->sep = defined('PHP_EOL')
- ? PHP_EOL
- : (strpos(PHP_OS, 'WIN') === false) ? "\n" : "\r\n";
- }
-
- /**
- * 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)
- {
- $recipients = implode(' ', array_map('escapeshellarg', $this->parseRecipients($recipients)));
-
- $headers = $this->_sanitizeHeaders($headers);
- list($from, $text_headers) = $this->prepareHeaders($headers);
-
- /* 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)) {
- throw new Horde_Mail_Exception('No From address given.');
- } elseif ((strpos($from, ' ') !== false) ||
- (strpos($from, ';') !== false) ||
- (strpos($from, '&') !== false) ||
- (strpos($from, '`') !== false)) {
- throw new Horde_Mail_Exception('From address specified with dangerous characters.');
- }
-
- $mail = @popen($this->_sendmailPath . (empty($this->_sendmailArgs) ? '' : ' ' . $this->_sendmailargs) . ' -f' . escapeshellarg($from) . ' -- ' . $recipients, 'w');
- if (!$mail) {
- throw new Horde_Mail_Exception('Failed to open sendmail [' . $this->_sendmailPath . '] for execution.');
- }
-
- // Write the headers following by two newlines: one to end the headers
- // section and a second to separate the headers block from the body.
- fputs($mail, $text_headers . $this->sep . $this->sep);
-
- if (is_resource($body)) {
- rewind($body);
- while (!feof($body)) {
- fputs($mail, fread($body, 8192));
- }
- } else {
- fputs($mail, $body);
- }
- $result = pclose($mail);
-
- if (!$result) {
- return;
- }
-
- switch ($result) {
- case 64: // EX_USAGE
- $msg = 'command line usage error';
- break;
-
- case 65: // EX_DATAERR
- $msg = 'data format error';
- break;
-
- case 66: // EX_NOINPUT
- $msg = 'cannot open input';
- break;
-
- case 67: // EX_NOUSER
- $msg = 'addressee unknown';
- break;
-
- case 68: // EX_NOHOST
- $msg = 'host name unknown';
- break;
-
- case 69: // EX_UNAVAILABLE
- $msg = 'service unavailable';
- break;
-
- case 70: // EX_SOFTWARE
- $msg = 'internal software error';
- break;
-
- case 71: // EX_OSERR
- $msg = 'system error';
- break;
-
- case 72: // EX_OSFILE
- $msg = 'critical system file missing';
- break;
-
- case 73: // EX_CANTCREAT
- $msg = 'cannot create output file';
- break;
-
- case 74: // EX_IOERR
- $msg = 'input/output error';
-
- case 75: // EX_TEMPFAIL
- $msg = 'temporary failure';
- break;
-
- case 76: // EX_PROTOCOL
- $msg = 'remote error in protocol';
- break;
-
- case 77: // EX_NOPERM
- $msg = 'permission denied';
- break;
-
- case 77: // EX_NOPERM
- $msg = 'permission denied';
- break;
-
- case 78: // EX_CONFIG
- $msg = 'configuration error';
- break;
-
- case 79: // EX_NOTFOUND
- $msg = 'entry not found';
- break;
-
- default:
- $msg = 'unknown error';
- break;
- }
-
- throw new Horde_Mail_Exception('sendmail: ' . $msg . ' (' . $result . ')', $result);
- }
-
-}
+++ /dev/null
-<?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_Smtp extends Horde_Mail_Driver
-{
- /* 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);
- }
-
-}
+++ /dev/null
-<?PHP
-/**
- * SMTP MX implementation.
- * Requires the Net_SMTP class.
- *
- * LICENSE:
- *
- * Copyright (c) 2010, gERD Schaufelberger
- * 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 gERD Schaufelberger <gerd@php-tools.net>
- * @copyright 2010 gERD Schaufelberger
- * @license http://opensource.org/licenses/bsd-license.php New BSD License
- */
-
-/**
- * SMTP MX implementation.
- *
- * @author gERD Schaufelberger <gerd@php-tools.net>
- * @category Horde
- * @package Mail
- */
-class Horde_Mail_Smtpmx extends Horde_Mail_Driver
-{
- /**
- * SMTP connection object.
- *
- * @var Net_SMTP
- */
- protected $_smtp = null;
-
- /**
- * Net_DNS_Resolver object.
- *
- * @var Net_DNS_Resolver
- */
- protected $_resolver;
-
- /**
- * Internal error codes.
- * Translate internal error identifier to human readable messages.
- *
- * @var array
- */
- protected $_errorCode = array(
- 'not_connected' => array(
- 'code' => 1,
- 'msg' => 'Could not connect to any mail server ({HOST}) at port {PORT} to send mail to {RCPT}.'
- ),
- 'failed_vrfy_rcpt' => array(
- 'code' => 2,
- 'msg' => 'Recipient "{RCPT}" could not be veryfied.'
- ),
- 'failed_set_from' => array(
- 'code' => 3,
- 'msg' => 'Failed to set sender: {FROM}.'
- ),
- 'failed_set_rcpt' => array(
- 'code' => 4,
- 'msg' => 'Failed to set recipient: {RCPT}.'
- ),
- 'failed_send_data' => array(
- 'code' => 5,
- 'msg' => 'Failed to send mail to: {RCPT}.'
- ),
- 'no_from' => array(
- 'code' => 5,
- 'msg' => 'No from address has be provided.'
- ),
- 'send_data' => array(
- 'code' => 7,
- 'msg' => 'Failed to create Net_SMTP object.'
- ),
- 'no_mx' => array(
- 'code' => 8,
- 'msg' => 'No MX-record for {RCPT} found.'
- ),
- 'no_resolver' => array(
- 'code' => 9,
- 'msg' => 'Could not start resolver! Install PEAR:Net_DNS or switch off "netdns"'
- ),
- 'failed_rset' => array(
- 'code' => 10,
- 'msg' => 'RSET command failed, SMTP-connection corrupt.'
- )
- );
-
- /**
- * Constructor.
- *
- * @param array $params Additional options:
- * <pre>
- * 'debug' - (boolean) Activate SMTP and Net_DNS debug mode?
- * DEFAULT: false
- * 'mailname' - (string) The name of the local mail system (a valid
- * hostname which matches the reverse lookup)
- * DEFAULT: Auto-determined
- * 'netdns' - (boolean) Use PEAR:Net_DNS (true) or the PHP builtin
- * getmxrr().
- * DEFAULT: true
- * 'port' - (integer) Port.
- * DEFAULT: Auto-determined
- * 'test' - (boolean) Activate test mode?
- * DEFAULT: false
- * 'timeout' - (integer) The SMTP connection timeout (in seconds).
- * DEFAULT: 10
- * 'verp' - (boolean) Whether to use VERP.
- * If not a boolean, the string value will be used as the VERP
- * separators.
- * DEFAULT: false
- * 'vrfy' - (boolean) Whether to use VRFY.
- * DEFAULT: false
- * </pre>
- */
- public function __construct(array $params = array())
- {
- /* Try to find a valid mailname. */
- if (!isset($params['mailname']) && function_exists('posix_uname')) {
- $uname = posix_uname();
- $params['mailname'] = $uname['nodename'];
- }
-
- if (!isset($params['port'])) {
- $params['port'] = getservbyname('smtp', 'tcp');
- }
-
- $this->_params = array_merge(array(
- 'debug' => false,
- 'mailname' => 'localhost',
- 'netdns' => true,
- 'port' => 25,
- 'test' => false,
- 'timeout' => 10,
- 'verp' => false,
- 'vrfy' => false
- ), $params);
- }
-
- /**
- * Destructor implementation to ensure that we disconnect from any
- * potentially-alive persistent SMTP connections.
- */
- public function __destruct()
- {
- if (is_object($this->_smtp)) {
- $this->_smtp->disconnect();
- $this->_smtp = null;
- }
- }
-
- /**
- * 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)
- {
- $headers = $this->_sanitizeHeaders($headers);
-
- // Prepare headers
- list($from, $textHeaders) = $this->prepareHeaders($headers);
-
- // Use 'Return-Path' if possible
- foreach (array_keys($headers) as $hdr) {
- if (strcasecmp($hdr, 'Return-Path') === 0) {
- $from = $headers['Return-Path'];
- break;
- }
- }
-
- if (!strlen($from)) {
- $this->_error('no_from');
- }
-
- // Prepare recipients
- foreach ($this->parseRecipients($recipients) as $rcpt) {
- list($user, $host) = explode('@', $rcpt);
-
- $mx = $this->_getMx($host);
- if (!$mx) {
- $this->_error('no_mx', array('rcpt' => $rcpt));
- }
-
- $connected = false;
- foreach ($mx as $mserver => $mpriority) {
- $this->_smtp = new Net_SMTP($mserver, $this->_params['port'], $this->_params['mailname']);
-
- // 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->_smtp = null;
- continue;
- }
-
- // connection established
- if ($res) {
- $connected = true;
- break;
- }
- }
-
- if (!$connected) {
- $this->_error('not_connected', array(
- 'host' => implode(', ', array_keys($mx)),
- 'port' => $this->_params['port'],
- 'rcpt' => $rcpt
- ));
- }
-
- // Verify recipient
- if ($this->_params['vrfy']) {
- $res = $this->_smtp->vrfy($rcpt);
- if ($res instanceof PEAR_Error) {
- $this->_error('failed_vrfy_rcpt', array('rcpt' => $rcpt));
- }
- }
-
- // mail from:
- $args['verp'] = $this->_params['verp'];
- $res = $this->_smtp->mailFrom($from, $args);
- if ($res instanceof PEAR_Error) {
- $this->_error('failed_set_from', array('from' => $from));
- }
-
- // rcpt to:
- $res = $this->_smtp->rcptTo($rcpt);
- if ($res instanceof PEAR_Error) {
- $this->_error('failed_set_rcpt', array('rcpt' => $rcpt));
- }
-
- // Don't send anything in test mode
- if ($this->_params['test']) {
- $res = $this->_smtp->rset();
- if ($res instanceof PEAR_Error) {
- $this->_error('failed_rset');
- }
-
- $this->_smtp->disconnect();
- $this->_smtp = null;
- return;
- }
-
- // Send data
- $res = $this->_smtp->data($body, $textHeaders);
- if ($res instanceof PEAR_Error) {
- $this->_error('failed_send_data', array('rcpt' => $rcpt));
- }
-
- $this->_smtp->disconnect();
- $this->_smtp = null;
- }
- }
-
- /**
- * Recieve MX records for a host.
- *
- * @param string $host Mail host.
- *
- * @return mixed Sorted MX list or false on error.
- */
- protected function _getMx($host)
- {
- $mx = array();
-
- if ($this->params['netdns']) {
- $this->_loadNetDns();
-
- $response = $this->_resolver->query($host, 'MX');
- if (!$response) {
- return false;
- }
-
- foreach ($response->answer as $rr) {
- if ($rr->type == 'MX') {
- $mx[$rr->exchange] = $rr->preference;
- }
- }
- } else {
- $mxHost = $mxWeight = array();
-
- if (!getmxrr($host, $mxHost, $mxWeight)) {
- return false;
- }
-
- for ($i = 0; $i < count($mxHost); ++$i) {
- $mx[$mxHost[$i]] = $mxWeight[$i];
- }
- }
-
- asort($mx);
-
- return $mx;
- }
-
- /**
- * Initialize Net_DNS_Resolver.
- */
- protected function _loadNetDns()
- {
- if (!$this->_resolver) {
- if (!class_exists('Net_DNS_Resolver')) {
- $this->_error('no_resolver');
- }
-
- $this->_resolver = new Net_DNS_Resolver();
- if ($this->_params['debug']) {
- $this->_resolver->test = 1;
- }
- }
- }
-
- /**
- * Format error message.
- *
- * @param string $id Maps error ids to codes and message.
- * @param array $info Optional information in associative array.
- *
- * @throws Horde_Mail_Exception
- */
- protected function _error($id, $info = array())
- {
- $msg = $this->_errorCode[$id]['msg'];
-
- // include info to messages
- if (!empty($info)) {
- $replace = $search = array();
-
- foreach ($info as $key => $value) {
- $search[] = '{' . strtoupper($key) . '}';
- $replace[] = $value;
- }
-
- $msg = str_replace($search, $replace, $msg);
- }
-
- throw new Horde_Mail_Exception($msg, $this->_errorCode[$id]['code']);
- }
-
-}
--- /dev/null
+<?php
+/**
+ * Mail transport base class.
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2002-2007, Richard Heyes
+ * 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 Mail
+ * @package Mail
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @copyright 1997-2010 Chuck Hagenbuch
+ * @copyright 2010 Michael Slusarz
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ */
+
+/**
+ * Mail transport base class.
+ *
+ * @access public
+ * @version $Revision: 294747 $
+ * @package Mail
+ */
+abstract class Horde_Mail_Transport
+{
+ /**
+ * Line terminator used for separating header lines.
+ *
+ * @var string
+ */
+ public $sep = "\r\n";
+
+ /**
+ * Configuration parameters.
+ *
+ * @var array
+ */
+ protected $_params = array();
+
+ /**
+ * 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
+ */
+ abstract public function send($recipients, array $headers, $body);
+
+ /**
+ * Take an array of mail headers and return a string containing text
+ * usable in sending a message.
+ *
+ * @param array $headers The array of headers to prepare, 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.
+ *
+ * @return mixed Returns false if it encounters a bad address; otherwise
+ * returns an array containing two elements: Any From:
+ * address found in the headers, and the plain text version
+ * of the headers.
+ * @throws Horde_Mail_Exception
+ */
+ public function prepareHeaders(array $headers)
+ {
+ $lines = array();
+ $from = null;
+
+ $parser = new Horde_Mail_Rfc822();
+
+ foreach ($headers as $key => $value) {
+ if (strcasecmp($key, 'From') === 0) {
+ $addresses = $parser->parseAddressList($value, array(
+ 'nest_groups' => false,
+ ));
+ $from = $addresses[0]->mailbox . '@' . $addresses[0]->host;
+
+ // Reject envelope From: addresses with spaces.
+ if (strstr($from, ' ')) {
+ return false;
+ }
+
+ $lines[] = $key . ': ' . $value;
+ } elseif (strcasecmp($key, 'Received') === 0) {
+ $received = array();
+ if (!is_array($value)) {
+ $value = array($value);
+ }
+
+ foreach ($value as $line) {
+ $received[] = $key . ': ' . $line;
+ }
+
+ // Put Received: headers at the top. Spam detectors often
+ // flag messages with Received: headers after the Subject:
+ // as spam.
+ $lines = array_merge($received, $lines);
+ } else {
+ // If $value is an array (i.e., a list of addresses), convert
+ // it to a comma-delimited string of its elements (addresses).
+ if (is_array($value)) {
+ $value = implode(', ', $value);
+ }
+ $lines[] = $key . ': ' . $value;
+ }
+ }
+
+ return array($from, isset($headers['_raw']) ? $headers['_raw'] : join($this->sep, $lines));
+ }
+
+ /**
+ * Take a set of recipients and parse them, returning an array of bare
+ * addresses (forward paths) that can be passed to sendmail or an SMTP
+ * server with the 'RCPT TO:' command.
+ *
+ * @param mixed Either a comma-separated list of recipients (RFC822
+ * compliant), or an array of recipients, each RFC822 valid.
+ *
+ * @return array Forward paths (bare addresses).
+ * @throws Horde_Mail_Exception
+ */
+ public function parseRecipients($recipients)
+ {
+ // if we're passed an array, assume addresses are valid and
+ // implode them before parsing.
+ if (is_array($recipients)) {
+ $recipients = implode(', ', $recipients);
+ }
+
+ // Parse recipients, leaving out all personal info. This is
+ // for smtp recipients, etc. All relevant personal information
+ // should already be in the headers.
+ $parser = new Horde_Mail_Rfc822();
+ $addresses = $parser->parseAddressList($recipients, array(
+ 'nest_groups' => false
+ ));
+
+ $recipients = array();
+ if (is_array($addresses)) {
+ foreach ($addresses as $ob) {
+ $recipients[] = $ob->mailbox . '@' . $ob->host;
+ }
+ }
+
+ return $recipients;
+ }
+
+ /**
+ * Sanitize an array of mail headers by removing any additional header
+ * strings present in a legitimate header's value. The goal of this
+ * filter is to prevent mail injection attacks.
+ *
+ * @param array $headers The associative array of headers to sanitize.
+ *
+ * @return array The sanitized headers.
+ */
+ protected function _sanitizeHeaders($headers)
+ {
+ foreach (array_keys($headers) as $key) {
+ $headers[$key] = preg_replace('=((<CR>|<LF>|0x0A/%0A|0x0D/%0D|\\n|\\r)\S).*=i', null, $headers[$key]);
+ }
+
+ return $headers;
+ }
+}
--- /dev/null
+<?php
+/**
+ * Internal PHP-mail() interface.
+ *
+ * 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 Chuck Hagenbuch <chuck@horde.org>
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @copyright 2010 Chuck Hagenbuch
+ * @copyright 2010 Michael Slusarz
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ */
+
+/**
+ * Internal PHP-mail() interface.
+ *
+ * @category Horde
+ * @package Mail
+ */
+class Horde_Mail_Transport_Mail extends Horde_Mail_Transport
+{
+ /**
+ * Constructor.
+ *
+ * @param array $params Additional parameters:
+ * <pre>
+ * 'args' - (string) Extra arguments for the mail() function.
+ * </pre>
+ */
+ public function __construct(array $params = array())
+ {
+ $this->_params = array_merge($this->_params, $params);
+
+ /* Because the mail() function may pass headers as command
+ * line arguments, we can't guarantee the use of the standard
+ * "\r\n" separator. Instead, we use the system's native line
+ * separator. */
+ $this->sep = defined('PHP_EOL')
+ ? PHP_EOL
+ : (strpos(PHP_OS, 'WIN') === false) ? "\n" : "\r\n";
+ }
+
+ /**
+ * 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)
+ {
+ $headers = $this->_sanitizeHeaders($headers);
+
+ // If we're passed an array of recipients, implode it.
+ if (is_array($recipients)) {
+ $recipients = array_map('trim', implode(',', $recipients));
+ }
+
+ $subject = '';
+
+ foreach (array_keys($headers) as $hdr) {
+ if (strcasecmp($hdr, 'Subject') === 0) {
+ // Get the Subject out of the headers array so that we can
+ // pass it as a separate argument to mail().
+ $subject = $headers[$hdr];
+ unset($headers[$hdr]);
+ } elseif (strcasecmp($hdr, 'To') === 0) {
+ // Remove the To: header. The mail() function will add its
+ // own To: header based on the contents of $recipients.
+ unset($headers[$hdr]);
+ }
+ }
+
+ // Flatten the headers out.
+ list(, $text_headers) = $this->prepareHeaders($headers);
+
+ // mail() requires a string for $body. If resource, need to convert
+ // to a string.
+ if (is_resource($body)) {
+ $body_str = '';
+ rewind($body);
+ while (!feof($body)) {
+ $body_str .= fread($body, 8192);
+ }
+ $body = $body_str;
+ }
+
+ // We only use mail()'s optional fifth parameter if the additional
+ // parameters have been provided and we're not running in safe mode.
+ if (empty($this->_params) || ini_get('safe_mode')) {
+ $result = mail($recipients, $subject, $body, $text_headers);
+ } else {
+ $result = mail($recipients, $subject, $body, $text_headers, isset($this->_params['args']) ? $this->_params['args'] : '');
+ }
+
+ // If the mail() function returned failure, we need to create an
+ // Exception and return it instead of the boolean result.
+ if ($result === false) {
+ throw new Horde_Mail_Exception('mail() returned failure.');
+ }
+ }
+}
--- /dev/null
+<?php
+/**
+ * Mock mail transport.
+ *
+ * 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 Chuck Hagenbuch <chuck@horde.org>
+ * @copyright 2010 Chuck Hagenbuch
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ */
+
+/**
+ * Mock implementation, for testing.
+ *
+ * @category Horde
+ * @package Mail
+ */
+class Horde_Mail_Transport_Mock extends Horde_Mail_Transport
+{
+ /**
+ * Array of messages that have been sent with the mock.
+ *
+ * @var array
+ */
+ public $sentMessages = array();
+
+ /**
+ * Callback before sending mail.
+ *
+ * @var callback
+ */
+ protected $_preSendCallback;
+
+ /**
+ * Callback after sending mai.
+ *
+ * @var callback
+ */
+ protected $_postSendCallback;
+
+ /**
+ * Constructor.
+ *
+ * @param array Optional parameters:
+ * <pre>
+ * 'preSendCallback' - (callback) Called before an email would be sent.
+ * 'postSendCallback' - (callback) Called after an email would have been
+ * sent.
+ * </pre>
+ */
+ public function __construct(array $params = array())
+ {
+ if (isset($params['preSendCallback']) &&
+ is_callable($params['preSendCallback'])) {
+ $this->_preSendCallback = $params['preSendCallback'];
+ }
+
+ if (isset($params['postSendCallback']) &&
+ is_callable($params['postSendCallback'])) {
+ $this->_postSendCallback = $params['postSendCallback'];
+ }
+ }
+
+ /**
+ * Send a message. Silently discards all mail.
+ *
+ * @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 ($this->_preSendCallback) {
+ call_user_func_array($this->_preSendCallback, array($this, $recipients, $headers, $body));
+ }
+
+ $headers = $this->_sanitizeHeaders($headers);
+ list(, $text_headers) = $this->prepareHeaders($headers);
+
+ $this->sentMessages[] = array(
+ 'body' => $body,
+ 'headers' => $headers,
+ 'header_text' => $text_headers,
+ 'recipients' => $recipients
+ );
+
+ if ($this->_postSendCallback) {
+ call_user_func_array($this->_postSendCallback, array($this, $recipients, $headers, $body));
+ }
+ }
+}
--- /dev/null
+<?php
+/**
+ * Null implementation of the mail transport interface.
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2010 Phil Kernick
+ * 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 Phil Kernick <philk@rotfl.com.au>
+ * @copyright 2010 Phil Kernick
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ */
+
+/**
+ * Null implementation of the mail transport interface.
+ *
+ * @category Horde
+ * @package Mail
+ */
+class Horde_Mail_Transport_Null extends Horde_Mail_Transport
+{
+ /**
+ * 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)
+ {
+ }
+}
--- /dev/null
+<?php
+/**
+ * Sendmail interface.
+ *
+ * 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 Chuck Hagenbuch <chuck@horde.org>
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @copyright 2010 Chuck Hagenbuch
+ * @copyright 2010 Michael Slusarz
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ */
+
+/**
+ * Sendmail interface.
+ *
+ * @category Horde
+ * @package Mail
+ */
+class Horde_Mail_Transport_Sendmail extends Horde_Mail_Transport
+{
+ /**
+ * Any extra command-line parameters to pass to the sendmail or
+ * sendmail wrapper binary.
+ *
+ * @var string
+ */
+ protected $_sendmailArgs = '-i';
+
+ /**
+ * The location of the sendmail or sendmail wrapper binary on the
+ * filesystem.
+ *
+ * @var string
+ */
+ protected $_sendmailPath = '/usr/sbin/sendmail';
+
+ /**
+ * Constructor.
+ *
+ * @param array $params Additional parameters:
+ * <pre>
+ * 'sendmail_args' - (string) Any extra parameters to pass to the sendmail
+ * or sendmail wrapper binary.
+ * DEFAULT: -i
+ * 'sendmail_path' - (string) The location of the sendmail binary on the
+ * filesystem.
+ * DEFAULT: /usr/sbin/sendmail
+ * </pre>
+ */
+ public function __construct(array $params = array())
+ {
+ if (isset($params['sendmail_args'])) {
+ $this->_sendmailArgs = $params['sendmail_args'];
+ }
+
+ if (isset($params['sendmail_path'])) {
+ $this->_sendmailPath = $params['sendmail_path'];
+ }
+
+ /* Because we need to pass message headers to the sendmail program on
+ * the commandline, we can't guarantee the use of the standard "\r\n"
+ * separator. Instead, we use the system's native line separator. */
+ $this->sep = defined('PHP_EOL')
+ ? PHP_EOL
+ : (strpos(PHP_OS, 'WIN') === false) ? "\n" : "\r\n";
+ }
+
+ /**
+ * 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)
+ {
+ $recipients = implode(' ', array_map('escapeshellarg', $this->parseRecipients($recipients)));
+
+ $headers = $this->_sanitizeHeaders($headers);
+ list($from, $text_headers) = $this->prepareHeaders($headers);
+
+ /* 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)) {
+ throw new Horde_Mail_Exception('No From address given.');
+ } elseif ((strpos($from, ' ') !== false) ||
+ (strpos($from, ';') !== false) ||
+ (strpos($from, '&') !== false) ||
+ (strpos($from, '`') !== false)) {
+ throw new Horde_Mail_Exception('From address specified with dangerous characters.');
+ }
+
+ $mail = @popen($this->_sendmailPath . (empty($this->_sendmailArgs) ? '' : ' ' . $this->_sendmailargs) . ' -f' . escapeshellarg($from) . ' -- ' . $recipients, 'w');
+ if (!$mail) {
+ throw new Horde_Mail_Exception('Failed to open sendmail [' . $this->_sendmailPath . '] for execution.');
+ }
+
+ // Write the headers following by two newlines: one to end the headers
+ // section and a second to separate the headers block from the body.
+ fputs($mail, $text_headers . $this->sep . $this->sep);
+
+ if (is_resource($body)) {
+ rewind($body);
+ while (!feof($body)) {
+ fputs($mail, fread($body, 8192));
+ }
+ } else {
+ fputs($mail, $body);
+ }
+ $result = pclose($mail);
+
+ if (!$result) {
+ return;
+ }
+
+ switch ($result) {
+ case 64: // EX_USAGE
+ $msg = 'command line usage error';
+ break;
+
+ case 65: // EX_DATAERR
+ $msg = 'data format error';
+ break;
+
+ case 66: // EX_NOINPUT
+ $msg = 'cannot open input';
+ break;
+
+ case 67: // EX_NOUSER
+ $msg = 'addressee unknown';
+ break;
+
+ case 68: // EX_NOHOST
+ $msg = 'host name unknown';
+ break;
+
+ case 69: // EX_UNAVAILABLE
+ $msg = 'service unavailable';
+ break;
+
+ case 70: // EX_SOFTWARE
+ $msg = 'internal software error';
+ break;
+
+ case 71: // EX_OSERR
+ $msg = 'system error';
+ break;
+
+ case 72: // EX_OSFILE
+ $msg = 'critical system file missing';
+ break;
+
+ case 73: // EX_CANTCREAT
+ $msg = 'cannot create output file';
+ break;
+
+ case 74: // EX_IOERR
+ $msg = 'input/output error';
+
+ case 75: // EX_TEMPFAIL
+ $msg = 'temporary failure';
+ break;
+
+ case 76: // EX_PROTOCOL
+ $msg = 'remote error in protocol';
+ break;
+
+ case 77: // EX_NOPERM
+ $msg = 'permission denied';
+ break;
+
+ case 77: // EX_NOPERM
+ $msg = 'permission denied';
+ break;
+
+ case 78: // EX_CONFIG
+ $msg = 'configuration error';
+ break;
+
+ case 79: // EX_NOTFOUND
+ $msg = 'entry not found';
+ break;
+
+ default:
+ $msg = 'unknown error';
+ break;
+ }
+
+ throw new Horde_Mail_Exception('sendmail: ' . $msg . ' (' . $result . ')', $result);
+ }
+}
--- /dev/null
+<?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);
+ }
+}
--- /dev/null
+<?PHP
+/**
+ * SMTP MX implementation.
+ * Requires the Net_SMTP class.
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2010, gERD Schaufelberger
+ * 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 gERD Schaufelberger <gerd@php-tools.net>
+ * @copyright 2010 gERD Schaufelberger
+ * @license http://opensource.org/licenses/bsd-license.php New BSD License
+ */
+
+/**
+ * SMTP MX implementation.
+ *
+ * @author gERD Schaufelberger <gerd@php-tools.net>
+ * @category Horde
+ * @package Mail
+ */
+class Horde_Mail_Transport_Smtpmx extends Horde_Mail_Transport
+{
+ /**
+ * SMTP connection object.
+ *
+ * @var Net_SMTP
+ */
+ protected $_smtp = null;
+
+ /**
+ * Net_DNS_Resolver object.
+ *
+ * @var Net_DNS_Resolver
+ */
+ protected $_resolver;
+
+ /**
+ * Internal error codes.
+ * Translate internal error identifier to human readable messages.
+ *
+ * @var array
+ */
+ protected $_errorCode = array(
+ 'not_connected' => array(
+ 'code' => 1,
+ 'msg' => 'Could not connect to any mail server ({HOST}) at port {PORT} to send mail to {RCPT}.'
+ ),
+ 'failed_vrfy_rcpt' => array(
+ 'code' => 2,
+ 'msg' => 'Recipient "{RCPT}" could not be veryfied.'
+ ),
+ 'failed_set_from' => array(
+ 'code' => 3,
+ 'msg' => 'Failed to set sender: {FROM}.'
+ ),
+ 'failed_set_rcpt' => array(
+ 'code' => 4,
+ 'msg' => 'Failed to set recipient: {RCPT}.'
+ ),
+ 'failed_send_data' => array(
+ 'code' => 5,
+ 'msg' => 'Failed to send mail to: {RCPT}.'
+ ),
+ 'no_from' => array(
+ 'code' => 5,
+ 'msg' => 'No from address has be provided.'
+ ),
+ 'send_data' => array(
+ 'code' => 7,
+ 'msg' => 'Failed to create Net_SMTP object.'
+ ),
+ 'no_mx' => array(
+ 'code' => 8,
+ 'msg' => 'No MX-record for {RCPT} found.'
+ ),
+ 'no_resolver' => array(
+ 'code' => 9,
+ 'msg' => 'Could not start resolver! Install PEAR:Net_DNS or switch off "netdns"'
+ ),
+ 'failed_rset' => array(
+ 'code' => 10,
+ 'msg' => 'RSET command failed, SMTP-connection corrupt.'
+ )
+ );
+
+ /**
+ * Constructor.
+ *
+ * @param array $params Additional options:
+ * <pre>
+ * 'debug' - (boolean) Activate SMTP and Net_DNS debug mode?
+ * DEFAULT: false
+ * 'mailname' - (string) The name of the local mail system (a valid
+ * hostname which matches the reverse lookup)
+ * DEFAULT: Auto-determined
+ * 'netdns' - (boolean) Use PEAR:Net_DNS (true) or the PHP builtin
+ * getmxrr().
+ * DEFAULT: true
+ * 'port' - (integer) Port.
+ * DEFAULT: Auto-determined
+ * 'test' - (boolean) Activate test mode?
+ * DEFAULT: false
+ * 'timeout' - (integer) The SMTP connection timeout (in seconds).
+ * DEFAULT: 10
+ * 'verp' - (boolean) Whether to use VERP.
+ * If not a boolean, the string value will be used as the VERP
+ * separators.
+ * DEFAULT: false
+ * 'vrfy' - (boolean) Whether to use VRFY.
+ * DEFAULT: false
+ * </pre>
+ */
+ public function __construct(array $params = array())
+ {
+ /* Try to find a valid mailname. */
+ if (!isset($params['mailname']) && function_exists('posix_uname')) {
+ $uname = posix_uname();
+ $params['mailname'] = $uname['nodename'];
+ }
+
+ if (!isset($params['port'])) {
+ $params['port'] = getservbyname('smtp', 'tcp');
+ }
+
+ $this->_params = array_merge(array(
+ 'debug' => false,
+ 'mailname' => 'localhost',
+ 'netdns' => true,
+ 'port' => 25,
+ 'test' => false,
+ 'timeout' => 10,
+ 'verp' => false,
+ 'vrfy' => false
+ ), $params);
+ }
+
+ /**
+ * Destructor implementation to ensure that we disconnect from any
+ * potentially-alive persistent SMTP connections.
+ */
+ public function __destruct()
+ {
+ if (is_object($this->_smtp)) {
+ $this->_smtp->disconnect();
+ $this->_smtp = null;
+ }
+ }
+
+ /**
+ * 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)
+ {
+ $headers = $this->_sanitizeHeaders($headers);
+
+ // Prepare headers
+ list($from, $textHeaders) = $this->prepareHeaders($headers);
+
+ // Use 'Return-Path' if possible
+ foreach (array_keys($headers) as $hdr) {
+ if (strcasecmp($hdr, 'Return-Path') === 0) {
+ $from = $headers['Return-Path'];
+ break;
+ }
+ }
+
+ if (!strlen($from)) {
+ $this->_error('no_from');
+ }
+
+ // Prepare recipients
+ foreach ($this->parseRecipients($recipients) as $rcpt) {
+ list($user, $host) = explode('@', $rcpt);
+
+ $mx = $this->_getMx($host);
+ if (!$mx) {
+ $this->_error('no_mx', array('rcpt' => $rcpt));
+ }
+
+ $connected = false;
+ foreach ($mx as $mserver => $mpriority) {
+ $this->_smtp = new Net_SMTP($mserver, $this->_params['port'], $this->_params['mailname']);
+
+ // 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->_smtp = null;
+ continue;
+ }
+
+ // connection established
+ if ($res) {
+ $connected = true;
+ break;
+ }
+ }
+
+ if (!$connected) {
+ $this->_error('not_connected', array(
+ 'host' => implode(', ', array_keys($mx)),
+ 'port' => $this->_params['port'],
+ 'rcpt' => $rcpt
+ ));
+ }
+
+ // Verify recipient
+ if ($this->_params['vrfy']) {
+ $res = $this->_smtp->vrfy($rcpt);
+ if ($res instanceof PEAR_Error) {
+ $this->_error('failed_vrfy_rcpt', array('rcpt' => $rcpt));
+ }
+ }
+
+ // mail from:
+ $args['verp'] = $this->_params['verp'];
+ $res = $this->_smtp->mailFrom($from, $args);
+ if ($res instanceof PEAR_Error) {
+ $this->_error('failed_set_from', array('from' => $from));
+ }
+
+ // rcpt to:
+ $res = $this->_smtp->rcptTo($rcpt);
+ if ($res instanceof PEAR_Error) {
+ $this->_error('failed_set_rcpt', array('rcpt' => $rcpt));
+ }
+
+ // Don't send anything in test mode
+ if ($this->_params['test']) {
+ $res = $this->_smtp->rset();
+ if ($res instanceof PEAR_Error) {
+ $this->_error('failed_rset');
+ }
+
+ $this->_smtp->disconnect();
+ $this->_smtp = null;
+ return;
+ }
+
+ // Send data
+ $res = $this->_smtp->data($body, $textHeaders);
+ if ($res instanceof PEAR_Error) {
+ $this->_error('failed_send_data', array('rcpt' => $rcpt));
+ }
+
+ $this->_smtp->disconnect();
+ $this->_smtp = null;
+ }
+ }
+
+ /**
+ * Recieve MX records for a host.
+ *
+ * @param string $host Mail host.
+ *
+ * @return mixed Sorted MX list or false on error.
+ */
+ protected function _getMx($host)
+ {
+ $mx = array();
+
+ if ($this->params['netdns']) {
+ $this->_loadNetDns();
+
+ $response = $this->_resolver->query($host, 'MX');
+ if (!$response) {
+ return false;
+ }
+
+ foreach ($response->answer as $rr) {
+ if ($rr->type == 'MX') {
+ $mx[$rr->exchange] = $rr->preference;
+ }
+ }
+ } else {
+ $mxHost = $mxWeight = array();
+
+ if (!getmxrr($host, $mxHost, $mxWeight)) {
+ return false;
+ }
+
+ for ($i = 0; $i < count($mxHost); ++$i) {
+ $mx[$mxHost[$i]] = $mxWeight[$i];
+ }
+ }
+
+ asort($mx);
+
+ return $mx;
+ }
+
+ /**
+ * Initialize Net_DNS_Resolver.
+ */
+ protected function _loadNetDns()
+ {
+ if (!$this->_resolver) {
+ if (!class_exists('Net_DNS_Resolver')) {
+ $this->_error('no_resolver');
+ }
+
+ $this->_resolver = new Net_DNS_Resolver();
+ if ($this->_params['debug']) {
+ $this->_resolver->test = 1;
+ }
+ }
+ }
+
+ /**
+ * Format error message.
+ *
+ * @param string $id Maps error ids to codes and message.
+ * @param array $info Optional information in associative array.
+ *
+ * @throws Horde_Mail_Exception
+ */
+ protected function _error($id, $info = array())
+ {
+ $msg = $this->_errorCode[$id]['msg'];
+
+ // include info to messages
+ if (!empty($info)) {
+ $replace = $search = array();
+
+ foreach ($info as $key => $value) {
+ $search[] = '{' . strtoupper($key) . '}';
+ $replace[] = $value;
+ }
+
+ $msg = str_replace($search, $replace, $msg);
+ }
+
+ throw new Horde_Mail_Exception($msg, $this->_errorCode[$id]['code']);
+ }
+}
<dir name="lib">
<dir name="Horde">
<dir name="Mail">
- <file name="Driver.php" role="php" />
+ <dir name="Transport">
+ <file name="Mail.php" role="php" />
+ <file name="Mock.php" role="php" />
+ <file name="Null.php" role="php" />
+ <file name="Sendmail.php" role="php" />
+ <file name="Smtp.php" role="php" />
+ <file name="Smtpmx.php" role="php" />
+ </dir> <!-- /lib/Horde/Mail/Transport -->
<file name="Exception.php" role="php" />
- <file name="Mail.php" role="php" />
- <file name="Mock.php" role="php" />
- <file name="Null.php" role="php" />
<file name="Rfc822.php" role="php" />
- <file name="Sendmail.php" role="php" />
- <file name="Smtp.php" role="php" />
- <file name="Smtpmx.php" role="php" />
+ <file name="Transport.php" role="php" />
</dir> <!-- /lib/Horde/Mail -->
<file name="Mail.php" role="php" />
</dir> <!-- /lib/Horde -->
</dependencies>
<phprelease>
<filelist>
- <install name="lib/Horde/Mail/Driver.php" as="Horde/Mail/Driver.php" />
+ <install name="lib/Horde/Mail/Transport/Mail.php" as="Horde/Mail/Transport/Mail.php" />
+ <install name="lib/Horde/Mail/Transport/Mock.php" as="Horde/Mail/Transport/Mock.php" />
+ <install name="lib/Horde/Mail/Transport/Null.php" as="Horde/Mail/Transport/Null.php" />
+ <install name="lib/Horde/Mail/Transport/Sendmail.php" as="Horde/Mail/Transport/Sendmail.php" />
+ <install name="lib/Horde/Mail/Transport/Smtp.php" as="Horde/Mail/Transport/Smtp.php" />
+ <install name="lib/Horde/Mail/Transport/Smtpmx.php" as="Horde/Mail/Transport/Smtpmx.php" />
<install name="lib/Horde/Mail/Exception.php" as="Horde/Mail/Exception.php" />
- <install name="lib/Horde/Mail/Mail.php" as="Horde/Mail/Mail.php" />
- <install name="lib/Horde/Mail/Mock.php" as="Horde/Mail/Mock.php" />
- <install name="lib/Horde/Mail/Null.php" as="Horde/Mail/Null.php" />
<install name="lib/Horde/Mail/Rfc822.php" as="Horde/Mail/Rfc822.php" />
- <install name="lib/Horde/Mail/Sendmail.php" as="Horde/Mail/Sendmail.php" />
- <install name="lib/Horde/Mail/Smtp.php" as="Horde/Mail/Smtp.php" />
- <install name="lib/Horde/Mail/Smtpmx.php" as="Horde/Mail/Smtpmx.php" />
+ <install name="lib/Horde/Mail/Transport.php" as="Horde/Mail/Transport.php" />
<install name="lib/Horde/Mail.php" as="Horde/Mail.php" />
- <install name="test/Horde/Mail/AllTests.php" as="test/Horde/Mail/AllTests.php" />
- <install name="test/Horde/Mail/ParseTest.php" as="test/Horde/Mail/ParseTest.php" />
</filelist>
</phprelease>
</package>
* @param string $email The address list to send to.
* @param Horde_Mime_Headers $headers The Horde_Mime_Headers object
* holding this message's headers.
- * @param Horde_Mail_Driver $mailer A Horde_Mail_Driver object.
+ * @param Horde_Mail_Transport $mailer A Horde_Mail_Transport object.
*
* @throws Horde_Mime_Exception
* @throws InvalidArgumentException
*/
- public function send($email, $headers, $mailer)
+ public function send($email, $headers, Horde_Mail_Transport $mailer)
{
- if (!($mailer instanceof Horde_Mail_Driver)) {
- throw new InvalidArgumentException('Invalid Horde_Mail_Driver object passed to send().');
- }
-
$old_basepart = $this->_basepart;
$this->_basepart = true;
$params['password'] = $imap_ob->getParam('password');
}
- return Horde_Mail::factory($GLOBALS['conf']['mailer']['type'], $params);
+ $transport = $GLOBALS['conf']['mailer']['type'];
+ $class = 'Horde_Mail_Transport_' . ucfirst($transport);
+ if (class_exists($class)) {
+ return new $class($params);
+ }
+ throw new Horde_Exception('Unable to find class for transport ' . $transport);
}
/**
{
return false;
}
-
}