Source for file LMTP.php
Documentation is available at LMTP.php
// +----------------------------------------------------------------------+
// +----------------------------------------------------------------------+
// | 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: Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar> |
// | Chuck Hagenbuch <chuck@horde.org> |
// | Jon Parise <jon@php.net> |
// +----------------------------------------------------------------------+
require_once 'Net/Socket.php';
* Provides an implementation of the LMTP protocol using PEAR's
* @author Chuck Hagenbuch <chuck@horde.org>
* @author Jon Parise <jon@php.net>
* @author Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar>
* The server to connect to.
* The port to connect to.
* The value to give when sending LHLO.
* Should debugging output be enabled?
* The socket resource being used to connect to the LMTP server.
* The most recent server response code.
* The most recent server response arguments.
var $_arguments = array ();
* Stores detected features of the LMTP server.
* The auth methods this class support
* The auth methods this class support
* Instantiates a new Net_LMTP object, overriding any defaults
* with parameters that are passed in.
* @param string The server to connect to.
* @param int The port to connect to.
* @param string The value to give when sending LHLO.
function Net_LMTP($host = 'localhost', $port = 2003 , $localhost = 'localhost')
$this->_localhost = $localhost;
$this->_socket = new Net_Socket ();
if ((@include_once 'Auth/SASL.php') == false ) {
* Set the value of the debugging flag.
* @param boolean $debug New value for the debugging flag.
* Send the given string of data to the server.
* @param string $data The string of data to send.
* @return mixed True on success or a PEAR_Error object on failure.
echo " DEBUG: Send: $data\n";
if (PEAR ::isError ($error = $this->_socket->write ($data))) {
return new PEAR_Error ('Failed to write to socket: ' .
* Send a command to the server with an optional string of arguments.
* A carriage return / linefeed (CRLF) sequence will be appended to each
* command string before it is sent to the LMTP server.
* @param string $command The LMTP command to send to the server.
* @param string $args A string of optional arguments to append
* @return mixed The result of the _send() call.
function _put ($command, $args = '')
return $this->_send ($command . ' ' . $args . "\r\n");
return $this->_send ($command . "\r\n");
* Read a reply from the LMTP server. The reply consists of a response
* code and a response message.
* @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 if the server returned a valid response code or
* a PEAR_Error object is an error condition is reached.
function _parseResponse ($valid)
$this->_arguments = array ();
while ($line = $this->_socket->readLine ()) {
echo " DEBUG: Recv: $line\n";
/* If we receive an empty line, the connection has been closed. */
return new PEAR_Error ("Connection was unexpectedly closed");
/* Read the code and store the rest in the arguments array. */
/* Check the syntax of the response code. */
$this->_code = (int) $code;
/* If this is not a multiline response, we're done. */
if (substr($line, 3 , 1 ) != '-') {
/* Compare the server's response code with the valid code. */
if (is_int($valid) && ($this->_code === $valid)) {
/* If we were given an array of valid response codes, check each one. */
foreach ($valid as $valid_code) {
if ($this->_code === $valid_code) {
return new PEAR_Error ("Invalid response code received from server");
* Return a 2-tuple containing the last response from the LMTP server.
* @return array A two-element array: the first element contains the
* response code as an integer and the second element
* contains the response's arguments as a string.
return array ($this->_code, join("\n", $this->_arguments));
* Attempt to connect to the LMTP server.
* @param int $timeout The timeout value (in seconds) for the
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
$result = $this->_socket->connect ($this->_host, $this->_port,
if (PEAR ::isError ($result)) {
return new PEAR_Error ('Failed to connect socket: ' .
if (PEAR ::isError ($error = $this->_parseResponse (220 ))) {
if (PEAR ::isError ($error = $this->_negotiate ())) {
* Attempt to disconnect from the LMTP server.
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
if (PEAR ::isError ($error = $this->_put ('QUIT'))) {
if (PEAR ::isError ($error = $this->_parseResponse (221 ))) {
if (PEAR ::isError ($error = $this->_socket->disconnect ())) {
return new PEAR_Error ('Failed to disconnect socket: ' .
* Attempt to send the LHLO command and obtain a list of ESMTP
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
if (PEAR ::isError ($error = $this->_put ('LHLO', $this->_localhost))) {
if (PEAR ::isError ($this->_parseResponse (250 ))) {
return new PEAR_Error ('LHLO was not accepted: ', $this->_code);
foreach ($this->_arguments as $argument) {
$verb = strtok($argument, ' ');
$this->_esmtp[$verb] = $arguments;
* Returns the name of the best authentication method that the server
* @return mixed Returns a string containing the name of the best
* supported authentication method or a PEAR_Error object
* if a failure condition is encountered.
function _getBestAuthMethod ()
$available_methods = explode(' ', $this->_esmtp['AUTH']);
if (in_array($method, $available_methods)) {
return new PEAR_Error ('No supported authentication methods');
* Attempt to do LMTP authentication.
* @param string The userid to authenticate as.
* @param string The password to authenticate with.
* @param string The requested authentication method. If none is
* specified, the best supported method will be used.
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
function auth($uid, $pwd , $method = '')
return new PEAR_Error ('LMTP server does no support authentication');
if (!isset ($this->_esmtp['STARTTLS'])) {
return PEAR ::raiseError ('LMTP server does not support authentication');
if (PEAR ::isError ($result = $this->_put ('STARTTLS'))) {
if (PEAR ::isError ($result = $this->_parseResponse (220 ))) {
if (PEAR ::isError ($result = $this->_socket->enableCrypto (true , STREAM_CRYPTO_METHOD_TLS_CLIENT ))) {
} elseif ($result !== true ) {
return PEAR ::raiseError ('STARTTLS failed');
/* Send LHLO again to recieve the AUTH string from the
if (empty ($this->_esmtp['AUTH'])) {
return PEAR ::raiseError ('LMTP server does not support authentication');
* If no method has been specified, get the name of the best supported
* method advertised by the LMTP server.
if (empty ($method) || $method === true ) {
if (PEAR ::isError ($method = $this->_getBestAuthMethod ())) {
/* Return the PEAR_Error object from _getBestAuthMethod(). */
$result = $this->_authDigest_MD5 ($uid, $pwd);
$result = $this->_authCRAM_MD5 ($uid, $pwd);
$result = $this->_authLogin ($uid, $pwd);
$result = $this->_authPlain ($uid, $pwd);
$result = new PEAR_Error (" $method is not a supported authentication method" );
/* If an error was encountered, return the PEAR_Error object. */
if (PEAR ::isError ($result)) {
/* RFC-2554 requires us to re-negotiate ESMTP after an AUTH. */
if (PEAR ::isError ($error = $this->_negotiate ())) {
/* Authenticates the user using the DIGEST-MD5 method.
* @param string The userid to authenticate as.
* @param string The password to authenticate with.
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
function _authDigest_MD5 ($uid, $pwd)
if (PEAR ::isError ($error = $this->_put ('AUTH', 'DIGEST-MD5'))) {
if (PEAR ::isError ($error = $this->_parseResponse (334 ))) {
$digest = &Auth_SASL ::factory ('digestmd5');
$auth_str = base64_encode($digest->getResponse ($uid, $pwd, $challenge,
if (PEAR ::isError ($error = $this->_put ($auth_str ))) {
if (PEAR ::isError ($error = $this->_parseResponse (334 ))) {
* We don't use the protocol's third step because LMTP doesn't allow
* subsequent authentication, so we just silently ignore it.
if (PEAR ::isError ($error = $this->_put (' '))) {
if (PEAR ::isError ($error = $this->_parseResponse (235 ))) {
/* Authenticates the user using the CRAM-MD5 method.
* @param string The userid to authenticate as.
* @param string The password to authenticate with.
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
function _authCRAM_MD5 ($uid, $pwd)
if (PEAR ::isError ($error = $this->_put ('AUTH', 'CRAM-MD5'))) {
if (PEAR ::isError ($error = $this->_parseResponse (334 ))) {
$cram = &Auth_SASL ::factory ('crammd5');
$auth_str = base64_encode($cram->getResponse ($uid, $pwd, $challenge));
if (PEAR ::isError ($error = $this->_put ($auth_str))) {
if (PEAR ::isError ($error = $this->_parseResponse (235 ))) {
* Authenticates the user using the LOGIN method.
* @param string The userid to authenticate as.
* @param string The password to authenticate with.
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
function _authLogin ($uid, $pwd)
if (PEAR ::isError ($error = $this->_put ('AUTH', 'LOGIN'))) {
if (PEAR ::isError ($error = $this->_parseResponse (334 ))) {
if (PEAR ::isError ($error = $this->_parseResponse (334 ))) {
if (PEAR ::isError ($error = $this->_parseResponse (235 ))) {
* Authenticates the user using the PLAIN method.
* @param string The userid to authenticate as.
* @param string The password to authenticate with.
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
function _authPlain ($uid, $pwd)
if (PEAR ::isError ($error = $this->_put ('AUTH', 'PLAIN'))) {
if (PEAR ::isError ($error = $this->_parseResponse (334 ))) {
if (PEAR ::isError ($error = $this->_put ($auth_str))) {
if (PEAR ::isError ($error = $this->_parseResponse (235 ))) {
* Send the MAIL FROM: command.
* @param string The sender (reverse path) to set.
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
if (PEAR ::isError ($error = $this->_put ('MAIL', " FROM:<$sender>" ))) {
if (PEAR ::isError ($error = $this->_parseResponse (250 ))) {
* Send the RCPT TO: command.
* @param string The recipient (forward path) to add.
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
if (PEAR ::isError ($error = $this->_put ('RCPT', " TO:<$recipient>" ))) {
if (PEAR ::isError ($error = $this->_parseResponse (array (250 , 251 )))) {
* @param string The message body to send.
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
if (isset ($this->_esmtp['SIZE']) && ($this->_esmtp['SIZE'] > 0 )) {
if (strlen($data) >= $this->_esmtp['SIZE']) {
return new PEAR_Error ('Message size excedes the server limit');
* Change Unix (\n) and Mac (\r) linefeeds into Internet-standard CRLF
* Because a single leading period (.) signifies an end to the data,
* legitimate leading periods need to be "doubled" (e.g. '..').
if (PEAR ::isError ($error = $this->_put ('DATA'))) {
if (PEAR ::isError ($error = $this->_parseResponse (354 ))) {
if (PEAR ::isError ($this->_send ($data . "\r\n.\r\n"))) {
return new PEAR_Error ('write to socket failed');
if (PEAR ::isError ($error = $this->_parseResponse (250 ))) {
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
if (PEAR ::isError ($error = $this->_put ('RSET'))) {
if (PEAR ::isError ($error = $this->_parseResponse (250 ))) {
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
if (PEAR ::isError ($error = $this->_put ('NOOP'))) {
if (PEAR ::isError ($error = $this->_parseResponse (250 ))) {
Documentation generated on Mon, 11 Mar 2019 15:45:26 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.
|