|
|
|
|
@ -62,7 +62,7 @@ class Net_SMTP
|
|
|
|
|
* @var array
|
|
|
|
|
* @access public
|
|
|
|
|
*/
|
|
|
|
|
var $auth_methods = array('DIGEST-MD5', 'CRAM-MD5', 'LOGIN', 'PLAIN');
|
|
|
|
|
var $auth_methods = array();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Use SMTP command pipelining (specified in RFC 2920) if the SMTP
|
|
|
|
|
@ -187,15 +187,16 @@ class Net_SMTP
|
|
|
|
|
$this->_socket_options = $socket_options;
|
|
|
|
|
$this->_timeout = $timeout;
|
|
|
|
|
|
|
|
|
|
/* 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]);
|
|
|
|
|
$pos = array_search('CRAM-MD5', $this->auth_methods);
|
|
|
|
|
unset($this->auth_methods[$pos]);
|
|
|
|
|
/* Include the Auth_SASL package. If the package is available, we
|
|
|
|
|
* enable the authentication methods that depend upon it. */
|
|
|
|
|
if ((@include_once 'Auth/SASL.php') === true) {
|
|
|
|
|
$this->setAuthMethod('CRAM-MD5', array($this, '_authCram_MD5'));
|
|
|
|
|
$this->setAuthMethod('DIGEST-MD5', array($this, '_authDigest_MD5'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* These standard authentication methods are always available. */
|
|
|
|
|
$this->setAuthMethod('LOGIN', array($this, '_authLogin'), false);
|
|
|
|
|
$this->setAuthMethod('PLAIN', array($this, '_authPlain'), false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -250,7 +251,8 @@ class Net_SMTP
|
|
|
|
|
*
|
|
|
|
|
* @param string $data The string of data to send.
|
|
|
|
|
*
|
|
|
|
|
* @return mixed True on success or a PEAR_Error object on failure.
|
|
|
|
|
* @return mixed The number of bytes that were actually written,
|
|
|
|
|
* or a PEAR_Error object on failure.
|
|
|
|
|
*
|
|
|
|
|
* @access private
|
|
|
|
|
* @since 1.1.0
|
|
|
|
|
@ -259,13 +261,14 @@ class Net_SMTP
|
|
|
|
|
{
|
|
|
|
|
$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");
|
|
|
|
|
$result = $this->_socket->write($data);
|
|
|
|
|
if (!$result || PEAR::isError($result)) {
|
|
|
|
|
$msg = ($result) ? $result->getMessage() : "unknown error";
|
|
|
|
|
return PEAR::raiseError("Failed to write to socket: $msg",
|
|
|
|
|
null, PEAR_ERROR_RETURN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -292,7 +295,8 @@ class Net_SMTP
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcspn($command, "\r\n") !== strlen($command)) {
|
|
|
|
|
return PEAR::raiseError('Commands cannot contain newlines');
|
|
|
|
|
return PEAR::raiseError('Commands cannot contain newlines',
|
|
|
|
|
null, PEAR_ERROR_RETURN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->_send($command . "\r\n");
|
|
|
|
|
@ -331,10 +335,11 @@ class Net_SMTP
|
|
|
|
|
while ($line = $this->_socket->readLine()) {
|
|
|
|
|
$this->_debug("Recv: $line");
|
|
|
|
|
|
|
|
|
|
/* If we receive an empty line, the connection has been closed. */
|
|
|
|
|
/* If we receive an empty line, the connection was closed. */
|
|
|
|
|
if (empty($line)) {
|
|
|
|
|
$this->disconnect();
|
|
|
|
|
return PEAR::raiseError('Connection was unexpectedly closed');
|
|
|
|
|
return PEAR::raiseError('Connection was closed',
|
|
|
|
|
null, PEAR_ERROR_RETURN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read the code and store the rest in the arguments array. */
|
|
|
|
|
@ -366,7 +371,32 @@ class Net_SMTP
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PEAR::raiseError('Invalid response code received from server',
|
|
|
|
|
$this->_code);
|
|
|
|
|
$this->_code, PEAR_ERROR_RETURN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Issue an SMTP command and verify its response.
|
|
|
|
|
*
|
|
|
|
|
* @param string $command The SMTP command string or data.
|
|
|
|
|
* @param mixed $valid The set of valid response codes. These
|
|
|
|
|
* may be specified as an array of integer
|
|
|
|
|
* values or as a single integer value.
|
|
|
|
|
*
|
|
|
|
|
* @return mixed True on success or a PEAR_Error object on failure.
|
|
|
|
|
*
|
|
|
|
|
* @access public
|
|
|
|
|
* @since 1.6.0
|
|
|
|
|
*/
|
|
|
|
|
function command($command, $valid)
|
|
|
|
|
{
|
|
|
|
|
if (PEAR::isError($error = $this->_put($command))) {
|
|
|
|
|
return $error;
|
|
|
|
|
}
|
|
|
|
|
if (PEAR::isError($error = $this->_parseResponse($valid))) {
|
|
|
|
|
return $error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -499,7 +529,8 @@ class Net_SMTP
|
|
|
|
|
return $error;
|
|
|
|
|
}
|
|
|
|
|
if (PEAR::isError($this->_parseResponse(250))) {
|
|
|
|
|
return PEAR::raiseError('HELO was not accepted: ', $this->_code);
|
|
|
|
|
return PEAR::raiseError('HELO was not accepted: ', $this->_code,
|
|
|
|
|
PEAR_ERROR_RETURN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
@ -533,13 +564,14 @@ class Net_SMTP
|
|
|
|
|
{
|
|
|
|
|
$available_methods = explode(' ', $this->_esmtp['AUTH']);
|
|
|
|
|
|
|
|
|
|
foreach ($this->auth_methods as $method) {
|
|
|
|
|
foreach ($this->auth_methods as $method => $callback) {
|
|
|
|
|
if (in_array($method, $available_methods)) {
|
|
|
|
|
return $method;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return PEAR::raiseError('No supported authentication methods');
|
|
|
|
|
return PEAR::raiseError('No supported authentication methods',
|
|
|
|
|
null, PEAR_ERROR_RETURN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -599,33 +631,27 @@ class Net_SMTP
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$method = strtoupper($method);
|
|
|
|
|
if (!in_array($method, $this->auth_methods)) {
|
|
|
|
|
if (!array_key_exists($method, $this->auth_methods)) {
|
|
|
|
|
return PEAR::raiseError("$method is not a supported authentication method");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch ($method) {
|
|
|
|
|
case 'DIGEST-MD5':
|
|
|
|
|
$result = $this->_authDigest_MD5($uid, $pwd, $authz);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'CRAM-MD5':
|
|
|
|
|
$result = $this->_authCRAM_MD5($uid, $pwd);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'LOGIN':
|
|
|
|
|
$result = $this->_authLogin($uid, $pwd);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'PLAIN':
|
|
|
|
|
$result = $this->_authPlain($uid, $pwd, $authz);
|
|
|
|
|
break;
|
|
|
|
|
if (!isset($this->auth_methods[$method])) {
|
|
|
|
|
return PEAR::raiseError("$method is not a supported authentication method");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
$result = PEAR::raiseError("$method is not a supported authentication method");
|
|
|
|
|
break;
|
|
|
|
|
if (!is_callable($this->auth_methods[$method], false)) {
|
|
|
|
|
return PEAR::raiseError("$method authentication method cannot be called");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_array($this->auth_methods[$method])) {
|
|
|
|
|
list($object, $method) = $this->auth_methods[$method];
|
|
|
|
|
$result = $object->{$method}($uid, $pwd, $authz, $this);
|
|
|
|
|
} else {
|
|
|
|
|
$func = $this->auth_methods[$method];
|
|
|
|
|
$result = $func($uid, $pwd, $authz, $this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If an error was encountered, return the PEAR_Error object. */
|
|
|
|
|
if (PEAR::isError($result)) {
|
|
|
|
|
return $result;
|
|
|
|
|
@ -634,6 +660,46 @@ class Net_SMTP
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add a new authentication method.
|
|
|
|
|
*
|
|
|
|
|
* @param string The authentication method name (e.g. 'PLAIN')
|
|
|
|
|
* @param mixed The authentication callback (given as the name of a
|
|
|
|
|
* function or as an (object, method name) array).
|
|
|
|
|
* @param bool Should the new method be prepended to the list of
|
|
|
|
|
* available methods? This is the default behavior,
|
|
|
|
|
* giving the new method the highest priority.
|
|
|
|
|
*
|
|
|
|
|
* @return mixed True on success or a PEAR_Error object on failure.
|
|
|
|
|
*
|
|
|
|
|
* @access public
|
|
|
|
|
* @since 1.6.0
|
|
|
|
|
*/
|
|
|
|
|
function setAuthMethod($name, $callback, $prepend = true)
|
|
|
|
|
{
|
|
|
|
|
if (!is_string($name)) {
|
|
|
|
|
return PEAR::raiseError('Method name is not a string');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_string($callback) && !is_array($callback)) {
|
|
|
|
|
return PEAR::raiseError('Method callback must be string or array');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_array($callback)) {
|
|
|
|
|
if (!is_object($callback[0]) || !is_string($callback[1]))
|
|
|
|
|
return PEAR::raiseError('Bad mMethod callback array');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($prepend) {
|
|
|
|
|
$this->auth_methods = array_merge(array($name => $callback),
|
|
|
|
|
$this->auth_methods);
|
|
|
|
|
} else {
|
|
|
|
|
$this->auth_methods[$name] = $callback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Authenticates the user using the DIGEST-MD5 method.
|
|
|
|
|
*
|
|
|
|
|
@ -691,13 +757,14 @@ class Net_SMTP
|
|
|
|
|
*
|
|
|
|
|
* @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
|
|
|
|
|
* kind of failure, or true on success.
|
|
|
|
|
* @access private
|
|
|
|
|
* @since 1.1.0
|
|
|
|
|
*/
|
|
|
|
|
function _authCRAM_MD5($uid, $pwd)
|
|
|
|
|
function _authCRAM_MD5($uid, $pwd, $authz = '')
|
|
|
|
|
{
|
|
|
|
|
if (PEAR::isError($error = $this->_put('AUTH', 'CRAM-MD5'))) {
|
|
|
|
|
return $error;
|
|
|
|
|
@ -730,13 +797,14 @@ class Net_SMTP
|
|
|
|
|
*
|
|
|
|
|
* @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
|
|
|
|
|
* kind of failure, or true on success.
|
|
|
|
|
* @access private
|
|
|
|
|
* @since 1.1.0
|
|
|
|
|
*/
|
|
|
|
|
function _authLogin($uid, $pwd)
|
|
|
|
|
function _authLogin($uid, $pwd, $authz = '')
|
|
|
|
|
{
|
|
|
|
|
if (PEAR::isError($error = $this->_put('AUTH', 'LOGIN'))) {
|
|
|
|
|
return $error;
|
|
|
|
|
@ -1012,7 +1080,16 @@ class Net_SMTP
|
|
|
|
|
/* 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)) {
|
|
|
|
|
while (strlen($line = fread($data, 8192)) > 0) {
|
|
|
|
|
/* If the last character is an newline, we need to grab the
|
|
|
|
|
* next character to check to see if it is a period. */
|
|
|
|
|
while (!feof($data)) {
|
|
|
|
|
$char = fread($data, 1);
|
|
|
|
|
$line .= $char;
|
|
|
|
|
if ($char != "\n") {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$this->quotedata($line);
|
|
|
|
|
if (PEAR::isError($result = $this->_send($line))) {
|
|
|
|
|
return $result;
|
|
|
|
|
|