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) {
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/Net/SMTP.php
===================================================================
--- branches/version-2_5-dev/data/module/Net/SMTP.php	(revision 16300)
+++ branches/version-2_5-dev/data/module/Net/SMTP.php	(revision 19942)
@@ -19,9 +19,8 @@
 // +----------------------------------------------------------------------+
 //
-// $Id: SMTP.php,v 1.58 2007/03/28 04:53:34 chagenbu Exp $
-
-$include_dir = realpath(dirname( __FILE__));
-require_once $include_dir . "/../PEAR.php";
-require_once $include_dir . "/../Net/Socket.php";
+// $Id: SMTP.php 304535 2010-10-20 06:48:06Z jon $
+
+require_once 'PEAR.php';
+require_once 'Net/Socket.php';
 
 /**
@@ -67,4 +66,24 @@
 
     /**
+     * Use SMTP command pipelining (specified in RFC 2920) if the SMTP
+     * server supports it.
+     *
+     * When pipeling is enabled, rcptTo(), mailFrom(), sendFrom(),
+     * somlFrom() and samlFrom() do not wait for a response from the
+     * SMTP server but return immediately.
+     *
+     * @var bool
+     * @access public
+     */
+    var $pipelining = false;
+
+    /**
+     * Number of pipelined commands.
+     * @var int
+     * @access private
+     */
+    var $_pipelined_commands = 0;
+
+    /**
      * Should debugging output be enabled?
      * @var boolean
@@ -74,4 +93,11 @@
 
     /**
+     * Debug output handler.
+     * @var callback
+     * @access private
+     */
+    var $_debug_handler = null;
+
+    /**
      * The socket resource being used to connect to the SMTP server.
      * @var resource
@@ -93,4 +119,11 @@
      */
     var $_arguments = array();
+
+    /**
+     * Stores the SMTP server's greeting string.
+     * @var string
+     * @access private
+     */
+    var $_greeting = null;
 
     /**
@@ -115,21 +148,28 @@
      * @param integer $port       The port to connect to.
      * @param string  $localhost  The value to give when sending EHLO or HELO.
+     * @param boolean $pipeling   Use SMTP command pipelining
      *
      * @access  public
      * @since   1.0
      */
-    function Net_SMTP($host = null, $port = null, $localhost = null)
-    {
-        if (isset($host)) $this->host = $host;
-        if (isset($port)) $this->port = $port;
-        if (isset($localhost)) $this->localhost = $localhost;
+    function Net_SMTP($host = null, $port = null, $localhost = null, $pipelining = false)
+    {
+        if (isset($host)) {
+            $this->host = $host;
+        }
+        if (isset($port)) {
+            $this->port = $port;
+        }
+        if (isset($localhost)) {
+            $this->localhost = $localhost;
+        }
+        $this->pipelining = $pipelining;
 
         $this->_socket = new Net_Socket();
 
-        /*
-         * Include the Auth_SASL package.  If the package is not available,
-         * we disable the authentication methods that depend upon it.
-         */
-        if ((@include_once '../Auth/SASL.php') === false) {
+        /* Include the Auth_SASL package.  If the package is not
+         * available, we disable the authentication methods that
+         * depend upon it. */
+        if ((@include_once 'Auth/SASL.php') === false) {
             $pos = array_search('DIGEST-MD5', $this->auth_methods);
             unset($this->auth_methods[$pos]);
@@ -147,7 +187,28 @@
      * @since   1.1.0
      */
-    function setDebug($debug)
+    function setDebug($debug, $handler = null)
     {
         $this->_debug = $debug;
+        $this->_debug_handler = $handler;
+    }
+
+    /**
+     * Write the given debug text to the current debug output handler.
+     *
+     * @param   string  $message    Debug mesage text.
+     *
+     * @access  private
+     * @since   1.3.3
+     */
+    function _debug($message)
+    {
+        if ($this->_debug) {
+            if ($this->_debug_handler) {
+                call_user_func_array($this->_debug_handler,
+                                     array(&$this, $message));
+            } else {
+                echo "DEBUG: $message\n";
+            }
+        }
     }
 
@@ -164,11 +225,10 @@
     function _send($data)
     {
-        if ($this->_debug) {
-            echo "DEBUG: Send: $data\n";
-        }
-
-        if (PEAR::isError($error = $this->_socket->write($data))) {
-            return PEAR::raiseError('Failed to write to socket: ' .
-                                    $error->getMessage());
+        $this->_debug("Send: $data");
+
+        $error = $this->_socket->write($data);
+        if ($error === false || PEAR::isError($error)) {
+            $msg = ($error) ? $error->getMessage() : "unknown error";
+            return PEAR::raiseError("Failed to write to socket: $msg");
         }
 
@@ -213,4 +273,7 @@
      *                              may be specified as an array of integer
      *                              values or as a single integer value.
+     * @param   bool    $later      Do not parse the response now, but wait
+     *                              until the last command in the pipelined
+     *                              command group
      *
      * @return  mixed   True if the server returned a valid response code or
@@ -222,50 +285,50 @@
      * @see     getResponse
      */
-    function _parseResponse($valid)
+    function _parseResponse($valid, $later = false)
     {
         $this->_code = -1;
         $this->_arguments = array();
 
-        while ($line = $this->_socket->readLine()) {
-            if ($this->_debug) {
-                echo "DEBUG: Recv: $line\n";
-            }
-
-            /* If we receive an empty line, the connection has been closed. */
-            if (empty($line)) {
-                $this->disconnect();
-                return PEAR::raiseError('Connection was unexpectedly closed');
-            }
-
-            /* Read the code and store the rest in the arguments array. */
-            $code = substr($line, 0, 3);
-            $this->_arguments[] = trim(substr($line, 4));
-
-            /* Check the syntax of the response code. */
-            if (is_numeric($code)) {
-                $this->_code = (int)$code;
-            } else {
-                $this->_code = -1;
-                break;
-            }
-
-            /* If this is not a multiline response, we're done. */
-            if (substr($line, 3, 1) != '-') {
-                break;
-            }
-        }
-
-        /* Compare the server's response code with the valid code. */
+        if ($later) {
+            $this->_pipelined_commands++;
+            return true;
+        }
+
+        for ($i = 0; $i <= $this->_pipelined_commands; $i++) {
+            while ($line = $this->_socket->readLine()) {
+                $this->_debug("Recv: $line");
+
+                /* If we receive an empty line, the connection has been closed. */
+                if (empty($line)) {
+                    $this->disconnect();
+                    return PEAR::raiseError('Connection was unexpectedly closed');
+                }
+
+                /* Read the code and store the rest in the arguments array. */
+                $code = substr($line, 0, 3);
+                $this->_arguments[] = trim(substr($line, 4));
+
+                /* Check the syntax of the response code. */
+                if (is_numeric($code)) {
+                    $this->_code = (int)$code;
+                } else {
+                    $this->_code = -1;
+                    break;
+                }
+
+                /* If this is not a multiline response, we're done. */
+                if (substr($line, 3, 1) != '-') {
+                    break;
+                }
+            }
+        }
+
+        $this->_pipelined_commands = 0;
+
+        /* Compare the server's response code with the valid code/codes. */
         if (is_int($valid) && ($this->_code === $valid)) {
             return true;
-        }
-
-        /* If we were given an array of valid response codes, check each one. */
-        if (is_array($valid)) {
-            foreach ($valid as $valid_code) {
-                if ($this->_code === $valid_code) {
-                    return true;
-                }
-            }
+        } elseif (is_array($valid) && in_array($this->_code, $valid, true)) {
+            return true;
         }
 
@@ -287,4 +350,18 @@
     {
         return array($this->_code, join("\n", $this->_arguments));
+    }
+
+    /**
+     * Return the SMTP server's greeting string.
+     *
+     * @return  string  A string containing the greeting string, or null if a 
+     *                  greeting has not been received.
+     *
+     * @access  public
+     * @since   1.3.3
+     */
+    function getGreeting()
+    {
+        return $this->_greeting;
     }
 
@@ -304,4 +381,5 @@
     function connect($timeout = null, $persistent = false)
     {
+        $this->_greeting = null;
         $result = $this->_socket->connect($this->host, $this->port,
                                           $persistent, $timeout);
@@ -314,4 +392,8 @@
             return $error;
         }
+
+        /* Extract and store a copy of the server's greeting string. */
+        list(, $this->_greeting) = $this->getResponse();
+
         if (PEAR::isError($error = $this->_negotiate())) {
             return $error;
@@ -385,4 +467,8 @@
         }
 
+        if (!isset($this->_esmtp['PIPELINING'])) {
+            $this->pipelining = false;
+        }
+
         return true;
     }
@@ -418,4 +504,7 @@
      * @param string The requested authentication method.  If none is
      *               specified, the best supported method will be used.
+     * @param bool   Flag indicating whether or not TLS should be attempted.
+     * @param string An optional authorization identifier.  If specified, this
+     *               identifier will be used as the authorization proxy.
      *
      * @return mixed Returns a PEAR_Error with an error message on any
@@ -424,32 +513,34 @@
      * @since  1.0
      */
-    function auth($uid, $pwd , $method = '')
-    {
+    function auth($uid, $pwd , $method = '', $tls = true, $authz = '')
+    {
+        /* We can only attempt a TLS connection if one has been requested,
+         * we're running PHP 5.1.0 or later, have access to the OpenSSL 
+         * extension, are connected to an SMTP server which supports the 
+         * STARTTLS extension, and aren't already connected over a secure 
+         * (SSL) socket connection. */
+        if ($tls && version_compare(PHP_VERSION, '5.1.0', '>=') &&
+            extension_loaded('openssl') && isset($this->_esmtp['STARTTLS']) &&
+            strncasecmp($this->host, 'ssl://', 6) !== 0) {
+            /* Start the TLS connection attempt. */
+            if (PEAR::isError($result = $this->_put('STARTTLS'))) {
+                return $result;
+            }
+            if (PEAR::isError($result = $this->_parseResponse(220))) {
+                return $result;
+            }
+            if (PEAR::isError($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT))) {
+                return $result;
+            } elseif ($result !== true) {
+                return PEAR::raiseError('STARTTLS failed');
+            }
+
+            /* Send EHLO again to recieve the AUTH string from the
+             * SMTP server. */
+            $this->_negotiate();
+        }
+
         if (empty($this->_esmtp['AUTH'])) {
-            if (version_compare(PHP_VERSION, '5.1.0', '>=')) {
-                if (!isset($this->_esmtp['STARTTLS'])) {
-                    return PEAR::raiseError('SMTP server does not support authentication');
-                }
-                if (PEAR::isError($result = $this->_put('STARTTLS'))) {
-                    return $result;
-                }
-                if (PEAR::isError($result = $this->_parseResponse(220))) {
-                    return $result;
-                }
-                if (PEAR::isError($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT))) {
-                    return $result;
-                } elseif ($result !== true) {
-                    return PEAR::raiseError('STARTTLS failed');
-                }
-
-                /* Send EHLO again to recieve the AUTH string from the
-                 * SMTP server. */
-                $this->_negotiate();
-                if (empty($this->_esmtp['AUTH'])) {
-                    return PEAR::raiseError('SMTP server does not support authentication');
-                }
-            } else {
-                return PEAR::raiseError('SMTP server does not support authentication');
-            }
+            return PEAR::raiseError('SMTP server does not support authentication');
         }
 
@@ -470,5 +561,5 @@
         switch ($method) {
         case 'DIGEST-MD5':
-            $result = $this->_authDigest_MD5($uid, $pwd);
+            $result = $this->_authDigest_MD5($uid, $pwd, $authz);
             break;
 
@@ -482,5 +573,5 @@
 
         case 'PLAIN':
-            $result = $this->_authPlain($uid, $pwd);
+            $result = $this->_authPlain($uid, $pwd, $authz);
             break;
 
@@ -503,4 +594,5 @@
      * @param string The userid to authenticate as.
      * @param string The password to authenticate with.
+     * @param string The optional authorization proxy identifier.
      *
      * @return mixed Returns a PEAR_Error with an error message on any
@@ -509,5 +601,5 @@
      * @since  1.1.0
      */
-    function _authDigest_MD5($uid, $pwd)
+    function _authDigest_MD5($uid, $pwd, $authz = '')
     {
         if (PEAR::isError($error = $this->_put('AUTH', 'DIGEST-MD5'))) {
@@ -539,5 +631,5 @@
          * allow subsequent authentication, so we just silently ignore
          * it. */
-        if (PEAR::isError($error = $this->_put(' '))) {
+        if (PEAR::isError($error = $this->_put(''))) {
             return $error;
         }
@@ -637,4 +729,5 @@
      * @param string The userid to authenticate as.
      * @param string The password to authenticate with.
+     * @param string The optional authorization proxy identifier.
      *
      * @return mixed Returns a PEAR_Error with an error message on any
@@ -643,5 +736,5 @@
      * @since  1.1.0
      */
-    function _authPlain($uid, $pwd)
+    function _authPlain($uid, $pwd, $authz = '')
     {
         if (PEAR::isError($error = $this->_put('AUTH', 'PLAIN'))) {
@@ -657,5 +750,5 @@
         }
 
-        $auth_str = base64_encode(chr(0) . $uid . chr(0) . $pwd);
+        $auth_str = base64_encode($authz . chr(0) . $uid . chr(0) . $pwd);
 
         if (PEAR::isError($error = $this->_put($auth_str))) {
@@ -691,4 +784,16 @@
 
         return true;
+    }
+
+    /**
+     * Return the list of SMTP service extensions advertised by the server.
+     *
+     * @return array The list of SMTP service extensions.
+     * @access public
+     * @since 1.3
+     */
+    function getServiceExtensions()
+    {
+        return $this->_esmtp;
     }
 
@@ -733,5 +838,5 @@
             return $error;
         }
-        if (PEAR::isError($error = $this->_parseResponse(250))) {
+        if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
             return $error;
         }
@@ -763,5 +868,5 @@
             return $error;
         }
-        if (PEAR::isError($error = $this->_parseResponse(array(250, 251)))) {
+        if (PEAR::isError($error = $this->_parseResponse(array(250, 251), $this->pipelining))) {
             return $error;
         }
@@ -798,5 +903,8 @@
      * Send the DATA command.
      *
-     * @param string $data  The message body to send.
+     * @param mixed $data     The message data, either as a string or an open
+     *                        file resource.
+     * @param string $headers The message headers.  If $headers is provided,
+     *                        $data is assumed to contain only body data.
      *
      * @return mixed Returns a PEAR_Error with an error message on any
@@ -805,21 +913,37 @@
      * @since  1.0
      */
-    function data($data)
-    {
-        /* RFC 1870, section 3, subsection 3 states "a value of zero
-         * indicates that no fixed maximum message size is in force".
-         * Furthermore, it says that if "the parameter is omitted no
-         * information is conveyed about the server's fixed maximum
-         * message size". */
-        if (isset($this->_esmtp['SIZE']) && ($this->_esmtp['SIZE'] > 0)) {
-            if (strlen($data) >= $this->_esmtp['SIZE']) {
-                $this->disconnect();
-                return PEAR::raiseError('Message size excedes the server limit');
-            }
-        }
-
-        /* Quote the data based on the SMTP standards. */
-        $this->quotedata($data);
-
+    function data($data, $headers = null)
+    {
+        /* Verify that $data is a supported type. */
+        if (!is_string($data) && !is_resource($data)) {
+            return PEAR::raiseError('Expected a string or file resource');
+        }
+
+        /* Start by considering the size of the optional headers string.  We
+         * also account for the addition 4 character "\r\n\r\n" separator
+         * sequence. */
+        $size = (is_null($headers)) ? 0 : strlen($headers) + 4;
+
+        if (is_resource($data)) {
+            $stat = fstat($data);
+            if ($stat === false) {
+                return PEAR::raiseError('Failed to get file size');
+            }
+            $size += $stat['size'];
+        } else {
+            $size += strlen($data);
+        }
+
+        /* RFC 1870, section 3, subsection 3 states "a value of zero indicates
+         * that no fixed maximum message size is in force".  Furthermore, it
+         * says that if "the parameter is omitted no information is conveyed
+         * about the server's fixed maximum message size". */
+        $limit = (isset($this->_esmtp['SIZE'])) ? $this->_esmtp['SIZE'] : 0;
+        if ($limit > 0 && $size >= $limit) {
+            $this->disconnect();
+            return PEAR::raiseError('Message size exceeds server limit');
+        }
+
+        /* Initiate the DATA command. */
         if (PEAR::isError($error = $this->_put('DATA'))) {
             return $error;
@@ -829,8 +953,67 @@
         }
 
-        if (PEAR::isError($result = $this->_send($data . "\r\n.\r\n"))) {
+        /* If we have a separate headers string, send it first. */
+        if (!is_null($headers)) {
+            $this->quotedata($headers);
+            if (PEAR::isError($result = $this->_send($headers . "\r\n\r\n"))) {
+                return $result;
+            }
+        }
+
+        /* Now we can send the message body data. */
+        if (is_resource($data)) {
+            /* Stream the contents of the file resource out over our socket 
+             * connection, line by line.  Each line must be run through the 
+             * quoting routine. */
+            while ($line = fgets($data, 1024)) {
+                $this->quotedata($line);
+                if (PEAR::isError($result = $this->_send($line))) {
+                    return $result;
+                }
+            }
+        } else {
+            /*
+             * Break up the data by sending one chunk (up to 512k) at a time.  
+             * This approach reduces our peak memory usage.
+             */
+            for ($offset = 0; $offset < $size;) {
+                $end = $offset + 512000;
+
+                /*
+                 * Ensure we don't read beyond our data size or span multiple 
+                 * lines.  quotedata() can't properly handle character data 
+                 * that's split across two line break boundaries.
+                 */
+                if ($end >= $size) {
+                    $end = $size;
+                } else {
+                    for (; $end < $size; $end++) {
+                        if ($data[$end] != "\n") {
+                            break;
+                        }
+                    }
+                }
+
+                /* Extract our chunk and run it through the quoting routine. */
+                $chunk = substr($data, $offset, $end - $offset);
+                $this->quotedata($chunk);
+
+                /* If we run into a problem along the way, abort. */
+                if (PEAR::isError($result = $this->_send($chunk))) {
+                    return $result;
+                }
+
+                /* Advance the offset to the end of this chunk. */
+                $offset = $end;
+            }
+        }
+
+        /* Finally, send the DATA terminator sequence. */
+        if (PEAR::isError($result = $this->_send("\r\n.\r\n"))) {
             return $result;
         }
-        if (PEAR::isError($error = $this->_parseResponse(250))) {
+
+        /* Verify that the data was successfully received by the server. */
+        if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
             return $error;
         }
@@ -854,5 +1037,5 @@
             return $error;
         }
-        if (PEAR::isError($error = $this->_parseResponse(250))) {
+        if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
             return $error;
         }
@@ -893,5 +1076,5 @@
             return $error;
         }
-        if (PEAR::isError($error = $this->_parseResponse(250))) {
+        if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
             return $error;
         }
@@ -932,5 +1115,5 @@
             return $error;
         }
-        if (PEAR::isError($error = $this->_parseResponse(250))) {
+        if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
             return $error;
         }
@@ -969,5 +1152,5 @@
             return $error;
         }
-        if (PEAR::isError($error = $this->_parseResponse(250))) {
+        if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
             return $error;
         }
Index: branches/version-2_5-dev/data/module/Mail.php
===================================================================
--- branches/version-2_5-dev/data/module/Mail.php	(revision 16523)
+++ branches/version-2_5-dev/data/module/Mail.php	(revision 19942)
@@ -1,23 +1,48 @@
 <?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: Mail.php,v 1.17 2006/09/15 03:41:18 jon Exp $
-
-require_once dirname(__FILE__) . "/PEAR.php";
+/**
+ *  PEAR's Mail:: interface.
+ *
+ * PHP versions 4 and 5
+ *
+ * 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>
+ * @copyright   1997-2010 Chuck Hagenbuch
+ * @license     http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version     CVS: $Id: Mail.php 294747 2010-02-08 08:18:33Z clockwerx $
+ * @link        http://pear.php.net/package/Mail/
+ */
+
+require_once 'PEAR.php';
 
 /**
@@ -27,5 +52,5 @@
  *
  * @access public
- * @version $Revision: 1.17 $
+ * @version $Revision: 294747 $
  * @package Mail
  */
@@ -50,6 +75,5 @@
     {
         $driver = strtolower($driver);
-        $include_dir = realpath(dirname( __FILE__));
-        include_once $include_dir ."/Mail/" . $driver . ".php";
+        @include_once 'Mail/' . $driver . '.php';
         $class = 'Mail_' . $driver;
         if (class_exists($class)) {
@@ -84,4 +108,5 @@
      *               containing a descriptive error message on
      *               failure.
+     *
      * @access public
      * @deprecated use Mail_mail::send instead
@@ -89,5 +114,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,8 +137,7 @@
 
         // flatten the headers out.
-        list(,$text_headers) = Mail::prepareHeaders($headers);
+        list(, $text_headers) = Mail::prepareHeaders($headers);
 
         return mail($recipients, $subject, $body, $text_headers);
-
     }
 
@@ -152,9 +183,8 @@
         foreach ($headers as $key => $value) {
             if (strcasecmp($key, 'From') === 0) {
-                $include_dir = realpath(dirname( __FILE__));
-                include_once $include_dir ."/Mail/RFC822.php";
-                $parser = &new Mail_RFC822();
+                include_once 'Mail/RFC822.php';
+                $parser = new Mail_RFC822();
                 $addresses = $parser->parseAddressList($value, 'localhost', false);
-                if (PEAR::isError($addresses)) {
+                if (is_a($addresses, 'PEAR_Error')) {
                     return $addresses;
                 }
@@ -210,5 +240,5 @@
     function parseRecipients($recipients)
     {
-        include_once dirname(__FILE__) . "/Mail/RFC822.php";
+        include_once 'Mail/RFC822.php';
 
         // if we're passed an array, assume addresses are valid and
@@ -224,5 +254,5 @@
 
         // If parseAddressList() returned a PEAR_Error object, just return it.
-        if (PEAR::isError($addresses)) {
+        if (is_a($addresses, 'PEAR_Error')) {
             return $addresses;
         }
