program/lib/Auth/SASL.php | ●●●●● patch | view | raw | blame | history | |
program/lib/Auth/SASL/Anonymous.php | ●●●●● patch | view | raw | blame | history | |
program/lib/Auth/SASL/Common.php | ●●●●● patch | view | raw | blame | history | |
program/lib/Auth/SASL/CramMD5.php | ●●●●● patch | view | raw | blame | history | |
program/lib/Auth/SASL/DigestMD5.php | ●●●●● patch | view | raw | blame | history | |
program/lib/Auth/SASL/Login.php | ●●●●● patch | view | raw | blame | history | |
program/lib/Auth/SASL/Plain.php | ●●●●● patch | view | raw | blame | history | |
program/lib/Net/SMTP.php | ●●●●● patch | view | raw | blame | history | |
program/lib/Net/Socket.php | ●●●●● patch | view | raw | blame | history |
program/lib/Auth/SASL.php
New file @@ -0,0 +1,98 @@ <?php // +-----------------------------------------------------------------------+ // | Copyright (c) 2002-2003 Richard Heyes | // | All rights reserved. | // | | // | Redistribution and use in source and binary forms, with or without | // | modification, are permitted provided that the following conditions | // | are met: | // | | // | o Redistributions of source code must retain the above copyright | // | notice, this list of conditions and the following disclaimer. | // | o 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.| // | o The names of the authors may not be used to endorse or promote | // | products derived from this software without specific prior written | // | permission. | // | | // | 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 | // | OWNER 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. | // | | // +-----------------------------------------------------------------------+ // | Author: Richard Heyes <richard@php.net> | // +-----------------------------------------------------------------------+ // // $Id$ /** * Client implementation of various SASL mechanisms * * @author Richard Heyes <richard@php.net> * @access public * @version 1.0 * @package Auth_SASL */ require_once('PEAR.php'); class Auth_SASL { /** * Factory class. Returns an object of the request * type. * * @param string $type One of: Anonymous * Plain * CramMD5 * DigestMD5 * Types are not case sensitive */ function &factory($type) { switch (strtolower($type)) { case 'anonymous': $filename = 'Auth/SASL/Anonymous.php'; $classname = 'Auth_SASL_Anonymous'; break; case 'login': $filename = 'Auth/SASL/Login.php'; $classname = 'Auth_SASL_Login'; break; case 'plain': $filename = 'Auth/SASL/Plain.php'; $classname = 'Auth_SASL_Plain'; break; case 'crammd5': $filename = 'Auth/SASL/CramMD5.php'; $classname = 'Auth_SASL_CramMD5'; break; case 'digestmd5': $filename = 'Auth/SASL/DigestMD5.php'; $classname = 'Auth_SASL_DigestMD5'; break; default: return PEAR::raiseError('Invalid SASL mechanism type'); break; } require_once($filename); return new $classname(); } } ?> program/lib/Auth/SASL/Anonymous.php
New file @@ -0,0 +1,71 @@ <?php // +-----------------------------------------------------------------------+ // | Copyright (c) 2002-2003 Richard Heyes | // | All rights reserved. | // | | // | Redistribution and use in source and binary forms, with or without | // | modification, are permitted provided that the following conditions | // | are met: | // | | // | o Redistributions of source code must retain the above copyright | // | notice, this list of conditions and the following disclaimer. | // | o 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.| // | o The names of the authors may not be used to endorse or promote | // | products derived from this software without specific prior written | // | permission. | // | | // | 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 | // | OWNER 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. | // | | // +-----------------------------------------------------------------------+ // | Author: Richard Heyes <richard@php.net> | // +-----------------------------------------------------------------------+ // // $Id$ /** * Implmentation of ANONYMOUS SASL mechanism * * @author Richard Heyes <richard@php.net> * @access public * @version 1.0 * @package Auth_SASL */ require_once('Auth/SASL/Common.php'); class Auth_SASL_Anonymous extends Auth_SASL_Common { /** * Not much to do here except return the token supplied. * No encoding, hashing or encryption takes place for this * mechanism, simply one of: * o An email address * o An opaque string not containing "@" that can be interpreted * by the sysadmin * o Nothing * * We could have some logic here for the second option, but this * would by no means create something interpretable. * * @param string $token Optional email address or string to provide * as trace information. * @return string The unaltered input token */ function getResponse($token = '') { return $token; } } ?> program/lib/Auth/SASL/Common.php
New file @@ -0,0 +1,74 @@ <?php // +-----------------------------------------------------------------------+ // | Copyright (c) 2002-2003 Richard Heyes | // | All rights reserved. | // | | // | Redistribution and use in source and binary forms, with or without | // | modification, are permitted provided that the following conditions | // | are met: | // | | // | o Redistributions of source code must retain the above copyright | // | notice, this list of conditions and the following disclaimer. | // | o 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.| // | o The names of the authors may not be used to endorse or promote | // | products derived from this software without specific prior written | // | permission. | // | | // | 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 | // | OWNER 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. | // | | // +-----------------------------------------------------------------------+ // | Author: Richard Heyes <richard@php.net> | // +-----------------------------------------------------------------------+ // // $Id$ /** * Common functionality to SASL mechanisms * * @author Richard Heyes <richard@php.net> * @access public * @version 1.0 * @package Auth_SASL */ class Auth_SASL_Common { /** * Function which implements HMAC MD5 digest * * @param string $key The secret key * @param string $data The data to protect * @return string The HMAC MD5 digest */ function _HMAC_MD5($key, $data) { if (strlen($key) > 64) { $key = pack('H32', md5($key)); } if (strlen($key) < 64) { $key = str_pad($key, 64, chr(0)); } $k_ipad = substr($key, 0, 64) ^ str_repeat(chr(0x36), 64); $k_opad = substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64); $inner = pack('H32', md5($k_ipad . $data)); $digest = md5($k_opad . $inner); return $digest; } } ?> program/lib/Auth/SASL/CramMD5.php
New file @@ -0,0 +1,68 @@ <?php // +-----------------------------------------------------------------------+ // | Copyright (c) 2002-2003 Richard Heyes | // | All rights reserved. | // | | // | Redistribution and use in source and binary forms, with or without | // | modification, are permitted provided that the following conditions | // | are met: | // | | // | o Redistributions of source code must retain the above copyright | // | notice, this list of conditions and the following disclaimer. | // | o 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.| // | o The names of the authors may not be used to endorse or promote | // | products derived from this software without specific prior written | // | permission. | // | | // | 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 | // | OWNER 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. | // | | // +-----------------------------------------------------------------------+ // | Author: Richard Heyes <richard@php.net> | // +-----------------------------------------------------------------------+ // // $Id$ /** * Implmentation of CRAM-MD5 SASL mechanism * * @author Richard Heyes <richard@php.net> * @access public * @version 1.0 * @package Auth_SASL */ require_once('Auth/SASL/Common.php'); class Auth_SASL_CramMD5 extends Auth_SASL_Common { /** * Implements the CRAM-MD5 SASL mechanism * This DOES NOT base64 encode the return value, * you will need to do that yourself. * * @param string $user Username * @param string $pass Password * @param string $challenge The challenge supplied by the server. * this should be already base64_decoded. * * @return string The string to pass back to the server, of the form * "<user> <digest>". This is NOT base64_encoded. */ function getResponse($user, $pass, $challenge) { return $user . ' ' . $this->_HMAC_MD5($pass, $challenge); } } ?> program/lib/Auth/SASL/DigestMD5.php
New file @@ -0,0 +1,194 @@ <?php // +-----------------------------------------------------------------------+ // | Copyright (c) 2002-2003 Richard Heyes | // | All rights reserved. | // | | // | Redistribution and use in source and binary forms, with or without | // | modification, are permitted provided that the following conditions | // | are met: | // | | // | o Redistributions of source code must retain the above copyright | // | notice, this list of conditions and the following disclaimer. | // | o 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.| // | o The names of the authors may not be used to endorse or promote | // | products derived from this software without specific prior written | // | permission. | // | | // | 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 | // | OWNER 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. | // | | // +-----------------------------------------------------------------------+ // | Author: Richard Heyes <richard@php.net> | // +-----------------------------------------------------------------------+ // // $Id$ /** * Implmentation of DIGEST-MD5 SASL mechanism * * @author Richard Heyes <richard@php.net> * @access public * @version 1.0 * @package Auth_SASL */ require_once('Auth/SASL/Common.php'); class Auth_SASL_DigestMD5 extends Auth_SASL_Common { /** * Provides the (main) client response for DIGEST-MD5 * requires a few extra parameters than the other * mechanisms, which are unavoidable. * * @param string $authcid Authentication id (username) * @param string $pass Password * @param string $challenge The digest challenge sent by the server * @param string $hostname The hostname of the machine you're connecting to * @param string $service The servicename (eg. imap, pop, acap etc) * @param string $authzid Authorization id (username to proxy as) * @return string The digest response (NOT base64 encoded) * @access public */ function getResponse($authcid, $pass, $challenge, $hostname, $service, $authzid = '') { $challenge = $this->_parseChallenge($challenge); $authzid_string = ''; if ($authzid != '') { $authzid_string = ',authzid="' . $authzid . '"'; } if (!empty($challenge)) { $cnonce = $this->_getCnonce(); $digest_uri = sprintf('%s/%s', $service, $hostname); $response_value = $this->_getResponseValue($authcid, $pass, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $authzid); return sprintf('username="%s",realm="%s"' . $authzid_string . ',nonce="%s",cnonce="%s",nc="00000001",qop=auth,digest-uri="%s",response=%s,%d', $authcid, $challenge['realm'], $challenge['nonce'], $cnonce, $digest_uri, $response_value, $challenge['maxbuf']); } else { return PEAR::raiseError('Invalid digest challenge'); } } /** * Parses and verifies the digest challenge* * * @param string $challenge The digest challenge * @return array The parsed challenge as an assoc * array in the form "directive => value". * @access private */ function _parseChallenge($challenge) { $tokens = array(); while (preg_match('/^([a-z-]+)=("[^"]+(?<!\\\)"|[^,]+)/i', $challenge, $matches)) { // Ignore these as per rfc2831 if ($matches[1] == 'opaque' OR $matches[1] == 'domain') { $challenge = substr($challenge, strlen($matches[0]) + 1); continue; } // Allowed multiple "realm" and "auth-param" if (!empty($tokens[$matches[1]]) AND ($matches[1] == 'realm' OR $matches[1] == 'auth-param')) { if (is_array($tokens[$matches[1]])) { $tokens[$matches[1]][] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]); } else { $tokens[$matches[1]] = array($tokens[$matches[1]], preg_replace('/^"(.*)"$/', '\\1', $matches[2])); } // Any other multiple instance = failure } elseif (!empty($tokens[$matches[1]])) { $tokens = array(); break; } else { $tokens[$matches[1]] = preg_replace('/^"(.*)"$/', '\\1', $matches[2]); } // Remove the just parsed directive from the challenge $challenge = substr($challenge, strlen($matches[0]) + 1); } /** * Defaults and required directives */ // Realm if (empty($tokens['realm'])) { $uname = posix_uname(); $tokens['realm'] = $uname['nodename']; } // Maxbuf if (empty($tokens['maxbuf'])) { $tokens['maxbuf'] = 65536; } // Required: nonce, algorithm if (empty($tokens['nonce']) OR empty($tokens['algorithm'])) { return array(); } return $tokens; } /** * Creates the response= part of the digest response * * @param string $authcid Authentication id (username) * @param string $pass Password * @param string $realm Realm as provided by the server * @param string $nonce Nonce as provided by the server * @param string $cnonce Client nonce * @param string $digest_uri The digest-uri= value part of the response * @param string $authzid Authorization id * @return string The response= part of the digest response * @access private */ function _getResponseValue($authcid, $pass, $realm, $nonce, $cnonce, $digest_uri, $authzid = '') { if ($authzid == '') { $A1 = sprintf('%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $authcid, $realm, $pass))), $nonce, $cnonce); } else { $A1 = sprintf('%s:%s:%s:%s', pack('H32', md5(sprintf('%s:%s:%s', $authcid, $realm, $pass))), $nonce, $cnonce, $authzid); } $A2 = 'AUTHENTICATE:' . $digest_uri; return md5(sprintf('%s:%s:00000001:%s:auth:%s', md5($A1), $nonce, $cnonce, md5($A2))); } /** * Creates the client nonce for the response * * @return string The cnonce value * @access private */ function _getCnonce() { if (file_exists('/dev/urandom')) { return base64_encode(fread(fopen('/dev/urandom', 'r'), 32)); } elseif (file_exists('/dev/random')) { return base64_encode(fread(fopen('/dev/random', 'r'), 32)); } else { $str = ''; mt_srand((double)microtime()*10000000); for ($i=0; $i<32; $i++) { $str .= chr(mt_rand(0, 255)); } return base64_encode($str); } } } ?> program/lib/Auth/SASL/Login.php
New file @@ -0,0 +1,65 @@ <?php // +-----------------------------------------------------------------------+ // | Copyright (c) 2002-2003 Richard Heyes | // | All rights reserved. | // | | // | Redistribution and use in source and binary forms, with or without | // | modification, are permitted provided that the following conditions | // | are met: | // | | // | o Redistributions of source code must retain the above copyright | // | notice, this list of conditions and the following disclaimer. | // | o 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.| // | o The names of the authors may not be used to endorse or promote | // | products derived from this software without specific prior written | // | permission. | // | | // | 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 | // | OWNER 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. | // | | // +-----------------------------------------------------------------------+ // | Author: Richard Heyes <richard@php.net> | // +-----------------------------------------------------------------------+ // // $Id$ /** * This is technically not a SASL mechanism, however * it's used by Net_Sieve, Net_Cyrus and potentially * other protocols , so here is a good place to abstract * it. * * @author Richard Heyes <richard@php.net> * @access public * @version 1.0 * @package Auth_SASL */ require_once('Auth/SASL/Common.php'); class Auth_SASL_Login extends Auth_SASL_Common { /** * Pseudo SASL LOGIN mechanism * * @param string $user Username * @param string $pass Password * @return string LOGIN string */ function getResponse($user, $pass) { return sprintf('LOGIN %s %s', $user, $pass); } } ?> program/lib/Auth/SASL/Plain.php
New file @@ -0,0 +1,63 @@ <?php // +-----------------------------------------------------------------------+ // | Copyright (c) 2002-2003 Richard Heyes | // | All rights reserved. | // | | // | Redistribution and use in source and binary forms, with or without | // | modification, are permitted provided that the following conditions | // | are met: | // | | // | o Redistributions of source code must retain the above copyright | // | notice, this list of conditions and the following disclaimer. | // | o 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.| // | o The names of the authors may not be used to endorse or promote | // | products derived from this software without specific prior written | // | permission. | // | | // | 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 | // | OWNER 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. | // | | // +-----------------------------------------------------------------------+ // | Author: Richard Heyes <richard@php.net> | // +-----------------------------------------------------------------------+ // // $Id$ /** * Implmentation of PLAIN SASL mechanism * * @author Richard Heyes <richard@php.net> * @access public * @version 1.0 * @package Auth_SASL */ require_once('Auth/SASL/Common.php'); class Auth_SASL_Plain extends Auth_SASL_Common { /** * Returns PLAIN response * * @param string $authcid Authentication id (username) * @param string $pass Password * @param string $authzid Autorization id * @return string PLAIN Response */ function getResponse($authcid, $pass, $authzid = '') { return $authzid . chr(0) . $authcid . chr(0) . $pass; } } ?> program/lib/Net/SMTP.php
New file @@ -0,0 +1,991 @@ <?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> | // +----------------------------------------------------------------------+ // // $Id$ 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. * * 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. * * @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 PEAR::raiseError('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 - 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. * * @access private * @since 1.1.0 */ 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. * * @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 PEAR::raiseError('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 PEAR::raiseError('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 PEAR::raiseError('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 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. * * @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 PEAR::raiseError('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 PEAR::raiseError('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 PEAR::raiseError('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 PEAR::raiseError("$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 = PEAR::raiseError("$method is not a supported authentication method"); break; } /* If an error was encountered, return the PEAR_Error object. */ if (PEAR::isError($result)) { return $result; } 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. * * @param array optional arguments. Currently supported: * verp boolean or string. If true or string * verp is enabled. If string the characters * are considered verp separators. * * @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, $args = array()) { $argstr = ''; if (isset($args['verp'])) { /* XVERP */ if ($args['verp'] === true) { $argstr .= ' XVERP'; /* XVERP=something */ } elseif (trim($args['verp'])) { $argstr .= ' XVERP=' . $args['verp']; } } if (PEAR::isError($error = $this->_put('MAIL', "FROM:<$sender>$argstr"))) { 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 $data 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(array('/(?<!\r)\n/','/\r(?!\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 = str_replace("\n.", "\n..", $data); } /** * Send the DATA command. * * @param string $data 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 PEAR::raiseError('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($result = $this->_send($data . "\r\n.\r\n"))) { return $result; } 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(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. * @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; } } program/lib/Net/Socket.php
New file @@ -0,0 +1,456 @@ <?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$ // require_once 'PEAR.php'; /** * Generalized Socket class. More docs to be written. * * @version 1.0 * @author Stig Bakken <ssb@php.net> * @author Chuck Hagenbuch <chuck@horde.org> */ class Net_Socket extends PEAR { // {{{ properties /** Socket file pointer. */ var $fp = null; /** Whether the socket is blocking. */ var $blocking = true; /** Whether the socket is persistent. */ var $persistent = false; /** The IP address to connect to. */ var $addr = ''; /** The port number to connect to. */ var $port = 0; /** Number of seconds to wait on socket connections before assuming there's no more data. */ var $timeout = false; /** Number of bytes to read at a time in readLine() and readAll(). */ var $lineLength = 2048; // }}} // {{{ constructor /** * Constructs a new Net_Socket object. * * @access public */ function Net_Socket() { $this->PEAR(); } // }}} // {{{ connect() /** * Connect to the specified port. If called when the socket is * already connected, it disconnects and connects again. * * @param $addr string IP address or host name * @param $port int TCP port number * @param $persistent bool (optional) whether the connection is * persistent (kept open between requests by the web server) * @param $timeout int (optional) how long to wait for data * @param $options array see options for stream_context_create * @access public * @return mixed true on success or error object */ function connect($addr, $port, $persistent = null, $timeout = null, $options = null) { if (is_resource($this->fp)) { @fclose($this->fp); $this->fp = null; } if (strspn($addr, '.0123456789') == strlen($addr)) { $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); } // }}} // {{{ disconnect() /** * 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)) { fclose($this->fp); $this->fp = null; return true; } return $this->raiseError("not connected"); } // }}} // {{{ isBlocking() /** * Find out if the socket is in blocking mode. * * @access public * @return bool the current blocking mode. */ function isBlocking() { return $this->blocking; } // }}} // {{{ setBlocking() /** * 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 $mode bool 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)) { $this->blocking = $mode; socket_set_blocking($this->fp, $this->blocking); return true; } return $this->raiseError("not connected"); } // }}} // {{{ setTimeout() /** * Sets the timeout value on socket descriptor, * expressed in the sum of seconds and microseconds * * @param $seconds int seconds * @param $microseconds int microseconds * @access public * @return mixed true on success or an error object otherwise */ function setTimeout($seconds, $microseconds) { if (is_resource($this->fp)) { socket_set_timeout($this->fp, $seconds, $microseconds); return true; } return $this->raiseError("not connected"); } // }}} // {{{ getStatus() /** * 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 socket_get_status($this->fp); } return $this->raiseError("not connected"); } // }}} // {{{ gets() /** * 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 fgets($this->fp, $size); } return $this->raiseError("not connected"); } // }}} // {{{ read() /** * 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 $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 fread($this->fp, $size); } return $this->raiseError("not connected"); } // }}} // {{{ write() /** * Write a specified amount of data. * * @access public * @return mixed true on success or an error object otherwise */ function write($data) { if (is_resource($this->fp)) { return fwrite($this->fp, $data); } return $this->raiseError("not connected"); } // }}} // {{{ writeLine() /** * 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->write($data . "\r\n"); } return $this->raiseError("not connected"); } // }}} // {{{ eof() /** * Tests for end-of-file on a socket descriptor * * @access public * @return bool */ function eof() { return (is_resource($this->fp) && feof($this->fp)); } // }}} // {{{ readByte() /** * 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 ord($this->read(1)); } return $this->raiseError("not connected"); } // }}} // {{{ readWord() /** * 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)) { $buf = $this->read(2); return (ord($buf[0]) + (ord($buf[1]) << 8)); } return $this->raiseError("not connected"); } // }}} // {{{ readInt() /** * Reads an int of data * * @access public * @return 1 int of data from the socket, or a PEAR_Error if * not connected. */ function readInt() { if (is_resource($this->fp)) { $buf = $this->read(4); return (ord($buf[0]) + (ord($buf[1]) << 8) + (ord($buf[2]) << 16) + (ord($buf[3]) << 24)); } return $this->raiseError("not connected"); } // }}} // {{{ readString() /** * Reads a zeroterminated string of data * * @access public * @return string, or a PEAR_Error if * not connected. */ function readString() { if (is_resource($this->fp)) { $string = ''; while (($char = $this->read(1)) != "\x00") { $string .= $char; } return $string; } return $this->raiseError("not connected"); } // }}} // {{{ readIPAddress() /** * 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)) { $buf = $this->read(4); return sprintf("%s.%s.%s.%s", ord($buf[0]), ord($buf[1]), ord($buf[2]), ord($buf[3])); } return $this->raiseError("not connected"); } // }}} // {{{ readLine() /** * 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)) { $line = ''; $timeout = time() + $this->timeout; while (!$this->eof() && (!$this->timeout || time() < $timeout)) { $line .= $this->gets($this->lineLength); if (substr($line, -2) == "\r\n" || substr($line, -1) == "\n") { return rtrim($line, "\r\n"); } } return $line; } return $this->raiseError("not connected"); } // }}} // {{{ readAll() /** * Read until the socket closes. THIS FUNCTION WILL NOT EXIT if the * socket is in blocking mode until the socket closes. * * @access public * @return All data until the socket closes, or a PEAR_Error if * not connected. */ function readAll() { if (is_resource($this->fp)) { $data = ''; while (!$this->eof()) $data .= $this->read($this->lineLength); return $data; } return $this->raiseError("not connected"); } // }}} }