pax_global_header00006660000000000000000000000064141720456170014521gustar00rootroot0000000000000052 comment=1220823a7f4f963019e53b4cb633bb6770e37e88 php-net-smtp-1.10.0/000077500000000000000000000000001417204561700141345ustar00rootroot00000000000000php-net-smtp-1.10.0/Net_SMTP-1.10.0/000077500000000000000000000000001417204561700162405ustar00rootroot00000000000000php-net-smtp-1.10.0/Net_SMTP-1.10.0/LICENSE000066400000000000000000000024501417204561700172460ustar00rootroot00000000000000Copyright 2002-2017 Jon Parise and 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: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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.. 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 HOLDER 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. php-net-smtp-1.10.0/Net_SMTP-1.10.0/Net/000077500000000000000000000000001417204561700167665ustar00rootroot00000000000000php-net-smtp-1.10.0/Net_SMTP-1.10.0/Net/SMTP.php000066400000000000000000001366001417204561700202700ustar00rootroot00000000000000 | // | Jon Parise | // | Damian Alejandro Fernandez Sosa | // +----------------------------------------------------------------------+ require_once 'PEAR.php'; require_once 'Net/Socket.php'; /** * Provides an implementation of the SMTP protocol using PEAR's * Net_Socket class. * * @package Net_SMTP * @author Chuck Hagenbuch * @author Jon Parise * @author Damian Alejandro Fernandez Sosa * @license http://opensource.org/licenses/bsd-license.php BSD-2-Clause * * @example basic.php A basic implementation of the Net_SMTP package. */ class Net_SMTP { /** * The server to connect to. * @var string */ public $host = 'localhost'; /** * The port to connect to. * @var int */ public $port = 25; /** * The value to give when sending EHLO or HELO. * @var string */ public $localhost = 'localhost'; /** * List of supported authentication methods, in preferential order. * @var array */ public $auth_methods = array(); /** * 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 */ public $pipelining = false; /** * Number of pipelined commands. * @var int */ protected $pipelined_commands = 0; /** * Should debugging output be enabled? * @var boolean */ protected $debug = false; /** * Debug output handler. * @var callback */ protected $debug_handler = null; /** * The socket resource being used to connect to the SMTP server. * @var resource */ protected $socket = null; /** * Array of socket options that will be passed to Net_Socket::connect(). * @see stream_context_create() * @var array */ protected $socket_options = null; /** * The socket I/O timeout value in seconds. * @var int */ protected $timeout = 0; /** * The most recent server response code. * @var int */ protected $code = -1; /** * The most recent server response arguments. * @var array */ protected $arguments = array(); /** * Stores the SMTP server's greeting string. * @var string */ protected $greeting = null; /** * Stores detected features of the SMTP server. * @var array */ protected $esmtp = array(); /** * Instantiates a new Net_SMTP object, overriding any defaults * with parameters that are passed in. * * If you have SSL support in PHP, you can connect to a server * over SSL using an 'ssl://' prefix: * * // 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. * @param boolean $pipelining Use SMTP command pipelining * @param integer $timeout Socket I/O timeout in seconds. * @param array $socket_options Socket stream_context_create() options. * @param string $gssapi_principal GSSAPI service principal name * @param string $gssapi_cname GSSAPI credentials cache * * @since 1.0 */ public function __construct($host = null, $port = null, $localhost = null, $pipelining = false, $timeout = 0, $socket_options = null, $gssapi_principal=null, $gssapi_cname=null ) { 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(); $this->socket_options = $socket_options; $this->timeout = $timeout; $this->gssapi_principal = $gssapi_principal; $this->gssapi_cname = $gssapi_cname; /* If PHP krb5 extension is loaded, we enable GSSAPI method. */ if (extension_loaded('krb5')) { $this->setAuthMethod('GSSAPI', array($this, 'authGSSAPI')); } /* 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') { $this->setAuthMethod('CRAM-MD5', array($this, 'authCramMD5')); $this->setAuthMethod('DIGEST-MD5', array($this, 'authDigestMD5')); } /* These standard authentication methods are always available. */ $this->setAuthMethod('LOGIN', array($this, 'authLogin'), false); $this->setAuthMethod('PLAIN', array($this, 'authPlain'), false); $this->setAuthMethod('XOAUTH2', array($this, 'authXOAuth2'), false); } /** * Set the socket I/O timeout value in seconds plus microseconds. * * @param integer $seconds Timeout value in seconds. * @param integer $microseconds Additional value in microseconds. * * @since 1.5.0 */ public function setTimeout($seconds, $microseconds = 0) { return $this->socket->setTimeout($seconds, $microseconds); } /** * Set the value of the debugging flag. * * @param boolean $debug New value for the debugging flag. * @param callback $handler Debug handler callback * * @since 1.1.0 */ public 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. * * @since 1.3.3 */ protected 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"; } } } /** * Send the given string of data to the server. * * @param string $data The string of data to send. * * @return mixed The number of bytes that were actually written, * or a PEAR_Error object on failure. * * @since 1.1.0 */ protected function send($data) { $this->debug("Send: $data"); $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"); } return $result; } /** * 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 * 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 $args A string of optional arguments to append * to the command. * * @return mixed The result of the send() call. * * @since 1.1.0 */ protected function put($command, $args = '') { if (!empty($args)) { $command .= ' ' . $args; } if (strcspn($command, "\r\n") !== strlen($command)) { return PEAR::raiseError('Commands cannot contain newlines'); } return $this->send($command . "\r\n"); } /** * Read a reply from the SMTP 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. * @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 * a PEAR_Error object is an error condition is reached. * * @since 1.1.0 * * @see getResponse */ protected function parseResponse($valid, $later = false) { $this->code = -1; $this->arguments = array(); 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 was closed. */ if (empty($line)) { $this->disconnect(); return PEAR::raiseError('Connection was 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; } elseif (is_array($valid) && in_array($this->code, $valid, true)) { return true; } return PEAR::raiseError('Invalid response code received from server', $this->code); } /** * 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. * * @since 1.6.0 */ public function command($command, $valid) { if (PEAR::isError($error = $this->put($command))) { return $error; } if (PEAR::isError($error = $this->parseResponse($valid))) { return $error; } return true; } /** * Return a 2-tuple containing the last response from the SMTP 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. * * @since 1.1.0 */ public function getResponse() { 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. * * @since 1.3.3 */ public function getGreeting() { return $this->greeting; } /** * Attempt to connect to the SMTP server. * * @param int $timeout The timeout value (in seconds) for the * socket connection attempt. * @param bool $persistent Should a persistent socket connection * be used? * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.0 */ public function connect($timeout = null, $persistent = false) { $this->greeting = null; $result = $this->socket->connect( $this->host, $this->port, $persistent, $timeout, $this->socket_options ); if (PEAR::isError($result)) { return PEAR::raiseError( 'Failed to connect socket: ' . $result->getMessage() ); } /* * Now that we're connected, reset the socket's timeout value for * future I/O operations. This allows us to have different socket * timeout values for the initial connection (our $timeout parameter) * and all other socket operations. */ if ($this->timeout > 0) { if (PEAR::isError($error = $this->setTimeout($this->timeout))) { return $error; } } if (PEAR::isError($error = $this->parseResponse(220))) { 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; } return true; } /** * Attempt to disconnect from the SMTP server. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.0 */ public function disconnect() { if (PEAR::isError($error = $this->put('QUIT'))) { return $error; } if (PEAR::isError($error = $this->parseResponse(221))) { return $error; } if (PEAR::isError($error = $this->socket->disconnect())) { return PEAR::raiseError( 'Failed to disconnect socket: ' . $error->getMessage() ); } return true; } /** * Attempt to send the EHLO command and obtain a list of ESMTP * extensions available, and failing that just send HELO. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * * @since 1.1.0 */ protected function negotiate() { if (PEAR::isError($error = $this->put('EHLO', $this->localhost))) { return $error; } if (PEAR::isError($this->parseResponse(250))) { /* If the EHLO failed, try the simpler HELO command. */ if (PEAR::isError($error = $this->put('HELO', $this->localhost))) { return $error; } if (PEAR::isError($this->parseResponse(250))) { return PEAR::raiseError('HELO was not accepted', $this->code); } return true; } foreach ($this->arguments as $argument) { $verb = strtok($argument, ' '); $len = strlen($verb); $arguments = substr($argument, $len + 1, strlen($argument) - $len - 1); $this->esmtp[$verb] = $arguments; } if (!isset($this->esmtp['PIPELINING'])) { $this->pipelining = false; } return true; } /** * Returns the name of the best authentication method that the server * has advertised. * * @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. * @since 1.1.0 */ protected function getBestAuthMethod() { $available_methods = explode(' ', $this->esmtp['AUTH']); foreach ($this->auth_methods as $method => $callback) { if (in_array($method, $available_methods)) { return $method; } } return PEAR::raiseError('No supported authentication methods'); } /** * Establish STARTTLS Connection. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, true on success, or false if SSL/TLS * isn't available. * @since 1.10.0 */ public function starttls() { /* 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 (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 (isset($this->socket_options['ssl']['crypto_method'])) { $crypto_method = $this->socket_options['ssl']['crypto_method']; } else { /* STREAM_CRYPTO_METHOD_TLS_ANY_CLIENT constant does not exist * and STREAM_CRYPTO_METHOD_SSLv23_CLIENT constant is * inconsistent across PHP versions. */ $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT | @STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT | @STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; } if (PEAR::isError($result = $this->socket->enableCrypto(true, $crypto_method))) { return $result; } elseif ($result !== true) { return PEAR::raiseError('STARTTLS failed'); } /* Send EHLO again to recieve the AUTH string from the * SMTP server. */ $this->negotiate(); } else { return false; } return true; } /** * Attempt to do SMTP authentication. * * @param string $uid The userid to authenticate as. * @param string $pwd The password to authenticate with. * @param string $method The requested authentication method. If none is * specified, the best supported method will be used. * @param bool $tls Flag indicating whether or not TLS should be attempted. * @param string $authz 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 * kind of failure, or true on success. * @since 1.0 */ public 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) { /* Start the TLS connection attempt. */ if (PEAR::isError($starttls = $this->starttls())) { return $starttls; } } if (empty($this->esmtp['AUTH'])) { return PEAR::raiseError('SMTP server does not support authentication'); } /* If no method has been specified, get the name of the best * supported method advertised by the SMTP server. */ if (empty($method)) { if (PEAR::isError($method = $this->getBestAuthMethod())) { /* Return the PEAR_Error object from _getBestAuthMethod(). */ return $method; } } else { $method = strtoupper($method); if (!array_key_exists($method, $this->auth_methods)) { return PEAR::raiseError("$method is not a supported authentication method"); } } if (!isset($this->auth_methods[$method])) { return PEAR::raiseError("$method is not a supported authentication method"); } 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; } return true; } /** * Add a new authentication method. * * @param string $name The authentication method name (e.g. 'PLAIN') * @param mixed $callback The authentication callback (given as the name of a * function or as an (object, method name) array). * @param bool $prepend 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. * * @since 1.6.0 */ public 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. * * @param string $uid The userid to authenticate as. * @param string $pwd The password to authenticate with. * @param string $authz The optional authorization proxy identifier. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.1.0 */ protected function authDigestMD5($uid, $pwd, $authz = '') { if (PEAR::isError($error = $this->put('AUTH', 'DIGEST-MD5'))) { return $error; } /* 334: Continue authentication request */ if (PEAR::isError($error = $this->parseResponse(334))) { /* 503: Error: already authenticated */ if ($this->code === 503) { return true; } return $error; } $auth_sasl = new Auth_SASL; $digest = $auth_sasl->factory('digest-md5'); $challenge = base64_decode($this->arguments[0]); $auth_str = base64_encode( $digest->getResponse($uid, $pwd, $challenge, $this->host, "smtp", $authz) ); if (PEAR::isError($error = $this->put($auth_str))) { return $error; } /* 334: Continue authentication request */ if (PEAR::isError($error = $this->parseResponse(334))) { return $error; } /* We don't use the protocol's third step because SMTP doesn't * allow subsequent authentication, so we just silently ignore * it. */ if (PEAR::isError($error = $this->put(''))) { return $error; } /* 235: Authentication successful */ if (PEAR::isError($error = $this->parseResponse(235))) { return $error; } } /** * Authenticates the user using the CRAM-MD5 method. * * @param string $uid The userid to authenticate as. * @param string $pwd The password to authenticate with. * @param string $authz The optional authorization proxy identifier. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.1.0 */ protected function authCRAMMD5($uid, $pwd, $authz = '') { if (PEAR::isError($error = $this->put('AUTH', 'CRAM-MD5'))) { return $error; } /* 334: Continue authentication request */ if (PEAR::isError($error = $this->parseResponse(334))) { /* 503: Error: already authenticated */ if ($this->code === 503) { return true; } return $error; } $auth_sasl = new Auth_SASL; $challenge = base64_decode($this->arguments[0]); $cram = $auth_sasl->factory('cram-md5'); $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge)); if (PEAR::isError($error = $this->put($auth_str))) { return $error; } /* 235: Authentication successful */ if (PEAR::isError($error = $this->parseResponse(235))) { return $error; } } /** * Authenticates the user using the LOGIN method. * * @param string $uid The userid to authenticate as. * @param string $pwd The password to authenticate with. * @param string $authz The optional authorization proxy identifier. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.1.0 */ protected function authLogin($uid, $pwd, $authz = '') { if (PEAR::isError($error = $this->put('AUTH', 'LOGIN'))) { return $error; } /* 334: Continue authentication request */ if (PEAR::isError($error = $this->parseResponse(334))) { /* 503: Error: already authenticated */ if ($this->code === 503) { return true; } return $error; } if (PEAR::isError($error = $this->put(base64_encode($uid)))) { return $error; } /* 334: Continue authentication request */ if (PEAR::isError($error = $this->parseResponse(334))) { return $error; } if (PEAR::isError($error = $this->put(base64_encode($pwd)))) { return $error; } /* 235: Authentication successful */ if (PEAR::isError($error = $this->parseResponse(235))) { return $error; } return true; } /** * Authenticates the user using the PLAIN method. * * @param string $uid The userid to authenticate as. * @param string $pwd The password to authenticate with. * @param string $authz The optional authorization proxy identifier. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.1.0 */ protected function authPlain($uid, $pwd, $authz = '') { if (PEAR::isError($error = $this->put('AUTH', 'PLAIN'))) { return $error; } /* 334: Continue authentication request */ if (PEAR::isError($error = $this->parseResponse(334))) { /* 503: Error: already authenticated */ if ($this->code === 503) { return true; } return $error; } $auth_str = base64_encode($authz . chr(0) . $uid . chr(0) . $pwd); if (PEAR::isError($error = $this->put($auth_str))) { return $error; } /* 235: Authentication successful */ if (PEAR::isError($error = $this->parseResponse(235))) { return $error; } return true; } /** * Authenticates the user using the GSSAPI method. * * PHP krb5 extension is required, * service principal and credentials cache must be set. * * @param string $uid The userid to authenticate as. * @param string $pwd The password to authenticate with. * @param string $authz The optional authorization proxy identifier. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. */ protected function authGSSAPI($uid, $pwd, $authz = '') { if (PEAR::isError($error = $this->put('AUTH', 'GSSAPI'))) { return $error; } /* 334: Continue authentication request */ if (PEAR::isError($error = $this->parseResponse(334))) { /* 503: Error: already authenticated */ if ($this->code === 503) { return true; } return $error; } if (!$this->gssapi_principal) { return PEAR::raiseError('No Kerberos service principal set', 2); } if (!empty($this->gssapi_cname)) { putenv('KRB5CCNAME=' . $this->gssapi_cname); } try { $ccache = new KRB5CCache(); if (!empty($this->gssapi_cname)) { $ccache->open($this->gssapi_cname); } $gssapicontext = new GSSAPIContext(); $gssapicontext->acquireCredentials($ccache); $token = ''; $success = $gssapicontext->initSecContext($this->gssapi_principal, null, null, null, $token); $token = base64_encode($token); } catch (Exception $e) { return PEAR::raiseError('GSSAPI authentication failed: ' . $e->getMessage()); } if (PEAR::isError($error = $this->put($token))) { return $error; } /* 334: Continue authentication request */ if (PEAR::isError($error = $this->parseResponse(334))) { return $error; } $response = $this->arguments[0]; try { $challenge = base64_decode($response); $gssapicontext->unwrap($challenge, $challenge); $gssapicontext->wrap($challenge, $challenge, true); } catch (Exception $e) { return PEAR::raiseError('GSSAPI authentication failed: ' . $e->getMessage()); } if (PEAR::isError($error = $this->put(base64_encode($challenge)))) { return $error; } /* 235: Authentication successful */ if (PEAR::isError($error = $this->parseResponse(235))) { return $error; } return true; } /** * Authenticates the user using the XOAUTH2 method. * * @param string $uid The userid to authenticate as. * @param string $token The access token to authenticate with. * @param string $authz The optional authorization proxy identifier. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.9.0 */ public function authXOAuth2($uid, $token, $authz, $conn) { $auth = base64_encode("user=$uid\1auth=$token\1\1"); if (PEAR::isError($error = $this->put('AUTH', 'XOAUTH2 ' . $auth))) { return $error; } /* 235: Authentication successful or 334: Continue authentication */ if (PEAR::isError($error = $this->parseResponse([235, 334]))) { return $error; } /* 334: Continue authentication request */ if ($this->code === 334) { /* Send an empty line as response to 334 */ if (PEAR::isError($error = $this->put(''))) { return $error; } /* Expect 235: Authentication successful */ if (PEAR::isError($error = $this->parseResponse(235))) { return $error; } } return true; } /** * Send the HELO command. * * @param string $domain The domain name to say we are. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.0 */ public function helo($domain) { if (PEAR::isError($error = $this->put('HELO', $domain))) { return $error; } if (PEAR::isError($error = $this->parseResponse(250))) { return $error; } return true; } /** * Return the list of SMTP service extensions advertised by the server. * * @return array The list of SMTP service extensions. * @since 1.3 */ public function getServiceExtensions() { return $this->esmtp; } /** * Send the MAIL FROM: command. * * @param string $sender The sender (reverse path) to set. * @param string $params String containing additional MAIL parameters, * such as the NOTIFY flags defined by RFC 1891 * or the VERP protocol. * * If $params is an array, only the 'verp' option * is supported. If 'verp' is true, the XVERP * parameter is appended to the MAIL command. * If the 'verp' value is a string, the full * XVERP=value parameter is appended. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.0 */ public function mailFrom($sender, $params = null) { $args = "FROM:<$sender>"; /* Support the deprecated array form of $params. */ if (is_array($params) && isset($params['verp'])) { if ($params['verp'] === true) { $args .= ' XVERP'; } elseif (trim($params['verp'])) { $args .= ' XVERP=' . $params['verp']; } } elseif (is_string($params) && !empty($params)) { $args .= ' ' . $params; } if (PEAR::isError($error = $this->put('MAIL', $args))) { return $error; } if (PEAR::isError($error = $this->parseResponse(250, $this->pipelining))) { return $error; } return true; } /** * Send the RCPT TO: command. * * @param string $recipient The recipient (forward path) to add. * @param string $params String containing additional RCPT parameters, * such as the NOTIFY flags defined by RFC 1891. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * * @since 1.0 */ public function rcptTo($recipient, $params = null) { $args = "TO:<$recipient>"; if (is_string($params)) { $args .= ' ' . $params; } if (PEAR::isError($error = $this->put('RCPT', $args))) { return $error; } if (PEAR::isError($error = $this->parseResponse(array(250, 251), $this->pipelining))) { return $error; } return true; } /** * Quote the data so that it meets SMTP standards. * * This is provided as a separate public function to facilitate * easier overloading for the cases where it is desirable to * customize the quoting behavior. * * @param string &$data The message text to quote. The string must be passed * by reference, and the text will be modified in place. * * @since 1.2 */ public function quotedata(&$data) { /* Because a single leading period (.) signifies an end to the * data, legitimate leading periods need to be "doubled" ('..'). */ $data = preg_replace('/^\./m', '..', $data); /* Change Unix (\n) and Mac (\r) linefeeds into CRLF's (\r\n). */ $data = preg_replace('/(?:\r\n|\n|\r(?!\n))/', "\r\n", $data); } /** * Send the DATA command. * * @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 * kind of failure, or true on success. * @since 1.0 */ public 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 = $headers_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) { return PEAR::raiseError('Message size exceeds server limit'); } /* Initiate the DATA command. */ if (PEAR::isError($error = $this->put('DATA'))) { return $error; } if (PEAR::isError($error = $this->parseResponse(354))) { return $error; } /* 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; } /* Subtract the headers size now that they've been sent. */ $size -= $headers_size; } /* 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 (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; } } $last = $line; } 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; } $last = $chunk; } /* Don't add another CRLF sequence if it's already in the data */ $terminator = (substr($last, -2) == "\r\n" ? '' : "\r\n") . ".\r\n"; /* Finally, send the DATA terminator sequence. */ if (PEAR::isError($result = $this->send($terminator))) { return $result; } /* Verify that the data was successfully received by the server. */ if (PEAR::isError($error = $this->parseResponse(250, $this->pipelining))) { return $error; } return true; } /** * Send the SEND FROM: command. * * @param string $path The reverse path to send. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.2.6 */ public function sendFrom($path) { if (PEAR::isError($error = $this->put('SEND', "FROM:<$path>"))) { return $error; } if (PEAR::isError($error = $this->parseResponse(250, $this->pipelining))) { return $error; } return true; } /** * Send the SOML FROM: command. * * @param string $path The reverse path to send. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.2.6 */ public function somlFrom($path) { if (PEAR::isError($error = $this->put('SOML', "FROM:<$path>"))) { return $error; } if (PEAR::isError($error = $this->parseResponse(250, $this->pipelining))) { return $error; } return true; } /** * Send the SAML FROM: command. * * @param string $path The reverse path to send. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.2.6 */ public function samlFrom($path) { if (PEAR::isError($error = $this->put('SAML', "FROM:<$path>"))) { return $error; } if (PEAR::isError($error = $this->parseResponse(250, $this->pipelining))) { return $error; } return true; } /** * Send the RSET command. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.0 */ public function rset() { if (PEAR::isError($error = $this->put('RSET'))) { return $error; } if (PEAR::isError($error = $this->parseResponse(250, $this->pipelining))) { return $error; } return true; } /** * Send the VRFY command. * * @param string $string The string to verify * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.0 */ public function vrfy($string) { /* Note: 251 is also a valid response code */ if (PEAR::isError($error = $this->put('VRFY', $string))) { return $error; } if (PEAR::isError($error = $this->parseResponse(array(250, 252)))) { return $error; } return true; } /** * Send the NOOP command. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @since 1.0 */ public function noop() { if (PEAR::isError($error = $this->put('NOOP'))) { return $error; } if (PEAR::isError($error = $this->parseResponse(250))) { return $error; } return true; } /** * Backwards-compatibility method. identifySender()'s functionality is * now handled internally. * * @return boolean This method always return true. * * @since 1.0 */ public function identifySender() { return true; } } php-net-smtp-1.10.0/Net_SMTP-1.10.0/README.rst000066400000000000000000000226521417204561700177360ustar00rootroot00000000000000====================== The Net_SMTP Package ====================== -------------------- User Documentation -------------------- :Author: Jon Parise :Contact: jon@php.net .. contents:: Table of Contents .. section-numbering:: Dependencies ============ The ``PEAR_Error`` Class ------------------------ The Net_SMTP package uses the `PEAR_Error`_ class for all of its `error handling`_. The ``Net_Socket`` Package -------------------------- The Net_Socket_ package is used as the basis for all network communications. Connection options can be specified via the `$socket_options` construction parameter:: $socket_options = array('ssl' => array('verify_peer_name' => false)); $smtp = new Net_SMTP($host, null, null, false, 0, $socket_options); **Note:** PHP 5.6 introduced `OpenSSL changes`_. Peer certificate verification is now enabled by default. Although not recommended, `$socket_options` can be used to disable peer verification (as shown above). .. _OpenSSL changes: https://php.net/manual/en/migration56.openssl.php The ``Auth_SASL`` Package ------------------------- The `Auth_SASL`_ package is an optional dependency. If it is available, the Net_SMTP package will be able to support the DIGEST-MD5_ and CRAM-MD5_ SMTP authentication methods. Otherwise, only the LOGIN_ and PLAIN_ methods will be available. Error Handling ============== All of the Net_SMTP class's public methods return a PEAR_Error_ object if an error occurs. The standard way to check for a PEAR_Error object is by using `PEAR::isError()`_:: if (PEAR::isError($error = $smtp->connect())) { die($error->getMessage()); } .. _PEAR::isError(): https://pear.php.net/manual/en/core.pear.pear.iserror.php SMTP Authentication =================== The Net_SMTP package supports the SMTP authentication standard (as defined by RFC-2554_). The Net_SMTP package supports the following authentication methods, in order of preference: .. _RFC-2554: https://www.ietf.org/rfc/rfc2554.txt GSSAPI ------ The GSSAPI authentication method uses Kerberos 5 protocol (RFC-4120_). Does not use user/password. Requires Service Principal ``gssapi_principal`` parameter and has an optional Credentials Cache ``gssapi_cname`` parameter. Requires DNS and Key Distribution Center (KDC) setup. It is considered the most secure method of SMTP authentication. **Note:** The GSSAPI authentication method is only supported if the krb5_ php extension is available. .. _RFC-4120: https://tools.ietf.org/html/rfc4120 .. _krb5: https://pecl.php.net/package/krb5 DIGEST-MD5 ---------- The DIGEST-MD5 authentication method uses `RSA Data Security Inc.`_'s MD5 Message Digest algorithm. It is considered a more secure method of SMTP authentication than PLAIN or LOGIN, while still vulnerable to MitM attacks without TLS/SSL. **Note:** The DIGEST-MD5 authentication method is only supported if the AUTH_SASL_ package is available. .. _RSA Data Security Inc.: https://www.rsasecurity.com/ CRAM-MD5 -------- The CRAM-MD5 authentication method has been superseded by the DIGEST-MD5_ method in terms of security. It is provided here for compatibility with older SMTP servers that may not support the newer DIGEST-MD5 algorithm. **Note:** The CRAM-MD5 authentication method is only supported if the AUTH_SASL_ package is available. LOGIN ----- The LOGIN authentication method encrypts the user's password using the Base64_ encoding scheme. Because decrypting a Base64-encoded string is trivial, LOGIN is not considered a secure authentication method and should be avoided. .. _Base64: https://www.php.net/manual/en/function.base64-encode.php PLAIN ----- The PLAIN authentication method sends the user's password in plain text. This method of authentication is not secure and should be avoided. XOAUTH2 ------- The XOAUTH2 authentication method sends a username and an OAuth2 access token as per `Gmail's SASL XOAUTH2 documentation`__. .. __: https://developers.google.com/gmail/imap/xoauth2-protocol#smtp_protocol_exchange Secure Connections ================== If `secure socket transports`_ have been enabled in PHP, it is possible to establish a secure connection to the remote SMTP server:: $smtp = new Net_SMTP('ssl://mail.example.com', 465); This example connects to ``mail.example.com`` on port 465 (a common SMTPS port) using the ``ssl://`` transport. TLS/SSL is enabled for authenticated connections by default (via the ``auth()`` method's ``$tls`` parameter), but the |STARTTLS|_ command can also be sent manually using the ``starttls()`` method. .. _secure socket transports: https://www.php.net/transports .. |STARTTLS| replace:: ``STARTTLS`` .. _STARTTLS: https://tools.ietf.org/html/rfc3207 Sending Data ============ Message data is sent using the ``data()`` method. The data can be supplied as a single string or as an open file resource. If a string is provided, it is passed through the `data quoting`_ system and sent to the socket connection as a single block. These operations are all memory-based, so sending large messages may result in high memory usage. If an open file resource is provided, the ``data()`` method will read the message data from the file line-by-line. Each chunk will be quoted and sent to the socket connection individually, reducing the overall memory overhead of this data sending operation. Header data can be specified separately from message body data by passing it as the optional second parameter to ``data()``. This is especially useful when an open file resource is being used to supply message data because it allows header fields (like *Subject:*) to be built dynamically at runtime. :: $smtp->data($fp, "Subject: My Subject"); Data Quoting ============ By default, all outbound string data is quoted in accordance with SMTP standards. This means that all native Unix (``\n``) and Mac (``\r``) line endings are converted to Internet-standard CRLF (``\r\n``) line endings. Also, because the SMTP protocol uses a single leading period (``.``) to signal an end to the message data, single leading periods in the original data string are "doubled" (e.g. "``..``"). These string transformation can be expensive when large blocks of data are involved. For example, the Net_SMTP package is not aware of MIME parts (it just sees the MIME message as one big string of characters), so it is not able to skip non-text attachments when searching for characters that may need to be quoted. Because of this, it is possible to extend the Net_SMTP class in order to implement your own custom quoting routine. Just create a new class based on the Net_SMTP class and reimplement the ``quotedata()`` method:: require 'Net_SMTP.php'; class Net_SMTP_custom extends Net_SMTP { function quotedata($data) { /* Perform custom data quoting */ } } Note that the ``$data`` parameter will be passed to the ``quotedata()`` function `by reference`_. This means that you can operate directly on ``$data``. It also the overhead of copying a large ``$data`` string to and from the ``quotedata()`` method. .. _by reference: https://www.php.net/manual/en/language.references.pass.php Server Responses ================ The Net_SMTP package retains the server's last response for further inspection. The ``getResponse()`` method returns a 2-tuple (two element array) containing the server's response code as an integer and the response's arguments as a string. Upon a successful connection, the server's greeting string is available via the ``getGreeting()`` method. Debugging ========= The Net_SMTP package contains built-in debugging output routines (disabled by default). Debugging output must be explicitly enabled via the ``setDebug()`` method:: $smtp->setDebug(true); The debugging messages will be sent to the standard output stream by default. If you need more control over the output, you can optionally install your own debug handler. :: function debugHandler($smtp, $message) { echo "[$smtp->host] $message\n"; } $smtp->setDebug(true, "debugHandler"); Examples ======== Basic Use --------- The following script demonstrates how a simple email message can be sent using the Net_SMTP package:: require 'Net/SMTP.php'; $host = 'mail.example.com'; $from = 'user@example.com'; $rcpt = array('recipient1@example.com', 'recipient2@example.com'); $subj = "Subject: Test Message\n"; $body = "Body Line 1\nBody Line 2"; /* Create a new Net_SMTP object. */ if (! ($smtp = new Net_SMTP($host))) { die("Unable to instantiate Net_SMTP object\n"); } /* Connect to the SMTP server. */ if (PEAR::isError($e = $smtp->connect())) { die($e->getMessage() . "\n"); } /* Send the 'MAIL FROM:' SMTP command. */ if (PEAR::isError($smtp->mailFrom($from))) { die("Unable to set sender to <$from>\n"); } /* Address the message to each of the recipients. */ foreach ($rcpt as $to) { if (PEAR::isError($res = $smtp->rcptTo($to))) { die("Unable to add recipient <$to>: " . $res->getMessage() . "\n"); } } /* Set the body of the message. */ if (PEAR::isError($smtp->data($subj . "\r\n" . $body))) { die("Unable to send data\n"); } /* Disconnect from the SMTP server. */ $smtp->disconnect(); .. _PEAR_Error: https://pear.php.net/manual/en/core.pear.pear-error.php .. _Net_Socket: https://pear.php.net/package/Net_Socket .. _Auth_SASL: https://pear.php.net/package/Auth_SASL .. vim: tabstop=4 shiftwidth=4 softtabstop=4 expandtab textwidth=78 ft=rst: php-net-smtp-1.10.0/Net_SMTP-1.10.0/examples/000077500000000000000000000000001417204561700200565ustar00rootroot00000000000000php-net-smtp-1.10.0/Net_SMTP-1.10.0/examples/basic.php000066400000000000000000000020301417204561700216430ustar00rootroot00000000000000connect())) { die($e->getMessage() . "\n"); } $smtp->auth('username','password'); /* Send the 'MAIL FROM:' SMTP command. */ if (PEAR::isError($smtp->mailFrom($from))) { die("Unable to set sender to <$from>\n"); } /* Address the message to each of the recipients. */ foreach ($rcpt as $to) { if (PEAR::isError($res = $smtp->rcptTo($to))) { die("Unable to add recipient <$to>: " . $res->getMessage() . "\n"); } } /* Set the body of the message. */ if (PEAR::isError($smtp->data($subj . "\r\n" . $body))) { die("Unable to send data\n"); } /* Disconnect from the SMTP server. */ $smtp->disconnect(); php-net-smtp-1.10.0/Net_SMTP-1.10.0/tests/000077500000000000000000000000001417204561700174025ustar00rootroot00000000000000php-net-smtp-1.10.0/Net_SMTP-1.10.0/tests/auth.phpt000066400000000000000000000010411417204561700212340ustar00rootroot00000000000000--TEST-- Net_SMTP: SMTP Authentication --SKIPIF-- connect())) { die($e->getMessage() . "\n"); } if (PEAR::isError($e = $smtp->auth(TEST_AUTH_USER, TEST_AUTH_PASS))) { die("Authentication failure\n"); } $smtp->disconnect(); echo 'Success!'; --EXPECT-- Success! php-net-smtp-1.10.0/Net_SMTP-1.10.0/tests/basic.phpt000066400000000000000000000016561417204561700213700ustar00rootroot00000000000000--TEST-- Net_SMTP: Basic Functionality --SKIPIF-- connect())) { die($e->getMessage() . "\n"); } if (PEAR::isError($e = $smtp->auth(TEST_AUTH_USER, TEST_AUTH_PASS))) { die("Authentication failure\n"); } if (PEAR::isError($smtp->mailFrom(TEST_FROM))) { die('Unable to set sender to <' . TEST_FROM . ">\n"); } if (PEAR::isError($res = $smtp->rcptTo(TEST_TO))) { die('Unable to add recipient <' . TEST_TO . '>: ' . $res->getMessage() . "\n"); } $headers = 'Subject: ' . TEST_SUBJECT; if (PEAR::isError($smtp->data(TEST_BODY, $headers))) { die("Unable to send data\n"); } $smtp->disconnect(); echo 'Success!'; --EXPECT-- Success! php-net-smtp-1.10.0/Net_SMTP-1.10.0/tests/config.php.dist000066400000000000000000000007501417204561700223240ustar00rootroot00000000000000 "\r\n", "\r\n" => "\r\n", "\nxx" => "\r\nxx", "xx\n" => "xx\r\n", "xx\nxx" => "xx\r\nxx", "\n\nxx" => "\r\n\r\nxx", "xx\n\nxx" => "xx\r\n\r\nxx", "xx\n\n" => "xx\r\n\r\n", "\r\nxx" => "\r\nxx", "xx\r\n" => "xx\r\n", "xx\r\nxx" => "xx\r\nxx", "\r\n\r\nxx" => "\r\n\r\nxx", "xx\r\n\r\nxx" => "xx\r\n\r\nxx", "xx\r\n\r\n" => "xx\r\n\r\n", "\r\n\nxx" => "\r\n\r\nxx", "\n\r\nxx" => "\r\n\r\nxx", "xx\r\n\nxx" => "xx\r\n\r\nxx", "xx\n\r\nxx" => "xx\r\n\r\nxx", "xx\r\n\n" => "xx\r\n\r\n", "xx\n\r\n" => "xx\r\n\r\n", "\r" => "\r\n", "\rxx" => "\r\nxx", "xx\rxx" => "xx\r\nxx", "xx\r" => "xx\r\n", "\r\r" => "\r\n\r\n", "\r\rxx" => "\r\n\r\nxx", "xx\r\rxx" => "xx\r\n\r\nxx", "xx\r\r" => "xx\r\n\r\n", "xx\rxx\nxx\r\nxx" => "xx\r\nxx\r\nxx\r\nxx", "\r\r\n\n" => "\r\n\r\n\r\n", /* Dots */ "." => "..", "xxx\n." => "xxx\r\n..", "xxx\n.\nxxx" => "xxx\r\n..\r\nxxx", "xxx.\n.xxx" => "xxx.\r\n..xxx", ); function literal($x) { return str_replace(array("\r", "\n"), array('\r', '\n'), $x); } $smtp = new Net_SMTP(); $error = false; foreach ($tests as $input => $expected) { $output = $input; $smtp->quotedata($output); if ($output != $expected) { printf("Error: '%s' => '%s' (expected: '%s')", literal($input), literal($output), literal($expected)); $error = true; } } if (!$error) { echo "success\n"; } --EXPECT-- success php-net-smtp-1.10.0/package.xml000066400000000000000000000050071417204561700162530ustar00rootroot00000000000000 Net_SMTP pear.php.net An implementation of the SMTP protocol Provides an implementation of the SMTP protocol using PEAR's Net_Socket class. Jon Parise jon jon@php.net yes Chuck Hagenbuch chagenbu chuck@horde.org yes 2021-03-19 1.10.0 1.4.0 stable stable BSD-2-Clause * Add the starttls() method 5.4.0 1.10.1 Net_Socket pear.php.net 1.0.7 Auth_SASL pear.php.net 1.0.5