Index: branches/version-2_5-dev/data/module/Mail/mail.php
===================================================================
--- branches/version-2_5-dev/data/module/Mail/mail.php	(revision 16503)
+++ branches/version-2_5-dev/data/module/Mail/mail.php	(revision 19942)
@@ -1,21 +1,46 @@
 <?php
-//
-// +----------------------------------------------------------------------+
-// | PHP Version 4                                                        |
-// +----------------------------------------------------------------------+
-// | Copyright (c) 1997-2003 The PHP Group                                |
-// +----------------------------------------------------------------------+
-// | This source file is subject to version 2.02 of the PHP license,      |
-// | that is bundled with this package in the file LICENSE, and is        |
-// | available at through the world-wide-web at                           |
-// | http://www.php.net/license/2_02.txt.                                 |
-// | If you did not receive a copy of the PHP license and are unable to   |
-// | obtain it through the world-wide-web, please send a note to          |
-// | license@php.net so we can mail you a copy immediately.               |
-// +----------------------------------------------------------------------+
-// | Author: Chuck Hagenbuch <chuck@horde.org>                            |
-// +----------------------------------------------------------------------+
-//
-// $Id$
+/**
+ * internal PHP-mail() implementation of the PEAR Mail:: interface.
+ *
+ * PHP versions 4 and 5
+ *
+ * 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    Mail
+ * @package     Mail
+ * @author      Chuck Hagenbuch <chuck@horde.org> 
+ * @copyright   2010 Chuck Hagenbuch
+ * @license     http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version     CVS: $Id$
+ * @link        http://pear.php.net/package/Mail/
+ */
 
 /**
@@ -42,7 +67,7 @@
     function Mail_mail($params = null)
     {
-        /* The other mail implementations accept parameters as arrays.
-         * In the interest of being consistent, explode an array into
-         * a string of parameter arguments. */
+        // The other mail implementations accept parameters as arrays.
+        // In the interest of being consistent, explode an array into
+        // a string of parameter arguments.
         if (is_array($params)) {
             $this->_params = join(' ', $params);
@@ -62,5 +87,5 @@
     }
 
-	/**
+    /**
      * Implements Mail_mail::send() function using php's built-in mail()
      * command.
@@ -90,5 +115,12 @@
     function send($recipients, $headers, $body)
     {
-        $this->_sanitizeHeaders($headers);
+        if (!is_array($headers)) {
+            return PEAR::raiseError('$headers must be an array');
+        }
+
+        $result = $this->_sanitizeHeaders($headers);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
 
         // If we're passed an array of recipients, implode it.
@@ -105,21 +137,17 @@
         }
 
-        /*
-         * Also remove the To: header.  The mail() function will add its own
-         * To: header based on the contents of $recipients.
-         */
+        // Also remove the To: header.  The mail() function will add its own
+        // To: header based on the contents of $recipients.
         unset($headers['To']);
 
         // Flatten the headers out.
         $headerElements = $this->prepareHeaders($headers);
-        if (PEAR::isError($headerElements)) {
+        if (is_a($headerElements, 'PEAR_Error')) {
             return $headerElements;
         }
         list(, $text_headers) = $headerElements;
 
-        /*
-         * We only use mail()'s optional fifth parameter if the additional
-         * parameters have been provided and we're not running in safe mode.
-         */
+        // 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);
@@ -129,8 +157,6 @@
         }
 
