Skip to content

Commit

Permalink
Adding initial support for external authentication methods.
Browse files Browse the repository at this point in the history
New authentication methods can be added (or existing methods replaced) by
calling setAuthMethod($method, $callback).

Submitted by: Emmanuel Dreyfus
  • Loading branch information
jparise committed May 19, 2011
1 parent aa6b60e commit f2130bf
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 32 deletions.
101 changes: 69 additions & 32 deletions SMTP.php
Expand Up @@ -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
Expand Down Expand Up @@ -187,15 +187,16 @@ function Net_SMTP($host = null, $port = null, $localhost = null,
$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);
}

/**
Expand Down Expand Up @@ -563,7 +564,7 @@ function _getBestAuthMethod()
{
$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;
}
Expand Down Expand Up @@ -630,33 +631,27 @@ function auth($uid, $pwd , $method = '', $tls = true, $authz = '')
}
} 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;
Expand All @@ -665,6 +660,46 @@ function auth($uid, $pwd , $method = '', $tls = true, $authz = '')
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.
*
Expand Down Expand Up @@ -722,13 +757,14 @@ function _authDigest_MD5($uid, $pwd, $authz = '')
*
* @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;
Expand Down Expand Up @@ -761,13 +797,14 @@ function _authCRAM_MD5($uid, $pwd)
*
* @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;
Expand Down
1 change: 1 addition & 0 deletions package.xml
Expand Up @@ -33,6 +33,7 @@
<notes>- Adding a new command() method for sending arbitrary SMTP commands.
- More kinds of socket write() failures are now detected.
- Improved PEAR_Error internal handling. (Bug 18469)
- External authentication methods are now supported via setAuthMethod().
</notes>
<contents>
<dir baseinstalldir="Net" name="/">
Expand Down

0 comments on commit f2130bf

Please sign in to comment.