aboutsummaryrefslogtreecommitdiffstats
path: root/3dparty/Net
diff options
context:
space:
mode:
authorJakob Sack <mail@jakobsack.de>2011-03-01 23:20:16 +0100
committerJakob Sack <mail@jakobsack.de>2011-03-01 23:20:16 +0100
commit132695ceb1d7ab0e4bfbb141e9e9639111dd25b5 (patch)
treef53fabcefd89a1e5bbeda9a2c2d2fff6683139d4 /3dparty/Net
parent1fd39a52fa750878e7d70fba63c099f252095762 (diff)
downloadnextcloud-server-132695ceb1d7ab0e4bfbb141e9e9639111dd25b5.tar.gz
nextcloud-server-132695ceb1d7ab0e4bfbb141e9e9639111dd25b5.zip
Start of the refactoring. Commit is quite big because I forgot to use git right from the beginning. Sorry.
Diffstat (limited to '3dparty/Net')
-rw-r--r--3dparty/Net/SMTP.php970
-rw-r--r--3dparty/Net/Socket.php528
2 files changed, 1498 insertions, 0 deletions
diff --git a/3dparty/Net/SMTP.php b/3dparty/Net/SMTP.php
new file mode 100644
index 00000000000..71dcb5e9bef
--- /dev/null
+++ b/3dparty/Net/SMTP.php
@@ -0,0 +1,970 @@
+<?php
+/* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | 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: Chuck Hagenbuch <chuck@horde.org> |
+// | Jon Parise <jon@php.net> |
+// | Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar> |
+// +----------------------------------------------------------------------+
+
+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 <chuck@horde.org>
+ * @author Jon Parise <jon@php.net>
+ * @author Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar>
+ *
+ * @example basic.php A basic implementation of the Net_SMTP package.
+ */
+class Net_SMTP
+{
+ /**
+ * The server to connect to.
+ * @var string
+ * @access public
+ */
+ var $host = 'localhost';
+
+ /**
+ * The port to connect to.
+ * @var int
+ * @access public
+ */
+ var $port = 25;
+
+ /**
+ * The value to give when sending EHLO or HELO.
+ * @var string
+ * @access public
+ */
+ var $localhost = 'localhost';
+
+ /**
+ * List of supported authentication methods, in preferential order.
+ * @var array
+ * @access public
+ */
+ var $auth_methods = array('DIGEST-MD5', 'CRAM-MD5', 'LOGIN', 'PLAIN');
+
+ /**
+ * Should debugging output be enabled?
+ * @var boolean
+ * @access private
+ */
+ var $_debug = false;
+
+ /**
+ * The socket resource being used to connect to the SMTP server.
+ * @var resource
+ * @access private
+ */
+ var $_socket = null;
+
+ /**
+ * The most recent server response code.
+ * @var int
+ * @access private
+ */
+ var $_code = -1;
+
+ /**
+ * The most recent server response arguments.
+ * @var array
+ * @access private
+ */
+ var $_arguments = array();
+
+ /**
+ * Stores detected features of the SMTP server.
+ * @var array
+ * @access private
+ */
+ var $_esmtp = array();
+
+ /**
+ * Instantiates a new Net_SMTP 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 EHLO or HELO.
+ *
+ * @access public
+ * @since 1.0
+ */
+ function Net_SMTP($host = null, $port = null, $localhost = null)
+ {
+ if (isset($host)) $this->host = $host;
+ if (isset($port)) $this->port = $port;
+ if (isset($localhost)) $this->localhost = $localhost;
+
+ $this->_socket = new Net_Socket();
+
+ /*
+ * 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]);
+ }
+ }
+
+ /**
+ * Set the value of the debugging flag.
+ *
+ * @param boolean $debug New value for the debugging flag.
+ *
+ * @access public
+ * @since 1.1.0
+ */
+ function setDebug($debug)
+ {
+ $this->_debug = $debug;
+ }
+
+ /**
+ * 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.
+ *
+ * @access private
+ * @since 1.1.0
+ */
+ function _send($data)
+ {
+ if ($this->_debug) {
+ echo "DEBUG: Send: $data\n";
+ }
+
+ if (PEAR::isError($error = $this->_socket->write($data))) {
+ return new PEAR_Error('Failed to write to socket: ' .
+ $error->getMessage());
+ }
+
+ return true;
+ }
+
+ /**
+ * 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.
+ *
+ * @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.
+ *
+ * @access private
+ * @since 1.1.0
+ */
+ function _put($command, $args = '')
+ {
+ if (!empty($args)) {
+ return $this->_send($command . ' ' . $args . "\r\n");
+ }
+
+ 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.
+ *
+ * @return mixed True if the server returned a valid response code or
+ * a PEAR_Error object is an error condition is reached.
+ *
+ * @access private
+ * @since 1.1.0
+ *
+ * @see getResponse
+ */
+ function _parseResponse($valid)
+ {
+ $this->_code = -1;
+ $this->_arguments = array();
+
+ while ($line = $this->_socket->readLine()) {
+ if ($this->_debug) {
+ echo "DEBUG: Recv: $line\n";
+ }
+
+ /* If we receive an empty line, the connection has been closed. */
+ if (empty($line)) {
+ $this->disconnect();
+ return new PEAR_Error("Connection was unexpectedly 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;
+ }
+ }
+
+ /* Compare the server's response code with the valid code. */
+ if (is_int($valid) && ($this->_code === $valid)) {
+ return true;
+ }
+
+ /* If we were given an array of valid response codes, check each one. */
+ if (is_array($valid)) {
+ foreach ($valid as $valid_code) {
+ if ($this->_code === $valid_code) {
+ return true;
+ }
+ }
+ }
+
+ return new PEAR_Error("Invalid response code received from server");
+ }
+
+ /**
+ * 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.
+ *
+ * @access public
+ * @since 1.1.0
+ */
+ function getResponse()
+ {
+ return array($this->_code, join("\n", $this->_arguments));
+ }
+
+ /**
+ * Attempt to connect to the SMTP server.
+ *
+ * @param int $timeout The timeout value (in seconds) for the
+ * socket connection.
+ * @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.
+ * @access public
+ * @since 1.0
+ */
+ function connect($timeout = null, $persistent = false)
+ {
+ $result = $this->_socket->connect($this->host, $this->port,
+ $persistent, $timeout);
+ if (PEAR::isError($result)) {
+ return new PEAR_Error('Failed to connect socket: ' .
+ $result->getMessage());
+ }
+
+ if (PEAR::isError($error = $this->_parseResponse(220))) {
+ return $error;
+ }
+ 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.
+ * @access public
+ * @since 1.0
+ */
+ 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 new PEAR_Error('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.
+ *
+ * @access private
+ * @since 1.1.0
+ */
+ function _negotiate()
+ {
+ if (PEAR::isError($error = $this->_put('EHLO', $this->localhost))) {
+ return $error;
+ }
+
+ if (PEAR::isError($this->_parseResponse(250))) {
+ /* If we receive a 503 response, we're already authenticated. */
+ if ($this->_code === 503) {
+ return true;
+ }
+
+ /* 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 new PEAR_Error('HELO was not accepted: ', $this->_code);
+ }
+
+ return true;
+ }
+
+ foreach ($this->_arguments as $argument) {
+ $verb = strtok($argument, ' ');
+ $arguments = substr($argument, strlen($verb) + 1,
+ strlen($argument) - strlen($verb) - 1);
+ $this->_esmtp[$verb] = $arguments;
+ }
+
+ 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.
+ * @access private
+ * @since 1.1.0
+ */
+ function _getBestAuthMethod()
+ {
+ $available_methods = explode(' ', $this->_esmtp['AUTH']);
+
+ foreach ($this->auth_methods as $method) {
+ if (in_array($method, $available_methods)) {
+ return $method;
+ }
+ }
+
+ return new PEAR_Error('No supported authentication methods');
+ }
+
+ /**
+ * Attempt to do SMTP 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.
+ * @access public
+ * @since 1.0
+ */
+ function auth($uid, $pwd , $method = '')
+ {
+ if (empty($this->_esmtp['AUTH'])) {
+ return new PEAR_Error('SMTP server does no 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 (!in_array($method, $this->auth_methods)) {
+ return new PEAR_Error("$method is not a supported authentication method");
+ }
+ }
+
+ switch ($method) {
+ case 'DIGEST-MD5':
+ $result = $this->_authDigest_MD5($uid, $pwd);
+ 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);
+ break;
+ default:
+ $result = new PEAR_Error("$method is not a supported authentication method");
+ break;
+ }
+
+ /* If an error was encountered, return the PEAR_Error object. */
+ if (PEAR::isError($result)) {
+ return $result;
+ }
+
+ /* RFC-2554 requires us to re-negotiate ESMTP after an AUTH. */
+ if (PEAR::isError($error = $this->_negotiate())) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * 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.
+ * @access private
+ * @since 1.1.0
+ */
+ function _authDigest_MD5($uid, $pwd)
+ {
+ 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;
+ }
+
+ $challenge = base64_decode($this->_arguments[0]);
+ $digest = &Auth_SASL::factory('digestmd5');
+ $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge,
+ $this->host, "smtp"));
+
+ 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 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.
+ * @access private
+ * @since 1.1.0
+ */
+ function _authCRAM_MD5($uid, $pwd)
+ {
+ 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;
+ }
+
+ $challenge = base64_decode($this->_arguments[0]);
+ $cram = &Auth_SASL::factory('crammd5');
+ $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 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.
+ * @access private
+ * @since 1.1.0
+ */
+ function _authLogin($uid, $pwd)
+ {
+ 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 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.
+ * @access private
+ * @since 1.1.0
+ */
+ function _authPlain($uid, $pwd)
+ {
+ 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(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;
+ }
+
+ /**
+ * Send the HELO command.
+ *
+ * @param string 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.
+ * @access public
+ * @since 1.0
+ */
+ function helo($domain)
+ {
+ if (PEAR::isError($error = $this->_put('HELO', $domain))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(250))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * 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.
+ * @access public
+ * @since 1.0
+ */
+ function mailFrom($sender)
+ {
+ if (PEAR::isError($error = $this->_put('MAIL', "FROM:<$sender>"))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(250))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * 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.
+ * @access public
+ * @since 1.0
+ */
+ function rcptTo($recipient)
+ {
+ if (PEAR::isError($error = $this->_put('RCPT', "TO:<$recipient>"))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(array(250, 251)))) {
+ 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 The message text to quote. The string must be passed
+ * by reference, and the text will be modified in place.
+ *
+ * @access public
+ * @since 1.2
+ */
+ function quotedata(&$data)
+ {
+ /*
+ * Change Unix (\n) and Mac (\r) linefeeds into Internet-standard CRLF
+ * (\r\n) linefeeds.
+ */
+ $data = preg_replace("/([^\r]{1})\n/", "\\1\r\n", $data);
+ $data = preg_replace("/\n\n/", "\n\r\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.
+ *
+ * @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.
+ * @access public
+ * @since 1.0
+ */
+ function data($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".
+ */
+ if (isset($this->_esmtp['SIZE']) && ($this->_esmtp['SIZE'] > 0)) {
+ if (strlen($data) >= $this->_esmtp['SIZE']) {
+ $this->disconnect();
+ return new PEAR_Error('Message size excedes the server limit');
+ }
+ }
+
+ /* Quote the data based on the SMTP standards. */
+ $this->quotedata($data);
+
+ if (PEAR::isError($error = $this->_put('DATA'))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(354))) {
+ return $error;
+ }
+
+ 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 $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Send the SEND FROM: command.
+ *
+ * @param string The reverse path to send.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.2.6
+ */
+ function sendFrom($path)
+ {
+ if (PEAR::isError($error = $this->_put('SEND', "FROM:<$path>"))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(250))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Backwards-compatibility wrapper for sendFrom().
+ *
+ * @param string The reverse path to send.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ *
+ * @access public
+ * @since 1.0
+ * @deprecated 1.2.6
+ */
+ function send_from($path)
+ {
+ return sendFrom($path);
+ }
+
+ /**
+ * Send the SOML FROM: command.
+ *
+ * @param string The reverse path to send.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.2.6
+ */
+ function somlFrom($path)
+ {
+ if (PEAR::isError($error = $this->_put('SOML', "FROM:<$path>"))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(250))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Backwards-compatibility wrapper for somlFrom().
+ *
+ * @param string The reverse path to send.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ *
+ * @access public
+ * @since 1.0
+ * @deprecated 1.2.6
+ */
+ function soml_from($path)
+ {
+ return somlFrom($path);
+ }
+
+ /**
+ * Send the SAML FROM: command.
+ *
+ * @param string The reverse path to send.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.2.6
+ */
+ function samlFrom($path)
+ {
+ if (PEAR::isError($error = $this->_put('SAML', "FROM:<$path>"))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(250))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Backwards-compatibility wrapper for samlFrom().
+ *
+ * @param string The reverse path to send.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ *
+ * @access public
+ * @since 1.0
+ * @deprecated 1.2.6
+ */
+ function saml_from($path)
+ {
+ return samlFrom($path);
+ }
+
+ /**
+ * Send the RSET command.
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.0
+ */
+ function rset()
+ {
+ if (PEAR::isError($error = $this->_put('RSET'))) {
+ return $error;
+ }
+ if (PEAR::isError($error = $this->_parseResponse(250))) {
+ return $error;
+ }
+
+ return true;
+ }
+
+ /**
+ * Send the VRFY command.
+ *
+ * @param string The string to verify
+ *
+ * @return mixed Returns a PEAR_Error with an error message on any
+ * kind of failure, or true on success.
+ * @access public
+ * @since 1.0
+ */
+ 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(250))) {
+ 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.
+ * @access public
+ * @since 1.0
+ */
+ 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.
+ *
+ * @access public
+ * @since 1.0
+ */
+ function identifySender()
+ {
+ return true;
+ }
+}
+
+?>
diff --git a/3dparty/Net/Socket.php b/3dparty/Net/Socket.php
new file mode 100644
index 00000000000..7c4ff1d4e21
--- /dev/null
+++ b/3dparty/Net/Socket.php
@@ -0,0 +1,528 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 2.0 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: Stig Bakken <ssb@php.net> |
+// | Chuck Hagenbuch <chuck@horde.org> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Socket.php,v 1.24 2005/02/03 20:40:16 chagenbu Exp $
+
+require_once 'PEAR.php';
+
+define('NET_SOCKET_READ', 1);
+define('NET_SOCKET_WRITE', 2);
+define('NET_SOCKET_ERROR', 3);
+
+/**
+ * Generalized Socket class.
+ *
+ * @version 1.1
+ * @author Stig Bakken <ssb@php.net>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ */
+class Net_Socket extends PEAR {
+
+ /**
+ * Socket file pointer.
+ * @var resource $fp
+ */
+ var $fp = null;
+
+ /**
+ * Whether the socket is blocking. Defaults to true.
+ * @var boolean $blocking
+ */
+ var $blocking = true;
+
+ /**
+ * Whether the socket is persistent. Defaults to false.
+ * @var boolean $persistent
+ */
+ var $persistent = false;
+
+ /**
+ * The IP address to connect to.
+ * @var string $addr
+ */
+ var $addr = '';
+
+ /**
+ * The port number to connect to.
+ * @var integer $port
+ */
+ var $port = 0;
+
+ /**
+ * Number of seconds to wait on socket connections before assuming
+ * there's no more data. Defaults to no timeout.
+ * @var integer $timeout
+ */
+ var $timeout = false;
+
+ /**
+ * Number of bytes to read at a time in readLine() and
+ * readAll(). Defaults to 2048.
+ * @var integer $lineLength
+ */
+ var $lineLength = 2048;
+
+ /**
+ * Connect to the specified port. If called when the socket is
+ * already connected, it disconnects and connects again.
+ *
+ * @param string $addr IP address or host name.
+ * @param integer $port TCP port number.
+ * @param boolean $persistent (optional) Whether the connection is
+ * persistent (kept open between requests
+ * by the web server).
+ * @param integer $timeout (optional) How long to wait for data.
+ * @param array $options See options for stream_context_create.
+ *
+ * @access public
+ *
+ * @return boolean | PEAR_Error True on success or a PEAR_Error on failure.
+ */
+ function connect($addr, $port = 0, $persistent = null, $timeout = null, $options = null)
+ {
+ if (is_resource($this->fp)) {
+ @fclose($this->fp);
+ $this->fp = null;
+ }
+
+ if (!$addr) {
+ return $this->raiseError('$addr cannot be empty');
+ } elseif (strspn($addr, '.0123456789') == strlen($addr) ||
+ strstr($addr, '/') !== false) {
+ $this->addr = $addr;
+ } else {
+ $this->addr = @gethostbyname($addr);
+ }
+
+ $this->port = $port % 65536;
+
+ if ($persistent !== null) {
+ $this->persistent = $persistent;
+ }
+
+ if ($timeout !== null) {
+ $this->timeout = $timeout;
+ }
+
+ $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
+ $errno = 0;
+ $errstr = '';
+ if ($options && function_exists('stream_context_create')) {
+ if ($this->timeout) {
+ $timeout = $this->timeout;
+ } else {
+ $timeout = 0;
+ }
+ $context = stream_context_create($options);
+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout, $context);
+ } else {
+ if ($this->timeout) {
+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $this->timeout);
+ } else {
+ $fp = @$openfunc($this->addr, $this->port, $errno, $errstr);
+ }
+ }
+
+ if (!$fp) {
+ return $this->raiseError($errstr, $errno);
+ }
+
+ $this->fp = $fp;
+
+ return $this->setBlocking($this->blocking);
+ }
+
+ /**
+ * Disconnects from the peer, closes the socket.
+ *
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function disconnect()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ @fclose($this->fp);
+ $this->fp = null;
+ return true;
+ }
+
+ /**
+ * Find out if the socket is in blocking mode.
+ *
+ * @access public
+ * @return boolean The current blocking mode.
+ */
+ function isBlocking()
+ {
+ return $this->blocking;
+ }
+
+ /**
+ * Sets whether the socket connection should be blocking or
+ * not. A read call to a non-blocking socket will return immediately
+ * if there is no data available, whereas it will block until there
+ * is data for blocking sockets.
+ *
+ * @param boolean $mode True for blocking sockets, false for nonblocking.
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function setBlocking($mode)
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $this->blocking = $mode;
+ socket_set_blocking($this->fp, $this->blocking);
+ return true;
+ }
+
+ /**
+ * Sets the timeout value on socket descriptor,
+ * expressed in the sum of seconds and microseconds
+ *
+ * @param integer $seconds Seconds.
+ * @param integer $microseconds Microseconds.
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function setTimeout($seconds, $microseconds)
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ return socket_set_timeout($this->fp, $seconds, $microseconds);
+ }
+
+ /**
+ * Returns information about an existing socket resource.
+ * Currently returns four entries in the result array:
+ *
+ * <p>
+ * timed_out (bool) - The socket timed out waiting for data<br>
+ * blocked (bool) - The socket was blocked<br>
+ * eof (bool) - Indicates EOF event<br>
+ * unread_bytes (int) - Number of bytes left in the socket buffer<br>
+ * </p>
+ *
+ * @access public
+ * @return mixed Array containing information about existing socket resource or an error object otherwise
+ */
+ function getStatus()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ return socket_get_status($this->fp);
+ }
+
+ /**
+ * Get a specified line of data
+ *
+ * @access public
+ * @return $size bytes of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function gets($size)
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ return @fgets($this->fp, $size);
+ }
+
+ /**
+ * Read a specified amount of data. This is guaranteed to return,
+ * and has the added benefit of getting everything in one fread()
+ * chunk; if you know the size of the data you're getting
+ * beforehand, this is definitely the way to go.
+ *
+ * @param integer $size The number of bytes to read from the socket.
+ * @access public
+ * @return $size bytes of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function read($size)
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ return @fread($this->fp, $size);
+ }
+
+ /**
+ * Write a specified amount of data.
+ *
+ * @param string $data Data to write.
+ * @param integer $blocksize Amount of data to write at once.
+ * NULL means all at once.
+ *
+ * @access public
+ * @return mixed true on success or an error object otherwise
+ */
+ function write($data, $blocksize = null)
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ if (is_null($blocksize) && !OS_WINDOWS) {
+ return fwrite($this->fp, $data);
+ } else {
+ if (is_null($blocksize)) {
+ $blocksize = 1024;
+ }
+
+ $pos = 0;
+ $size = strlen($data);
+ while ($pos < $size) {
+ $written = @fwrite($this->fp, substr($data, $pos, $blocksize));
+ if ($written === false) {
+ return false;
+ }
+ $pos += $written;
+ }
+
+ return $pos;
+ }
+ }
+
+ /**
+ * Write a line of data to the socket, followed by a trailing "\r\n".
+ *
+ * @access public
+ * @return mixed fputs result, or an error
+ */
+ function writeLine($data)
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ return fwrite($this->fp, $data . "\r\n");
+ }
+
+ /**
+ * Tests for end-of-file on a socket descriptor.
+ *
+ * @access public
+ * @return bool
+ */
+ function eof()
+ {
+ return (is_resource($this->fp) && feof($this->fp));
+ }
+
+ /**
+ * Reads a byte of data
+ *
+ * @access public
+ * @return 1 byte of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readByte()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ return ord(@fread($this->fp, 1));
+ }
+
+ /**
+ * Reads a word of data
+ *
+ * @access public
+ * @return 1 word of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readWord()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $buf = @fread($this->fp, 2);
+ return (ord($buf[0]) + (ord($buf[1]) << 8));
+ }
+
+ /**
+ * Reads an int of data
+ *
+ * @access public
+ * @return integer 1 int of data from the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readInt()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $buf = @fread($this->fp, 4);
+ return (ord($buf[0]) + (ord($buf[1]) << 8) +
+ (ord($buf[2]) << 16) + (ord($buf[3]) << 24));
+ }
+
+ /**
+ * Reads a zero-terminated string of data
+ *
+ * @access public
+ * @return string, or a PEAR_Error if
+ * not connected.
+ */
+ function readString()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $string = '';
+ while (($char = @fread($this->fp, 1)) != "\x00") {
+ $string .= $char;
+ }
+ return $string;
+ }
+
+ /**
+ * Reads an IP Address and returns it in a dot formated string
+ *
+ * @access public
+ * @return Dot formated string, or a PEAR_Error if
+ * not connected.
+ */
+ function readIPAddress()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $buf = @fread($this->fp, 4);
+ return sprintf("%s.%s.%s.%s", ord($buf[0]), ord($buf[1]),
+ ord($buf[2]), ord($buf[3]));
+ }
+
+ /**
+ * Read until either the end of the socket or a newline, whichever
+ * comes first. Strips the trailing newline from the returned data.
+ *
+ * @access public
+ * @return All available data up to a newline, without that
+ * newline, or until the end of the socket, or a PEAR_Error if
+ * not connected.
+ */
+ function readLine()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $line = '';
+ $timeout = time() + $this->timeout;
+ while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {
+ $line .= @fgets($this->fp, $this->lineLength);
+ if (substr($line, -1) == "\n") {
+ return rtrim($line, "\r\n");
+ }
+ }
+ return $line;
+ }
+
+ /**
+ * Read until the socket closes, or until there is no more data in
+ * the inner PHP buffer. If the inner buffer is empty, in blocking
+ * mode we wait for at least 1 byte of data. Therefore, in
+ * blocking mode, if there is no data at all to be read, this
+ * function will never exit (unless the socket is closed on the
+ * remote end).
+ *
+ * @access public
+ *
+ * @return string All data until the socket closes, or a PEAR_Error if
+ * not connected.
+ */
+ function readAll()
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $data = '';
+ while (!feof($this->fp)) {
+ $data .= @fread($this->fp, $this->lineLength);
+ }
+ return $data;
+ }
+
+ /**
+ * Runs the equivalent of the select() system call on the socket
+ * with a timeout specified by tv_sec and tv_usec.
+ *
+ * @param integer $state Which of read/write/error to check for.
+ * @param integer $tv_sec Number of seconds for timeout.
+ * @param integer $tv_usec Number of microseconds for timeout.
+ *
+ * @access public
+ * @return False if select fails, integer describing which of read/write/error
+ * are ready, or PEAR_Error if not connected.
+ */
+ function select($state, $tv_sec, $tv_usec = 0)
+ {
+ if (!is_resource($this->fp)) {
+ return $this->raiseError('not connected');
+ }
+
+ $read = null;
+ $write = null;
+ $except = null;
+ if ($state & NET_SOCKET_READ) {
+ $read[] = $this->fp;
+ }
+ if ($state & NET_SOCKET_WRITE) {
+ $write[] = $this->fp;
+ }
+ if ($state & NET_SOCKET_ERROR) {
+ $except[] = $this->fp;
+ }
+ if (false === ($sr = stream_select($read, $write, $except, $tv_sec, $tv_usec))) {
+ return false;
+ }
+
+ $result = 0;
+ if (count($read)) {
+ $result |= NET_SOCKET_READ;
+ }
+ if (count($write)) {
+ $result |= NET_SOCKET_WRITE;
+ }
+ if (count($except)) {
+ $result |= NET_SOCKET_ERROR;
+ }
+ return $result;
+ }
+
+}