| version 1.1, 2005/06/05 12:14:33 |
version 1.2, 2005/11/07 20:37:52 |
|
|
| // | Jon Parise <jon@php.net> | |
// | Jon Parise <jon@php.net> | |
| // | Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar> | |
// | Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar> | |
| // +----------------------------------------------------------------------+ |
// +----------------------------------------------------------------------+ |
| |
// |
| |
// $Id$ |
| |
|
| require_once 'PEAR.php'; |
require_once 'PEAR.php'; |
| require_once 'Net/Socket.php'; |
require_once 'Net/Socket.php'; |
| Line 34 require_once 'Net/Socket.php'; |
|
| Line 36 require_once 'Net/Socket.php'; |
|
| */ |
*/ |
| class Net_SMTP |
class Net_SMTP |
| { |
{ |
| |
|
| /** |
/** |
| * The server to connect to. |
* The server to connect to. |
| * @var string |
* @var string |
|
|
| * Instantiates a new Net_SMTP object, overriding any defaults |
* Instantiates a new Net_SMTP object, overriding any defaults |
| * with parameters that are passed in. |
* with parameters that are passed in. |
| * |
* |
| * @param string The server to connect to. |
* If you have SSL support in PHP, you can connect to a server |
| * @param int The port to connect to. |
* over SSL using an 'ssl://' prefix: |
| * @param string The value to give when sending EHLO or HELO. |
* |
| |
* // 465 is a common smtps port. |
| |
* $smtp = new Net_SMTP('ssl://mail.host.com', 465); |
| |
* $smtp->connect(); |
| |
* |
| |
* @param string $host The server to connect to. |
| |
* @param integer $port The port to connect to. |
| |
* @param string $localhost The value to give when sending EHLO or HELO. |
| * |
* |
| * @access public |
* @access public |
| * @since 1.0 |
* @since 1.0 |
|
|
| if (isset($port)) $this->port = $port; |
if (isset($port)) $this->port = $port; |
| if (isset($localhost)) $this->localhost = $localhost; |
if (isset($localhost)) $this->localhost = $localhost; |
| |
|
| $this->_socket = new Net_Socket(); |
$this->_socket = &new Net_Socket(); |
| |
|
| /* |
/* |
| * Include the Auth_SASL package. If the package is not available, |
* Include the Auth_SASL package. If the package is not available, |
|
|
| } |
} |
| |
|
| if (PEAR::isError($error = $this->_socket->write($data))) { |
if (PEAR::isError($error = $this->_socket->write($data))) { |
| return new PEAR_Error('Failed to write to socket: ' . |
return PEAR::raiseError('Failed to write to socket: ' . |
| $error->getMessage()); |
$error->getMessage()); |
| } |
} |
| |
|
| return true; |
return true; |
| } |
} |
| |
|
| /** |
/** |
| * Send a command to the server with an optional string of arguments. |
* Send a command to the server with an optional string of |
| * A carriage return / linefeed (CRLF) sequence will be appended to each |
* arguments. A carriage return / linefeed (CRLF) sequence will |
| * command string before it is sent to the SMTP server. |
* be appended to each command string before it is sent to the |
| |
* SMTP server - an error will be thrown if the command string |
| |
* already contains any newline characters. Use _send() for |
| |
* commands that must contain newlines. |
| * |
* |
| * @param string $command The SMTP command to send to the server. |
* @param string $command The SMTP command to send to the server. |
| * @param string $args A string of optional arguments to append |
* @param string $args A string of optional arguments to append |
|
|
| function _put($command, $args = '') |
function _put($command, $args = '') |
| { |
{ |
| if (!empty($args)) { |
if (!empty($args)) { |
| return $this->_send($command . ' ' . $args . "\r\n"); |
$command .= ' ' . $args; |
| |
} |
| |
|
| |
if (strcspn($command, "\r\n") !== strlen($command)) { |
| |
return PEAR::raiseError('Commands cannot contain newlines'); |
| } |
} |
| |
|
| return $this->_send($command . "\r\n"); |
return $this->_send($command . "\r\n"); |
|
|
| /* If we receive an empty line, the connection has been closed. */ |
/* If we receive an empty line, the connection has been closed. */ |
| if (empty($line)) { |
if (empty($line)) { |
| $this->disconnect(); |
$this->disconnect(); |
| return new PEAR_Error("Connection was unexpectedly closed"); |
return PEAR::raiseError('Connection was unexpectedly closed'); |
| } |
} |
| |
|
| /* Read the code and store the rest in the arguments array. */ |
/* Read the code and store the rest in the arguments array. */ |
|
|
| } |
} |
| } |
} |
| |
|
| return new PEAR_Error("Invalid response code received from server"); |
return PEAR::raiseError('Invalid response code received from server'); |
| } |
} |
| |
|
| /** |
/** |
|
|
| $result = $this->_socket->connect($this->host, $this->port, |
$result = $this->_socket->connect($this->host, $this->port, |
| $persistent, $timeout); |
$persistent, $timeout); |
| if (PEAR::isError($result)) { |
if (PEAR::isError($result)) { |
| return new PEAR_Error('Failed to connect socket: ' . |
return PEAR::raiseError('Failed to connect socket: ' . |
| $result->getMessage()); |
$result->getMessage()); |
| } |
} |
| |
|
| if (PEAR::isError($error = $this->_parseResponse(220))) { |
if (PEAR::isError($error = $this->_parseResponse(220))) { |
|
|
| return $error; |
return $error; |
| } |
} |
| if (PEAR::isError($error = $this->_socket->disconnect())) { |
if (PEAR::isError($error = $this->_socket->disconnect())) { |
| return new PEAR_Error('Failed to disconnect socket: ' . |
return PEAR::raiseError('Failed to disconnect socket: ' . |
| $error->getMessage()); |
$error->getMessage()); |
| } |
} |
| |
|
| return true; |
return true; |
|
|
| return $error; |
return $error; |
| } |
} |
| if (PEAR::isError($this->_parseResponse(250))) { |
if (PEAR::isError($this->_parseResponse(250))) { |
| return new PEAR_Error('HELO was not accepted: ', $this->_code); |
return PEAR::raiseError('HELO was not accepted: ', $this->_code); |
| } |
} |
| |
|
| return true; |
return true; |
|
|
| } |
} |
| } |
} |
| |
|
| return new PEAR_Error('No supported authentication methods'); |
return PEAR::raiseError('No supported authentication methods'); |
| } |
} |
| |
|
| /** |
/** |
|
|
| function auth($uid, $pwd , $method = '') |
function auth($uid, $pwd , $method = '') |
| { |
{ |
| if (empty($this->_esmtp['AUTH'])) { |
if (empty($this->_esmtp['AUTH'])) { |
| return new PEAR_Error('SMTP server does no support authentication'); |
return PEAR::raiseError('SMTP server does no support authentication'); |
| } |
} |
| |
|
| /* |
/* If no method has been specified, get the name of the best |
| * If no method has been specified, get the name of the best supported |
* supported method advertised by the SMTP server. */ |
| * method advertised by the SMTP server. |
|
| */ |
|
| if (empty($method)) { |
if (empty($method)) { |
| if (PEAR::isError($method = $this->_getBestAuthMethod())) { |
if (PEAR::isError($method = $this->_getBestAuthMethod())) { |
| /* Return the PEAR_Error object from _getBestAuthMethod(). */ |
/* Return the PEAR_Error object from _getBestAuthMethod(). */ |
|
|
| } else { |
} else { |
| $method = strtoupper($method); |
$method = strtoupper($method); |
| if (!in_array($method, $this->auth_methods)) { |
if (!in_array($method, $this->auth_methods)) { |
| return new PEAR_Error("$method is not a supported authentication method"); |
return PEAR::raiseError("$method is not a supported authentication method"); |
| } |
} |
| } |
} |
| |
|
|
|
| $result = $this->_authPlain($uid, $pwd); |
$result = $this->_authPlain($uid, $pwd); |
| break; |
break; |
| default: |
default: |
| $result = new PEAR_Error("$method is not a supported authentication method"); |
$result = PEAR::raiseError("$method is not a supported authentication method"); |
| break; |
break; |
| } |
} |
| |
|
|
|
| return $result; |
return $result; |
| } |
} |
| |
|
| /* RFC-2554 requires us to re-negotiate ESMTP after an AUTH. */ |
|
| if (PEAR::isError($error = $this->_negotiate())) { |
|
| return $error; |
|
| } |
|
| |
|
| return true; |
return true; |
| } |
} |
| |
|
|
|
| return $error; |
return $error; |
| } |
} |
| |
|
| /* |
/* We don't use the protocol's third step because SMTP doesn't |
| * We don't use the protocol's third step because SMTP doesn't allow |
* allow subsequent authentication, so we just silently ignore |
| * subsequent authentication, so we just silently ignore it. |
* it. */ |
| */ |
|
| if (PEAR::isError($error = $this->_put(' '))) { |
if (PEAR::isError($error = $this->_put(' '))) { |
| return $error; |
return $error; |
| } |
} |
|
|
| */ |
*/ |
| function _authLogin($uid, $pwd) |
function _authLogin($uid, $pwd) |
| { |
{ |
| if (PEAR::isError($error = $this->_put('AUTH', 'LOGIN'))) { |
if (PEAR::isError($error = $this->_put('AUTH', 'LOGIN'))) { |
| return $error; |
return $error; |
| } |
} |
| /* 334: Continue authentication request */ |
/* 334: Continue authentication request */ |
|
|
| * |
* |
| * @param string The sender (reverse path) to set. |
* @param string The sender (reverse path) to set. |
| * |
* |
| |
* @param array optional arguments. Currently supported: |
| |
* verp boolean or string. If true or string |
| |
* verp is enabled. If string the characters |
| |
* are considered verp separators. |
| |
* |
| * @return mixed Returns a PEAR_Error with an error message on any |
* @return mixed Returns a PEAR_Error with an error message on any |
| * kind of failure, or true on success. |
* kind of failure, or true on success. |
| * @access public |
* @access public |
| * @since 1.0 |
* @since 1.0 |
| */ |
*/ |
| function mailFrom($sender) |
function mailFrom($sender, $args = array()) |
| { |
{ |
| if (PEAR::isError($error = $this->_put('MAIL', "FROM:<$sender>"))) { |
$argstr = ''; |
| |
|
| |
if (isset($args['verp'])) { |
| |
/* XVERP */ |
| |
if ($args['verp'] === true) { |
| |
$argstr .= ' XVERP'; |
| |
|
| |
/* XVERP=something */ |
| |
} elseif (trim($args['verp'])) { |
| |
$argstr .= ' XVERP=' . $args['verp']; |
| |
} |
| |
} |
| |
|
| |
if (PEAR::isError($error = $this->_put('MAIL', "FROM:<$sender>$argstr"))) { |
| return $error; |
return $error; |
| } |
} |
| if (PEAR::isError($error = $this->_parseResponse(250))) { |
if (PEAR::isError($error = $this->_parseResponse(250))) { |
|
|
| /** |
/** |
| * Quote the data so that it meets SMTP standards. |
* Quote the data so that it meets SMTP standards. |
| * |
* |
| * This is provided as a separate public function to facilitate easier |
* This is provided as a separate public function to facilitate |
| * overloading for the cases where it is desirable to customize the |
* easier overloading for the cases where it is desirable to |
| * quoting behavior. |
* customize the quoting behavior. |
| * |
* |
| * @param string The message text to quote. The string must be passed |
* @param string $data The message text to quote. The string must be passed |
| * by reference, and the text will be modified in place. |
* by reference, and the text will be modified in place. |
| * |
* |
| * @access public |
* @access public |
| * @since 1.2 |
* @since 1.2 |
| */ |
*/ |
| function quotedata(&$data) |
function quotedata(&$data) |
| { |
{ |
| /* |
/* Change Unix (\n) and Mac (\r) linefeeds into |
| * Change Unix (\n) and Mac (\r) linefeeds into Internet-standard CRLF |
* Internet-standard CRLF (\r\n) linefeeds. */ |
| * (\r\n) linefeeds. |
$data = preg_replace(array('/(?<!\r)\n/','/\r(?!\n)/'), "\r\n", $data); |
| */ |
|
| $data = preg_replace("/([^\r]{1})\n/", "\\1\r\n", $data); |
/* Because a single leading period (.) signifies an end to the |
| $data = preg_replace("/\n\n/", "\n\r\n", $data); |
* data, legitimate leading periods need to be "doubled" |
| |
* (e.g. '..'). */ |
| /* |
$data = str_replace("\n.", "\n..", $data); |
| * Because a single leading period (.) signifies an end to the data, |
|
| * legitimate leading periods need to be "doubled" (e.g. '..'). |
|
| */ |
|
| $data = preg_replace("/\n\./", "\n..", $data); |
|
| } |
} |
| |
|
| /** |
/** |
| * Send the DATA command. |
* Send the DATA command. |
| * |
* |
| * @param string The message body to send. |
* @param string $data The message body to send. |
| * |
* |
| * @return mixed Returns a PEAR_Error with an error message on any |
* @return mixed Returns a PEAR_Error with an error message on any |
| * kind of failure, or true on success. |
* kind of failure, or true on success. |
|
|
| */ |
*/ |
| function data($data) |
function data($data) |
| { |
{ |
| /* |
/* RFC 1870, section 3, subsection 3 states "a value of zero |
| * RFC 1870, section 3, subsection 3 states "a value of zero indicates |
* indicates that no fixed maximum message size is in force". |
| * that no fixed maximum message size is in force". Furthermore, it |
* Furthermore, it says that if "the parameter is omitted no |
| * says that if "the parameter is omitted no information is conveyed |
* information is conveyed about the server's fixed maximum |
| * about the server's fixed maximum message size". |
* message size". */ |
| */ |
|
| if (isset($this->_esmtp['SIZE']) && ($this->_esmtp['SIZE'] > 0)) { |
if (isset($this->_esmtp['SIZE']) && ($this->_esmtp['SIZE'] > 0)) { |
| if (strlen($data) >= $this->_esmtp['SIZE']) { |
if (strlen($data) >= $this->_esmtp['SIZE']) { |
| $this->disconnect(); |
$this->disconnect(); |
| return new PEAR_Error('Message size excedes the server limit'); |
return PEAR::raiseError('Message size excedes the server limit'); |
| } |
} |
| } |
} |
| |
|
|
|
| return $error; |
return $error; |
| } |
} |
| |
|
| if (PEAR::isError($this->_send($data . "\r\n.\r\n"))) { |
if (PEAR::isError($result = $this->_send($data . "\r\n.\r\n"))) { |
| return new PEAR_Error('write to socket failed'); |
return $result; |
| } |
} |
| if (PEAR::isError($error = $this->_parseResponse(250))) { |
if (PEAR::isError($error = $this->_parseResponse(250))) { |
| return $error; |
return $error; |
|
|
| if (PEAR::isError($error = $this->_put('VRFY', $string))) { |
if (PEAR::isError($error = $this->_put('VRFY', $string))) { |
| return $error; |
return $error; |
| } |
} |
| if (PEAR::isError($error = $this->_parseResponse(250))) { |
if (PEAR::isError($error = $this->_parseResponse(array(250, 252)))) { |
| return $error; |
return $error; |
| } |
} |
| |
|
|
|
| { |
{ |
| return true; |
return true; |
| } |
} |
| } |
|
| |
|
| ?> |
} |