-        /*
-         * If the mail() function returned failure, we need to create a
-         * PEAR_Error object and return it instead of the boolean result.
-         */
+        // If the mail() function returned failure, we need to create a
+        // PEAR_Error object and return it instead of the boolean result.
         if ($result === false) {
             $result = PEAR::raiseError('mail() returned failure');
Index: branches/version-2_5-dev/data/module/Mail/smtp.php
===================================================================
--- branches/version-2_5-dev/data/module/Mail/smtp.php	(revision 18253)
+++ branches/version-2_5-dev/data/module/Mail/smtp.php	(revision 19942)
@@ -1,20 +1,47 @@
 <?php
-//
-// +----------------------------------------------------------------------+
-// | PHP Version 4                                                        |
-// +----------------------------------------------------------------------+
-// | Copyright (c) 1997-2003 The PHP Group                                |
-// +----------------------------------------------------------------------+
-// | This source file is subject to version 2.02 of the PHP license,      |
-// | that is bundled with this package in the file LICENSE, and is        |
-// | available at through the world-wide-web at                           |
-// | http://www.php.net/license/2_02.txt.                                 |
-// | If you did not receive a copy of the PHP license and are unable to   |
-// | obtain it through the world-wide-web, please send a note to          |
-// | license@php.net so we can mail you a copy immediately.               |
-// +----------------------------------------------------------------------+
-// | Authors: Chuck Hagenbuch <chuck@horde.org>                           |
-// |          Jon Parise <jon@php.net>                                    |
-// +----------------------------------------------------------------------+
+/**
+ * SMTP implementation of the PEAR Mail interface. Requires the Net_SMTP class.
+ *
+ * PHP versions 4 and 5
+ *
+ * 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    HTTP
+ * @package     HTTP_Request
+ * @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
+ * @version     CVS: $Id: smtp.php 294747 2010-02-08 08:18:33Z clockwerx $
+ * @link        http://pear.php.net/package/Mail/
+ */
 
 /** Error: Failed to create a Net_SMTP object */
@@ -43,5 +70,5 @@
  * @access public
  * @package Mail
- * @version $Revision: 1.28 $
+ * @version $Revision: 294747 $
  */
 class Mail_smtp extends Mail {
@@ -56,4 +83,11 @@
 
     /**
+     * The list of service extension parameters to pass to the Net_SMTP
+     * mailFrom() command.
+     * @var array
+     */
+    var $_extparams = array();
+
+    /**
      * The SMTP host to connect to.
      * @var string
@@ -108,12 +142,4 @@
 
     /**
-     * Whether to use VERP or not. If not a boolean, the string value
-     * will be used as the VERP separators.
-     *
-     * @var mixed boolean or string
-     */
-    var $verp = false;
-
-    /**
      * Turn on Net_SMTP debugging?
      *
@@ -129,4 +155,12 @@
      */
     var $persist = false;
+
+    /**
+     * Use SMTP command pipelining (specified in RFC 2920) if the SMTP server
+     * supports it. This speeds up delivery over high-latency connections. By
+     * default, use the default value supplied by Net_SMTP.
+     * @var bool
+     */
+    var $pipelining;
 
     /**
@@ -143,6 +177,8 @@
      *     timeout     The SMTP connection timeout. Defaults to none.
      *     verp        Whether to use VERP or not. Defaults to false.
+     *                 DEPRECATED as of 1.2.0 (use setMailParams()).
      *     debug       Activate SMTP debug mode? Defaults to false.
      *     persist     Should the SMTP connection persist?
+     *     pipelining  Use SMTP command pipelining
      *
      * If a parameter is present in the $params array, it replaces the
@@ -162,7 +198,12 @@
         if (isset($params['localhost'])) $this->localhost = $params['localhost'];
         if (isset($params['timeout'])) $this->timeout = $params['timeout'];
-        if (isset($params['verp'])) $this->verp = $params['verp'];
-        if (isset($params['debug'])) $this->debug = (boolean)$params['debug'];
-        if (isset($params['persist'])) $this->persist = (boolean)$params['persist'];
+        if (isset($params['debug'])) $this->debug = (bool)$params['debug'];
+        if (isset($params['persist'])) $this->persist = (bool)$params['persist'];
+        if (isset($params['pipelining'])) $this->pipelining = (bool)$params['pipelining'];
+
+        // Deprecated options
+        if (isset($params['verp'])) {
+            $this->addServiceExtensionParameter('XVERP', is_bool($params['verp']) ? null : $params['verp']);
+        }
 
         register_shutdown_function(array(&$this, '_Mail_smtp'));
@@ -195,5 +236,5 @@
      *
      * @param string $body The full text of the message body, including any
-     *               Mime parts, etc.
+     *               MIME parts, etc.
      *
      * @return mixed Returns true on success, or a PEAR_Error
@@ -204,49 +245,18 @@
     function send($recipients, $headers, $body)
     {
-        $include_dir = realpath(dirname( __FILE__));
-        include_once $include_dir . "/../Net/SMTP.php";
-
         /* If we don't already have an SMTP object, create one. */
-        if (is_object($this->_smtp) === false) {
-            $this->_smtp =& new Net_SMTP($this->host, $this->port,
-                                         $this->localhost);
-
-            /* If we still don't have an SMTP object at this point, fail. */
-            if (is_object($this->_smtp) === false) {
-                return PEAR::raiseError('Failed to create a Net_SMTP object',
-                                        PEAR_MAIL_SMTP_ERROR_CREATE);
-            }
-
-            /* Configure the SMTP connection. */
-            if ($this->debug) {
-                $this->_smtp->setDebug(true);
-            }
-
-            /* Attempt to connect to the configured SMTP server. */
-            if (PEAR::isError($res = $this->_smtp->connect($this->timeout))) {
-                $error = $this->_error('Failed to connect to ' .
-                                       $this->host . ':' . $this->port,
-                                       $res);
-                return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_CONNECT);
-            }
-
-            /* Attempt to authenticate if authentication has been enabled. */
-            if ($this->auth) {
-                $method = is_string($this->auth) ? $this->auth : '';
-
-                if (PEAR::isError($res = $this->_smtp->auth($this->username,
-                                                            $this->password,
-                                                            $method))) {
-                    $error = $this->_error("$method authentication failure",
-                                           $res);
-                    $this->_smtp->rset();
-                    return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_AUTH);
-                }
-            }
+        $result = &$this->getSMTPObject();
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        if (!is_array($headers)) {
+            return PEAR::raiseError('$headers must be an array');
         }
 
         $this->_sanitizeHeaders($headers);
+
         $headerElements = $this->prepareHeaders($headers);
-        if (PEAR::isError($headerElements)) {
+        if (is_a($headerElements, 'PEAR_Error')) {
             $this->_smtp->rset();
             return $headerElements;
@@ -267,6 +277,11 @@
         }
 
-        $args['verp'] = $this->verp;
-        if (PEAR::isError($res = $this->_smtp->mailFrom($from, $args))) {
+        $params = null;
+        if (!empty($this->_extparams)) {
+            foreach ($this->_extparams as $key => $val) {
+                $params .= ' ' . $key . (is_null($val) ? '' : '=' . $val);
+            }
+        }
+        if (PEAR::isError($res = $this->_smtp->mailFrom($from, ltrim($params)))) {
             $error = $this->_error("Failed to set sender: $from", $res);
             $this->_smtp->rset();
@@ -275,5 +290,5 @@
 
         $recipients = $this->parseRecipients($recipients);
-        if (PEAR::isError($recipients)) {
+        if (is_a($recipients, 'PEAR_Error')) {
             $this->_smtp->rset();
             return $recipients;
@@ -281,7 +296,7 @@
 
         foreach ($recipients as $recipient) {
-            if (PEAR::isError($res = $this->_smtp->rcptTo($recipient))) {
-                $error = $this->_error("Failed to add recipient: $recipient",
-                                       $res);
+            $res = $this->_smtp->rcptTo($recipient);
+            if (is_a($res, 'PEAR_Error')) {
+                $error = $this->_error("Failed to add recipient: $recipient", $res);
                 $this->_smtp->rset();
                 return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_RECIPIENT);
@@ -290,5 +305,16 @@
 
         /* Send the message's headers and the body as SMTP data. */
-        if (PEAR::isError($res = $this->_smtp->data($textHeaders . "\r\n\r\n" . $body))) {
+        $res = $this->_smtp->data($textHeaders . "\r\n\r\n" . $body);
+		list(,$args) = $this->_smtp->getResponse();
+
+		if (preg_match("/Ok: queued as (.*)/", $args, $queued)) {
+			$this->queued_as = $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 (is_a($res, 'PEAR_Error')) {
             $error = $this->_error('Failed to send data', $res);
             $this->_smtp->rset();
@@ -305,4 +331,75 @@
 
     /**
+     * Connect to the SMTP server by instantiating a Net_SMTP object.
+     *
+     * @return mixed Returns a reference to the Net_SMTP object on success, or
+     *               a PEAR_Error containing a descriptive error message on
+     *               failure.
+     *
+     * @since  1.2.0
+     * @access public
+     */
+    function &getSMTPObject()
+    {
+        if (is_object($this->_smtp) !== false) {
+            return $this->_smtp;
+        }
+
+        include_once 'Net/SMTP.php';
+        $this->_smtp = &new Net_SMTP($this->host,
+                                     $this->port,
+                                     $this->localhost);
+
+        /* If we still don't have an SMTP object at this point, fail. */
+        if (is_object($this->_smtp) === false) {
+            return PEAR::raiseError('Failed to create a Net_SMTP object',
+                                    PEAR_MAIL_SMTP_ERROR_CREATE);
+        }
+
+        /* Configure the SMTP connection. */
+        if ($this->debug) {
+            $this->_smtp->setDebug(true);
+        }
+
+        /* Attempt to connect to the configured SMTP server. */
+        if (PEAR::isError($res = $this->_smtp->connect($this->timeout))) {
+            $error = $this->_error('Failed to connect to ' .
+                                   $this->host . ':' . $this->port,
+                                   $res);
+            return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_CONNECT);
+        }
+
+        /* Attempt to authenticate if authentication has been enabled. */
+        if ($this->auth) {
+            $method = is_string($this->auth) ? $this->auth : '';
+
+            if (PEAR::isError($res = $this->_smtp->auth($this->username,
+                                                        $this->password,
+                                                        $method))) {
+                $error = $this->_error("$method authentication failure",
+                                       $res);
+                $this->_smtp->rset();
+                return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_AUTH);
+            }
+        }
+
+        return $this->_smtp;
+    }
+
+    /**
+     * Add parameter associated with a SMTP service extension.
+     *
+     * @param string Extension keyword.
+     * @param string Any value the keyword needs.
+     *
+     * @since 1.2.0
+     * @access public
+     */
+    function addServiceExtensionParameter($keyword, $value = null)
+    {
+        $this->_extparams[$keyword] = $value;
+    }
+
+    /**
      * Disconnect and destroy the current SMTP connection.
      *
@@ -340,9 +437,7 @@
 
         /* Build our standardized error string. */
-        $msg = $text;
-        $msg .= ' [SMTP: ' . $error->getMessage();
-        $msg .= " (code: $code, response: $response)]";
-
-        return $msg;
+        return $text
+            . ' [SMTP: ' . $error->getMessage()
+            . " (code: $code, response: $response)]";
     }
 
Index: branches/version-2_5-dev/data/module/Mail/RFC822.php
===================================================================
--- branches/version-2_5-dev/data/module/Mail/RFC822.php	(revision 16302)
+++ branches/version-2_5-dev/data/module/Mail/RFC822.php	(revision 19942)
@@ -1,36 +1,47 @@
 <?php
-// +-----------------------------------------------------------------------+
-// | Copyright (c) 2001-2002, 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.  |
-// |                                                                       |
-// +-----------------------------------------------------------------------+
-// | Authors: Richard Heyes <richard@phpguru.org>                          |
-// |          Chuck Hagenbuch <chuck@horde.org>                            |
-// +-----------------------------------------------------------------------+
+/**
+ * RFC 822 Email address list validation Utility
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2001-2010, 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      Richard Heyes <richard@phpguru.org>
+ * @author      Chuck Hagenbuch <chuck@horde.org
+ * @copyright   2001-2010 Richard Heyes
+ * @license     http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version     CVS: $Id: RFC822.php 294749 2010-02-08 08:22:25Z clockwerx $
+ * @link        http://pear.php.net/package/Mail/
+ */
 
 /**
@@ -53,5 +64,5 @@
  * @author  Richard Heyes <richard@phpguru.org>
  * @author  Chuck Hagenbuch <chuck@horde.org>
- * @version $Revision: 1.23 $
+ * @version $Revision: 294749 $
  * @license BSD
  * @package Mail
@@ -185,5 +196,5 @@
 
         if ($this->address === false || isset($this->error)) {
-            require_once dirname(__FILE__) . '/../PEAR.php';
+            require_once 'PEAR.php';
             return PEAR::raiseError($this->error);
         }
@@ -195,5 +206,5 @@
 
             if ($valid === false || isset($this->error)) {
-                require_once dirname(__FILE__) . '/../PEAR.php';
+                require_once 'PEAR.php';
                 return PEAR::raiseError($this->error);
             }
@@ -343,20 +354,37 @@
 
     /**
-     * Checks if a string has an unclosed quotes or not.
-     *
-     * @access private
-     * @param string $string The string to check.
-     * @return boolean True if there are unclosed quotes inside the string, false otherwise.
+     * Checks if a string has unclosed quotes or not.
+     *
+     * @access private
+     * @param string $string  The string to check.
+     * @return boolean  True if there are unclosed quotes inside the string,
+     *                  false otherwise.
      */
     function _hasUnclosedQuotes($string)
     {
-        $string     = explode('"', $string);
-        $string_cnt = count($string);
-
-        for ($i = 0; $i < (count($string) - 1); $i++)
-            if (substr($string[$i], -1) == '\\')
-                $string_cnt--;
-
-        return ($string_cnt % 2 === 0);
+        $string = trim($string);
+        $iMax = strlen($string);
+        $in_quote = false;
+        $i = $slashes = 0;
+
+        for (; $i < $iMax; ++$i) {
+            switch ($string[$i]) {
+            case '\\':
+                ++$slashes;
+                break;
+
+            case '"':
+                if ($slashes % 2 == 0) {
+                    $in_quote = !$in_quote;
+                }
+                // Fall through to default action below.
+
+            default:
+                $slashes = 0;
+                break;
+            }
+        }
+
+        return $in_quote;
     }
 
@@ -619,6 +647,6 @@
                 $comments[] = $comment;
 
-                // +1 is for the trailing )
-                $_mailbox   = substr($_mailbox, strpos($_mailbox, $comment)+strlen($comment)+1);
+                // +2 is for the brackets
+                $_mailbox = substr($_mailbox, strpos($_mailbox, '('.$comment)+strlen($comment)+2);
             } else {
                 break;
Index: branches/version-2_5-dev/data/module/Mail/smtpmx.php
===================================================================
--- branches/version-2_5-dev/data/module/Mail/smtpmx.php	(revision 19942)
+++ branches/version-2_5-dev/data/module/Mail/smtpmx.php	(revision 19942)
@@ -0,0 +1,502 @@
+<?PHP
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * SMTP MX
+ *
+ * SMTP MX implementation of the PEAR Mail interface. Requires the Net_SMTP class.
+ *
+ * PHP versions 4 and 5
+ *
+ * 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   Mail
+ * @package    Mail_smtpmx
+ * @author     gERD Schaufelberger <gerd@php-tools.net>
+ * @copyright  2010 gERD Schaufelberger
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    CVS: $Id: smtpmx.php 294747 2010-02-08 08:18:33Z clockwerx $
+ * @link       http://pear.php.net/package/Mail/
+ */
+
+require_once 'Net/SMTP.php';
+
+/**
+ * SMTP MX implementation of the PEAR Mail interface. Requires the Net_SMTP class.
+ *
+ *
+ * @access public
+ * @author  gERD Schaufelberger <gerd@php-tools.net>
+ * @package Mail
+ * @version $Revision: 294747 $
+ */
+class Mail_smtpmx extends Mail {
+
+    /**
+     * SMTP connection object.
+     *
+     * @var object
+     * @access private
+     */
+    var $_smtp = null;
+
+    /**
+     * The port the SMTP server is on.
+     * @var integer
+     * @see getservicebyname()
+     */
+    var $port = 25;
+
+    /**
+     * Hostname or domain that will be sent to the remote SMTP server in the
+     * HELO / EHLO message.
+     *
+     * @var string
+     * @see posix_uname()
+     */
+    var $mailname = 'localhost';
+
+    /**
+     * SMTP connection timeout value.  NULL indicates no timeout.
+     *
+     * @var integer
+     */
+    var $timeout = 10;
+
+    /**
+     * use either PEAR:Net_DNS or getmxrr
+     *
+     * @var boolean
+     */
+    var $withNetDns = true;
+
+    /**
+     * PEAR:Net_DNS_Resolver
+     *
+     * @var object
+     */
+    var $resolver;
+
+    /**
+     * Whether to use VERP or not. If not a boolean, the string value
+     * will be used as the VERP separators.
+     *
+     * @var mixed boolean or string
+     */
+    var $verp = false;
+
+    /**
+     * Whether to use VRFY or not.
+     *
+     * @var boolean $vrfy
+     */
+    var $vrfy = false;
+
+    /**
+     * Switch to test mode - don't send emails for real
+     *
+     * @var boolean $debug
+     */
+    var $test = false;
+
+    /**
+     * Turn on Net_SMTP debugging?
+     *
+     * @var boolean $peardebug
+     */
+    var $debug = false;
+
+    /**
+     * internal error codes
+     *
+     * translate internal error identifier to PEAR-Error codes and human
+     * readable messages.
+     *
+     * @var boolean $debug
+     * @todo as I need unique error-codes to identify what exactly went wrond
+     *       I did not use intergers as it should be. Instead I added a "namespace"
+     *       for each code. This avoids conflicts with error codes from different
+     *       classes. How can I use unique error codes and stay conform with PEAR?
+     */
+    var $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.
+     *
+     * Instantiates a new Mail_smtp:: object based on the parameters
+     * passed in. It looks for the following parameters:
+     *     mailname    The name of the local mail system (a valid hostname which matches the reverse lookup)
+     *     port        smtp-port - the default comes from getservicebyname() and should work fine
+     *     timeout     The SMTP connection timeout. Defaults to 30 seconds.
+     *     vrfy        Whether to use VRFY or not. Defaults to false.
+     *     verp        Whether to use VERP or not. Defaults to false.
+     *     test        Activate test mode? Defaults to false.
+     *     debug       Activate SMTP and Net_DNS debug mode? Defaults to false.
+     *     netdns      whether to use PEAR:Net_DNS or the PHP build in function getmxrr, default is true
+     *
+     * If a parameter is present in the $params array, it replaces the
+     * default.
+     *
+     * @access public
+     * @param array Hash containing any parameters different from the
+     *              defaults.
+     * @see _Mail_smtpmx()
+     */
+    function __construct($params)
+    {
+        if (isset($params['mailname'])) {
+            $this->mailname = $params['mailname'];
+        } else {
+            // try to find a valid mailname
+            if (function_exists('posix_uname')) {
+                $uname = posix_uname();
+                $this->mailname = $uname['nodename'];
+            }
+        }
+
+        // port number
+        if (isset($params['port'])) {
+            $this->_port = $params['port'];
+        } else {
+            $this->_port = getservbyname('smtp', 'tcp');
+        }
+
+        if (isset($params['timeout'])) $this->timeout = $params['timeout'];
+        if (isset($params['verp'])) $this->verp = $params['verp'];
+        if (isset($params['test'])) $this->test = $params['test'];
+        if (isset($params['peardebug'])) $this->test = $params['peardebug'];
+        if (isset($params['netdns'])) $this->withNetDns = $params['netdns'];
+    }
+
+    /**
+     * Constructor wrapper for PHP4
+     *
+     * @access public
+     * @param array Hash containing any parameters different from the defaults
+     * @see __construct()
+     */
+    function Mail_smtpmx($params)
+    {
+        $this->__construct($params);
+        register_shutdown_function(array(&$this, '__destruct'));
+    }
+
+    /**
+     * Destructor implementation to ensure that we disconnect from any
+     * potentially-alive persistent SMTP connections.
+     */
+    function __destruct()
+    {
+        if (is_object($this->_smtp)) {
+            $this->_smtp->disconnect();
+            $this->_smtp = null;
+        }
+    }
+
+    /**
+     * Implements Mail::send() function using SMTP direct delivery
+     *
+     * @access public
+     * @param mixed $recipients in RFC822 style or array
+     * @param array $headers The array of headers to send with the mail.
+     * @param string $body The full text of the message body,
+     * @return mixed Returns true on success, or a PEAR_Error
+     */
+    function send($recipients, $headers, $body)
+    {
+        if (!is_array($headers)) {
+            return PEAR::raiseError('$headers must be an array');
+        }
+
+        $result = $this->_sanitizeHeaders($headers);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
+        // Prepare headers
+        $headerElements = $this->prepareHeaders($headers);
+        if (is_a($headerElements, 'PEAR_Error')) {
+            return $headerElements;
+        }
+        list($from, $textHeaders) = $headerElements;
+
+        // use 'Return-Path' if possible
+        if (!empty($headers['Return-Path'])) {
+            $from = $headers['Return-Path'];
+        }
+        if (!isset($from)) {
+            return $this->_raiseError('no_from');
+        }
+
+        // Prepare recipients
+        $recipients = $this->parseRecipients($recipients);
+        if (is_a($recipients, 'PEAR_Error')) {
+            return $recipients;
+        }
+
+        foreach ($recipients as $rcpt) {
+            list($user, $host) = explode('@', $rcpt);
+
+            $mx = $this->_getMx($host);
+            if (is_a($mx, 'PEAR_Error')) {
+                return $mx;
+            }
+
+            if (empty($mx)) {
+                $info = array('rcpt' => $rcpt);
+                return $this->_raiseError('no_mx', $info);
+            }
+
+            $connected = false;
+            foreach ($mx as $mserver => $mpriority) {
+                $this->_smtp = new Net_SMTP($mserver, $this->port, $this->mailname);
+
+                // configure the SMTP connection.
+                if ($this->debug) {
+                    $this->_smtp->setDebug(true);
+                }
+
+                // attempt to connect to the configured SMTP server.
+                $res = $this->_smtp->connect($this->timeout);
+                if (is_a($res, 'PEAR_Error')) {
+                    $this->_smtp = null;
+                    continue;
+                }
+
+                // connection established
+                if ($res) {
+                    $connected = true;
+                    break;
+                }
+            }
+
+            if (!$connected) {
+                $info = array(
+                    'host' => implode(', ', array_keys($mx)),
+                    'port' => $this->port,
+                    'rcpt' => $rcpt,
+                );
+                return $this->_raiseError('not_connected', $info);
+            }
+
+            // Verify recipient
+            if ($this->vrfy) {
+                $res = $this->_smtp->vrfy($rcpt);
+                if (is_a($res, 'PEAR_Error')) {
+                    $info = array('rcpt' => $rcpt);
+                    return $this->_raiseError('failed_vrfy_rcpt', $info);
+                }
+            }
+
+            // mail from:
+            $args['verp'] = $this->verp;
+            $res = $this->_smtp->mailFrom($from, $args);
+            if (is_a($res, 'PEAR_Error')) {
+                $info = array('from' => $from);
+                return $this->_raiseError('failed_set_from', $info);
+            }
+
+            // rcpt to:
+            $res = $this->_smtp->rcptTo($rcpt);
+            if (is_a($res, 'PEAR_Error')) {
+                $info = array('rcpt' => $rcpt);
+                return $this->_raiseError('failed_set_rcpt', $info);
+            }
+
+            // Don't send anything in test mode
+            if ($this->test) {
+                $result = $this->_smtp->rset();
+                $res = $this->_smtp->rset();
+                if (is_a($res, 'PEAR_Error')) {
+                    return $this->_raiseError('failed_rset');
+                }
+
+                $this->_smtp->disconnect();
+                $this->_smtp = null;
+                return true;
+            }
+
+            // Send data
+            $res = $this->_smtp->data("$textHeaders\r\n$body");
+            if (is_a($res, 'PEAR_Error')) {
+                $info = array('rcpt' => $rcpt);
+                return $this->_raiseError('failed_send_data', $info);
+            }
+
+            $this->_smtp->disconnect();
+            $this->_smtp = null;
+        }
+
+        return true;
+    }
+
+    /**
+     * Recieve mx rexords for a spciefied host
+     *
+     * The MX records
+     *
+     * @access private
+     * @param string $host mail host
+     * @return mixed sorted
+     */
+    function _getMx($host)
+    {
+        $mx = array();
+
+        if ($this->withNetDns) {
+            $res = $this->_loadNetDns();
+            if (is_a($res, 'PEAR_Error')) {
+                return $res;
+            }
+
+            $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 = array();
+            $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 PEAR:Net_DNS_Resolver
+     *
+     * @access private
+     * @return boolean true on success
+     */
+    function _loadNetDns()
+    {
+        if (is_object($this->resolver)) {
+            return true;
+        }
+
+        if (!include_once 'Net/DNS.php') {
+            return $this->_raiseError('no_resolver');
+        }
+
+        $this->resolver = new Net_DNS_Resolver();
+        if ($this->debug) {
+            $this->resolver->test = 1;
+        }
+
+        return true;
+    }
+
+    /**
+     * raise standardized error
+     *
+     * include additional information in error message
+     *
+     * @access private
+     * @param string $id maps error ids to codes and message
+     * @param array $info optional information in associative array
+     * @see _errorCode
+     */
+    function _raiseError($id, $info = array())
+    {
+        $code = $this->errorCode[$id]['code'];
+        $msg = $this->errorCode[$id]['msg'];
+
+        // include info to messages
+        if (!empty($info)) {
+            $search = array();
+            $replace = array();
+
+            foreach ($info as $key => $value) {
+                array_push($search, '{' . strtoupper($key) . '}');
+                array_push($replace, $value);
+            }
+
+            $msg = str_replace($search, $replace, $msg);
+        }
+
+        return PEAR::raiseError($msg, $code);
+    }
+
+}
Index: branches/version-2_5-dev/data/module/Mail/mock.php
===================================================================
--- branches/version-2_5-dev/data/module/Mail/mock.php	(revision 19942)
+++ branches/version-2_5-dev/data/module/Mail/mock.php	(revision 19942)
@@ -0,0 +1,143 @@
+<?php
+/**
+ * Mock implementation
+ *
+ * PHP versions 4 and 5
+ *
+ * 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    Mail
+ * @package     Mail
+ * @author      Chuck Hagenbuch <chuck@horde.org> 
+ * @copyright   2010 Chuck Hagenbuch
+ * @license     http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version     CVS: $Id: mock.php 294747 2010-02-08 08:18:33Z clockwerx $
+ * @link        http://pear.php.net/package/Mail/
+ */
+
+/**
+ * Mock implementation of the PEAR Mail:: interface for testing.
+ * @access public
+ * @package Mail
+ * @version $Revision: 294747 $
+ */
+class Mail_mock extends Mail {
+
+    /**
+     * Array of messages that have been sent with the mock.
+     *
+     * @var array
+     * @access public
+     */
+    var $sentMessages = array();
+
+    /**
+     * Callback before sending mail.
+     *
+     * @var callback
+     */
+    var $_preSendCallback;
+
+    /**
+     * Callback after sending mai.
+     *
+     * @var callback
+     */
+    var $_postSendCallback;
+
+    /**
+     * Constructor.
+     *
+     * Instantiates a new Mail_mock:: object based on the parameters
+     * passed in. It looks for the following parameters, both optional:
+     *     preSendCallback   Called before an email would be sent.
+     *     postSendCallback  Called after an email would have been sent.
+     *
+     * @param array Hash containing any parameters.
+     * @access public
+     */
+    function Mail_mock($params)
+    {
+        if (isset($params['preSendCallback']) &&
+            is_callable($params['preSendCallback'])) {
+            $this->_preSendCallback = $params['preSendCallback'];
+        }
+
+        if (isset($params['postSendCallback']) &&
+            is_callable($params['postSendCallback'])) {
+            $this->_postSendCallback = $params['postSendCallback'];
+        }
+    }
+
+    /**
+     * Implements Mail_mock::send() function. 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 array of 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'.
+     *
+     * @param string $body The full text of the message body, including any
+     *               Mime parts, etc.
+     *
+     * @return mixed Returns true on success, or a PEAR_Error
+     *               containing a descriptive error message on
+     *               failure.
+     * @access public
+     */
+    function send($recipients, $headers, $body)
+    {
+        if ($this->_preSendCallback) {
+            call_user_func_array($this->_preSendCallback,
+                                 array(&$this, $recipients, $headers, $body));
+        }
+
+        $entry = array('recipients' => $recipients, 'headers' => $headers, 'body' => $body);
+        $this->sentMessages[] = $entry;
+
+        if ($this->_postSendCallback) {
+            call_user_func_array($this->_postSendCallback,
+                                 array(&$this, $recipients, $headers, $body));
+        }
+
+        return true;
+    }
+
+}
Index: branches/version-2_5-dev/data/module/Mail/null.php
===================================================================
--- branches/version-2_5-dev/data/module/Mail/null.php	(revision 16503)
+++ branches/version-2_5-dev/data/module/Mail/null.php	(revision 19942)
@@ -1,22 +1,46 @@
 <?php
-//
-// +----------------------------------------------------------------------+
-// | PHP Version 4                                                        |
-// +----------------------------------------------------------------------+
-// | Copyright (c) 1997-2003 The PHP Group                                |
-// +----------------------------------------------------------------------+
-// | This source file is subject to version 2.02 of the PHP license,      |
-// | that is bundled with this package in the file LICENSE, and is        |
-// | available at through the world-wide-web at                           |
-// | http://www.php.net/license/2_02.txt.                                 |
-// | If you did not receive a copy of the PHP license and are unable to   |
-// | obtain it through the world-wide-web, please send a note to          |
-// | license@php.net so we can mail you a copy immediately.               |
-// +----------------------------------------------------------------------+
-// | Author: Phil Kernick <philk@rotfl.com.au>                            |
-// +----------------------------------------------------------------------+
-//
-// $Id$
-//
+/**
+ * Null implementation of the PEAR Mail interface
+ *
+ * PHP versions 4 and 5
+ *
+ * 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    Mail
+ * @package     Mail
+ * @author      Phil Kernick <philk@rotfl.com.au>
+ * @copyright   2010 Phil Kernick
+ * @license     http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version     CVS: $Id$
+ * @link        http://pear.php.net/package/Mail/
+ */
 
 /**
Index: branches/version-2_5-dev/data/module/Mail/sendmail.php
===================================================================
--- branches/version-2_5-dev/data/module/Mail/sendmail.php	(revision 16503)
+++ branches/version-2_5-dev/data/module/Mail/sendmail.php	(revision 19942)
@@ -25,5 +25,5 @@
 class Mail_sendmail extends Mail {
 
-	/**
+    /**
      * The location of the sendmail or sendmail wrapper binary on the
      * filesystem.
@@ -32,5 +32,5 @@
     var $sendmail_path = '/usr/sbin/sendmail';
 
-	/**
+    /**
      * Any extra command-line parameters to pass to the sendmail or
      * sendmail wrapper binary.
@@ -39,5 +39,5 @@
     var $sendmail_args = '-i';
 
-	/**
+    /**
      * Constructor.
      *
@@ -78,5 +78,5 @@
     }
 
-	/**
+    /**
      * Implements Mail::send() function using the sendmail
      * command-line binary.
@@ -105,16 +105,31 @@
     function send($recipients, $headers, $body)
     {
+        if (!is_array($headers)) {
+            return PEAR::raiseError('$headers must be an array');
+        }
+
+        $result = $this->_sanitizeHeaders($headers);
+        if (is_a($result, 'PEAR_Error')) {
+            return $result;
+        }
+
         $recipients = $this->parseRecipients($recipients);
-        if (PEAR::isError($recipients)) {
+        if (is_a($recipients, 'PEAR_Error')) {
             return $recipients;
         }
-        $recipients = escapeShellCmd(implode(' ', $recipients));
+        $recipients = implode(' ', array_map('escapeshellarg', $recipients));
 
-        $this->_sanitizeHeaders($headers);
         $headerElements = $this->prepareHeaders($headers);
-        if (PEAR::isError($headerElements)) {
+        if (is_a($headerElements, 'PEAR_Error')) {
             return $headerElements;
         }
         list($from, $text_headers) = $headerElements;
+
+        /* 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. */
+        if (!empty($headers['Return-Path'])) {
+            $from = $headers['Return-Path'];
+        }
 
         if (!isset($from)) {
@@ -127,5 +142,6 @@
         }
 
-        $from = escapeShellCmd($from);
+        $from = escapeshellarg($from); // Security bug #16200
+
         $mail = @popen($this->sendmail_path . (!empty($this->sendmail_args) ? ' ' . $this->sendmail_args : '') . " -f$from -- $recipients", 'w');
         if (!$mail) {
