diff --git a/thirdparty/pear/Net/CheckIP.php b/thirdparty/pear/Net/CheckIP.php new file mode 100644 index 0000000..b0fbc73 --- /dev/null +++ b/thirdparty/pear/Net/CheckIP.php @@ -0,0 +1,75 @@ + | +// | Guido Haeger | +// +----------------------------------------------------------------------+ +// +// $Id: CheckIP.php,v 1.5 2002/08/17 09:41:24 mj Exp $ + +/** +* Class to validate the syntax of IPv4 adresses +* +* Usage: +* +* +* @author Martin Jansen +* @author Guido Haeger +* @package Net_CheckIP +* @version 1.1 +* @access public +*/ +class Net_CheckIP +{ + + /** + * Validate the syntax of the given IP adress + * + * This function splits the IP address in 4 pieces + * (separated by ".") and checks for each piece + * if it's an integer value between 0 and 255. + * If all 4 parameters pass this test, the function + * returns true. + * + * @param string $ip IP adress + * @return bool true if syntax is valid, otherwise false + */ + function check_ip($ip) + { + $oct = explode('.', $ip); + if (count($oct) != 4) { + return false; + } + + for ($i = 0; $i < 4; $i++) { + if (!is_numeric($oct[$i])) { + return false; + } + + if ($oct[$i] < 0 || $oct[$i] > 255) { + return false; + } + } + + return true; + } +} +?> diff --git a/thirdparty/pear/Net/Curl.php b/thirdparty/pear/Net/Curl.php new file mode 100644 index 0000000..d8c65d0 --- /dev/null +++ b/thirdparty/pear/Net/Curl.php @@ -0,0 +1,799 @@ + + * @author Sterling Hughes + * @author Joe Stump + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Revision: 1.11 $ + * @link http://pear.php.net/package/Net_Curl + */ + +require_once('PEAR.php'); + +class Net_Curl +{ + // {{{ Public Properties + /** + * The URL for cURL to work with + * + * @var string $url + * @access public + */ + var $url; + + /** + * The Username for standard HTTP Authentication + * + * @var string $username + * @access public + */ + var $username = ''; + + /** + * The Password for standard HTTP Authentication + * + * @var string $password + * @access public + */ + var $password = ''; + + /** + * The SSL version for the transfer + * + * @var integer $sslVersion + * @access public + */ + var $sslVersion; + + /** + * The filename of the SSL certificate + * + * @var string $sslCert + * @access public + */ + var $sslCert; + + /** + * The password corresponding to the certificate + * in the $sslCert property + * + * @var string $sslCertPasswd + * @access public + */ + var $sslCertPasswd; + + /** + * User Agent string when making an HTTP request + * + * @var string $userAgent + * @access public + */ + var $userAgent; + + /** + * Whether or not to include the header in the results + * of the CURL transfer + * + * @var boolean $header + */ + var $header = false; + + /** + * Whether or not to output debug information while executing a + * curl transfer + * + * @var boolean $verbose + * @access public + */ + var $verbose = false; + + /** + * Whether or not to display a progress meter for the current transfer + * + * @var boolean $progress + * @access public + */ + var $progress = false; + + /** + * Whether or not to suppress error messages + * + * @var boolean $mute + * @access public + */ + var $mute = false; + + /** + * Whether or not to follow HTTP Location headers. + * + * @var boolean $followLocation + * @access public + */ + var $followLocation = true; + + /** + * Whether or not to follow HTTP Location headers. + * + * @var boolean $follow_location + * @access public + * @deprecated + */ + var $follow_location = false; + + /** + * Time allowed for current transfer, in seconds. 0 means no limit + * + * @var int $timeout + * @access public + */ + var $timeout = 0; + + /** + * Whether or not to return the results of the + * current transfer + * + * @var boolean $returnTransfer + * @access public + */ + var $returnTransfer = true; + + /** + * Whether or not to return the results of the + * current transfer + * + * @var boolean $return_transfer + * @access public + * @deprecated + */ + var $return_transfer = false; + + /** + * The type of transfer to perform (ie. 'POST', 'GET', 'PUT', etc) + * + * @var string $type + * @access public + */ + var $type; + + /** + * The file to upload + * + * @var string $file + * @access public + */ + var $file; + + /** + * The file size of the file pointed to by the $file + * property + * + * @var integer $fileSize + * @access public + */ + var $fileSize; + + /** + * The file size of the file pointed to by the $file + * property + * + * @var integer $file_size + * @access public + * @deprecated + */ + var $file_size = false; + + + /** + * The cookies to send to the remote site + * + * @var array $cookies + * @access public + */ + var $cookies = array(); + + /** + * Additional HTTP headers to send to the remote site + * + * @var array $httpHeaders + * @access public + */ + var $httpHeaders = null; + + /** + * Additional HTTP headers to send to the remote site + * + * @var array $http_headers + * @access public + * @deprecated + */ + var $http_headers = false; + + /** + * The fields to send in a 'POST' request + * + * @var array $fields + * @access public + */ + var $fields; + + /** + * The proxy server to go through + * + * @var string $proxy + * @access public + */ + var $proxy; + + /** + * The username for the Proxy server + * + * @var string $proxyUser + * @access public + */ + var $proxyUser; + + /** + * The password for the Proxy server + * + * @var string $proxyPassword + * @access public + */ + var $proxyPassword; + + /** + * $verifyPeer + * + * FALSE to stop CURL from verifying the peer's certificate. + * Alternate certificates to verify against can be specified + * with the CURLOPT_CAINFO option or a certificate directory + * can be specified with the CURLOPT_CAPATH option. + * CURLOPT_SSL_VERIFYHOST may also need to be TRUE or FALSE + * if CURLOPT_SSL_VERIFYPEER is disabled (it defaults to 2). + * + * @var boolean $verifyPeer + * @access public + */ + var $verifyPeer = true; + + /** + * $verifyHost + * + * 0 : to stop CURL from verifying the host's certificate. + * 1 : to check the existence of a common name in the SSL peer certificate. + * 2 : to check the existence of a common name and also verify that it + * matches the hostname provided. + * + * @var bool $verifyHost + * @access public + */ + var $verifyHost = 2; + + /** + * $caInfo + * + * Set value for CURLOPT_CAINFO. The name of a file holding one or more + * certificates to verify the peer with. This only makes sense when used + * in combination with CURLOPT_SSL_VERIFYPEER. curl-ca-bundle.crt is + * avaible on the Curl website http://curl.haxx.se/ for download inside + * the packages. + * + * @var string $caInfo + * @access public + */ + var $caInfo = ''; + + /** + * $caPath + * + * Set value for CURLOPT_CAPATH. A directory that holds multiple CA + * certificates. Use this option alongside CURLOPT_SSL_VERIFYPEER. + * + * @var string $caPath + * @access public + */ + var $caPath; + // }}} + // {{{ Private Properties + /** + * The current curl handle + * + * @var resource $_ch + * @access private + * @see Net_Curl::create() + */ + var $_ch = null; + + /** + * The file upload resource + * + * The CURLOPT_INFILE requires a file resource and not just a file name. + * This is used by execute to open the file. + * + * @var resource $_fp + * @access private + * @see Net_Curl::execute() + */ + var $_fp = null; + // }}} + // {{{ __construct($url = '', $userAgent = '') + /** + * The Net_Curl PHP 5.x constructor, called when a new Net_Curl object + * is initialized (also called via 4.x constructor) + * + * @param string $url The URL to fetch (can be set using the $url property as well) + * @param string $userAgent The userAgent string (can be set using the $userAgent property as well) + * @access public + * @author Joe Stump + */ + function __construct($url = '', $userAgent = '') + { + if (is_string($url) && strlen($url)) { + $this->url = $url; + } + + if (is_string($userAgent) && strlen($userAgent)) { + $this->userAgent = $userAgent; + } + } + // }}} + // {{{ Net_Curl($url = '', $userAgent = '') + /** + * Net_Curl + * + * PHP 4.x constructor. + * + * @access public + * @author Joe Stump + */ + function Net_Curl($url = '', $userAgent = '') + { + $this->__construct($url,$userAgent); + } + // }}} + // {{{ execute() + /** + * Executes a prepared CURL transfer + * + * Run this function to execute your cURL request. If all goes well you + * should get a string (the output from the remote host regarding your + * request) or true (if you choose to output directly to the browser). If + * something fails then PEAR_Error is returned. + * + * + * fields = array('foo' => '1', 'bar' => 'apple'); + * $result = $curl->execute(); + * if (!PEAR::isError($result)) { + * echo $result; + * } + * ?> + * + * + * @access public + * @author Sterling Hughes + * @author Joe Stump + * @return PEAR_Error on failure, true/result on success + * @since PHP 4.0.5 + */ + function execute() + { + // Create cURL handle if it hasn't already been created + if (!is_resource($this->_ch)) { + $result = $this->create(); + if (PEAR::isError($result)) { + return $result; + } + } + + // Map the deprecated variables and throw a bunch of errors + $this->_mapDeprecatedVariables(); + + // Default return value is true. + $ret = true; + + // Basic stuff + $ret = curl_setopt($this->_ch, CURLOPT_URL, $this->url); + $ret = curl_setopt($this->_ch, CURLOPT_HEADER, $this->header); + + // Whether or not to return the transfer contents + if ($this->returnTransfer === true) { + $ret = curl_setopt($this->_ch, CURLOPT_RETURNTRANSFER, true); + } + + // HTTP Authentication + if ($this->username != '') { + $ret = curl_setopt($this->_ch, CURLOPT_USERPWD, $this->username . ':' . $this->password); + } + + // SSL Checks + if (isset($this->sslVersion)) { + $ret = curl_setopt($this->_ch, CURLOPT_SSLVERSION, $this->sslVersion); + } + + if (isset($this->sslCert)) { + $ret = curl_setopt($this->_ch, CURLOPT_SSLCERT, $this->sslCert); + } + + if (isset($this->sslCertPasswd)) { + $ret = curl_setopt($this->_ch, CURLOPT_SSLCERTPASSWD, $this->sslCertPasswd); + } + + // Proxy Related checks + if (isset($this->proxy)) { + $ret = curl_setopt($this->_ch, CURLOPT_PROXY, $this->proxy); + } + + if (isset($this->proxyUser) || isset($this->proxyPassword)) { + $ret = curl_setopt($this->_ch, CURLOPT_PROXYUSERPWD, $this->proxyUser . ':' . $this->proxyPassword); + } + + if (is_bool($this->verifyPeer)) { + if(!$this->setOption(CURLOPT_SSL_VERIFYPEER,$this->verifyPeer)) { + return PEAR::raiseError('Error setting CURLOPT_SSL_VERIFYPEER'); + } + } + + if (is_numeric($this->verifyHost) && $this->verifyHost >= 0 && + $this->verifyHost <= 2) { + if(!$this->setOption(CURLOPT_SSL_VERIFYHOST,$this->verifyHost)) { + return PEAR::raiseError('Error setting CURLOPT_SSL_VERIFYPEER'); + } + } + + if (is_bool($this->verifyPeer) && $this->verifyPeer == true) { + if (isset($this->caInfo) && strlen($this->caInfo)) { + if (file_exists($this->caInfo)) { + if (!$this->setOption(CURLOPT_CAINFO,$this->caInfo)) { + return PEAR::raiseError('Error setting CURLOPT_CAINFO'); + } + } else { + return PEAR::raiseError('Could not find CA info: '.$this->caInfo); + } + } + + if (isset($this->caPath) && is_string($this->caPath)) { + if (!$this->setOption(CURLOPT_CAPATH,$this->caPath)) { + return PEAR::raiseError('Error setting CURLOPT_CAPATH'); + } + } + } + + // Transfer type + if (isset($this->type)) { + switch (strtolower($this->type)) { + case 'post': + $ret = curl_setopt($this->_ch, CURLOPT_POST, true); + break; + case 'put': + $ret = curl_setopt($this->_ch, CURLOPT_PUT, true); + break; + } + } + + // Transfer upload, etc. related + if (isset($this->file)) { + if (!file_exists($this->file)) { + return PEAR::raiseError('File does not exist: '.$this->file); + } + + $this->_fp = fopen($this->file,'r'); + if (!is_resource($this->_fp)) { + return PEAR::raiseError('Could not open file: '.$this->file); + } + + if (!isset($this->fileSize)) { + $this->fileSize = filesize($this->file); + } + + $ret = curl_setopt($this->_ch, CURLOPT_INFILE, $this->_fp); + $ret = curl_setopt($this->_ch, CURLOPT_INFILESIZE, $this->fileSize); + $ret = curl_setopt($this->_ch, CURLOPT_UPLOAD, true); + } + + if (isset($this->fields)) { + if (!isset($this->type)) { + $this->type = 'post'; + $ret = curl_setopt($this->_ch, CURLOPT_POST, true); + } + + // If fields is an array then turn it into a string. Somtimes + // cURL doesn't like fields as an array. + if (is_array($this->fields)) { + $sets = array(); + foreach ($this->fields as $key => $val) { + $sets[] = $key . '=' . urlencode($val); + } + + $fields = implode('&',$sets); + } else { + $fields = $this->fields; + } + + $ret = curl_setopt($this->_ch, CURLOPT_POSTFIELDS, $fields); + } + + // Error related + if ($this->progress === true) { + $ret = curl_setopt($this->_ch, CURLOPT_PROGRESS, true); + } + + if ($this->verbose === true) { + $ret = curl_setopt($this->_ch, CURLOPT_VERBOSE, true); + } + + if ($this->mute !== true) { + $ret = curl_setopt($this->_ch, CURLOPT_MUTE, false); + } + + // If a Location: header is passed then follow it + $ret = curl_setopt($this->_ch, CURLOPT_FOLLOWLOCATION, $this->followLocation); + + // If a timeout is set and is greater then zero then set it + if (is_numeric($this->timeout) && $this->timeout > 0) { + $ret = curl_setopt($this->_ch, CURLOPT_TIMEOUT, $this->timeout); + } + + if (isset($this->userAgent)) { + $ret = curl_setopt($this->_ch, CURLOPT_USERAGENT, $this->userAgent); + } + + // Cookies + if (is_array($this->cookies) && count($this->cookies)) { + $cookieData = ''; + foreach ($this->cookies as $name => $value) { + $cookieData = $name . '=' . $value . ';'; + } + + $ret = curl_setopt($this->_ch, CURLOPT_COOKIE, $cookieData); + } + + // Other HTTP headers + if ($this->httpHeaders !== null) { + if (is_array($this->httpHeaders)) { + $ret = curl_setopt($this->_ch, CURLOPT_HTTPHEADER, $this->httpHeaders); + } else { + return PEAR::raiseError('Net_Curl::$httpHeaders must be an array'); + } + } + + $ret = curl_exec($this->_ch); + + // Close the file before we return anything + if (is_resource($this->_fp)) { + fclose($this->_fp); + } + + if (curl_errno($this->_ch)) { + return PEAR::raiseError(curl_error($this->_ch), curl_errno($this->_ch)); + } + + // Check to make sure we get a 2XX/3XX code and not a 404 or something. + $info = $this->getInfo(); + if (!isset($info['http_code'])) { + return PEAR::raiseError('Unknown or invalid HTTP response'); + } else { + $type = substr($info['http_code'],0,1); + if ($type != 2 && $type != 3) { + return PEAR::raiseError('Unexpected HTTP code: '.$info['http_code']); + } + } + + return $ret; + } + // }}} + // {{{ setOption($option, $value) + /** + * setOption + * + * Set a option for your cURL session. Please note that the cURL handler + * is NOT created before execute(). This is for error checking purposes. + * You should use setOption() in the following manner: + * + * + * create(); + * if (!PEAR::isError($check)) { + * $curl->setOption(CURLOPT_FOO,'bar'); + * $result = $curl->execute(); + * if (!PEAR::isError($result)) { + * echo $result; + * } + * } + * + * ?> + * + * + * @author Joe Stump + * @access public + * @param int $option cURL constant (ie. CURLOPT_URL) + * @param mixed $value + * @return boolean + */ + function setOption($option, $value) + { + if (is_resource($this->_ch)) { + return curl_setopt($this->_ch, $option, $value); + } + + return false; + } + // }}} + // {{{ getInfo() + /** + * getInfo + * + * Returns the info from the cURL session. PEAR_Error if you try and run + * this before you execute the session. + * + * @author Joe Stump + * @access public + * @return mixed PEAR_Error if there is no resource, info on success + */ + function getInfo() + { + if (is_resource($this->_ch)) { + return curl_getinfo($this->_ch); + } + + return PEAR::isError('cURL handler does not exist!'); + } + // }}} + // {{{ create() + /** + * create + * + * Create a cURL resource. If curl_init() doesn't exist or we could not + * create a resource it will error out. + * + * @author Joe Stump + * @return mixed PEAR_Error on failure, true on success + */ + function create() + { + if (!function_exists('curl_init')) { + return PEAR::raiseError('Function curl_init() not found'); + } + + $this->_ch = curl_init(); + if (!is_resource($this->_ch)) { + return PEAR::raiseError('Could not initialize cURL handler'); + } + + return true; + } + // }}} + // {{{ verboseAll() + /** + * Set a verbose output + * + * Turns on super debugging mode by not suppressing errors, turning on + * verbose mode, showing headers and displaying progress. + * + * @access public + * @author David Costa + * @return void + */ + function verboseAll() + { + $this->verbose = true; + $this->mute = false; + $this->header = true; + $this->progress = true; + } + // }}} + // {{{ verbose_all() + /** + * Set a verbose output + * + * @access public + * @author David Costa + * @deprecated + */ + function verbose_all() + { + $this->verboseAll(); + PEAR::raiseError('Net_Curl::verbose_all() is deprecated! Please use Net_Curl::verboseAll()'."
\n",null,PEAR_ERROR_PRINT); + + } + // }}} + // {{{ close() + /** + * Closes the curl transfer and finishes the object (kinda ;) + * + * @access public + * @author Sterling Hughes + * @return void + * @since PHP 4.0.5 + */ + function close() + { + if (is_resource($this->_ch)) { + curl_close($this->_ch); + } + } + // }}} + // {{{ _mapDeprecatedVariables() + /** + * _mapDeprecatedVariables + * + * Maps deprecated variables into the appropriate places. It also throws + * the necessary notices. + * + * @author Joe Stump + * @access private + * @return void + */ + function _mapDeprecatedVariables() { + $bad = array(); + if ($this->follow_location !== false) { + if ($this->follow_location > 0) { + $this->followLocation = true; + } else { + $this->followLocation = false; + } + + $bad[] = array('follow_location','followLocation'); + } + + if ($this->return_transfer !== false) { + if ($this->return_transfer > 0) { + $this->returnTransfer = true; + } else { + $this->returnTransfer = false; + } + + $bad[] = array('return_transfer','returnTransfer'); + } + + if ($this->file_size !== false) { + $this->fileSize = $this->file_size; + $bad[] = array('file_size','fileSize'); + } + + if ($this->http_headers !== false) { + $this->httpHeaders = $this->http_headers; + $bad[] = array('http_headers','httpHeaders'); + } + + foreach ($bad as $map) { + PEAR::raiseError('Net_Curl::$'.$map[0].' is deprecated! Please use Net_Curl::$'.$map[1]." instead!
\n",null,PEAR_ERROR_PRINT); + } + } + // {{{ __destruct() + /** + * __destruct + * + * PHP 5.x destructor. Runs Net_Curl::close() to make sure we close our + * cURL connection. + * + * @author Joe Stump + * @see Net_Curl::close() + */ + function __destruct() + { + $this->close(); + } + // }}} +} + +?> diff --git a/thirdparty/pear/Net/DIME.php b/thirdparty/pear/Net/DIME.php new file mode 100644 index 0000000..6c54208 --- /dev/null +++ b/thirdparty/pear/Net/DIME.php @@ -0,0 +1,629 @@ + | +// | Ralf Hofmann | +// +----------------------------------------------------------------------+ +// +// $Id: DIME.php,v 1.5 2002/09/29 01:55:16 shane Exp $ +// + +require_once 'PEAR.php'; +/** + * + * DIME Encoding/Decoding + * + * What is it? + * This class enables you to manipulate and build + * a DIME encapsulated message. + * + * http://www.ietf.org/internet-drafts/draft-nielsen-dime-02.txt + * + * 09/18/02 Ralf - A huge number of changes to be compliant + * with the DIME Specification Release 17 June 2002 + * + * TODO: lots of stuff needs to be tested. + * Definitily have to go through DIME spec and + * make things work right, most importantly, sec 3.3 + * make examples, document + * + * see test/dime_mesage_test.php for example of usage + * + * @author Shane Caraveo , + * Ralf Hofmann + * @version $Revision: 1.5 $ + * @package Net_DIME + */ +define('NET_DIME_TYPE_UNCHANGED',0x00); +define('NET_DIME_TYPE_MEDIA',0x01); +define('NET_DIME_TYPE_URI',0x02); +define('NET_DIME_TYPE_UNKNOWN',0x03); +define('NET_DIME_TYPE_NONE',0x04); + +define('NET_DIME_VERSION',0x0001); + +define('NET_DIME_RECORD_HEADER',12); + +define('NET_DIME_FLAGS', 0); +define('NET_DIME_OPTS_LEN', 1); +define('NET_DIME_ID_LEN', 2); +define('NET_DIME_TYPE_LEN', 3); +define('NET_DIME_DATA_LEN', 4); +define('NET_DIME_OPTS', 5); +define('NET_DIME_ID', 6); +define('NET_DIME_TYPE', 7); +define('NET_DIME_DATA', 8); + +class Net_DIME_Record extends PEAR +{ + // these are used to hold the padded length + var $OPTS_LENGTH = 0; + var $ID_LENGTH = 0; + var $TYPE_LENGTH = 0; + var $DATA_LENGTH = 0; + var $_haveOpts = FALSE; + var $_haveID = FALSE; + var $_haveType = FALSE; + var $_haveData = FALSE; + var $debug = FALSE; + var $padstr = "\0"; + /** + * Elements + * [NET_DIME_FLAGS], 16 bits: VERSION:MB:ME:CF:TYPE_T + * [NET_DIME_OPTS_LEN], 16 bits: OPTIONS_LENGTH + * [NET_DIME_ID_LEN], 16 bits: ID_LENGTH + * [NET_DIME_TYPE_LEN], 16 bits: TYPE_LENGTH + * [NET_DIME_DATA_LEN], 32 bits: DATA_LENGTH + * [NET_DIME_OPTS] : OPTIONS + * [NET_DIME_ID] : ID + * [NET_DIME_TYPE] : TYPE + * [NET_DIME_DATA] : DATA + */ + var $Elements = array(NET_DIME_FLAGS => 0, NET_DIME_OPTS_LEN => 0, + NET_DIME_ID_LEN => 0, NET_DIME_TYPE_LEN => 0, + NET_DIME_DATA_LEN => 0, + NET_DIME_OPTS => '', + NET_DIME_ID => '', + NET_DIME_TYPE => '', + NET_DIME_DATA => ''); + + function Net_DIME_Record($debug = FALSE) + { + $this->debug = $debug; + if ($debug) $this->padstr = '*'; + } + + function setMB() + { + $this->Elements[NET_DIME_FLAGS] |= 0x0400; + } + + function setME() + { + $this->Elements[NET_DIME_FLAGS] |= 0x0200; + } + + function setCF() + { + $this->Elements[NET_DIME_FLAGS] |= 0x0100; + } + + function isChunk() + { + return $this->Elements[NET_DIME_FLAGS] & 0x0100; + } + + function isEnd() + { + return $this->Elements[NET_DIME_FLAGS] & 0x0200; + } + + function isStart() + { + return $this->Elements[NET_DIME_FLAGS] & 0x0400; + } + + function getID() + { + return $this->Elements[NET_DIME_ID]; + } + + function getType() + { + return $this->Elements[NET_DIME_TYPE]; + } + + function getData() + { + return $this->Elements[NET_DIME_DATA]; + } + + function getDataLength() + { + return $this->Elements[NET_DIME_DATA_LEN]; + } + + function setType($typestring, $type=NET_DIME_TYPE_UNKNOWN) + { + $typelen = strlen($typestring) & 0xFFFF; + $type = $type << 4; + $this->Elements[NET_DIME_FLAGS] = ($this->Elements[NET_DIME_FLAGS] & 0xFF0F) | $type; + $this->Elements[NET_DIME_TYPE_LEN] = $typelen; + $this->TYPE_LENGTH = $this->_getPadLength($typelen); + $this->Elements[NET_DIME_TYPE] = $typestring; + } + + function generateID() + { + $id = md5(time()); + $this->setID($id); + return $id; + } + + function setID($id) + { + $idlen = strlen($id) & 0xFFFF; + $this->Elements[NET_DIME_ID_LEN] = $idlen; + $this->ID_LENGTH = $this->_getPadLength($idlen); + $this->Elements[NET_DIME_ID] = $id; + } + + function setData($data, $size=0) + { + $datalen = $size?$size:strlen($data); + $this->Elements[NET_DIME_DATA_LEN] = $datalen; + $this->DATA_LENGTH = $this->_getPadLength($datalen); + $this->Elements[NET_DIME_DATA] = $data; + } + + function encode() + { + // insert version + $this->Elements[NET_DIME_FLAGS] = ($this->Elements[NET_DIME_FLAGS] & 0x07FF) | (NET_DIME_VERSION << 11); + + // the real dime encoding + $format = '%c%c%c%c%c%c%c%c%c%c%c%c'. + '%'.$this->OPTS_LENGTH.'s'. + '%'.$this->ID_LENGTH.'s'. + '%'.$this->TYPE_LENGTH.'s'. + '%'.$this->DATA_LENGTH.'s'; + return sprintf($format, + ($this->Elements[NET_DIME_FLAGS]&0x0000FF00)>>8, + ($this->Elements[NET_DIME_FLAGS]&0x000000FF), + ($this->Elements[NET_DIME_OPTS_LEN]&0x0000FF00)>>8, + ($this->Elements[NET_DIME_OPTS_LEN]&0x000000FF), + ($this->Elements[NET_DIME_ID_LEN]&0x0000FF00)>>8, + ($this->Elements[NET_DIME_ID_LEN]&0x000000FF), + ($this->Elements[NET_DIME_TYPE_LEN]&0x0000FF00)>>8, + ($this->Elements[NET_DIME_TYPE_LEN]&0x000000FF), + ($this->Elements[NET_DIME_DATA_LEN]&0xFF000000)>>24, + ($this->Elements[NET_DIME_DATA_LEN]&0x00FF0000)>>16, + ($this->Elements[NET_DIME_DATA_LEN]&0x0000FF00)>>8, + ($this->Elements[NET_DIME_DATA_LEN]&0x000000FF), + str_pad($this->Elements[NET_DIME_OPTS], $this->OPTS_LENGTH, $this->padstr), + str_pad($this->Elements[NET_DIME_ID], $this->ID_LENGTH, $this->padstr), + str_pad($this->Elements[NET_DIME_TYPE], $this->TYPE_LENGTH, $this->padstr), + str_pad($this->Elements[NET_DIME_DATA], $this->DATA_LENGTH, $this->padstr)); + } + + function _getPadLength($len) + { + $pad = 0; + if ($len) { + $pad = $len % 4; + if ($pad) $pad = 4 - $pad; + } + return $len + $pad; + } + + function decode(&$data) + { + // REAL DIME decoding + $this->Elements[NET_DIME_FLAGS] = (hexdec(bin2hex($data[0]))<<8) + hexdec(bin2hex($data[1])); + $this->Elements[NET_DIME_OPTS_LEN] = (hexdec(bin2hex($data[2]))<<8) + hexdec(bin2hex($data[3])); + $this->Elements[NET_DIME_ID_LEN] = (hexdec(bin2hex($data[4]))<<8) + hexdec(bin2hex($data[5])); + $this->Elements[NET_DIME_TYPE_LEN] = (hexdec(bin2hex($data[6]))<<8) + hexdec(bin2hex($data[7])); + $this->Elements[NET_DIME_DATA_LEN] = (hexdec(bin2hex($data[8]))<<24) + + (hexdec(bin2hex($data[9]))<<16) + + (hexdec(bin2hex($data[10]))<<8) + + hexdec(bin2hex($data[11])); + $p = 12; + + $version = (($this->Elements[NET_DIME_FLAGS]>>11) & 0x001F); + + if ($version == NET_DIME_VERSION) + { + $this->OPTS_LENGTH = $this->_getPadLength($this->Elements[NET_DIME_OPTS_LEN]); + $this->ID_LENGTH = $this->_getPadLength($this->Elements[NET_DIME_ID_LEN]); + $this->TYPE_LENGTH = $this->_getPadLength($this->Elements[NET_DIME_TYPE_LEN]); + $this->DATA_LENGTH = $this->_getPadLength($this->Elements[NET_DIME_DATA_LEN]); + + $datalen = strlen($data); + $this->Elements[NET_DIME_OPTS] = substr($data,$p,$this->Elements[NET_DIME_OPTS_LEN]); + $this->_haveOpts = (strlen($this->Elements[NET_DIME_OPTS]) == $this->Elements[NET_DIME_OPTS_LEN]); + if ($this->_haveOpts) { + $p += $this->OPTS_LENGTH; + $this->Elements[NET_DIME_ID] = substr($data,$p,$this->Elements[NET_DIME_ID_LEN]); + $this->_haveID = (strlen($this->Elements[NET_DIME_ID]) == $this->Elements[NET_DIME_ID_LEN]); + if ($this->_haveID) { + $p += $this->ID_LENGTH; + $this->Elements[NET_DIME_TYPE] = substr($data,$p,$this->Elements[NET_DIME_TYPE_LEN]); + $this->_haveType = (strlen($this->Elements[NET_DIME_TYPE]) == $this->Elements[NET_DIME_TYPE_LEN]); + if ($this->_haveType) { + $p += $this->TYPE_LENGTH; + $this->Elements[NET_DIME_DATA] = substr($data,$p,$this->Elements[NET_DIME_DATA_LEN]); + $this->_haveData = (strlen($this->Elements[NET_DIME_DATA]) == $this->Elements[NET_DIME_DATA_LEN]); + if ($this->_haveData) { + $p += $this->DATA_LENGTH; + } else { + $p += strlen($this->Elements[NET_DIME_DATA]); + } + } else { + $p += strlen($this->Elements[NET_DIME_TYPE]); + } + } else { + $p += strlen($this->Elements[NET_DIME_ID]); + } + } else { + $p += strlen($this->Elements[NET_DIME_OPTS]); + } + } + return substr($data, $p); + } + + function addData(&$data) + { + $datalen = strlen($data); + $p = 0; + if (!$this->_haveOpts) { + $have = strlen($this->Elements[NET_DIME_OPTS]); + $this->Elements[NET_DIME_OPTS] .= substr($data,$p,$this->Elements[NET_DIME_OPTS_LEN]-$have); + $this->_haveOpts = (strlen($this->Elements[NET_DIME_OPTS]) == $this->Elements[DIME_OTPS_LEN]); + if (!$this->_haveOpts) return NULL; + $p += $this->OPTS_LENGTH-$have; + } + if (!$this->_haveID) { + $have = strlen($this->Elements[NET_DIME_ID]); + $this->Elements[NET_DIME_ID] .= substr($data,$p,$this->Elements[NET_DIME_ID_LEN]-$have); + $this->_haveID = (strlen($this->Elements[NET_DIME_ID]) == $this->Elements[NET_DIME_ID_LEN]); + if (!$this->_haveID) return NULL; + $p += $this->ID_LENGTH-$have; + } + if (!$this->_haveType && $p < $datalen) { + $have = strlen($this->Elements[NET_DIME_TYPE]); + $this->Elements[NET_DIME_TYPE] .= substr($data,$p,$this->Elements[NET_DIME_TYPE_LEN]-$have); + $this->_haveType = (strlen($this->Elements[NET_DIME_TYPE]) == $this->Elements[NET_DIME_TYPE_LEN]); + if (!$this->_haveType) return NULL; + $p += $this->TYPE_LENGTH-$have; + } + if (!$this->_haveData && $p < $datalen) { + $have = strlen($this->Elements[NET_DIME_DATA]); + $this->Elements[NET_DIME_DATA] .= substr($data,$p,$this->Elements[NET_DIME_DATA_LEN]-$have); + $this->_haveData = (strlen($this->Elements[NET_DIME_DATA]) == $this->Elements[NET_DIME_DATA_LEN]); + if (!$this->_haveData) return NULL; + $p += $this->DATA_LENGTH-$have; + } + return substr($data,$p); + } +} + + +class Net_DIME_Message extends PEAR +{ + + var $record_size = 4096; + #var $records =array(); + var $parts = array(); + var $currentPart = -1; + var $stream = NULL; + var $_currentRecord; + var $_proc = array(); + var $type; + var $typestr; + var $mb = 1; + var $me = 0; + var $cf = 0; + var $id = NULL; + var $debug = FALSE; + /** + * constructor + * + * this currently takes a file pointer as provided + * by fopen + * + * TODO: integrate with the php streams stuff + */ + function Net_DIME_Message($stream=NULL, $record_size = 4096, $debug = FALSE) + { + $this->stream = $stream; + $this->record_size = $record_size; + $this->debug = $debug; + } + + function _makeRecord(&$data, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN) + { + $record = new Net_DIME_Record($this->debug); + if ($this->mb) { + $record->setMB(); + // all subsequent records are not message begin! + $this->mb = 0; + } + if ($this->me) $record->setME(); + if ($this->cf) $record->setCF(); + $record->setData($data); + $record->setType($typestr,$type); + if ($id) $record->setID($id); + #if ($this->debug) { + # print str_replace('\0','*',$record->encode()); + #} + return $record->encode(); + } + + function startChunk(&$data, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN) + { + $this->me = 0; + $this->cf = 1; + $this->type = $type; + $this->typestr = $typestr; + if ($id) { + $this->id = $id; + } else { + $this->id = md5(time()); + } + return $this->_makeRecord($data, $this->typestr, $this->id, $this->type); + } + + function doChunk(&$data) + { + $this->me = 0; + $this->cf = 1; + return $this->_makeRecord($data, NULL, NULL, NET_DIME_TYPE_UNCHANGED); + } + + function endChunk() + { + $this->cf = 0; + $data = NULL; + $rec = $this->_makeRecord($data, NULL, NULL, NET_DIME_TYPE_UNCHANGED); + $this->id = 0; + $this->cf = 0; + $this->id = 0; + $this->type = NET_DIME_TYPE_UNKNOWN; + $this->typestr = NULL; + return $rec; + } + + function endMessage() + { + $this->me = 1; + $data = NULL; + $rec = $this->_makeRecord($data, NULL, NULL, NET_DIME_TYPE_NONE); + $this->me = 0; + $this->mb = 1; + $this->id = 0; + return $rec; + } + + /** + * sendRecord + * + * given a chunk of data, it creates DIME records + * and writes them to the stream + * + */ + function sendData(&$data, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN) + { + $len = strlen($data); + if ($len > $this->record_size) { + $chunk = substr($data, 0, $this->record_size); + $p = $this->record_size; + $rec = $this->startChunk($chunk,$typestr,$id,$type); + fwrite($this->stream, $rec); + while ($p < $len) { + $chunk = substr($data, $p, $this->record_size); + $p += $this->record_size; + $rec = $this->doChunk($chunk); + fwrite($this->stream, $rec); + } + $rec = $this->endChunk(); + fwrite($this->stream, $rec); + return; + } + $rec = $this->_makeRecord($data, $typestr,$id,$type); + fwrite($this->stream, $rec); + } + + function sendEndMessage() + { + $rec = $this->endMessage(); + fwrite($this->stream, $rec); + } + + /** + * sendFile + * + * given a filename, it reads the file, + * creates records and writes them to the stream + * + */ + function sendFile($filename, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN) + { + $f = fopen($filename, "rb"); + if ($f) { + if ($data = fread($f, $this->record_size)) { + $this->startChunk($data,$typestr,$id,$type); + } + while ($data = fread($f, $this->record_size)) { + $this->doChunk($data,$typestr,$id,$type); + } + $this->endChunk(); + fclose($f); + } + } + + /** + * encodeData + * + * given data, encode it in DIME + * + */ + function encodeData($data, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN) + { + $len = strlen($data); + $resp = ''; + if ($len > $this->record_size) { + $chunk = substr($data, 0, $this->record_size); + $p = $this->record_size; + $resp .= $this->startChunk($chunk,$typestr,$id,$type); + while ($p < $len) { + $chunk = substr($data, $p, $this->record_size); + $p += $this->record_size; + $resp .= $this->doChunk($chunk); + } + $resp .= $this->endChunk(); + } else { + $resp .= $this->_makeRecord($data, $typestr,$id,$type); + } + return $resp; + } + + /** + * sendFile + * + * given a filename, it reads the file, + * creates records and writes them to the stream + * + */ + function encodeFile($filename, $typestr='', $id=NULL, $type=NET_DIME_TYPE_UNKNOWN) + { + $f = fopen($filename, "rb"); + if ($f) { + if ($data = fread($f, $this->record_size)) { + $resp = $this->startChunk($data,$typestr,$id,$type); + } + while ($data = fread($f, $this->record_size)) { + $resp = $this->doChunk($data,$typestr,$id,$type); + } + $resp = $this->endChunk(); + fclose($f); + } + return $resp; + } + + /** + * _processData + * + * creates Net_DIME_Records from provided data + * + */ + function _processData(&$data) + { + $leftover = NULL; + if (!$this->_currentRecord) { + $this->_currentRecord = new Net_DIME_Record($this->debug); + $data = $this->_currentRecord->decode($data); + } else { + $data = $this->_currentRecord->addData($data); + } + + if ($this->_currentRecord->_haveData) { + if (count($this->parts)==0 && !$this->_currentRecord->isStart()) { + // raise an error! + return PEAR::raiseError('First Message is not a DIME begin record!'); + } + + if ($this->_currentRecord->isEnd() && $this->_currentRecord->getDataLength()==0) { + return NULL; + } + + if ($this->currentPart < 0 && !$this->_currentRecord->isChunk()) { + $this->parts[] = array(); + $this->currentPart = count($this->parts)-1; + $this->parts[$this->currentPart]['id'] = $this->_currentRecord->getID(); + $this->parts[$this->currentPart]['type'] = $this->_currentRecord->getType(); + $this->parts[$this->currentPart]['data'] = $this->_currentRecord->getData(); + $this->currentPart = -1; + } else { + if ($this->currentPart < 0) { + $this->parts[] = array(); + $this->currentPart = count($this->parts)-1; + $this->parts[$this->currentPart]['id'] = $this->_currentRecord->getID(); + $this->parts[$this->currentPart]['type'] = $this->_currentRecord->getType(); + $this->parts[$this->currentPart]['data'] = $this->_currentRecord->getData(); + } else { + $this->parts[$this->currentPart]['data'] .= $this->_currentRecord->getData(); + if (!$this->_currentRecord->isChunk()) { + // we reached the end of the chunk + $this->currentPart = -1; + } + } + } + #$this->records[] = $this->_currentRecord; + if (!$this->_currentRecord->isEnd()) $this->_currentRecord = NULL; + } + return NULL; + } + + /** + * decodeData + * + * decodes a DIME encrypted string of data + * + */ + function decodeData(&$data) { + while (strlen($data) >= NET_DIME_RECORD_HEADER) { + $err = $this->_processData($data); + if (PEAR::isError($err)) { + return $err; + } + } + } + + /** + * read + * + * reads the stream and creates + * an array of records + * + * it can accept the start of a previously read buffer + * this is usefull in situations where you need to read + * headers before discovering that the data is DIME encoded + * such as in the case of reading an HTTP response. + */ + function read($buf=NULL) + { + while ($data = fread($this->stream, 8192)) { + if ($buf) { + $data = $buf.$data; + $buf = NULL; + } + if ($this->debug) + echo "read: ".strlen($data)." bytes\n"; + $err = $this->decodeData($data); + if (PEAR::isError($err)) { + return $err; + } + + // store any leftover data to be used again + // should be < NET_DIME_RECORD_HEADER bytes + $buf = $data; + } + if (!$this->_currentRecord || !$this->_currentRecord->isEnd()) { + return PEAR::raiseError('reached stream end without end record'); + } + return NULL; + } +} +?> \ No newline at end of file diff --git a/thirdparty/pear/Net/Dict.php b/thirdparty/pear/Net/Dict.php new file mode 100644 index 0000000..0acc797 --- /dev/null +++ b/thirdparty/pear/Net/Dict.php @@ -0,0 +1,586 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Dict.php,v 1.3 2002/10/06 04:55:33 cnb Exp $ + +// }}} +// {{{ includes +require_once 'PEAR.php'; +require_once 'Net/Socket.php'; + +// }}} +// {{{ defines +define('NET_DICT_SERVER', 'dict.org'); +define('NET_DICT_PORT', '2628'); +// }}} + +/** +* +* @author Chandrashekhar Bhosle +* @package Net +*/ +class Net_Dict { + + // {{{ properties + /** + * Default DICT server name + * + * @var string + */ + var $server = NET_DICT_SERVER; + + /** + * Default DICT Port + * + * @var int + */ + var $port = NET_DICT_PORT; + + /** + * Socket object + * + * @var object + */ + var $_socket; + + /** + * Server Information + * + * @var string + */ + var $servinfo; + + /** + * if caching is on or off + * + * @var boolean + */ + var $caching = false; + + /** + * PEAR Cache + * + * @var object + */ + var $cache; + + // }}} + // {{{ Constructor + /** + * Constructor + */ + function Net_Dict() + { + } + + // }}} + // {{{ define() + /** + * Gets definitions for the specified word in the specified database. + * + * @param string $word + * @param string $database + * + * @return mixed Array of definitions if sucessful, else PEAR_Error + */ + function define($word, $database='*') + { + if ($this->caching) { + + if ($defines = $this->cache->get($word, 'Net_Dict_Defs')) { + return $defines; + } + } + + if (!is_object($_socket)) { + $this->connect(); + } + + $resp = $this->_sendCmd("DEFINE $database '$word'"); + + if (PEAR::isError($resp)) { + return $resp; + } + + list($num) = explode(' ', $resp['text'], 2); + + for ($i = 0; $i < $num; $i++) { + $resp = $this->_socket->readLine(); + + preg_match("/(\d{3})\s+?\"(.+)?\"\s+?(\S+)\s+?\"(.+)?\"/", + $resp, $matches); + + $defines[$i]['response'] = $resp; + $defines[$i]['word'] = $matches[2]; + $defines[$i]['database'] = $matches[3]; + $defines[$i]['description'] = $matches[4]; + + $resp = $this->_getMultiline(); + + $defines[$i]['definition'] = $resp['text']; + } + + $this->readLine(); /* discard status */ + + if ($this->caching) { + $this->cache->save($word, $defines, 0, 'Net_Dict_Defs'); + } + + return $defines; + } + + // }}} + // {{{ match() + /** + * Searches an index for the dictionary, and reports words + * which were found using a particular strategy. + * + * @param string $word + * @param string $strategy + * @param string $database + * + * @return mixed Array of matches if successful, else PEAR_Error + */ + function match($word, $strategy='substring', $database='*') + { + $resp = $this->_sendCmd("MATCH $database $strategy '$word'"); + + if (PEAR::isError($resp)) { + return $resp; + } + + $resp = $this->_getMultiLine(); + + $this->readLine(); /* discard status */ + + preg_match_all("/(\S+)?\s\"(.+?)\"/", $resp['text'], $matches); + + for ($i = 0; $i < count($matches[0]); $i++) { + $matched[$i]['database'] = $matches[1][$i]; + $matched[$i]['word'] = $matches[2][$i]; + } + + return $matched; + } + + // }}} + // {{{ showDatabases() + /** + * Gets list of available databases + * + * @return mixed Array of databases if successful, else PEAR_Error + */ + function showDatabases() + { + $resp = $this->_sendCmd('SHOW DB'); + + if (PEAR::isError($resp)) { + return $resp; + } + + $resp = $this->_getMultiLine(); + + $this->readLine(); /* discard status */ + + preg_match_all("/(\S+)?\s+?\"(.+?)\"/", $resp['text'], $matches); + + for ($i = 0; $i < count($matches[0]); $i++) { + $databases[$i]['database'] = $matches[1][$i]; + $databases[$i]['description'] = $matches[2][$i]; + } + + return $databases; + } + + // }}} + // {{{ showStrategies() + /** + * Gets a list of available strategies + * + * @return mixed Array of strategies if successful, else PEAR_Error + */ + function showStrategies() + { + $resp = $this->_sendCmd('SHOW STRAT'); + + if (PEAR::isError($resp)) { + return $resp; + } + + $resp = $this->_getMultiLine(); + + $this->readLine(); /* discard status */ + + preg_match_all("/(\S+)?\s+?\"(.+?)\"/", $resp['text'], $matches); + + for ($i = 0; $i < count($matches[0]); $i++) { + $strategies[$i]['strategy'] = $matches[1][$i]; + $strategies[$i]['description'] = $matches[2][$i]; + } + + return $strategies; + } + + // }}} + // {{{ showInfo() + /** + * Gets source, copyright, and licensing information about the + * specified database. + * + * @param string $database + * + * @return mixed string if successful, else PEAR_Error + */ + function showInfo($database) + { + return $this->simpleQuery('SHOW INFO ' . $database); + } + + // }}} + // {{{ showServer() + /** + * Gets local server information written by the local administrator. + * This could include information about local databases or strategies, + * or administrative information such as who to contact for access to + * databases requiring authentication. + * + * @return mixed string if sucessful, else PEAR_Error + */ + function showServer() + { + return $this->simpleQuery('SHOW SERVER'); + } + + // }}} + // {{{ client() + /** + * Allows the client to provide information about itself + * for possible logging and statistical purposes. All clients SHOULD + * send this command after connecting to the server. All DICT servers + * MUST implement this command (note, though, that the server doesn't + * have to do anything with the information provided by the client). + * + * @param string $text + * + * @return mixed string if successful, else PEAR_Error + */ + function client($text='cnb') + { + $this->_sendCmd('CLIENT ' . $text); + } + + // }}} + // {{{ status() + /** + * Display some server-specific timing or debugging information. This + * information may be useful in debugging or tuning a DICT server. All + * DICT servers MUST implement this command (note, though, that the text + * part of the response is not specified and may be omitted). + * + * @return mixed string if successful, else PEAR_Error + */ + function status() + { + $resp = $this->_sendCmd('STATUS'); + + return $resp['text']; + } + + // }}} + // {{{ help() + /** + * Provides a short summary of commands that are understood by this + * implementation of the DICT server. The help text will be presented + * as a textual response, terminated by a single period on a line by + * itself. All DICT servers MUST implement this command. + * + * @return mixed string on success, else PEAR_Error + */ + function help() + { + return $this->simpleQuery('HELP'); + } + + // }}} + // {{{ quit() + /** + * This command is used by the client to cleanly exit the server. + * All DICT servers MUST implement this command. + * + * @return mixed string on success, else PEAR_Error + */ + function quit() + { + return $this->_sendCmd('QUIT'); + } + + // }}} + // {{{ optionMIME() + /** + * Requests that all text responses be prefaced by a MIME header + * [RFC2045] followed by a single blank line (CRLF). + * + * @return mixed + */ + function optionMIME() + { + } + + // }}} + // {{{ auth() + /** + * The client can authenticate itself to the server using a username and + * password. The authentication-string will be computed as in the APOP + * protocol discussed in [RFC1939]. + * + * @param string $user + * @param string $auth + * + * @return mixed + */ + function auth($user, $auth) + { + + } + + // }}} + // {{{ SASLAuth() + /** + * The Simple Authentication and Security Layer (SASL) is currently + * being developed [RFC2222]. The DICT protocol reserves the SASLAUTH + * and SASLRESP commands for this method of authentication. + * + * @param string $mechanism + * @param string $initial_response + * + * @return mixed + */ + function SASLAuth($mechanism, $initial_response) + { + + } + + // }}} + // {{{ SASLResp() + /** + * The client will send all responses using the SASLRESP command and a + * BASE64-encoded parameter. + * + * @param string $response + * + * @return mixed + */ + function SASLResp($response) + { + + } + + // }}} + // {{{ connect() + /** + * Connects to a dict server and sets up a socket + * + * @param string $server + * @param integer $port + * + * @return mixed true on success, else PEAR_Error + */ + function connect($server='', $port = 0) + { + $s = new Net_Socket; + + if (empty($server)) + $server = $this->server; + + if (0 == $port) + $port = $this->port; + + $err = $s->connect($server, $port); + + if (PEAR::isError($err)) { + return $err; + } + + $banner = $s->readLine(); + + preg_match("/\d{3} (.*) <(.*)> <(.*)>/", $banner, &$reg); + $this->servinfo["signature"] = $reg[1]; + $this->servinfo["capabilities"] = explode(".", $reg[2]); + $this->servinfo["msg-id"] = $reg[3]; + + $this->_socket = $s; + + return true; + } + + // }}} + // {{{ setServer() + /** + * Sets the server and port of dict server + * + * @param string $server + * @param int $port + */ + function setServer($server, $port = 0) + { + $this->server = $server; + + if (0 < $port) { + $this->port = $port; + } + } + + // }}} + // {{{ setCache() + /** + * Sets caching on or off and provides the cache type and parameters + * + * @param boolean $cache + * @param string $container + * @param array $container_options + */ + function setCache($flag = false, $container = '', $container_options = '') + { + $this->caching = $flag; + + if ($this->caching) { + + require_once 'Cache.php'; + + if (is_object($this->cache)) + unset($this->cache); + + $this->cache = new Cache($container, $container_options); + } + } + + // }}} + // {{{ _sendCmd() + /** + * Sends a command, checks the reponse, and + * if good returns the reponse, other wise + * returns false. + * + * @param $cmd Command to send (\r\n will be appended) + * + * @return mixed First line of response if successful, otherwise false + */ + function _sendCmd($cmd) + { + $result = $this->_socket->writeLine($cmd); + + if (PEAR::isError($result) AND $result) { + return $result; + } + + $data = $this->_socket->readLine(); + + if (PEAR::isError($data)) { + return $data; + } + + $resp['code'] = substr($data, 0, 3); + $resp['text'] = ltrim(substr($data, 3)); + + if (!Net_Dict::isOK($resp)) { + return new PEAR_Error( $resp['text'], + $resp['code']); + } + + return $resp; + } + + // }}} + // {{{ _getMultiline() + /** + * Reads a multiline reponse and returns the data + * + * @return mixed string on success or PEAR_Error + */ + function _getMultiline() + { + $data = ''; + while(($tmp = $this->readLine()) != '.') { + if (substr($tmp, 0, 2) == '..') { + $tmp = substr($tmp, 1); + } + $data .= $tmp . "\r\n"; + } + + $resp['text'] = substr($data, 0, -2); + + return $resp; + } + + // }}} + // {{{ _readLine() + /** + * Alias to Net_Socket::readLine(); + */ + function readLine() + { + return $this->_socket->readLine(); + } + + // }}} + // {{{ simpleQuery() + /** + * Runs a generic dict query + * + * @param string $query + * + * @return mixed string on success, else PEAR_Error + */ + function simpleQuery($query) + { + $resp = $this->_sendCmd($query); + + if (PEAR::isError($resp)) { + return $resp; + } + + $resp = $this->_getMultiLine(); + + $this->readLine(); /* discard status */ + + return $resp['text']; + } + + // }}} + // {{{ isOK() + /** + * Checks if a response code is positive + * + * @param array $resp + * + * @return boolean + */ + function isOK($resp) + { + $positives = array(1, 2, 3); + + return in_array(substr($resp['code'], 0, 1), $positives); + } + + // }}} + +} // end class Dict diff --git a/thirdparty/pear/Net/Dig.php b/thirdparty/pear/Net/Dig.php new file mode 100644 index 0000000..662399b --- /dev/null +++ b/thirdparty/pear/Net/Dig.php @@ -0,0 +1,449 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Dig.php,v 1.1 2002/04/25 14:57:23 cmv Exp $ +// +// A nice friendly OO interface to dig +// + +require_once('PEAR.php'); + +class Net_Dig extends PEAR +{ + // {{{ Public Properties + + /** + * The address to dig + * + * @var string $address + * @access public + */ + var $address; + + /** + * The server to use for digging + * + * @var string $server + * @access public + */ + var $server; + + /** + * The type of DNS records to dig for + * + * @var string $query_type + * @access public + */ + var $query_type; + + /** + * The last system command executed (for debugging) + * + * @var string $cmd + * @access public + */ + var $cmd; + + /** + * The raw output of the system command (for debugging) + * + * @var string $raw_data + * @access public + */ + var $raw_data; + + /** + * The location of the system dig program + * + * @var string $dig_prg + * @access public + */ + var $dig_prog; + + /** + * The parsed result of the last dig + * + * @var string $result + * @access public + */ + var $result; + + // }}} + + + // {{{ Net_Dig() + + /** + * The Net_Dig constructor + * Called when a new Net_Dig object is initialized + * + * @param string [$address] The address to dig (can be set + * using the $address property as well) + * + * @param string [$server] The server to dig at (can be set + * using the $server property as well) + * + * @return object Net_Dig $obj A new Net_Dig object + * + * @access public + * @author Colin Viebrock + * @since PHP 4.0.5 + */ + function Net_Dig($address = false, $server = false ) + { + + $this->address = $address; + $this->server = $server; + $this->query_type = false; + + $this->cmd = ''; + $this->raw_data = ''; + + $this->result = false; + + $this->dig_prog = trim(`which dig`); + if (!$this->dig_prog) { + $this = new PEAR_Error("Couldn't find system dig program"); + } + + } + + // }}} + + + + // {{{ dig() + + /** + * Does a dig of the given address (or $this->address) + * + * @param string [$address] The address to dig (can be set + * using the $address property as well) + * + * @return object Net_Dig_result $obj A new Net_Dig_result object + * + * @access public + * @author Colin Viebrock + * @since PHP 4.0.5 + */ + function dig($address=false) + { + + if ($address) { + $this->address = $address; + } + + if (!$this->address) { + return new PEAR_Error("No address specified"); + } + + if (!$this->_validate_type()) { + return new PEAR_Error($this->query_type." is an invalid query type"); + } + + $cmd = escapeshellcmd( + sprintf("%s %s %s %s", + $this->dig_prog, + ($this->server ? '@'.$this->server : ''), + $this->address, + ($this->query_type ? $this->query_type : '' ) + ) + ); + + $this->cmd = $cmd; + + + $this->raw_data = `$cmd`; + $this->raw_data = trim( $this->raw_data ); + + return $this->_parse_data(); + + } + + // }}} + + + // {{{ _validate_type() + + /** + * Validates the value of $this->query_type + * + * @return boolean $return True if $this->query_type is a + * valid dig query, otherwise false + * + * @access private + * @author Colin Viebrock + * @since PHP 4.0.5 + */ + function _validate_type() + { + $return = true; + if ($this->query_type) { + $this->query_type = strtolower($this->query_type); + switch ($this->query_type) { + case 'a': + case 'any': + case 'mx': + case 'ns': + case 'soa': + case 'hinfo': + case 'axfr': + case 'txt': + break; + default: + $return = false; + } + } + return $return; + } + + // }}} + + + + // {{{ _parse_data() + + /** + * Parses the raw data in $this->raw_data + * + * @return obj Net_Dig_result $return A Net_Dig_result object + * + * @access private + * @author Colin Viebrock + * @since PHP 4.0.5 + */ + function _parse_data() + { + + if (!$this->raw_data) { + return new PEAR_Error("No raw data to parse"); + } + + $regex = '/' . + '^;(.*?)' . + ';; QUESTION SECTION\:(.*?)' . + '(;; ANSWER SECTION\:(.*?))?' . + '(;; AUTHORITY SECTION\:(.*?))?' . + '(;; ADDITIONAL SECTION\:(.*?))?' . + '(;;.*)' . + '/ims'; + + if (preg_match($regex, $this->raw_data, $matches)) { + + $result = new Net_Dig_result; + + /* Start parsing the data */ + + + /* the header ... */ + + + $temp = explode("\n", trim($matches[1])); + if (preg_match('/DiG (.*?) /i', $temp[0], $m)) { + $result->dig_version = trim($m[1]); + } + if (preg_match('/status: (.*?), id: (.*?)$/i', $temp[3], $m)) { + $result->status = trim($m[1]); + $result->id = trim($m[2]); + } + + if (preg_match('/flags: (.*?); query: (.*?), answer: (.*?), authority: (.*?), additional: (.*?)$/i', $temp[4], $m)) { + $result->flags = trim($m[1]); + $result->query_count = (int)trim($m[2]); + $result->answer_count = (int)trim($m[3]); + $result->authority_count = (int)trim($m[4]); + $result->additional_count = (int)trim($m[5]); + } + + + /* query section */ + + $line = trim(preg_replace('/^(;*)/', '', trim($matches[2]))); + list($host, $class, $type) = preg_split('/[\s]+/', $line, 3); + $result->query[] = new Net_Dig_resource($host, false, $class, $type, false); + + + /* answer section */ + + $temp = trim($matches[4]); + if ($temp) { + $temp = explode("\n", $temp); + if (count($temp)) { + foreach($temp as $line) { + $result->answer[] = $this->_parse_resource($line); + } + } + } + + + /* authority section */ + + $temp = trim($matches[6]); + if ($temp) { + $temp = explode("\n", $temp); + if (count($temp)) { + foreach($temp as $line) { + $result->authority[] = $this->_parse_resource($line); + } + } + } + + + /* additional section */ + + $temp = trim($matches[8]); + if ($temp) { + $temp = explode("\n", $temp); + if (count($temp)) { + foreach($temp as $line) { + $result->additional[] = $this->_parse_resource($line); + } + } + } + + /* footer */ + + $temp = explode("\n", trim($matches[9])); + if (preg_match('/query time: (.*?)$/i', $temp[0], $m)) { + $result->query_time = trim($m[1]); + } + if (preg_match('/server: (.*?)#(.*?)\(/i', $temp[1], $m)) { + $result->dig_server = trim($m[1]); + $result->dig_port = trim($m[2]); + } + + /* done */ + + $result->consistency_check = ( + (count($result->query) == $result->query_count) && + (count($result->answer) == $result->answer_count) && + (count($result->authority) == $result->authority_count) && + (count($result->additional) == $result->additional_count) + ); + + return $result; + + } + + return new PEAR_Error("Can't parse raw data"); + } + + // }}} + + + // {{{ _parse_resource() + + /** + * Parses a resource record line + * + * @param string $line The line to parse + * + * @return obj Net_Dig_resource $return A Net_Dig_resource object + * + * @access private + * @author Colin Viebrock + * @since PHP 4.0.5 + */ + function _parse_resource($line) + { + + /* trim and remove leading ;, if present */ + + $line = trim(preg_replace('/^(;*)/', '', trim($line))); + + if ($line) { + list($host, $ttl, $class, $type, $data) = preg_split('/[\s]+/', $line, 5); + return new Net_Dig_resource($host, $ttl, $class, $type, $data); + } + + return false; + + } + + // }}} + +} + + + +class Net_Dig_result { + + // {{{ Public Properties + + var $status; + var $id; + var $flags; + var $query_count; + var $answer_count; + var $authority_count; + var $additional_count; + + var $dig_version; + var $dig_server; + var $dig_port; + + var $query; + var $answer; + var $authority; + var $additional; + + var $consistency_check; + + function Net_Dig_result() { + $this->status = false; + $this->id = false; + $this->flags = false; + $this->query_count = false; + $this->answer_count = false; + $this->authority_count = false; + $this->additional_count = false; + + $this->dig_version = false; + $this->dig_server = false; + $this->dig_port = false; + + $this->query = array(); + $this->answer = array(); + $this->authority = array(); + $this->additional = array(); + + $this->consistency_check = false; + + } + +} + +class Net_Dig_resource { + + var $host; + var $ttl; + var $class; + var $type; + var $data; + + function Net_Dig_resource($host=false, $ttl=false, $class=false, $type=false, $data=false) { + $this->host = $host; + $this->ttl = $ttl; + $this->class = $class; + $this->type = $type; + $this->data = $data; + } + +} + +?> diff --git a/thirdparty/pear/Net/Finger.php b/thirdparty/pear/Net/Finger.php new file mode 100644 index 0000000..fd6f365 --- /dev/null +++ b/thirdparty/pear/Net/Finger.php @@ -0,0 +1,60 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Finger.php,v 1.5 2003/01/04 11:55:46 mj Exp $ +// + +require_once 'PEAR.php'; +require_once 'Net/Socket.php'; + +/** + * PEAR's Net_Finger:: interface. + * + * Provides functions useful for Finger-Queries. + * + * @version $Revision: 1.5 $ + * @author Sebastian Nohn + * @package Net + */ +class Net_Finger +{ + + /** + * Implements Net_Finger::query() function using PEAR's socket functions + * + * @param string $server The finger-server to query + * @param string $query The finger database object to lookup + * @return mixed The data returned from the finger-server as string + * or a PEAR_Error ( see Net_Socket for error codes) + */ + function query($server, $query) + { + $socket = new Net_Socket; + if( PEAR::isError( $sockerror = $socket->connect($server, 79))) { + $data = new PEAR_Error( "Error connecting to $server ( Net_Socket says: ". + $sockerror->getMessage().")", $sockerror->getCode()); + } else { + $query .= "\n"; + $socket->write($query); + $data = $socket->read(16384); + $socket->disconnect(); + } + return $data; + } +} +?> diff --git a/thirdparty/pear/Net/Geo.php b/thirdparty/pear/Net/Geo.php new file mode 100644 index 0000000..1bc3bad --- /dev/null +++ b/thirdparty/pear/Net/Geo.php @@ -0,0 +1,645 @@ + | +// | | +// +----------------------------------------------------------------------+ +// +// $Id: Geo.php,v 1.7 2004/02/18 22:59:20 lsmith Exp $ + +require_once 'PEAR.php'; +require_once 'Cache/Function.php'; + +/** + * NetGeo - determine geographic information on an internet address + * + * Can accept input of an AS number, an IP address or a host name + * Input can be individual or an array of addresses + * + * $geo = new Net_Geo(); + * $geo->getRecord("php.net"); + * $geo->getRecord(array("php.net", "google.com")); + * + * Results returned are a single array of results if a string is passed in + * or in the case of an array, a multi-dim array with the as the key + * + * Query service type (CAIDA or localizer) is not available as a constructer + * to retain compatibility with existing CAIDA NetGeo classes (perl + java) + * + * @version 1.0 + * @package NetGeo + * @author Graeme Merrall + */ + +define('NETGEO_INPUT_ERROR', 'INPUT_ERROR'); +define('NETGEO_HTTP_ERROR', 'HTTP_ERROR'); +define('NETGEO_NO_MATCH', 'NO MATCH'); +define('NETGEO_NO_COUNTRY', 'NO_COUNTRY'); +define('NETGEO_LIMIT_EXCEEDED', 'NETGEO_LIMIT_EXCEEDED'); + +class Net_Geo +{ + + /** + * Path to local cache file. + * Caching is compulsory to reduce load on CAIDA server + * + * @var string + * @access public + */ + var $cache_path = "/tmp/"; + + /** + * How long to wait befire rechecking cached entries in *days* + * This should be comething nice and high + * + * @var in + * @access public + */ + var $cache_ttl = 30; + + /** + * CAIDA only + * + * Maximum length of time, in seconds, which will be allowed during a whois + * lookup by the NetGeo server. + * The actual default value is maintained by the server. + * + * @var int + * @access public + */ + var $default_timeout = 60; + + /** + * CAIDA only + * + * Location of the default netgeo server + * If port not speicifed, defaults to 80 + * + * @var string + * @access public + */ + var $default_server = "http://netgeo.caida.org/perl/netgeo.cgi"; + + /** + * localizer only + * + * Location of the localizer data file + * + * @var string + * @access public + */ + var $localizer_data = "./demo.csv"; + + /** + * Type of service to use. May be either 'caida' or 'localizer' + * Default is 'caida' + * + * @var string + @ @access private + */ + var $service; + + /** + * Cache filename prefix + * + * @var string + * @access private + */ + var $cache_prefix = "netgeo"; + + /** + * CAIDA only + * + * User Agent string. + * + * @var string + * @access private + */ + var $useragent = "PHP/NetGeo"; + + /** + * CAIDA only + * + * Class version + * + * @var string + * @access private + */ + var $useragent_version = "1.0"; + + /** + * CAIDA only + * + * How many targets can be read in at once + * Should be enough for most everyone + * + * @var string + * @access private + */ + var $array_limit = 100; + + /** + * Function cache object + * + * @var object + * @access private + */ + var $cache; + + /** + * Name of global var for copying $this when calling function cache + * This is needed for the cache function to operate correctly + * + * @var string + * @access private + */ + var $netgeo_global = "netgeo_global"; + + + /** + * Constructor + * Both $applicationName and $alternateServerUrl are for compatibility + * with the perl and java netgeo classes. + * I don't guarantee to use these variables + * + * @param string $applicationName Application using the NetGeo class. + * @param string $alternateServerUrl Alternate NetGeo server url + * @return bool + * @access public + */ + function Net_Geo($applicationName="", $alternateServerUrl="") + { + $this->applicationName = $applicationName; + $this->alternateServerUrl = $alternateServerUrl; + + // init cache object + $this->cache = new Cache_Function('file', + array('cache_dir' => $this->cache_path, + 'filename_prefix' => $this->cache_prefix + ), + $this->cache_ttl * 86400 + ); + + return true; + } + + + function setService($service = "caida") { + + if ($service == "localizer") { + + if (@localizer_read($this->localizer_data, FALSE) == FALSE) { + PEAR::raiseError("Can't read localizer data file ".$this->localizer_data); + return false; + } + + } elseif ($service == "caida") { + + // check to see if an alternate server URL is used + if (!empty($alternateServerUrl)) { + $this->default_server = $this->alternateServerUrl; + } + + $this->useragent = sprintf("%s %s", $this->useragent, + $this->useragent_version + ); + + // set the custom user agent + if (!empty($applicationName)) { + // trim whitespace + $applicationName = trim($applicationName); + + // also set the agent name + $this->useragent = sprintf("%s/%s", $this->applicationName, + $this->useragent + ); + } + + } else { + // return error + return new PEAR_Error("No service specified"); + + } + + + $this->service = $service; + return true; + + } + + /** + * Gets a complete record for an address + * Returns either a single or multidimentional arrray + * if input is a string or an array respectively + * + * @param mixed $target Single or list of addresses + * @return array + * @access public + */ + function getRecord($target) + { + return $this->_execute("getRecord", $target); + } + + /** + * Returns the 2-letter ISO 3166 country code + * Returns NO_MATCH if the AS number has been looked up + * but nothing was found in the whois lookups. + * Returns NO_COUNTRY if the lookup returned a record + * but no country could be found. + * Returns an empty string if nothing was found in the database + * + * @param string $target single address + * @return array + * @access public + */ + function getCountry($target) + { + $result = $this->_execute("getCountry", $target); + if (is_array($result)) { + return $result["COUNTRY"]; + } + + return $result; + } + + /** + * Returns an array with keys LAT, LONG, LAT_LONG_GRAN, and STATUS. + * Lat/Long will be (0,0) if the target has been looked up but there was no + * match in the whois lookups, or if no address could be parsed from the + * whois record, or if the lat/long for the address is unknown. + * Returns an empty string if nothing was found in the database + * + * @param string $target single address + * @return array + * @access public + */ + function getLatLong($target) + { + return $this->_execute("getLatLong", $target); + } + + /** + * Included here to make the NetGeo class as similar as possible to + * the NetGeoClient.java interface. + * It's probably just as easy for the user to extract lat and long directly + * from the array. + * + * @param string $target single address + * @return double + * @access public + */ + function getLat($latLongRef) + { + if (is_array($latLongRef)) { + $lat = $latLongRef["LAT"]; + } else { + $lat = 0; + } + + return sprintf("%.2f", $lat); + } + + /** + * Included here to make the NetGeo class as similar as possible to + * the NetGeoClient.java interface. + * It's probably just as easy for the user to extract lat and long directly + * from the array + * + * @param string $target single address + * @return double + * @access public + */ + function getLong($latLongHashRef) + { + if (is_array($latLongHashRef)) { + $long = $latLongHashRef["LONG"]; + } else { + $long = 0; + } + + return sprintf("%.2f", $long); + } + + /** + * Interface to the public functions + * + * @param string $methodName Lookup method + * @param mixed $target Address(es) to lookup + * @return array + * @access private + */ + function _execute($methodName, $input) + { + + // if we haven't got a service set, then do it now + if (empty($this->service)) { + $this->setService(); + } + + // Test the target strings in the input array. Any targets not in + // an acceptable format will have their STATUS field set to INPUT_ERROR. + // This method will also store the standardized target into the array + // for use as a key in the cache table. + $inputArray = $this->_verifyInputFormatArray($methodName, $input); + if (PEAR::isError($inputArray)) { + return $inputArray; + } + + $resultArray = $this->_processArray($methodName, $inputArray); + + // if there is only one array, move the whole thing up one + if (count($resultArray) == 1) { + $resultArray = $resultArray[0]; + } + + return $resultArray; + } + + + /** + * Verify the type of the target argument and verify types of array elements + * Also converts the input array into the start of the output array + * + * @param string $methodName Lookup method + * @param mixed $inputArray Address(es) to lookup + * @return array or pear error object on failure + * @access private + */ + function _verifyInputFormatArray($methodName, $inputArray) + { + // makes sure that the input is an array + // if length is > than ARRAY_LIMIT_LENTH then bomb ou + if (count($inputArray) > $this->array_limit) { + // raise an error + $error = new PEAR_Error("Too many entries. Limit is ".$this->array_limit); + return $error; + } + + // convert into a useable array + $inputArray = $this->_convertInputArray($inputArray); + return $inputArray; + } + + /** + * Utility function to check what the input array + * and to convert to a correct array format for processing + * + * @param mixed $inputArray Address array + * @return array + * @access private + */ + function _convertInputArray($inputArray) + { + // first check the darn thing is actually an array + if (!is_array($inputArray)) { + $inputArray = array($inputArray); + } + + // now convert to the correct array form + foreach ($inputArray as $entry) { + $returnArray[]["TARGET"] = $entry; + } + + return $returnArray; + + } + + + /** + * Main function that processes addresses + * + * It might be a good idea to move the caching up one level? + * + * @param string $methodName Lookup method + * @param array $inputArray Formatted address array + * @return array + * @access private + */ + function _processArray($methodName, $inputArray) + { + $i = 0; + foreach ($inputArray as $entry) { + $entry = $this->_verifyInputFormat($entry); + + if (isset($entry["TARGET"]) && !isset($entry["INPUT_ERROR"])) { + + // set up the cache work around + $GLOBALS[$this->netgeo_global] =& $this; + + if ($this->service == "localizer") { + + $response = $this->cache->call('localizer_search', $entry["TARGET"]); + + } else { + + // else do the HTTP request + $url = sprintf("%s?method=%s&target=%s", $this->default_server, + $methodName, $entry["TARGET"] + ); + + $response =& $this->cache->call($this->netgeo_global.'->_executeHttpRequest', $url); + + } + + if (!isset($response)) { + $entry["STATUS"] = NETGEO_HTTP_ERROR; + } + + // parse it all into something useful + // at this point we should look for NETGEO_LIMIT_EXCEEDED as well + $dataArray[$i] = $this->_processResult($response); + + } else { + $dataArray[$i] = $entry; + } + + $i++; + } + + if (is_array($dataArray)) { + return $dataArray; + } else { + return array("STATUS"=>NETGEO_HTTP_ERROR); + } + } + + /** + * Test the input and make sure it is in an acceptable format. The input + * can be an AS number (with or without a leading "AS"), an IP address in + * dotted decimal format, or a domain name. Stores the standardized targe + * string into the hash if input target is valid format, otherwise stores + * undef into hash. + * + * @param array $inputArray Address(es) to lookup + * @return array + * @access private + + */ + function _verifyInputFormat($inputArray) + { + $target = trim($inputArray["TARGET"]); + + // look for AS|as + if (preg_match('/^(?:AS|as)?\s?(\d{1,})$/', $target, $matches)) { + // check the AS number. Btwn 1 and 65536 + if ($matches[1] >= 1 && $matches[1] < 65536) { + $standardizedTarget = $matches[0]; + } else { + $inputArray["INPUT_ERROR"] = NETGEO_INPUT_ERROR; + // raise some error tex + // Bad format for input. AS number must be between 1 and 65536 + return $inputArray; + } + + // IP number + } elseif (preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $target, $matches)) { + if ($matches[1] <= 255 && $matches[2] <= 255 && $matches[3] <= 255 && $matches[4] <= 255) { + $standardizedTarget = $target; + } else { + $inputArray["INPUT_ERROR"] = NETGEO_INPUT_ERROR; + // raise some error tex + // Bad format for input. each octet in IP address must be between 0 and 255 + return $inputArray; + } + + // TLD + } elseif (preg_match('/^(?:[\w\-]+\.)*[\w\-]+\.([A-Za-z]{2,3})$/', $target, $matches)) { + $tld = $matches[1]; + + // TLD length is either 2 or 3. If length is 2 we just accept it, + // otherwise we test the TLD against the list. + if (strlen($tld) == 2 || preg_match('/^(com|net|org|edu|gov|mil|int)/i', $tld)) { + $standardizedTarget = $target; + } else { + $inputArray["INPUT_ERROR"] = NETGEO_INPUT_ERROR; + // raise some error tex + // Bad TLD in domain name. 3-letter TLDs must be one of com,net,org,edu,gov,mil,in + return $inputArray; + } + } else { + $inputArray["INPUT_ERROR"] = NETGEO_INPUT_ERROR; + // raise some error text + // unrecognized format for input + return $inputArray; + } + + return $inputArray; + + } + + /** + * Executes a request to the netgeo server + * + * @param array $inputArray Address(es) to lookup + * @return string Response from netgeo server + * @access private + */ + function _executeHttpRequest($url) + { + $response = ""; + + if (function_exists('curl_init')) { + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $response = curl_exec($ch); + curl_close($ch); + } else { + // split the server url + $urlinfo = parse_url($url); + if (!isset($urlinfo["port"])) { + $urlinfo["port"] = 80; + } + + $sp = @fsockopen($urlinfo["host"], $urlinfo["port"], $errno, $errstr, $this->default_timeout); + if (!$sp) { + return false; + } + + fputs($sp, "GET " . $urlinfo["path"] ."?". $urlinfo["query"] . " HTTP/1.0\r\n"); + fputs($sp, "User-Agent: " . $this->useragent . "\r\n\r\n"); + while (!feof($sp)) { + $response .= fgets($sp,128); + } + fclose ($sp); + } + + return $response; + } + + /** + * Parses the results from the server into an array + * + * @param string $response Response from netgeo server + * @return array + * @access private + */ + function _processResult($response) + { + // process the localizer result differently + // since we already have an array + if ($this->service == "localizer") { + + foreach ($response as $key=>$val) { + + $retarray[strtoupper($key)] = $val; + } + + } elseif ($this->service == "caida") { + + $lineArray = preg_split("/\n/", $response); + $line = array_shift($lineArray); + + // first check for anything icky from the server + if (preg_match("/".NETGEO_HTTP_ERROR."/", $line) || preg_match('/^\s*$/', $response)) { + + // empty empty empty + if (preg_match('/^\s*$/', $text)) { + $text = "Empty content string"; + return array("STATUS"=>$text); + } + + } elseif (preg_match("/".NETGEO_LIMIT_EXCEEDED."/", $line)) { + return array("STATUS"=>$text); + } + + // now loop through. This should being us out at TARGET + while (isset($line) && !preg_match("/^TARGET:/", $line)) { + $line = array_shift($lineArray); + } + + // keep going + while (isset($line)) { + if (preg_match("/^TARGET:\s+(.*\S)\s*
/", $line, $matches)) { + $retarray["TARGET"] = $matches[1]; + } elseif (preg_match("/^STATUS:\s+([\w\s]+\S)\s*
/", $line, $matches)) { + $retarray["STATUS"] = $matches[1]; + } elseif (preg_match("/^(\w+):\s+(.*\S)\s*
/", $line, $matches)) { + $retarray[$matches[1]] = $matches[2]; + } + $line = array_shift($lineArray); + } + + } + + return $retarray; + + } + +} + + +?> diff --git a/thirdparty/pear/Net/IMAP.php b/thirdparty/pear/Net/IMAP.php new file mode 100644 index 0000000..bb8d733 --- /dev/null +++ b/thirdparty/pear/Net/IMAP.php @@ -0,0 +1,1839 @@ + | +// +----------------------------------------------------------------------+ + + +require_once 'Net/IMAPProtocol.php'; + + +/** + * Provides an implementation of the IMAP protocol using PEAR's + * Net_Socket:: class. + * + * @package Net_IMAP + * @author Damian Alejandro Fernandez Sosa + */ +class Net_IMAP extends Net_IMAPProtocol { + + /** + * Constructor + * + * 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. + */ + + function Net_IMAP($host = 'localhost', $port = 143) + { + $this->Net_IMAPProtocol(); + $ret = $this->connect( $host , $port ); + } + + + + + + + /** + * Attempt to connect to the IMAP server located at $host $port + * @param string $host The IMAP server + * @param string $port The IMAP port + * + * It is only useful in a very few circunstances + * because the contructor already makes this job + * @return true on success or PEAR_Error + * + * @access public + * @since 1.0 + */ + function connect($host, $port) + { + $ret=$this->cmdConnect($host,$port); + if($ret === true ){ + return $ret; + } + if(empty($ret)){ + return new PEAR_Error("Unexpected response on connection"); + } + if(PEAR::isError($ret) ){ + return $ret; + } + if(isset( $ret["RESPONSE"]["CODE"] ) ){ + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + } + return $ret; + } + + + + + + + + + + + /** + * Attempt to authenticate to the IMAP server. + * @param string $user The userid to authenticate as. + * @param string $pass The password to authenticate with. + * @param string $useauthenticate true: authenticate using + * the IMAP AUTHENTICATE command. false: authenticate using + * the IMAP AUTHENTICATE command. 'string': authenticate using + * the IMAP AUTHENTICATE command but using the authMethod in 'string' + * @param boolean $selectMailbox automaticaly select inbox on login (false does not) + * + * @return true on success or PEAR_Error + * + * @access public + * @since 1.0 + */ + + function login($user, $pass, $useauthenticate = true, $selectMailbox=true) + { + if ( $useauthenticate ){ + //$useauthenticate is a string if the user hardcodes an AUTHMethod + // (the user calls $imap->login("user","password","CRAM-MD5"); for example! + + $method = is_string( $useauthenticate ) ? $useauthenticate : null; + + //Try the selected Auth method + if ( PEAR::isError( $ret = $this->cmdAuthenticate( $user , $pass , $method ) ) ) { + // Verify the methods that we have in common with the server + if(is_array($this->_serverAuthMethods)){ + $commonMethods=array_intersect ($this->supportedAuthMethods, $this->_serverAuthMethods ); + }else{ + $this->_serverAuthMethods=null; + } + if($this->_serverAuthMethods == null || count($commonMethods) == 0 || $this->supportedAuthMethods == null ){ + // The server does not have any auth method, so I try LOGIN + if ( PEAR::isError( $ret = $this->cmdLogin( $user, $pass ) ) ) { + return $ret; + } + }else{ + return $ret; + } + } + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + }else{ + //The user request "PLAIN" auth, we use the login command + if ( PEAR::isError( $ret = $this->cmdLogin( $user, $pass ) ) ) { + return $ret; + } + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + } + + if($selectMailbox){ + //Select INBOX + if ( PEAR::isError( $ret=$this->cmdSelect( $this->getCurrentMailbox() ) ) ) { + return $ret; + } + } + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + + + /* + * Disconnect function. Sends the QUIT command + * and closes the socket. + * + * @return bool Success/Failure + */ + function disconnect($expungeOnExit = false) + { + if($expungeOnExit){ + $ret=$this->cmdExpunge(); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + $ret=$this->cmdLogout(); + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + } + $ret=$this->cmdLogout(); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + + + + + + /* + * Changes the default/current mailbox th $mailbox + * + * + * @return bool Success/Pear_Error Failure + */ + function selectMailbox($mailbox) + { + $ret=$this->cmdSelect($mailbox); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + + + + + + /* + * Checks the mailbox $mailbox + * + * + * @return bool Success/Pear_Error Failure + */ + function examineMailbox($mailbox) + { + $ret=$this->cmdExamine($mailbox); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + + //$ret_aux["EXISTS"]=$ret["PARSED"]["EXISTS"]; + //$ret_aux["RECENT"]=$ret["PARSED"]["RECENT"]; + return $ret; + } + + + + + + + + + /* + * Returns the raw headers of the specified message. + * + * @param $msg_id Message number + * @return mixed Either raw headers or false on error + */ + function getRawHeaders($msg_id) + { + $ret=$this->cmdFetch($msg_id, "BODY[HEADER]"); + if(strtoupper( $ret["RESPONSE"]["CODE"]) != "OK" ){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + $ret=$ret["PARSED"][0]["EXT"]["BODY[HEADER]"]["CONTENT"]; + return $ret; + } + + + + + /* + * Returns the headers of the specified message in an + * associative array. Array keys are the header names, array + * values are the header values. In the case of multiple headers + * having the same names, eg Received:, the array value will be + * an indexed array of all the header values. + * + * @param $msg_id Message number + * @return mixed Either array of headers or false on error + */ + function getParsedHeaders($msg_id) + { + $ret=$this->getRawHeaders($msg_id); + + $raw_headers = rtrim($ret); + $raw_headers = preg_replace("/\r\n[ \t]+/", ' ', $raw_headers); // Unfold headers + $raw_headers = explode("\r\n", $raw_headers); + foreach ($raw_headers as $value) { + $name = substr($value, 0, $pos = strpos($value, ':')); + $value = ltrim(substr($value, $pos + 1)); + if (isset($headers[$name]) AND is_array($headers[$name])) { + $headers[$name][] = $value; + } elseif (isset($headers[$name])) { + $headers[$name] = array($headers[$name], $value); + } else { + $headers[$name] = $value; + } + } + + return $headers; + } + + + + + + /* + * Returns an array containing the message ID, the size and the UID + * of each message selected. + * message selection can be a valid IMAP command, a number or an array of + * messages + * + * @param $msg_id Message number + * @return mixed Either array of message data or PearError on error + */ + + function getMessagesList($msg_id = null) + { + if( $msg_id != null){ + if(is_array($msg_id)){ + $message_set=$this->_getSearchListFromArray($msg_id); + }else{ + $message_set=$msg_id; + } + }else{ + $message_set="1:*"; + } + $ret=$this->cmdFetch($message_set,"(RFC822.SIZE UID)"); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + foreach($ret["PARSED"] as $msg){ + $ret_aux[]=array("msg_id"=>$msg["NRO"],"size" => $msg["EXT"]["RFC822.SIZE"],"uidl"=> $msg["EXT"]["UID"]); + } + return $ret_aux; + } + + + + + + + + + + + function getSummary($msg_id = null) + { + if( $msg_id != null){ + if(is_array($msg_id)){ + $message_set=$this->_getSearchListFromArray($msg_id); + }else{ + $message_set=$msg_id; + } + }else{ + $message_set="1:*"; + } + $ret=$this->cmdFetch($message_set,"(RFC822.SIZE UID FLAGS ENVELOPE INTERNALDATE)"); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + + + if(isset( $ret["PARSED"] ) ){ + for($i=0; $icmdFetch($msg_id,"BODY[TEXT]"); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + $ret=$ret["PARSED"][0]["EXT"]["BODY[TEXT]"]["CONTENT"]; + //$ret=$resp["PARSED"][0]["EXT"]["RFC822"]["CONTENT"]; + return $ret; + } + + + + + + + + + /* + * Returns the entire message with given message number. + * + * @param $msg_id Message number + * @return mixed Either entire message or false on error + */ + function getMessages($msg_id = null, $indexIsMessageNumber=true) + { + //$resp=$this->cmdFetch($msg_id,"(BODY[TEXT] BODY[HEADER])"); + if( $msg_id != null){ + if(is_array($msg_id)){ + $message_set=$this->_getSearchListFromArray($msg_id); + }else{ + $message_set=$msg_id; + } + }else{ + $message_set="1:*"; + } + + $ret=$this->cmdFetch($message_set,"RFC822"); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + if(isset($ret["PARSED"])){ + foreach($ret["PARSED"] as $msg){ + if(isset($msg["EXT"]["RFC822"]["CONTENT"])){ + if($indexIsMessageNumber){ + $ret_aux[$msg["NRO"]]=$msg["EXT"]["RFC822"]["CONTENT"]; + }else{ + $ret_aux[]=$msg["EXT"]["RFC822"]["CONTENT"]; + } + } + } + return $ret_aux; + } + return array(); + } + + + + + + + + + + + + /* + * Returns number of messages in this mailbox + * + * @param string $mailbox the mailbox + * @return mixed Either number of messages or Pear_Error on error + */ + function getNumberOfMessages($mailbox = '') + { + if ( $mailbox == '' || $mailbox == null ){ + $mailbox=$this->getCurrentMailbox(); + } + $ret=$this->cmdStatus( $mailbox , "MESSAGES" ); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + if( isset($ret["PARSED"]["STATUS"]["ATTRIBUTES"]["MESSAGES"] ) ){ + if( !is_numeric( $ret["PARSED"]["STATUS"]["ATTRIBUTES"]["MESSAGES"] ) ){ + // if this array does not exists means that there is no messages in the mailbox + return 0; + }else{ + return $ret["PARSED"]["STATUS"]["ATTRIBUTES"]["MESSAGES"]; + } + + } + return 0; + } + + + /* + * Returns number of UnSeen messages in this mailbox + * + * @param string $mailbox the mailbox + * @return mixed Either number of messages or Pear_Error on error + */ + function getNumberOfUnSeenMessages($mailbox = '') + { + if ( $mailbox == '' ){ + $mailbox=$this->getCurrentMailbox(); + } + $ret=$this->cmdStatus( $mailbox , "UNSEEN" ); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + if( isset($ret["PARSED"]["STATUS"]["ATTRIBUTES"]["UNSEEN"] ) ){ + if( !is_numeric( $ret["PARSED"]["STATUS"]["ATTRIBUTES"]["UNSEEN"] ) ){ + // if this array does not exists means that there is no messages in the mailbox + return 0; + }else{ + return $ret["PARSED"]["STATUS"]["ATTRIBUTES"]["UNSEEN"]; + } + + } + return 0; + } + + /* + * Returns number of UnSeen messages in this mailbox + * + * @param string $mailbox the mailbox + * @return mixed Either number of messages or Pear_Error on error + */ + function getNumberOfRecentMessages($mailbox = '') + { + if ( $mailbox == '' ){ + $mailbox=$this->getCurrentMailbox(); + } + $ret=$this->cmdStatus( $mailbox , "RECENT" ); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + if( isset($ret["PARSED"]["STATUS"]["ATTRIBUTES"]["RECENT"] ) ){ + if( !is_numeric( $ret["PARSED"]["STATUS"]["ATTRIBUTES"]["RECENT"] ) ){ + // if this array does not exists means that there is no messages in the mailbox + return 0; + }else{ + return $ret["PARSED"]["STATUS"]["ATTRIBUTES"]["RECENT"]; + } + + } + return 0; + } + + + + + + + + /* + * Returns an array containing the message envelope + * + * @return mixed Either the envelopes or Pear_Error on error + */ + function getEnvelope($mailbox = '', $msg_id = null) + { + if ( $mailbox == '' ){ + $mailbox=$this->getCurrentMailbox(); + } + + if( $msg_id != null){ + if(is_array($msg_id)){ + $message_set=$this->_getSearchListFromArray($msg_id); + }else{ + $message_set=$msg_id; + } + }else{ + $message_set="1:*"; + } + + + $ret=$this->cmdFetch($message_set,"ENVELOPE"); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + + if(isset( $ret["PARSED"] ) ){ + for($i=0; $igetCurrentMailbox() ){ + // store the actual selected mailbox name + $mailbox_aux = $this->getCurrentMailbox(); + if ( PEAR::isError( $ret = $this->selectMailbox( $mailbox ) ) ) { + return $ret; + } + } + + $ret=$this->cmdFetch("1:*","RFC822.SIZE"); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + // Restore the default mailbox if it was changed + if ( $mailbox != '' && $mailbox != $this->getCurrentMailbox() ){ + if ( PEAR::isError( $ret = $this->selectMailbox( $mailbox_aux ) ) ) { + return $ret; + } + } + // return 0 because the server says that there is no message in the mailbox + return 0; + } + + $sum=0; + + if(!isset($ret["PARSED"]) ){ + // if the server does not return a "PARSED" part + // we think that it does not suppoprt select or has no messages in it. + return 0; + } + foreach($ret["PARSED"] as $msgSize){ + if( isset($msgSize["EXT"]["RFC822.SIZE"]) ){ + $sum+= $msgSize["EXT"]["RFC822.SIZE"]; + } + } + + if ( $mailbox != '' && $mailbox != $this->getCurrentMailbox() ){ + // re-select the mailbox + if ( PEAR::isError( $ret = $this->selectMailbox( $mailbox_aux ) ) ) { + return $ret; + } + } + + return $sum; + } + + + + + + + + + + + + + + + /* + * Marks a message for deletion. Only will be deleted if the + * disconnect() method is called with auto-expunge on true or expunge() + * method is called. + * + * @param $msg_id Message to delete + * @return bool Success/Failure + */ + function deleteMessages($msg_id = null) + { + /* As said in RFC2060... + C: A003 STORE 2:4 +FLAGS (\Deleted) + S: * 2 FETCH FLAGS (\Deleted \Seen) + S: * 3 FETCH FLAGS (\Deleted) + S: * 4 FETCH FLAGS (\Deleted \Flagged \Seen) + S: A003 OK STORE completed + */ + //Called without parammeters deletes all the messages in the mailbox + // You can also provide an array of numbers to delete those emails + if( $msg_id != null){ + if(is_array($msg_id)){ + $message_set=$this->_getSearchListFromArray($msg_id); + }else{ + $message_set=$msg_id; + } + }else{ + $message_set="1:*"; + } + + + $dataitem="+FLAGS.SILENT"; + $value="\Deleted"; + $ret=$this->cmdStore($message_set,$dataitem,$value); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + + + + + + + + /** + * Copies mail from one folder to another + * + * @param string $dest_mailbox mailbox name to copy sessages to + * @param mixed $message_set the messages that I want to copy (all by default) it also + * can be an array + * @param string $source_mailbox mailbox name from where the messages are copied + * + * @return mixed true on Success/PearError on Failure + * @since 1.0 + */ + function copyMessages($dest_mailbox, $msg_id = null , $source_mailbox = null ) + { + + if($source_mailbox == null){ + $source_mailbox = $this->getCurrentMailbox(); + }else{ + if ( PEAR::isError( $ret = $this->selectMailbox( $source_mailbox ) ) ) { + return $ret; + } + } + //Called without parammeters copies all messages in the mailbox + // You can also provide an array of numbers to copy those emails + if( $msg_id != null){ + if(is_array($msg_id)){ + $message_set=$this->_getSearchListFromArray($msg_id); + }else{ + $message_set=$msg_id; + } + }else{ + $message_set="1:*"; + } + + + + if ( PEAR::isError( $ret = $this->cmdCopy($message_set, $dest_mailbox ) ) ) { + return $ret; + } + return true; + } + + + + + + + + + + + + + + /** + * Appends a mail to a mailbox + * + * @param string $rfc_message the message to append in RFC822 format + * @param string $mailbox mailbox name to append to + * + * @return mixed true on Success/PearError on Failure + * @since 1.0 + */ + function appendMessage($rfc_message, $mailbox = null ) + { + if($mailbox == null){ + $mailbox = $this->getCurrentMailbox(); + } + $ret=$this->cmdAppend($mailbox,$rfc_message); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + + + + + + + + + + + + + + /****************************************************************** + ** ** + ** MAILBOX RELATED METHODS ** + ** ** + ******************************************************************/ + + + + + + /** + * Gets the HierachyDelimiter character used to create subfolders cyrus users "." + * and wu-imapd uses "/" + * + * $param string the mailbox to get the hierarchy from + * @return string the hierarchy delimiter + * + * @access public + * @since 1.0 + */ + function getHierarchyDelimiter( $mailbox = '' ) + { + + /* RFC2060 says: "the command LIST "" "" means get the hierachy delimiter: + An empty ("" string) mailbox name argument is a special request to + return the hierarchy delimiter and the root name of the name given + in the reference. The value returned as the root MAY be null if + the reference is non-rooted or is null. In all cases, the + hierarchy delimiter is returned. This permits a client to get the + hierarchy delimiter even when no mailboxes by that name currently + exist." + */ + if( PEAR::isError( $ret = $this->cmdList( $mailbox , '' ) ) ){ + return $ret; + } + if(isset($ret["PARSED"][0]["EXT"]["LIST"]["HIERACHY_DELIMITER"]) ){ + return $ret["PARSED"][0]["EXT"]["LIST"]["HIERACHY_DELIMITER"]; + } + return new PEAR_Error( 'the IMAP Server does not support HIERACHY_DELIMITER!' ); + } + + + + + + + + + /** + * Returns an array containing the names of the selected mailboxes + * + * @param string $mailbox_base base mailbox to start the search + * $mailbox_base if $mailbox_base == '' then $mailbox_base is the curent selected mailbox + * @param string $restriction_search false or 0 means return all mailboxes true or 1 return only the mailbox that contains that exact name + 2 return all mailboxes in that hierarchy level + * @param string $returnAttributes true means return an assoc array containing mailbox names and mailbox attributes + false - the default - means return an array of mailboxes + * + * @return mixed true on Success/PearError on Failure + * @since 1.0 + */ + + + + function getMailboxes($reference = '' , $restriction_search = 0, $returnAttributes=false ) + { + + if ( is_bool($restriction_search) ){ + $restriction_search = (int) $restriction_search; + } + + if ( is_int( $restriction_search ) ){ + switch ( $restriction_search ) { + case 0: + $mailbox = "*"; + break; + case 1: + $mailbox = $reference; + $reference = '%'; + break; + case 2: + $mailbox = "%"; + break; + } + }else{ + if ( is_string( $restriction_search ) ){ + $mailbox = $restriction_search; + }else { + return new PEAR_Error("UPS... you "); + } + } + + if( PEAR::isError( $ret = $this->cmdList($reference, $mailbox) ) ){ + return $ret; + } + + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + $ret_aux=array(); + if( isset($ret["PARSED"]) ){ + foreach( $ret["PARSED"] as $mbox ){ + + //If the folder has the \NoSelect atribute we don't put in the list + // it solves a bug in wu-imap that crash the IMAP server if we select that mailbox + if( isset($mbox["EXT"]["LIST"]["NAME_ATTRIBUTES"]) ){ + if( !in_array('\NoSelect',$mbox["EXT"]["LIST"]["NAME_ATTRIBUTES"]) ){ + if( $returnAttributes){ + $ret_aux[]=array( 'MAILBOX' => $mbox["EXT"]["LIST"]["MAILBOX_NAME"], + 'ATTRIBUTES' => $mbox["EXT"]["LIST"]["NAME_ATTRIBUTES"] , + 'HIERACHY_DELIMITER' => $mbox["EXT"]["LIST"]["HIERACHY_DELIMITER"] ) ; + }else{ + $ret_aux[]=$mbox["EXT"]["LIST"]["MAILBOX_NAME"]; + } + } + } + } + } + return $ret_aux; + } + + + + + + /** + * check if the mailbox name exists + * + * @param string $mailbox mailbox name to check existance + * + * @return boolean true on Success/false on Failure + * @since 1.0 + */ + + function mailboxExist($mailbox) + { + // true means do an exact match + if( PEAR::isError( $ret = $this->getMailboxes( $mailbox , true ) ) ){ + return $ret; + } + if( count( $ret ) > 0 ){ + return true; + } + return false; + } + + + + + + + + /** + * Creates the mailbox $mailbox + * + * @param string $mailbox mailbox name to create + * + * @return mixed true on Success/PearError on Failure + * @since 1.0 + */ + function createMailbox($mailbox) + { + $ret=$this->cmdCreate($mailbox); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + + + /** + * Deletes the mailbox $mailbox + * + * @param string $mailbox mailbox name to delete + * + * @return mixed true on Success/PearError on Failure + * @since 1.0 + */ + function deleteMailbox($mailbox) + { + // TODO verificar que el mailbox se encuentra vacio y, sino borrar los mensajes antes~!!!!!! + $ret=$this->cmdDelete($mailbox); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + + + + + + + + /** + * Renames the mailbox $mailbox + * + * @param string $mailbox mailbox name to rename + * + * @return mixed true on Success/PearError on Failure + * @since 1.0 + */ + function renameMailbox($oldmailbox, $newmailbox) + { + $ret=$this->cmdRename($oldmailbox,$newmailbox); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + + + + + + + + /****************************************************************** + ** ** + ** SUBSCRIPTION METHODS ** + ** ** + ******************************************************************/ + + + + + + /** + * Subscribes to the selected mailbox + * + * @param string $mailbox mailbox name to subscribe + * + * @return mixed true on Success/PearError on Failure + * @since 1.0 + */ + function subscribeMailbox($mailbox = null ) + { + if($mailbox == null){ + $mailbox = $this->getCurrentMailbox(); + } + $ret=$this->cmdSubscribe($mailbox); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + + + + /** + * Removes the subscription to a mailbox + * + * @param string $mailbox mailbox name to unsubscribe + * + * @return mixed true on Success/PearError on Failure + * @since 1.0 + */ + function unsubscribeMailbox($mailbox = null) + { + if($mailbox == null){ + $mailbox = $this->getCurrentMailbox(); + } + $ret=$this->cmdUnsubscribe($mailbox); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + + /** + * Lists the subscription to mailboxes + * + * @param string $mailbox_base mailbox name start the search (see to getMailboxes() ) + * @param string $mailbox_name mailbox name filter the search (see to getMailboxes() ) + * + * @return mixed true on Success/PearError on Failure + * @since 1.0 + */ + + function listsubscribedMailboxes($reference = '' , $restriction_search = 0, $returnAttributes = false) + { + if ( is_bool($restriction_search) ){ + $restriction_search = (int) $restriction_search; + } + + if ( is_int( $restriction_search ) ){ + switch ( $restriction_search ) { + case 0: + $mailbox = "*"; + break; + case 1: + $mailbox = $reference; + $reference = '%'; + break; + case 2: + $mailbox = "%"; + break; + } + }else{ + if ( is_string( $restriction_search ) ){ + $mailbox = $restriction_search; + }else { + return new PEAR_Error("UPS... you "); + } + } + + if( PEAR::isError( $ret=$this->cmdLsub($reference, $mailbox) ) ){ + return $ret; + } + //$ret=$this->cmdLsub($mailbox_base, $mailbox_name); + + + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + + $ret_aux=array(); + if( isset($ret["PARSED"]) ){ + foreach( $ret["PARSED"] as $mbox ){ + if( isset($mbox["EXT"]["LSUB"]["MAILBOX_NAME"]) ){ + if( $returnAttributes){ + $ret_aux[]=array( + 'MAILBOX' => $mbox["EXT"]["LSUB"]["MAILBOX_NAME"], + 'ATTRIBUTES' => $mbox["EXT"]["LSUB"]["NAME_ATTRIBUTES"], + 'HIERACHY_DELIMITER' => $mbox["EXT"]["LSUB"]["HIERACHY_DELIMITER"] + ) ; + }else{ + $ret_aux[]=$mbox["EXT"]["LSUB"]["MAILBOX_NAME"]; + + } + } + } + } + return $ret_aux; + } + + + + + + + + + + + + + /****************************************************************** + ** ** + ** FLAGS METHODS ** + ** ** + ******************************************************************/ + + + + + /** + * Lists the flags of the selected messages + * + * @param mixes $msg_id the message list + * + * @return mixed array on Success/PearError on Failure + * @since 1.0 + */ + function getFlags( $msg_id = null ) + { + // You can also provide an array of numbers to those emails + if( $msg_id != null){ + if(is_array($msg_id)){ + $message_set=$this->_getSearchListFromArray($msg_id); + }else{ + $message_set=$msg_id; + } + }else{ + $message_set="1:*"; + } + + + $ret=$this->cmdFetch($message_set,"FLAGS"); + if(strtoupper($ret["RESPONSE"]["CODE"]) != "OK"){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + $flags=array(); + if(isset($ret["PARSED"])){ + foreach($ret["PARSED"] as $msg_flags){ + if(isset($msg_flags["EXT"]["FLAGS"])){ + $flags[]=$msg_flags["EXT"]["FLAGS"]; + } + } + } + return $flags; + } + + + + + /** + * check the Seen flag + * + * @param mixes $message_nro the message to check + * + * @return mixed true or false if the flag is sert PearError on Failure + * @since 1.0 + */ + function isSeen($message_nro) + { + return $this->hasFlag( $message_nro, "\\Seen" ); + } + + + + + /** + * check the Answered flag + * + * @param mixes $message_nro the message to check + * + * @return mixed true or false if the flag is sert PearError on Failure + * @since 1.0 + */ + function isAnswered($message_nro) + { + return $this->hasFlag( $message_nro, "\\Answered" ); + } + + + + + + /** + * check the flagged flag + * + * @param mixes $message_nro the message to check + * + * @return mixed true or false if the flag is sert PearError on Failure + * @since 1.0 + */ + function isFlagged($message_nro) + { + return $this->hasFlag( $message_nro, "\\Flagged" ); + } + + + + + + + /** + * check the Draft flag + * + * @param mixes $message_nro the message to check + * + * @return mixed true or false if the flag is sert PearError on Failure + * @since 1.0 + */ + function isDraft($message_nro) + { + return $this->hasFlag( $message_nro, "\\Draft" ); + } + + + + + + + + /** + * check the Deleted flag + * + * @param mixes $message_nro the message to check + * + * @return mixed true or false if the flag is sert PearError on Failure + * @since 1.0 + */ + function isDeleted($message_nro) + { + return $this->hasFlag( $message_nro, "\\Deleted" ); + } + + + + + + + function hasFlag($message_nro,$flag) + { + if ( PEAR::isError( $resp = $this->getFlags( $message_nro ) ) ) { + return $resp; + } + if(isset($resp[0]) ){ + if( is_array( $resp[0] ) ){ + if( in_array( $flag , $resp[0] ) ) + return true; + } + } + return false; + } + + + + + + /****************************************************************** + ** ** + ** MISC METHODS ** + ** ** + ******************************************************************/ + + + + + + + /* + * expunge function. Sends the EXPUNGE command + * + * + * @return bool Success/Failure + */ + function expunge() + { + $ret = $this->cmdExpunge(); + if( strtoupper( $ret["RESPONSE"]["CODE"]) != "OK" ){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + + + + + + /* + * search function. Sends the SEARCH command + * + * + * @return bool Success/Failure + */ + function search($search_list,$uidSearch=false) + { + if($uidSearch){ + $ret = $this->cmdUidSearch($search_list); + }else{ + $ret = $this->cmdSearch($search_list); + } + + if( strtoupper( $ret["RESPONSE"]["CODE"]) != "OK" ){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return $ret["PARSED"]["SEARCH"]["SEARCH_LIST"]; + } + + + + + + + + + /****************************************************************** + ** ** + ** QUOTA METHODS ** + ** ** + ******************************************************************/ + + + + + + + /** + * Returns STORAGE quota details + * @param string $mailbox_name Mailbox to get quota info. + * @return assoc array contaning the quota info on success or PEAR_Error + * + * @access public + * @since 1.0 + */ + function getStorageQuota($mailbox_name = null ) + { + if($mailbox_name == null){ + $mailbox_name = $this->getCurrentMailbox(); + } + + + if ( PEAR::isError( $ret = $this->cmdGetQuota($mailbox_name) ) ) { + return new PEAR_Error($ret->getMessage()); + } + + if( strtoupper( $ret["RESPONSE"]["CODE"]) != "OK" ){ + // if the error is that the user does not have quota set return an array + // and not pear error + if( substr(strtoupper($ret["RESPONSE"]["STR_CODE"]),0,5) == "QUOTA" ){ + return array('USED'=>'NOT SET', 'QMAX'=>'NOT SET'); + } + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + + if( isset( $ret['PARSED']['EXT']['QUOTA']['STORAGE'] ) ){ + return $ret['PARSED']['EXT']['QUOTA']['STORAGE']; + } + return array('USED'=>'NOT SET', 'QMAX'=>'NOT SET'); + } + + + + /** + * Returns MESSAGES quota details + * @param string $mailbox_name Mailbox to get quota info. + * @return assoc array contaning the quota info on success or PEAR_Error + * + * @access public + * @since 1.0 + */ + function getMessagesQuota($mailbox_name = null ) + { + if($mailbox_name == null){ + $mailbox_name = $this->getCurrentMailbox(); + } + + if ( PEAR::isError( $ret = $this->cmdGetQuota($mailbox_name) ) ) { + return new PEAR_Error($ret->getMessage()); + } + + if( strtoupper( $ret["RESPONSE"]["CODE"]) != "OK" ){ + // if the error is that the user does not have quota set return an array + // and not pear error + if( substr(strtoupper($ret["RESPONSE"]["STR_CODE"]),0,5) == "QUOTA" ){ + return array('USED'=>'NOT SET', 'QMAX'=>'NOT SET'); + } + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + + if( isset( $ret['PARSED']['EXT']['QUOTA']['MESSAGES'] ) ){ + return $ret['PARSED']['EXT']['QUOTA']['MESSAGES']; + } + return array('USED'=>'NOT SET', 'QMAX'=>'NOT SET'); + } + + + + + /** + * sets STORAGE quota details + * @param string $mailbox_name Mailbox to get quota info. + * @return true on success or PEAR_Error + * + * @access public + * @since 1.0 + */ + function setStorageQuota($mailbox_name, $quota) + { + if ( PEAR::isError( $ret = $this->cmdSetQuota($mailbox_name,$quota) ) ) { + return new PEAR_Error($ret->getMessage()); + } + if( strtoupper( $ret["RESPONSE"]["CODE"]) != "OK" ){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + + + /** + * sets MESSAGES quota details + * @param string $mailbox_name Mailbox to get quota info. + * @return true on success or PEAR_Error + * + * @access public + * @since 1.0 + */ + function setMessagesQuota($mailbox_name, $quota) + { + if ( PEAR::isError( $ret = $this->cmdSetQuota($mailbox_name,'',$quota) ) ) { + return new PEAR_Error($ret->getMessage()); + } + if( strtoupper( $ret["RESPONSE"]["CODE"]) != "OK" ){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + + + + + + + + /****************************************************************** + ** ** + ** ACL METHODS ** + ** ** + ******************************************************************/ + + + + + + + /** + * get the Access Control List details + * @param string $mailbox_name Mailbox to get ACL info. + * @return string on success or PEAR_Error + * + * @access public + * @since 1.0 + */ + function getACL($mailbox_name = null ) + { + if($mailbox_name == null){ + $mailbox_name = $this->getCurrentMailbox(); + } + if ( PEAR::isError( $ret = $this->cmdGetACL($mailbox_name) ) ) { + return new PEAR_Error($ret->getMessage()); + } + + if( strtoupper( $ret["RESPONSE"]["CODE"]) != "OK" ){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + + if( isset($ret['PARSED']['USERS']) ){ + return $ret['PARSED']['USERS']; + }else{ + return false; + } + } + + + + + + + + + /** + * Set ACL on a mailbox + * + * @param string $mailbox_name the mailbox + * @param string $user user to set the ACL + * @param string $acl ACL list + * @return mixed True on success, or PEAR_Error on false + * + * @access public + * @since 1.0 + */ + function setACL($mailbox_name, $user, $acl) + { + if ( PEAR::isError( $ret = $this->cmdSetACL($mailbox_name, $user, $acl) ) ) { + return new PEAR_Error($ret->getMessage()); + } + if( strtoupper( $ret["RESPONSE"]["CODE"]) != "OK" ){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + /** + * deletes the ACL on a mailbox + * + * @param string $mailbox_name the mailbox + * @param string $user user to set the ACL + * @return mixed True on success, or PEAR_Error on false + * + * @access public + * @since 1.0 + */ + function deleteACL($mailbox_name, $user) + { + if ( PEAR::isError( $ret = $this->cmdDeleteACL($mailbox_name, $user) ) ) { + return new PEAR_Error($ret->getMessage()); + } + if( strtoupper( $ret["RESPONSE"]["CODE"]) != "OK" ){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + return true; + } + + + + /** + * returns the rights that the user logged on has on the mailbox + * this method can be used by any user, not only the administrator + * + * @param string $mailbox_name the mailbox to query rights + * @return mixed string contailing the list of rights on success, or PEAR_Error on failure + * + * @access public + * @since 1.0 + */ + function getMyRights($mailbox_name = null) + { + + if($mailbox_name == null){ + $mailbox_name = $this->getCurrentMailbox(); + } + + + if ( PEAR::isError( $ret = $this->cmdMyRights($mailbox_name) ) ) { + return new PEAR_Error($ret->getMessage()); + } + if( strtoupper( $ret["RESPONSE"]["CODE"]) != "OK" ){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + + if(isset($ret['PARSED']['GRANTED'])){ + return $ret['PARSED']['GRANTED']; + } + + return new PEAR_Error('Bogus response from server!' ); + + } + + + + + + + + + + /** + * returns an array containing the rights that a user logged on has on the mailbox + * this method can be used by any user, not only the administrator + * + * @param string $mailbox_name the mailbox to query rights + * @return mixed string contailing the list of rights on success, or PEAR_Error on failure + * + * @access public + * @since 1.0 + */ + function getACLRights($user,$mailbox_name = null) + { + + if($mailbox_name == null){ + $mailbox_name = $this->getCurrentMailbox(); + } + + + if ( PEAR::isError( $ret = $this->cmdListRights($mailbox_name, $user) ) ) { + return new PEAR_Error($ret->getMessage()); + } + if( strtoupper( $ret["RESPONSE"]["CODE"]) != "OK" ){ + return new PEAR_Error($ret["RESPONSE"]["CODE"] . ", " . $ret["RESPONSE"]["STR_CODE"]); + } + + + if(isset($ret['PARSED']['GRANTED'])){ + return $ret['PARSED']['GRANTED']; + } + + return new PEAR_Error('Bogus response from server!' ); + + } + + + + + + + + + + + + + /****************************************************************** + ** ** + ** ANNOTATEMORE METHODS ** + ** ** + ******************************************************************/ + + + + + + + function setAnnotation($entry, $values, $mailbox_name = null ) + { + if($mailbox_name == null){ + $mailbox_name = $this->getCurrentMailbox(); + } + + if (PEAR::isError($ret = $this->cmdSetAnnotation($mailbox_name, $entry, $values))) { + return new PEAR_Error($ret->getMessage()); + } + if (strtoupper($ret['RESPONSE']['CODE']) != 'OK') { + return new PEAR_Error($ret['RESPONSE']['CODE'] . ', ' . $ret['RESPONSE']['STR_CODE']); + } + return true; + } + + + + + + + function deleteAnnotation($entry, $values, $mailbox_name = null ) + { + if($mailbox_name == null){ + $mailbox_name = $this->getCurrentMailbox(); + } + + if (PEAR::isError($ret = $this->cmdDeleteAnnotation($mailbox_name, $entry, $values))) { + return new PEAR_Error($ret->getMessage()); + } + if (strtoupper($ret['RESPONSE']['CODE']) != 'OK') { + return new PEAR_Error($ret['RESPONSE']['CODE'] . ', ' . $ret['RESPONSE']['STR_CODE']); + } + return true; + } + + + + + + + function getAnnotation($entries, $values, $mailbox_name = null) + { + if($mailbox_name == null){ + $mailbox_name = $this->getCurrentMailbox(); + } + if (!is_array($entries)) { + $entries = array($entries); + } + if (!is_array($values)) { + $values = array($values); + } + + if (PEAR::isError($ret = $this->cmdGetAnnotation($mailbox_name, $entries, $values))) { + return new PEAR_Error($ret->getMessage()); + } + if (strtoupper($ret['RESPONSE']['CODE']) != 'OK') { + return new PEAR_Error($ret['RESPONSE']['CODE'] . ', ' . $ret['RESPONSE']['STR_CODE']); + } + $ret_aux = array(); + if (isset($ret['PARSED'])) { + foreach ($ret['PARSED'] as $mbox) { + $rawvalues = $mbox['EXT']['ATTRIBUTES']; + $values = array(); + for ($i = 0; $i < count($rawvalues); $i += 2) { + $values[$rawvalues[$i]] = $rawvalues[$i + 1]; + } + $mbox['EXT']['ATTRIBUTES'] = $values; + $ret_aux[] = $mbox['EXT']; + } + } + if (count($ret_aux) == 1 && $ret_aux[0]['MAILBOX'] == $mailbox_name) { + if (count($entries) == 1 && $ret_aux[0]['ENTRY'] == $entries[0]) { + if (count($ret_aux[0]['ATTRIBUTES']) == 1 && count($values) == 1) { + $attrs = array_keys($ret_aux[0]['ATTRIBUTES']); + $vals = array_keys($values); + if ($attrs[0] == $vals[0]) { + return $ret_aux[0]['ATTRIBUTES'][$attrs[0]]; + } + } + } + } + return $ret_aux; + } + + + + + + + + + + /* + * Transform an array to a list to be used in the cmdFetch method + * + */ + function _getSearchListFromArray($arr){ + + $txt=implode(',' , $arr); + return $txt; + } + + + + + + + + + /***************************************************** + Net_POP3 Compatibility functions: + + Warning!!! + Those functions could dissapear in the future + + *********************************************************/ + + + + + function getSize(){ + return $this->getMailboxSize(); + } + + + function numMsg($mailbox = null){ + return $this->getNumberOfMessages($mailbox); + } + + + + /* + * Returns the entire message with given message number. + * + * @param $msg_id Message number + * @return mixed Either entire message or false on error + */ + function getMsg($msg_id) + { + $ret=$this->getMessages($msg_id,false); + // false means that getMessages() must not use the msg number as array key + if(isset($ret[0])){ + return $ret[0]; + }else{ + return $ret; + } + + } + + + function getListing($msg_id = null) + { + return $this->getMessagesList($msg_id); + } + + + function deleteMsg($msg_id){ + return $this->deleteMessages($msg_id); + } + + + + + +} +?> diff --git a/thirdparty/pear/Net/IMAPProtocol.php b/thirdparty/pear/Net/IMAPProtocol.php new file mode 100644 index 0000000..5c555f6 --- /dev/null +++ b/thirdparty/pear/Net/IMAPProtocol.php @@ -0,0 +1,3048 @@ + | +// +----------------------------------------------------------------------+ +require_once 'Net/Socket.php'; + + + +/** + * Provides an implementation of the IMAP protocol using PEAR's + * Net_Socket:: class. + * + * @package Net_IMAP/Protocol + * @author Damian Alejandro Fernandez Sosa + */ +class Net_IMAPProtocol { + + + /** + * The auth methods this class support + * @var array + */ + var $supportedAuthMethods=array('DIGEST-MD5', 'CRAM-MD5', 'LOGIN'); + + + /** + * The auth methods this class support + * @var array + */ + var $supportedSASLAuthMethods=array('DIGEST-MD5', 'CRAM-MD5'); + + /** + * _serverAuthMethods + * @var boolean + */ + var $_serverAuthMethods = null; + + + /** + * The the current mailbox + * @var string + */ + var $currentMailbox = "INBOX" ; + + + /** + * The socket resource being used to connect to the IMAP server. + * @var resource + */ + var $_socket = null; + + /** + * To allow class debuging + * @var boolean + */ + var $_debug = false; + + var $dbgDialog = ''; + + /** + * Command Number + * @var int + */ + var $_cmd_counter = 1; + + + /** + * Command Number for IMAP commands + * @var int + */ + var $_lastCmdID = 1; + + /** + * Command Number + * @var boolean + */ + var $_unParsedReturn = false; + + + + /** + * _connected: checks if there is a connection made to a imap server or not + * @var boolean + */ + var $_connected = false; + /** + * Capabilities + * @var boolean + */ + var $_serverSupportedCapabilities = null; + + + + /** + * Use UTF-7 funcionallity + * @var boolean + */ + //var $_useUTF_7 = false; + var $_useUTF_7 = true; + + + + /** + * Constructor + * + * Instantiates a new Net_IMAP object. + * + * @since 1.0 + */ + function Net_IMAPProtocol() + { + $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) { + foreach($this->supportedSASLAuthMethods as $SASLMethod){ + $pos = array_search( $SASLMethod , $this->supportedAuthMethods); + unset($this->supportedAuthMethods[$pos]); + } + } + + } + + + /** + * Attempt to connect to the IMAP 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 cmdConnect($host= "localhost" , $port = 143) + { + if( $this->_connected ){ + return new PEAR_Error( 'already connected, logout first!' ); + } + if ( PEAR::isError( $this->_socket->connect( $host , $port ) ) ) { + return new PEAR_Error( 'unable to open socket' ); + } + if ( PEAR::isError( $this->_getRawResponse() ) ) { + return new PEAR_Error( 'unable to open socket' ); + } + $this->_connected = true; + return true; + } + + + /** + * get the cmd ID + * + * @return string Returns the CmdID and increment the counter + * + * @access private + * @since 1.0 + */ + function _getCmdId() + { + $this->_lastCmdID = "A000" . $this->_cmd_counter ; + $this->_cmd_counter++; + return $this->_lastCmdID; + } + + + /** + * get the last cmd ID + * + * @return string Returns the last cmdId + * + * @access public + * @since 1.0 + */ + function getLastCmdId() + { + return $this->_lastCmdID; + } + + + + + /** + * get current mailbox name + * + * @return string Returns the current mailbox + * + * @access public + * @since 1.0 + */ + function getCurrentMailbox() + { + return $this->currentMailbox; + } + + + + + /** + * Sets the debuging information on or off + * + * @param boolean True or false + * + * @return nothing + * @access public + * @since 1.0 + */ + function setDebug($debug = true) + { + $this->_debug = $debug; + } + + + function getDebugDialog() + { + return $this->dbgDialog; + } + + + + /** + * 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.0 + */ + function _send($data) + { + if($this->_socket->eof() ){ + return new PEAR_Error( 'Failed to write to socket: (connection lost!) ' ); + } + if ( PEAR::isError( $error = $this->_socket->write( $data ) ) ) { + + return new PEAR_Error( 'Failed to write to socket: ' . + $error->getMessage() ); + } + + if( $this->_debug ){ + // C: means this data was sent by the client (this class) + echo "C: $data"; + $this->dbgDialog.="C: $data"; + } + return true; + } + + /** + * Receive the given string of data from the server. + * + * @return mixed a line of response on success or a PEAR_Error object on failure. + * + * @access private + * @since 1.0 + */ + function _recvLn() + { + + if (PEAR::isError( $this->lastline = $this->_socket->gets( 8192 ) ) ) { + return new PEAR_Error('Failed to write to socket: ' . + $this->lastline->getMessage() ); + } + if($this->_debug){ + // S: means this data was sent by the IMAP Server + echo "S: " . $this->lastline . "" ; + $this->dbgDialog.="S: " . $this->lastline . "" ; + } + if( $this->lastline == '' ){ + return new PEAR_Error('Failed to receive from the socket: ' ); + } + return $this->lastline; + } + + + + + + /** + * 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 IMAP server. + * + * @param string $commandId The IMAP cmdID to send to the server. + * @param string $command The IMAP 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.0 + */ + function _putCMD($commandId , $command, $args = '') + { + if ( !empty( $args ) ) { + return $this->_send( $commandId . " " . $command . ' ' . $args . "\r\n" ); + } + return $this->_send( $commandId . " " . $command . "\r\n" ); + } + + + + + + + /** + * Get a response from the server with an optional string of commandID. + * A carriage return / linefeed (CRLF) sequence will be appended to each + * command string before it is sent to the IMAP server. + * + * @param string $commandid The IMAP commandid retrive from the server. + * + * @return string The result response. + * + * @access private + */ + function _getRawResponse($commandId = '*') + { + $arguments = ''; + while ( !PEAR::isError( $this->_recvLn() ) ) { + $reply_code = strtok( $this->lastline , ' ' ); + $arguments.= $this->lastline; + if ( !(strcmp( $commandId , $reply_code ) ) ) { + return $arguments; + } + } + return $arguments; + } + + + + + + /** + * get the "returning of the unparsed response" feature status + * + * @return boolean return if the unparsed response is returned or not + * + * @access public + * @since 1.0 + * + */ + function getUnparsedResponse() + { + return $this->_unParsedReturn; + } + + + + + + + /** + * set the "returning of the unparsed response" feature on or off + * + * @param boolean $status: true: feature is on + * @return nothing + * + * @access public + * @since 1.0 + */ + function setUnparsedResponse($status) + { + $this->_unParsedReturn = $status; + } + + + + + + + /** + * Attempt to login to the iMAP server. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * + * @return array Returns an array containing the response + * + * @access public + * @since 1.0 + */ + function cmdLogin($uid , $pwd) + { + $param="\"$uid\" \"$pwd\""; + return $this->_genericCommand('LOGIN', $param); + } + + + + + + + /** + * Attempt to authenticate to the iMAP server. + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * @param string The cmdID. + * + * @return array Returns an array containing the response + * + * @access public + * @since 1.0 + */ + function cmdAuthenticate($uid , $pwd , $userMethod = null) + { + + if( !$this->_connected ){ + return new PEAR_Error('not connected!'); + } + + $cmdid = $this->_getCmdId(); + + + if ( PEAR::isError( $method = $this->_getBestAuthMethod($userMethod) ) ) { + return $method; + } + + + switch ($method) { + case 'DIGEST-MD5': + $result = $this->_authDigest_MD5( $uid , $pwd , $cmdid ); + break; + case 'CRAM-MD5': + $result = $this->_authCRAM_MD5( $uid , $pwd ,$cmdid ); + break; + case 'LOGIN': + $result = $this->_authLOGIN( $uid , $pwd , $cmdid ); + break; + + default : + $result = new PEAR_Error( "$method is not a supported authentication method" ); + break; + } + + $args = $this->_getRawResponse( $cmdid ); + return $this->_genericImapResponseParser( $args , $cmdid ); + + } + + + + + + + + + /* Authenticates the user using the DIGEST-MD5 method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * @param string The cmdID. + * + * @return array Returns an array containing the response + * + * @access private + * @since 1.0 + */ + function _authDigest_MD5($uid , $pwd , $cmdid) + { + + if ( PEAR::isError($error = $this->_putCMD( $cmdid ,"AUTHENTICATE" , "DIGEST-MD5") ) ) { + return $error; + } + + if (PEAR::isError( $args = $this->_recvLn() ) ) { + return $args; + } + + $this->_getNextToken( $args , $plus ); + + $this->_getNextToken( $args , $space ); + + $this->_getNextToken( $args , $challenge ); + + $challenge = base64_decode( $challenge ); + + $digest = &Auth_SASL::factory('digestmd5'); + + $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge,"localhost", "imap")); + + if ( PEAR::isError( $error = $this->_send("$auth_str\r\n"))) { + return $error; + } + + if ( PEAR::isError( $args = $this->_recvLn() )) { + return $args; + } + /* + * We don't use the protocol's third step because IMAP doesn't allow + * subsequent authentication, so we just silently ignore it. + */ + if ( PEAR::isError( $error = $this->_send( "\r\n" ) ) ) { + return $error; + } + } + + + + + + + + + /* Authenticates the user using the CRAM-MD5 method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * @param string The cmdID. + * + * @return array Returns an array containing the response + * + * @access private + * @since 1.0 + */ + function _authCRAM_MD5($uid, $pwd, $cmdid) + { + + + + if ( PEAR::isError($error = $this->_putCMD( $cmdid ,"AUTHENTICATE" , "CRAM-MD5") ) ) { + return $error; + } + + if ( PEAR::isError( $args = $this->_recvLn() ) ) { + return $args; + } + + $this->_getNextToken( $args , $plus ); + + $this->_getNextToken( $args , $space ); + + $this->_getNextToken( $args , $challenge ); + + $challenge = base64_decode( $challenge ); + + $cram = &Auth_SASL::factory('crammd5'); + + $auth_str = base64_encode( $cram->getResponse( $uid , $pwd , $challenge ) ); + + if ( PEAR::isError( $error = $this->_send( $auth_str."\r\n" ) ) ) { + return $error; + } + + } + + + + + + + + + + /* Authenticates the user using the LOGIN method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * @param string The cmdID. + * + * @return array Returns an array containing the response + * + * @access private + * @since 1.0 + */ + function _authLOGIN($uid, $pwd, $cmdid) + { + + if (PEAR::isError($error = $this->_putCMD($cmdid,"AUTHENTICATE", "LOGIN"))) { + return $error; + } + + if (PEAR::isError($args = $this->_recvLn() )) { + return $args; + } + + $this->_getNextToken( $args , $plus ); + + $this->_getNextToken( $args , $space ); + + $this->_getNextToken( $args , $challenge ); + + $challenge = base64_decode( $challenge ); + + $auth_str = base64_encode( "$uid" ); + + if ( PEAR::isError( $error = $this->_send( $auth_str."\r\n" ) ) ) { + return $error; + } + + if (PEAR::isError( $args = $this->_recvLn() ) ) { + return $args; + } + + $auth_str = base64_encode( "$pwd" ); + + if ( PEAR::isError($error = $this->_send( $auth_str."\r\n" ) ) ) { + return $error; + } + + } + + + + + + + + + /** + * Returns the name of the best authentication method that the server + * has advertised. + * + * @param string if !=null,authenticate with this method ($userMethod). + * + * @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.0 + */ + function _getBestAuthMethod($userMethod = null) + { + $this->cmdCapability(); + + if($userMethod != null ){ + + $methods = array(); + + $methods[] = $userMethod; + + }else{ + $methods = $this->supportedAuthMethods; + } + + if( ($methods != null) && ($this->_serverAuthMethods != null)){ + foreach ( $methods as $method ) { + if ( in_array( $method , $this->_serverAuthMethods ) ) { + return $method; + } + } + $serverMethods=implode(',' ,$this->_serverAuthMethods); + $myMethods=implode(',' ,$this->supportedAuthMethods); + return new PEAR_Error("$method NOT supported authentication method!. This IMAP server " . + "supports these methods: $serverMethods, but I support $myMethods"); + }else{ + return new PEAR_Error("This IMAP server don't support any Auth methods"); + } + } + + + + + + + + + + /** + * Attempt to disconnect from the iMAP server. + * + * @return array Returns an array containing the response + * + * @access public + * @since 1.0 + */ + function cmdLogout() + { + if( !$this->_connected ){ + return new PEAR_Error( 'not connected!' ); + } + + $cmdid = $this->_getCmdId(); + if ( PEAR::isError( $error = $this->_putCMD( $cmdid , 'LOGOUT' ) ) ) { + return $error; + } + if ( PEAR::isError($args = $this->_getRawResponse() ) ) { + return $args; + } + if (PEAR::isError( $this->_socket->disconnect() ) ) { + return new PEAR_Error('socket disconnect failed'); + } + + return $args; + // not for now + //return $this->_genericImapResponseParser($args,$cmdid); + + } + + + + + + /** + * Send the NOOP command. + * + * @return array Returns an array containing the response + * + * @access public + * @since 1.0 + */ + function cmdNoop() + { + return $this->_genericCommand('NOOP'); + } + + + + + + + + + + /** + * Send the CHECK command. + * + * @return array Returns an array containing the response + * + * @access public + * @since 1.0 + */ + function cmdCheck() + { + return $this->_genericCommand('CHECK'); + } + + + + + + + + + + + /** + * Send the Select Mailbox Command + * + * @param string The mailbox to select. + * + * @return array Returns an array containing the response + * + * @access public + * @since 1.0 + */ + function cmdSelect($mailbox) + { + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) ); + if( !PEAR::isError( $ret= $this->_genericCommand('SELECT', $mailbox_name) ) ){ + $this->currentMailbox = $mailbox; + } + return $ret; + } + + + + + + + + + + + /** + * Send the EXAMINE Mailbox Command + * + * @param string The mailbox to examine. + * @return array Returns an array containing the response + * + * @access public + * @since 1.0 + */ + function cmdExamine($mailbox) + { + + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) ); + $ret=$this->_genericCommand('EXAMINE', $mailbox_name); + $parsed=''; + if(isset( $ret["PARSED"] ) ){ + for($i=0;$i$parsed,"RESPONSE"=>$ret["RESPONSE"]); + } + + + + + + + + /** + * Send the CREATE Mailbox Command + * + * @param string The mailbox to create. + * @return array Returns an array containing the response + * + * @access public + * @since 1.0 + */ + function cmdCreate($mailbox) + { + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) ); + return $this->_genericCommand('CREATE', $mailbox_name); + } + + + + + + + + /** + * Send the RENAME Mailbox Command + * + * @param string The old mailbox name. + * @param string The new (renamed) mailbox name. + * + * @return array Returns an array containing the response + * + * @access public + * @since 1.0 + */ + function cmdRename($mailbox, $new_mailbox) + { + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) ); + $new_mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($new_mailbox) ); + return $this->_genericCommand('RENAME', "$mailbox_name $new_mailbox_name" ); + } + + + + + + + + + /** + * Send the DELETE Mailbox Command + * + * @param string The mailbox name to delete. + * + * @return array Returns an array containing the response + * + * @access public + * @since 1.0 + */ + function cmdDelete($mailbox) + { + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) ); + return $this->_genericCommand('DELETE', $mailbox_name); + } + + + + + + + + /** + * Send the SUSCRIBE Mailbox Command + * + * @param string The mailbox name to suscribe. + * + * @return array Returns an array containing the response + * + * @access public + * @since 1.0 + */ + function cmdSubscribe($mailbox) + { + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) ); + return $this->_genericCommand('SUBSCRIBE', $mailbox_name ); + } + + + + + + + + + /** + * Send the UNSUSCRIBE Mailbox 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 cmdUnsubscribe($mailbox) + { + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) ); + return $this->_genericCommand('UNSUBSCRIBE', $mailbox_name ); + } + + + + + + + + + /** + * Send the FETCH 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 cmdFetch($msgset, $fetchparam) + { + return $this->_genericCommand('FETCH' , "$msgset $fetchparam" ); + } + + + + + + + + /** + * Send the CAPABILITY 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 cmdCapability() + { + $ret = $this->_genericCommand( 'CAPABILITY' ); + + if(isset( $ret["PARSED"] ) ){ + $ret["PARSED"]=$ret["PARSED"][0]["EXT"]["CAPABILITY"]; + //fill the $this->_serverAuthMethods and $this->_serverSupportedCapabilities arrays + foreach( $ret["PARSED"]["CAPABILITIES"] as $auth_method ){ + if( strtoupper( substr( $auth_method , 0 ,5 ) ) == "AUTH=" ) + $this->_serverAuthMethods[] = substr( $auth_method , 5 ); + } + // Keep the capabilities response to use ir later + $this->_serverSupportedCapabilities = $ret["PARSED"]["CAPABILITIES"]; + } + + return $ret; + } + + + + + + + + + + + /** + * Send the STATUS Mailbox Command + * + * @param string $mailbox the mailbox name + * @param string $request the request status it could be: + * MESSAGES | RECENT | UIDNEXT + * UIDVALIDITY | UNSEEN + * @return array Returns a Parsed Response + * + * @access public + * @since 1.0 + */ + function cmdStatus($mailbox, $request) + { + + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) ); + + if( $request!="MESSAGES" && $request!="RECENT" && $request!="UIDNEXT" && + $request!="UIDVALIDITY" && $request!="UNSEEN" ){ + // TODO: fix this error! + $this->_prot_error("request '$request' is invalid! see RFC2060!!!!" , __LINE__ , __FILE__, false ); + } + $ret = $this->_genericCommand('STATUS', "$mailbox_name ($request)" ); + if(isset( $ret["PARSED"] ) ){ + $ret['PARSED']=$ret["PARSED"][count($ret['PARSED'])-1]["EXT"]; + } + return $ret; + } + + + + + + + /** + * Send the LIST 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 cmdList($mailbox_base, $mailbox) + { + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) ); + $mailbox_base=sprintf("\"%s\"",$this->utf_7_encode($mailbox_base) ); + return $this->_genericCommand('LIST', "$mailbox_base $mailbox_name" ); + } + + + + + + + /** + * Send the LSUB 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 cmdLsub($mailbox_base, $mailbox) + { + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) ); + $mailbox_base=sprintf("\"%s\"",$this->utf_7_encode($mailbox_base) ); + return $this->_genericCommand('LSUB', "$mailbox_base $mailbox_name" ); + } + + + + + + + /** + * Send the APPEND 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 cmdAppend($mailbox, $msg , $flags_list = '' ,$time = '') + { + if(!$this->_connected){ + return new PEAR_Error('not connected!'); + } + + + $cmdid=$this->_getCmdId(); + $msg_size=strlen($msg); + + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) ); + // TODO: + // Falta el codigo para que flags list y time hagan algo!! + if( $this->hasCapability( "LITERAL+" ) == true ){ + $param=sprintf("%s %s%s{%s+}\r\n%s",$mailbox_name,$flags_list,$time,$msg_size,$msg); + if (PEAR::isError($error = $this->_putCMD($cmdid , 'APPEND' , $param ) ) ) { + return $error; + } + }else{ + $param=sprintf("%s %s%s{%s}\r\n",$mailbox_name,$flags_list,$time,$msg_size); + if (PEAR::isError($error = $this->_putCMD($cmdid , 'APPEND' , $param ) ) ) { + return $error; + } + if (PEAR::isError($error = $this->_recvLn() ) ) { + return $error; + } + + if (PEAR::isError($error = $this->_send( $msg ) ) ) { + return $error; + } + } + + + $args=$this->_getRawResponse($cmdid); + $ret = $this->_genericImapResponseParser($args,$cmdid); + return $ret; + } + + + + /** + * Send the CLOSE 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 cmdClose() + { + return $this->_genericCommand('CLOSE'); + } + + + + + + + /** + * Send the EXPUNGE 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 cmdExpunge() + { + $ret=$this->_genericCommand('EXPUNGE'); + + if(isset( $ret["PARSED"] ) ){ + $parsed=$ret["PARSED"]; + unset($ret["PARSED"]); + foreach($parsed as $command){ + if( strtoupper($command["COMMAND"]) == 'EXPUNGE' ){ + $ret["PARSED"][$command["COMMAND"]][]=$command["NRO"]; + }else{ + $ret["PARSED"][$command["COMMAND"]]=$command["NRO"]; + } + } + } + return $ret; + } + + + + + + + + /** + * Send the SEARCH 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 cmdSearch($search_cmd) + { + /* if($_charset != '' ) + $_charset = "[$_charset] "; + $param=sprintf("%s%s",$charset,$search_cmd); + */ + $ret = $this->_genericCommand('SEARCH', $search_cmd ); + if(isset( $ret["PARSED"] ) ){ + $ret["PARSED"]=$ret["PARSED"][0]["EXT"]; + } + return $ret; + } + + + + + + + /** + * Send the STORE command. + * + * @param string $message_set the sessage_set + * @param string $dataitem: the way we store the flags + * FLAGS: replace the flags whith $value + * FLAGS.SILENT: replace the flags whith $value but don't return untagged responses + * + * +FLAGS: Add the flags whith $value + * +FLAGS.SILENT: Add the flags whith $value but don't return untagged responses + * + * -FLAGS: Remove the flags whith $value + * -FLAGS.SILENT: Remove the flags whith $value but don't return untagged responses + * + * @param string $value + * @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 cmdStore($message_set, $dataitem, $value) + { + /* As said in RFC2060... + C: A003 STORE 2:4 +FLAGS (\Deleted) + S: * 2 FETCH FLAGS (\Deleted \Seen) + S: * 3 FETCH FLAGS (\Deleted) + S: * 4 FETCH FLAGS (\Deleted \Flagged \Seen) + S: A003 OK STORE completed + */ + if( $dataitem!="FLAGS" && $dataitem!="FLAGS.SILENT" && $dataitem!="+FLAGS" && + $dataitem!="+FLAGS.SILENT" && $dataitem!="-FLAGS" && $dataitem!="-FLAGS.SILENT" ){ + $this->_prot_error("dataitem '$dataitem' is invalid! see RFC2060!!!!" , __LINE__ , __FILE__ ); + } + $param=sprintf("%s %s (%s)",$message_set,$dataitem,$value); + return $this->_genericCommand('STORE', $param ); + } + + + + + + + + /** + * Send the COPY 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 cmdCopy($message_set, $mailbox) + { + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) ); + return $this->_genericCommand('COPY', sprintf("%s %s",$message_set,$mailbox_name) ); + } + + + + + + + + + + + + + + function cmdUidFetch($msgset, $fetchparam) + { + return $this->_genericCommand('UID FETCH', sprintf("%s %s",$msgset,$fetchparam) ); + } + + + + + + + + + function cmdUidCopy($message_set, $mailbox) + { + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox) ); + return $this->_genericCommand('UID COPY', sprintf("%s %s",$message_set,$mailbox_name) ); + } + + + + + + + + + /** + * Send the UID STORE command. + * + * @param string $message_set the sessage_set + * @param string $dataitem: the way we store the flags + * FLAGS: replace the flags whith $value + * FLAGS.SILENT: replace the flags whith $value but don't return untagged responses + * + * +FLAGS: Add the flags whith $value + * +FLAGS.SILENT: Add the flags whith $value but don't return untagged responses + * + * -FLAGS: Remove the flags whith $value + * -FLAGS.SILENT: Remove the flags whith $value but don't return untagged responses + * + * @param string $value + * @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 cmdUidStore($message_set, $dataitem, $value) + { + /* As said in RFC2060... + C: A003 STORE 2:4 +FLAGS (\Deleted) + S: * 2 FETCH FLAGS (\Deleted \Seen) + S: * 3 FETCH FLAGS (\Deleted) + S: * 4 FETCH FLAGS (\Deleted \Flagged \Seen) + S: A003 OK STORE completed + */ + if( $dataitem!="FLAGS" && $dataitem!="FLAGS.SILENT" && $dataitem!="+FLAGS" && + $dataitem!="+FLAGS.SILENT" && $dataitem!="-FLAGS" && $dataitem!="-FLAGS.SILENT" ){ + $this->_prot_error("dataitem '$dataitem' is invalid! see RFC2060!!!!" , __LINE__ , __FILE__ ); + } + return $this->_genericCommand('UID STORE', sprintf("%s %s (%s)",$message_set,$dataitem,$value) ); + } + + + + + + + + + + + /** + * Send the SEARCH 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 cmdUidSearch($search_cmd) + { + $ret=$this->_genericCommand('UID SEARCH', sprintf("%s",$search_cmd) ); + if(isset( $ret["PARSED"] ) ){ + $ret["PARSED"]=$ret["PARSED"][0]["EXT"]; + } + return $ret; + } + + + + + + + + + + + + /** + * Send the X 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 cmdX($atom, $parameters) + { + return $this->_genericCommand("X$atom", $parameters ); + } + + + + + + + + +/******************************************************************** +*** +*** HERE ENDS the RFC2060 IMAPS FUNCTIONS +*** AND BEGIN THE EXTENSIONS FUNCTIONS +*** +********************************************************************/ + + + + + + + +/******************************************************************** +*** RFC2087 IMAP4 QUOTA extension BEGINS HERE +********************************************************************/ + + + /** + * Send the GETQUOTA command. + * + * @param string $mailbox_name the mailbox name to query for quota data + * @return mixed Returns a PEAR_Error with an error message on any + * kind of failure, or quota data on success + * @access public + * @since 1.0 + */ + + function cmdGetQuota($mailbox_name) + { + + + //Check if the IMAP server has QUOTA support + if( ! $this->hasQuotaSupport() ){ + return new PEAR_Error("This IMAP server does not support QUOTA's! "); + } + $mailbox_name=sprintf("%s",$this->utf_7_encode($mailbox_name) ); + $ret = $this->_genericCommand('GETQUOTA', $mailbox_name ); + if(isset( $ret["PARSED"] ) ){ + // remove the array index because the quota response returns only 1 line of output + $ret['PARSED']=$ret["PARSED"][0]; + } + return $ret; + } + + + /** + * Send the GETQUOTAROOT command. + * + * @param string $mailbox_name the mailbox name to query for quota data + * @return mixed Returns a PEAR_Error with an error message on any + * kind of failure, or quota data on success + * @access public + * @since 1.0 + */ + + function cmdGetQuotaRoot($mailbox_name) + { + //Check if the IMAP server has QUOTA support + if( ! $this->hasQuotaSupport() ){ + return new PEAR_Error("This IMAP server does not support QUOTA's! "); + } + $mailbox_name=sprintf("%s",$this->utf_7_encode($mailbox_name) ); + $ret = $this->_genericCommand('GETQUOTAROOT', $mailbox_name ); + + if(isset( $ret["PARSED"] ) ){ + // remove the array index because the quota response returns only 1 line of output + $ret['PARSED']=$ret["PARSED"][0]; + } + return $ret; + } + + + + + /** + * Send the SETQUOTA command. + * + * @param string $mailbox_name the mailbox name to query for quota data + * @param string $storageQuota sets the max number of bytes this mailbox can handle + * @param string $messagesQuota sets the max number of messages this mailbox can handle + * @return mixed Returns a PEAR_Error with an error message on any + * kind of failure, or quota data on success + * @access public + * @since 1.0 + */ +// TODO: implement the quota by number of emails!! + function cmdSetQuota($mailbox_name, $storageQuota = null ,$messagesQuota = null ) + { + //Check if the IMAP server has QUOTA support + if( ! $this->hasQuotaSupport() ){ + return new PEAR_Error("This IMAP server does not support QUOTA's! "); + } + + if( ($messagesQuota == null) && ( $storageQuota == null) ){ + return new PEAR_Error('$storageQuota and $messagesQuota parameters can\'t be both null if you want to use quota'); + } + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) ); + //Make the command request + $param=sprintf("%s (",$mailbox_name); + if($storageQuota != null ){ + $param=sprintf("%sSTORAGE %s",$param,$storageQuota); + if( $messagesQuota != null ){ + //if we have both types of quota on the same call we must append an space between + // those parameters + $param=sprintf("%s ",$param); + } + } + if($messagesQuota != null ){ + $param=sprintf("%sMESSAGES %s",$param,$messagesQuota); + + } + $param=sprintf("%s)",$param); + + return $this->_genericCommand('SETQUOTA', $param ); + } + + + + /** + * Send the SETQUOTAROOT command. + * + * @param string $mailbox_name the mailbox name to query for quota data + * @param string $storageQuota sets the max number of bytes this mailbox can handle + * @param string $messagesQuota sets the max number of messages this mailbox can handle + * @return mixed Returns a PEAR_Error with an error message on any + * kind of failure, or quota data on success + * @access public + * @since 1.0 + */ + function cmdSetQuotaRoot($mailbox_name, $storageQuota = null ,$messagesQuota = null) + { + //Check if the IMAP server has QUOTA support + if( ! $this->hasQuotaSupport() ){ + return new PEAR_Error("This IMAP server does not support QUOTA's! "); + } + + if( ($messagesQuota == null) && ( $storageQuota == null) ){ + return new PEAR_Error('$storageQuota and $messagesQuota parameters can\'t be both null if you want to use quota'); + } + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) ); + //Make the command request + $param=sprintf("%s (",$mailbox_name); + if($storageQuota != null ){ + $param=sprintf("%sSTORAGE %s",$param,$storageQuota); + if( $messagesQuota != null ){ + //if we have both types of quota on the same call we must append an space between + // those parameters + $param=sprintf("%s ",$param); + } + } + if($messagesQuota != null ){ + $param=sprintf("%sMESSAGES %s",$param,$messagesQuota); + + } + $param=sprintf("%s)",$param); + + return $this->_genericCommand('SETQUOTAROOT', $param ); + } + + + +/******************************************************************** +*** RFC2087 IMAP4 QUOTA extension ENDS HERE +********************************************************************/ + + + + + + +/******************************************************************** +*** RFC2086 IMAP4 ACL extension BEGINS HERE +********************************************************************/ + + + + + function cmdSetACL($mailbox_name, $user, $acl) + { + + //Check if the IMAP server has ACL support + if( ! $this->hasAclSupport() ){ + return new PEAR_Error("This IMAP server does not support ACL's! "); + } + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) ); + $user_name=sprintf("\"%s\"",$this->utf_7_encode($user) ); + if(is_array($acl)){ + $acl=implode('',$acl); + } + return $this->_genericCommand('SETACL', sprintf("%s %s \"%s\"",$mailbox_name,$user_name,$acl) ); + } + + + + + + + function cmdDeleteACL($mailbox_name, $user) + { + //Check if the IMAP server has ACL support + if( ! $this->hasAclSupport() ){ + return new PEAR_Error("This IMAP server does not support ACL's! "); + } + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) ); + + return $this->_genericCommand('DELETEACL', sprintf("%s \"%s\"",$mailbox_name,$user) ); + } + + + + + + + + + + function cmdGetACL($mailbox_name) + { + //Check if the IMAP server has ACL support + if( ! $this->hasAclSupport() ){ + return new PEAR_Error("This IMAP server does not support ACL's! "); + } + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) ); + $ret = $this->_genericCommand('GETACL', sprintf("%s",$mailbox_name) ); + if(isset( $ret["PARSED"] ) ){ + $ret['PARSED']=$ret["PARSED"][0]["EXT"]; + + } + return $ret; + } + + + + + + + + function cmdListRights($mailbox_name, $user) + { + //Check if the IMAP server has ACL support + if( ! $this->hasAclSupport() ){ + return new PEAR_Error("This IMAP server does not support ACL's! "); + } + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) ); + $ret = $this->_genericCommand('LISTRIGHTS', sprintf("%s \"%s\"",$mailbox_name,$user) ); + if(isset( $ret["PARSED"] ) ){ + $ret["PARSED"]=$ret["PARSED"][0]["EXT"]; + } + return $ret; + } + + + + + + + + + + function cmdMyRights($mailbox_name) + { + //Check if the IMAP server has ACL support + if( ! $this->hasAclSupport() ){ + return new PEAR_Error("This IMAP server does not support ACL's! "); + } + $mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) ); + $ret = $this->_genericCommand('MYRIGHTS', sprintf("%s",$mailbox_name) ); + if(isset( $ret["PARSED"] ) ){ + $ret["PARSED"]=$ret["PARSED"][0]["EXT"]; + } + return $ret; + } + + +/******************************************************************** +*** RFC2086 IMAP4 ACL extension ENDs HERE +********************************************************************/ + + + + + + + + + + + + +/******************************************************************************* +*** draft-daboo-imap-annotatemore-05 IMAP4 ANNOTATEMORE extension BEGINS HERE +********************************************************************************/ + + + + function cmdSetAnnotation($mailbox_name, $entry, $values) + { + // Check if the IMAP server has ANNOTATEMORE support + if(!$this->hasAnnotateMoreSupport()) { + return new PEAR_Error('This IMAP server does not support the ANNOTATEMORE extension!'); + } + if (!is_array($values)) { + return new PEAR_Error('Invalid $values argument passed to cmdSetAnnotation'); + } + + $vallist = ''; + foreach ($values as $name => $value) { + $vallist .= "\"$name\" \"$value\" "; + } + $vallist = rtrim($vallist); + + return $this->_genericCommand('SETANNOTATION', sprintf('"%s" "%s" (%s)', $mailbox_name, $entry, $vallist)); + } + + + + + + + + + + + + + + function cmdDeleteAnnotation($mailbox_name, $entry, $values) + { + // Check if the IMAP server has ANNOTATEMORE support + if(!$this->hasAnnotateMoreSupport()) { + return new PEAR_Error('This IMAP server does not support the ANNOTATEMORE extension!'); + } + if (!is_array($values)) { + return new PEAR_Error('Invalid $values argument passed to cmdDeleteAnnotation'); + } + + $vallist = ''; + foreach ($values as $name) { + $vallist .= "\"$name\" NIL "; + } + $vallist = rtrim($vallist); + + return $this->_genericCommand('SETANNOTATION', sprintf('"%s" "%s" (%s)', $mailbox_name, $entry, $vallist)); + } + + + + + + + + + + + + + function cmdGetAnnotation($mailbox_name, $entries, $values) + { + // Check if the IMAP server has ANNOTATEMORE support + if(!$this->hasAnnotateMoreSupport()) { + return new PEAR_Error('This IMAP server does not support the ANNOTATEMORE extension!'); + } + + $entlist = ''; + + if (!is_array($entries)) { + $entries = array($entries); + } + + foreach ($entries as $name) { + $entlist .= "\"$name\" "; + } + $entlist = rtrim($entlist); + if (count($entries) > 1) { + $entlist = "($entlist)"; + } + + + + $vallist = ''; + if (!is_array($values)) { + $values = array($values); + } + + foreach ($values as $name) { + $vallist .= "\"$name\" "; + } + $vallist = rtrim($vallist); + if (count($values) > 1) { + $vallist = "($vallist)"; + } + + return $this->_genericCommand('GETANNOTATION', sprintf('"%s" %s %s', $mailbox_name, $entlist, $vallist)); + } + + +/***************************************************************************** +*** draft-daboo-imap-annotatemore-05 IMAP4 ANNOTATEMORE extension ENDs HERE +******************************************************************************/ + + + + + + + +/******************************************************************** +*** +*** HERE ENDS THE EXTENSIONS FUNCTIONS +*** AND BEGIN THE AUXILIARY FUNCTIONS +*** +********************************************************************/ + + + + + + /** + * tell if the server has capability $capability + * + * @return true or false + * + * @access public + * @since 1.0 + */ + function getServerAuthMethods() + { + if( $this->_serverAuthMethods == null ){ + $this->cmdCapability(); + return $this->_serverAuthMethods; + } + return false; + } + + + + + + + + /** + * tell if the server has capability $capability + * + * @return true or false + * + * @access public + * @since 1.0 + */ + function hasCapability($capability) + { + if( $this->_serverSupportedCapabilities == null ){ + $this->cmdCapability(); + } + if($this->_serverSupportedCapabilities != null ){ + if( in_array( $capability , $this->_serverSupportedCapabilities ) ){ + return true; + } + } + return false; + } + + + + /** + * tell if the server has Quota support + * + * @return true or false + * + * @access public + * @since 1.0 + */ + function hasQuotaSupport() + { + return $this->hasCapability('QUOTA'); + } + + + + + + /** + * tell if the server has Quota support + * + * @return true or false + * + * @access public + * @since 1.0 + */ + function hasAclSupport() + { + return $this->hasCapability('ACL'); + } + + + + + + /** + * tell if the server has support for the ANNOTATEMORE extension + * + * @return true or false + * + * @access public + * @since 1.0 + */ + function hasAnnotateMoreSupport() + { + return $this->hasCapability('ANNOTATEMORE'); + } + + + + + + + + + + + /** + * Parses the responses like RFC822.SIZE and INTERNALDATE + * + * @param string the IMAP's server response + * + * @return string containing the parsed response + * @access private + * @since 1.0 + */ + + function _parseOneStringResponse(&$str, $line,$file) + { + $this->_parseSpace($str , $line , $file ); + $size = $this->_getNextToken($str,$uid); + return $uid; + } + + + /** + * Parses the FLAG response + * + * @param string the IMAP's server response + * + * @return Array containing the parsed response + * @access private + * @since 1.0 + */ + function _parseFLAGSresponse(&$str) + { + $this->_parseSpace($str , __LINE__ , __FILE__ ); + $params_arr[] = $this->_arrayfy_content($str); + $flags_arr=array(); + for( $i = 0 ; $i < count($params_arr[0]) ; $i++ ){ + $flags_arr[] = $params_arr[0][$i]; + } + return $flags_arr; + } + + + + + + /** + * Parses the BODY response + * + * @param string the IMAP's server response + * + * @return Array containing the parsed response + * @access private + * @since 1.0 + */ + + function _parseBodyResponse(&$str, $command){ + + $this->_parseSpace($str , __LINE__ , __FILE__ ); + while($str[0] != ')' && $str!=''){ + $params_arr[] = $this->_arrayfy_content($str); + } + + return $params_arr; + } + + + + + + + /** + * Makes the content an Array + * + * @param string the IMAP's server response + * + * @return Array containing the parsed response + * @access private + * @since 1.0 + */ + function _arrayfy_content(&$str) + { + $params_arr=array(); + $this->_getNextToken($str,$params); + if($params != '(' ){ + return $params; + } + $this->_getNextToken($str,$params,false,false); + while ( $str != '' && $params != ')'){ + if($params != '' ){ + if($params[0] == '(' ){ + $params=$this->_arrayfy_content( $params ); + } + if($params != ' ' ){ + //I don't remove the colons (") to handle the case of retriving " " + // If I remove the colons the parser will interpret this field as an imap separator (space) + // instead of a valid field so I remove the colons here + if($params=='""'){ + $params=''; + }else{ + if($params[0]=='"'){ + $params=substr($params,1,strlen($params)-2); + } + } + $params_arr[]=$params; + } + }else{ + //if params if empty (for example i'm parsing 2 quotes ("") + // I'll append an array entry to mantain compatibility + $params_arr[]=$params; + } + $this->_getNextToken($str,$params,false,false); + } + return $params_arr; + } + + + + + /** + * Parses the BODY[],BODY[TEXT],.... responses + * + * @param string the IMAP's server response + * + * @return Array containing the parsed response + * @access private + * @since 1.0 + */ + function _parseContentresponse(&$str, $command) + { + $content = ''; + $this->_parseSpace($str , __LINE__ , __FILE__ ); + $size =$this->_getNextToken($str,$content); + return array( "CONTENT"=> $content , "CONTENT_SIZE" =>$size ); + } + + + + + + + + + /** + * Parses the ENVELOPE response + * + * @param string the IMAP's server response + * + * @return Array containing the parsed response + * @access private + * @since 1.0 + */ + function _parseENVELOPEresponse(&$str) + { + $content = ''; + $this->_parseSpace($str , __LINE__ , __FILE__ ); + + $this->_getNextToken($str,$parenthesis); + if( $parenthesis != '(' ){ + $this->_prot_error("must be a '(' but is a '$parenthesis' !!!!" , __LINE__ , __FILE__ ); + } + // Get the email's Date + $this->_getNextToken($str,$date); + + $this->_parseSpace($str , __LINE__ , __FILE__ ); + + // Get the email's Subject: + $this->_getNextToken($str,$subject); + //$subject=$this->decode($subject); + + $this->_parseSpace($str , __LINE__ , __FILE__ ); + + //FROM LIST; + $from_arr = $this->_getAddressList($str); + + $this->_parseSpace($str , __LINE__ , __FILE__ ); + + //"SENDER LIST\n"; + $sender_arr = $this->_getAddressList($str); + + $this->_parseSpace($str , __LINE__ , __FILE__ ); + + //"REPLY-TO LIST\n"; + $reply_to_arr=$this->_getAddressList($str); + + $this->_parseSpace($str , __LINE__ , __FILE__ ); + + //"TO LIST\n"; + $to_arr = $this->_getAddressList($str); + + $this->_parseSpace($str , __LINE__ , __FILE__ ); + + //"CC LIST\n"; + $cc_arr = $this->_getAddressList($str); + + $this->_parseSpace($str , __LINE__ , __FILE__ ); + + //"BCC LIST|$str|\n"; + $bcc_arr = $this->_getAddressList($str); + + $this->_parseSpace($str , __LINE__ , __FILE__ ); + + $this->_getNextToken($str,$in_reply_to); + + $this->_parseSpace($str , __LINE__ , __FILE__ ); + + $this->_getNextToken($str,$message_id); + + $this->_getNextToken($str,$parenthesis); + + if( $parenthesis != ')' ){ + $this->_prot_error("must be a ')' but is a '$parenthesis' !!!!" , __LINE__ , __FILE__ ); + } + + return array( "DATE"=> $date , "SUBJECT" => $subject,"FROM" => $from_arr, + "SENDER" => $sender_arr , "REPLY_TO" => $reply_to_arr, "TO" => $to_arr, + "CC" =>$cc_arr, "BCC"=> $bcc_arr, "IN_REPLY_TO" =>$in_reply_to, "MESSAGE_ID"=>$message_id ); + } + + + + + + /** + * Parses the ARRDLIST as defined in RFC + * + * @param string the IMAP's server response + * + * @return Array containing the parsed response + * @access private + * @since 1.0 + */ + function _getAddressList(&$str) + { + $params_arr = $this->_arrayfy_content($str); + if( !isset( $params_arr ) ){ + return $params_arr; + } + + + if( is_array($params_arr) ){ + $personal_name = $params_arr[0][0]; + $at_domain_list = $params_arr[0][1]; + $mailbox_name = $params_arr[0][2]; + $host_name = $params_arr[0][3]; + if( $mailbox_name!='' && $host_name!='' ){ + $email=$mailbox_name . "@" . $host_name; + }else{ + $email=false; + } + if($email==false){ + $rfc822_email=false; + }else{ + if(!isset($personal_name)){ + $rfc822_email= "<". $email . ">"; + }else{ + $rfc822_email= "\"". $personal_name ."\" <". $email . ">"; + } + } + $email_arr[] = array ( "PERSONAL_NAME"=> $personal_name , "AT_DOMAIN_LIST"=>$at_domain_list , + "MAILBOX_NAME"=> $this->utf_7_decode($mailbox_name), "HOST_NAME"=> $host_name, + "EMAIL"=>$email , "RFC822_EMAIL" => $rfc822_email ); + return $email_arr; + } + + return array(); + } + + + + + + + + /** + * Utility funcion to find the closing parenthesis ")" Position it takes care of quoted ones + * + * @param string the IMAP's server response + * + * @return int containing the pos of the closing parenthesis ")" + * @access private + * @since 1.0 + */ + function _getClosingBracesPos($str_line, $startDelim ='(', $stopDelim = ')' ) + { + $len = strlen( $str_line ); + $pos = 0; + // ignore all extra characters + // If inside of a string, skip string -- Boundary IDs and other + // things can have ) in them. + if ( $str_line[$pos] != $startDelim ) { + $this->_prot_error("_getClosingParenthesisPos: must start with a '(' but is a '". $str_line[$pos] ."'!!!!\n" . + "STR_LINE:$str_line|size:$len|POS: $pos\n" , __LINE__ , __FILE__ ); + return( $len ); + } + for( $pos = 1 ; $pos < $len ; $pos++ ){ + if ($str_line[$pos] == $stopDelim ) { + break; + } + if ($str_line[$pos] == '"') { + $pos++; + while ( $str_line[$pos] != '"' && $pos < $len ) { + if ($str_line[$pos] == "\\" && $str_line[$pos + 1 ] == '"' ) + $pos++; + if ($str_line[$pos] == "\\" && $str_line[$pos + 1 ] == "\\" ) + $pos++; + $pos++; + } + } + if ( $str_line[$pos] == $startDelim ) { + $str_line_aux = substr( $str_line , $pos ); + $pos_aux = $this->_getClosingBracesPos( $str_line_aux ); + $pos+=$pos_aux; + } + } + if( $str_line[$pos] != $stopDelim ){ + $this->_prot_error("_getClosingBracesPos: must be a $stopDelim but is a '". $str_line[$pos] ."'|POS:$pos|STR_LINE:$str_line!!!!" , __LINE__ , __FILE__ ); + } + + if( $pos >= $len ) + return false; + return $pos; + } + + + + + + + /** + * Utility funcion to get from here to the end of the line + * + * @param string the IMAP's server response + * + * @return string containing the string to the end of the line + * @access private + * @since 1.0 + */ + + function _getToEOL(&$str , $including = true) + { + $len = strlen( $str ); + if( $including ){ + for($i=0;$i<$len;$i++){ + if( $str[$i] =="\n" ) + break; + } + $content=substr($str,0,$i + 1); + $str=substr($str,$i + 1); + return $content; + + }else{ + for( $i = 0 ; $i < $len ; $i++ ){ + if( $str[$i] =="\n" || $str[$i] == "\r") + break; + } + $content = substr( $str ,0 , $i ); + $str = substr( $str , $i ); + return $content; + } + } + + + + + /** + * Fetches the next IMAP token or parenthesis + * + * @param string the IMAP's server response + * @param string the next token + * @param boolean true: the parenthesis IS a token, false: I consider + * all the response in parenthesis as a token + * + * @return int containing the content size + * @access private + * @since 1.0 + */ + + + function _getNextToken(&$str, &$content, $parenthesisIsToken=true,$colonIsToken=true){ + $len = strlen($str); + $pos = 0; + $content_size = false; + $content = false; + if($str == '' || $len < 2 ){ + $content=$str; + return $len; + } + switch( $str[0] ){ + case '{': + if( ($posClosingBraces = $this->_getClosingBracesPos($str, '{' , '}' )) == false ){ + $this->_prot_error("_getClosingBracesPos() error!!!" , __LINE__ , __FILE__ ); + } + if(! is_numeric( ( $strBytes = substr( $str , 1 , $posClosingBraces - 1) ) ) ){ + $this->_prot_error("must be a number but is a '" . $strBytes ."'!!!!" , __LINE__ , __FILE__ ); + } + if( $str[$posClosingBraces] != '}' ){ + $this->_prot_error("must be a '}' but is a '" . $str[$posClosingBraces] ."'!!!!" , __LINE__ , __FILE__ ); + } + if( $str[$posClosingBraces + 1] != "\r" ){ + $this->_prot_error("must be a '\\r' but is a '" . $str[$posClosingBraces + 1] ."'!!!!" , __LINE__ , __FILE__ ); + } + if( $str[$posClosingBraces + 2] != "\n" ){ + $this->_prot_error("must be a '\\n' but is a '" . $str[$posClosingBraces + 2] ."'!!!!" , __LINE__ , __FILE__ ); + } + $content = substr( $str , $posClosingBraces + 3 , $strBytes ); + if( strlen( $content ) != $strBytes ){ + $this->_prot_error("content size is ". strlen($content) . " but the string reports a size of $strBytes!!!\n" , __LINE__ , __FILE__ ); + } + $content_size = $strBytes; + //Advance the string + $str = substr( $str , $posClosingBraces + $strBytes + 3 ); + break; + case '"': + if($colonIsToken){ + for($pos=1;$pos<$len;$pos++){ + if ( $str[$pos] == "\"" ) { + break; + } + if ($str[$pos] == "\\" && $str[$pos + 1 ] == "\"" ) + $pos++; + if ($str[$pos] == "\\" && $str[$pos + 1 ] == "\\" ) + $pos++; + } + if($str[$pos] != '"' ){ + $this->_prot_error("must be a '\"' but is a '" . $str[$pos] ."'!!!!" , __LINE__ , __FILE__ ); + } + $content_size = $pos; + $content = substr( $str , 1 , $pos - 1 ); + //Advance the string + $str = substr( $str , $pos + 1 ); + }else{ + for($pos=1;$pos<$len;$pos++){ + if ( $str[$pos] == "\"" ) { + break; + } + if ($str[$pos] == "\\" && $str[$pos + 1 ] == "\"" ) + $pos++; + if ($str[$pos] == "\\" && $str[$pos + 1 ] == "\\" ) + $pos++; + } + if($str[$pos] != '"' ){ + $this->_prot_error("must be a '\"' but is a '" . $str[$pos] ."'!!!!" , __LINE__ , __FILE__ ); + } + $content_size = $pos; + $content = substr( $str , 0 , $pos + 1 ); + //Advance the string + $str = substr( $str , $pos + 1 ); + + } + break; + + case "\r": + $pos = 1; + if( $str[1] == "\n") + $pos++; + $content_size = $pos; + $content = substr( $str , 0 , $pos ); + $str = substr( $str , $pos ); + break; + case "\n": + $pos = 1; + $content_size = $pos; + $content = substr( $str , 0 , $pos ); + $str = substr( $str , $pos ); + break; + case '(': + if( $parenthesisIsToken == false ){ + $pos = $this->_getClosingBracesPos( $str ); + $content_size = $pos + 1; + $content = substr( $str , 0 , $pos + 1 ); + $str = substr( $str , $pos + 1 ); + }else{ + $pos = 1; + $content_size = $pos; + $content = substr( $str , 0 , $pos ); + $str = substr( $str , $pos ); + } + break; + case ')': + $pos = 1; + $content_size = $pos; + $content = substr( $str , 0 , $pos ); + $str = substr( $str , $pos ); + break; + case ' ': + $pos = 1; + $content_size = $pos; + $content = substr( $str , 0 , $pos ); + $str = substr( $str , $pos ); + break; + default: + for( $pos = 0 ; $pos < $len ; $pos++ ){ + if ( $str[$pos] == ' ' || $str[$pos] == "\r" || $str[$pos] == ')' || $str[$pos] == '(' || $str[$pos] == "\n" ) { + break; + } + if ( $str[$pos] == "\\" && $str[$pos + 1 ] == ' ' ) + $pos++; + if ( $str[$pos] == "\\" && $str[$pos + 1 ] == "\\" ) + $pos++; + } + //Advance the string + if( $pos == 0 ){ + $content_size = 1; + $content = substr( $str , 0 , 1 ); + $str = substr( $str , 1 ); + }else{ + $content_size = $pos; + $content = substr( $str , 0 , $pos ); + if($pos < $len){ + $str = substr( $str , $pos ); + }else{ + //if this is the end of the string... exit the switch + break; + } + + + } + break; + } + return $content_size; + } + + + + + + + /** + * Utility funcion to display to console the protocol errors + * + * @param string the error + * @param int the line producing the error + * @param string file where the error was produced + * + * @return string containing the error + * @access private + * @since 1.0 + */ + function _prot_error($str , $line , $file,$printError=true) + { + if($printError){ + echo "$line,$file,PROTOCOL ERROR!:$str\n"; + } + } + + + + + + + + function _getEXTarray(&$str , $startDelim = '(' , $stopDelim = ')'){ + /* I let choose the $startDelim and $stopDelim to allow parsing + the OK response so I also can parse a response like this + * OK [UIDNEXT 150] Predicted next UID + */ + $this->_getNextToken( $str , $parenthesis ); + if( $parenthesis != $startDelim ){ + $this->_prot_error("must be a '$startDelim' but is a '$parenthesis' !!!!" , __LINE__ , __FILE__ ); + } + $parenthesis = ''; + $struct_arr = array(); + while( $parenthesis != $stopDelim && $str != '' ){ + // The command + $this->_getNextToken( $str , $token ); + $token = strtoupper( $token ); + + if( ( $ret = $this->_retrParsedResponse( $str , $token ) ) != false ){ + //$struct_arr[$token] = $ret; + $struct_arr=array_merge($struct_arr, $ret); + } + + $parenthesis=$token; + + }//While + + if( $parenthesis != $stopDelim ){ + $this->_prot_error("1_must be a '$stopDelim' but is a '$parenthesis' !!!!" , __LINE__ , __FILE__ ); + } + return $struct_arr; + } + + + + + + function _retrParsedResponse( &$str , $token, $previousToken = null) + { + + //echo "\n\nTOKEN:$token\r\n"; + switch( $token ){ + case "RFC822.SIZE" : + return array($token=>$this->_parseOneStringResponse( $str,__LINE__ , __FILE__ )); + break; +// case "RFC822.TEXT" : + +// case "RFC822.HEADER" : + + + case "RFC822" : + return array($token=>$this->_parseContentresponse( $str , $token )); + break; + case "FLAGS" : + + case "PERMANENTFLAGS" : + return array($token=>$this->_parseFLAGSresponse( $str )); + break; + + case "ENVELOPE" : + return array($token=>$this->_parseENVELOPEresponse( $str )); + break; + case "EXPUNGE" : + return false; + break; + + case "UID" : + + case "UIDNEXT" : + + case "UIDVALIDITY" : + + case "UNSEEN" : + + case "MESSAGES" : + + case "UIDNEXT" : + + case "UIDVALIDITY" : + + case "UNSEEN" : + + case "INTERNALDATE" : + return array($token=>$this->_parseOneStringResponse( $str,__LINE__ , __FILE__ )); + break; + case "BODY" : + + case "BODYSTRUCTURE" : + return array($token=>$this->_parseBodyResponse( $str , $token )); + break; + case "RECENT" : + if( $previousToken != null ){ + $aux["RECENT"]=$previousToken; + return $aux; + }else{ + return array($token=>$this->_parseOneStringResponse( $str,__LINE__ , __FILE__ )); + } + break; + + case "EXISTS" : + return array($token=>$previousToken); + break; + case "READ-WRITE" : + + case "READ-ONLY" : + return array($token=>$token); + break; + case "QUOTA" : + /* + A tipical GETQUOTA DIALOG IS AS FOLLOWS + + C: A0004 GETQUOTA user.damian + S: * QUOTA user.damian (STORAGE 1781460 4000000) + S: A0004 OK Completed + */ + + $mailbox = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ ); + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + $this->_parseString( $str , '(' , __LINE__ , __FILE__ ); + + $ret_aux = array("MAILBOX"=>$this->utf_7_decode($mailbox) ); + $this->_getNextToken( $str , $quota_resp ); + if( ( $ext = $this->_retrParsedResponse( $str , $quota_resp )) == false){ + $this->_prot_error("bogus response!!!!" , __LINE__ , __FILE__ ); + } + $ret_aux=array_merge($ret_aux,$ext); + + $this->_getNextToken( $str , $separator ); + if( $separator == ')' ){ + return array($token=>$ret_aux); + } + + + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + + $this->_getNextToken( $str , $quota_resp ); + if( ( $ext = $this->_retrParsedResponse( $str , $quota_resp )) == false){ + $this->_prot_error("bogus response!!!!" , __LINE__ , __FILE__ ); + } + $ret_aux=array_merge($ret_aux,$ext); + + $this->_parseString( $str , ')' , __LINE__ , __FILE__ ); + return array($token=>$ret_aux); + break; + + case "QUOTAROOT" : + /* + A tipical GETQUOTA DIALOG IS AS FOLLOWS + + C: A0004 GETQUOTA user.damian + S: * QUOTA user.damian (STORAGE 1781460 4000000) + S: A0004 OK Completed + */ + $mailbox = $this->utf_7_decode($this->_parseOneStringResponse( $str,__LINE__ , __FILE__ )); + + $str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 0 ) ); + + $quotaroot = $this->_parseOneStringResponse( $str_line,__LINE__ , __FILE__ ); + $ret = @array( "MAILBOX"=>$this->utf_7_decode($mailbox) , $token=>$quotaroot ); + return array($token=>$ret); + break; + case "STORAGE" : + $used = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ ); + $qmax = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ ); + return array($token=>array("USED"=> $used, "QMAX" => $qmax)); + break; + case "MESSAGE" : + $mused = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ ); + $mmax = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ ); + return array($token=>array("MUSED"=> $mused, "MMAX" => $mmax)); + break; + case "FETCH" : + $this->_parseSpace( $str ,__LINE__ ,__FILE__ ); + // Get the parsed pathenthesis + $struct_arr = $this->_getEXTarray( $str ); + return $struct_arr; + break; + case "CAPABILITY" : + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + $str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 0 ) ); + $struct_arr["CAPABILITIES"] = explode( ' ' , $str_line ); + return array($token=>$struct_arr); + break; + case "STATUS" : + $mailbox = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ ); + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + $ext = $this->_getEXTarray( $str ); + $struct_arr["MAILBOX"] = $this->utf_7_decode($mailbox); + $struct_arr["ATTRIBUTES"] = $ext; + return array($token=>$struct_arr); + break; + case "LIST" : + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + $params_arr = $this->_arrayfy_content( $str ); + + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + $this->_getNextToken( $str , $hierarchydelim ); + + $this->_parseSpace( $str,__LINE__ , __FILE__); + $this->_getNextToken( $str , $mailbox_name ); + + $result_array = array( "NAME_ATTRIBUTES"=>$params_arr , "HIERACHY_DELIMITER"=>$hierarchydelim , "MAILBOX_NAME"=> $this->utf_7_decode($mailbox_name) ); + return array($token=>$result_array); + break; + case "LSUB" : + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + $params_arr = $this->_arrayfy_content( $str ); + + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + $this->_getNextToken( $str , $hierarchydelim ); + + $this->_parseSpace( $str,__LINE__ , __FILE__); + $this->_getNextToken( $str , $mailbox_name ); + + $result_array = array( "NAME_ATTRIBUTES"=>$params_arr , "HIERACHY_DELIMITER"=>$hierarchydelim , "MAILBOX_NAME"=> $this->utf_7_decode($mailbox_name) ); + return array($token=>$result_array); + break; + + case "SEARCH" : + $str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 1) ); + $struct_arr["SEARCH_LIST"] = explode( ' ' , $str_line ); + if(count($struct_arr["SEARCH_LIST"]) == 1 && $struct_arr["SEARCH_LIST"][0]==''){ + $struct_arr["SEARCH_LIST"]=null; + } + return array($token=>$struct_arr); + break; + case "OK" : + /* TODO: + parse the [ .... ] part of the response, use the method + _getEXTarray(&$str,'[',$stopDelim=']') + + */ + $str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 1 ) ); + if($str_line[0] == '[' ){ + $braceLen=$this->_getClosingBracesPos($str_line, '[', ']' ); + $str_aux='('. substr($str_line,1,$braceLen -1). ')'; + $ext_arr=$this->_getEXTarray($str_aux); + //$ext_arr=array($token=>$this->_getEXTarray($str_aux)); + }else{ + $ext_arr=$str_line; + //$ext_arr=array($token=>$str_line); + } + $result_array = $ext_arr; + return $result_array; + break; + case "NO" : + /* TODO: + parse the [ .... ] part of the response, use the method + _getEXTarray(&$str,'[',$stopDelim=']') + + */ + + $str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 1 ) ); + $result_array[] = @array( "COMMAND"=>$token , "EXT"=>$str_line ); + return $result_array; + break; + case "BAD" : + /* TODO: + parse the [ .... ] part of the response, use the method + _getEXTarray(&$str,'[',$stopDelim=']') + + */ + + $str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 1 ) ); + $result_array[] = array( "COMMAND"=>$token , "EXT"=>$str_line ); + return $result_array; + break; + case "BYE" : + /* TODO: + parse the [ .... ] part of the response, use the method + _getEXTarray(&$str,'[',$stopDelim=']') + + */ + + $str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 1 ) ); + $result_array[] = array( "COMMAND"=>$command , "EXT"=> $str_line ); + return $result_array; + break; + + case "LISTRIGHTS" : + $this->_parseSpace( $str ,__LINE__ , __FILE__ ); + $this->_getNextToken( $str , $mailbox ); + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + $this->_getNextToken( $str , $user ); + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + $this->_getNextToken( $str , $granted ); + + $ungranted = explode( ' ' , rtrim( substr( $this->_getToEOL( $str , false ) , 1 ) ) ); + + $result_array = @array( "MAILBOX"=>$this->utf_7_decode($mailbox) , "USER"=>$user , "GRANTED"=>$granted , "UNGRANTED"=>$ungranted ); + return $result_array; + break; + + case "MYRIGHTS" : + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + $this->_getNextToken( $str ,$mailbox ); + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + $this->_getNextToken( $str , $granted ); + + $result_array = array( "MAILBOX"=>$this->utf_7_decode($mailbox) , "GRANTED"=>$granted ); + return $result_array; + break; + + case "ACL" : + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + $this->_getNextToken( $str , $mailbox ); + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + $acl_arr = explode( ' ' , rtrim( substr( $this->_getToEOL( $str , false ) , 0 ) ) ); + + for( $i = 0 ; $i < count( $acl_arr ) ; $i += 2 ){ + $arr[] = array( "USER"=>$acl_arr[$i] , "RIGHTS"=>$acl_arr[ $i + 1 ] ); + } + + $result_array = array( "MAILBOX"=>$this->utf_7_decode($mailbox) , "USERS"=>$arr ); + return $result_array; + break; + + case "ANNOTATION" : + $this->_parseSpace($str, __LINE__, __FILE__); + $this->_getNextToken($str, $mailbox); + + $this->_parseSpace($str, __LINE__, __FILE__); + $this->_getNextToken($str, $entry); + + $this->_parseSpace($str, __LINE__, __FILE__); + $attrs = $this->_arrayfy_content($str); + + $result_array = array('MAILBOX' => $mailbox, 'ENTRY' => $entry , 'ATTRIBUTES' => $attrs); + return $result_array; + break; + + case "": + $this->_prot_error( "PROTOCOL ERROR!:str empty!!" , __LINE__ , __FILE__ ); + break; + case "(": + $this->_prot_error("OPENING PARENTHESIS ERROR!!!!!!!!!!!!!!!!!" , __LINE__ , __FILE__ ); + break; + case ")": + //"CLOSING PARENTHESIS BREAK!!!!!!!" + break; + case "\r\n": + $this->_prot_error("BREAK!!!!!!!!!!!!!!!!!" , __LINE__ , __FILE__ ); + break; + case ' ': + // this can happen and we just ignore it + // This happens when - for example - fetch returns more than 1 parammeter + // for example you ask to get RFC822.SIZE and UID + //$this->_prot_error("SPACE BREAK!!!!!!!!!!!!!!!!!" , __LINE__ , __FILE__ ); + break; + default: + $body_token=strtoupper(substr($token,0,5)); + //echo "BODYYYYYYY: $body_token\n"; + $rfc822_token=strtoupper(substr($token,0,7)); + //echo "BODYYYYYYY: $rfc822_token|$token\n"; + + if( $body_token == 'BODY[' || $body_token == 'BODY.' || $rfc822_token == 'RFC822.' ) { + //echo "TOKEN:$token\n"; + //$this->_getNextToken( $str , $mailbox ); + return array($token=>$this->_parseContentresponse( $str , $token )); + }else{ + $this->_prot_error( "UNIMPLEMMENTED! I don't know the parameter '$token' !!!" , __LINE__ , __FILE__ ); + } + break; + } + return false; +} + + + + + + + /* + * Verifies that the next character IS a space + */ + function _parseSpace(&$str,$line,$file, $printError = true) + { + /* + This code repeats a lot in this class + so i make it a function to make all the code shorter + */ + $this->_getNextToken( $str , $space ); + if( $space != ' ' ){ + $this->_prot_error("must be a ' ' but is a '$space' !!!!" , $line , $file,$printError ); + } + return $space; + } + + + + + + + function _parseString( &$str , $char , $line , $file ) + { + /* + This code repeats a lot in this class + so i make it a function to make all the code shorter + */ + $this->_getNextToken( $str , $char_aux ); + if( strtoupper($char_aux) != strtoupper( $char ) ){ + $this->_prot_error("must be a $char but is a '$char_aux' !!!!", $line , $file ); + } + return $char_aux; + } + + + + + + function _genericImapResponseParser( &$str , $cmdid = null ) + { + + $result_array=array(); + if( $this->_unParsedReturn ){ + $unparsed_str = $str; + } + + $this->_getNextToken( $str , $token ); + + while( $token != $cmdid && $str != '' ){ + if($token == "+" ){ + //if the token is + ignore the line + // TODO: verify that this is correct!!! + $this->_getToEOL( $str ); + $this->_getNextToken( $str , $token ); + } + + $this->_parseString( $str , ' ' , __LINE__ , __FILE__ ); + + $this->_getNextToken( $str , $token ); + if( $token == '+' ){ + $this->_getToEOL( $str ); + $this->_getNextToken( $str , $token ); + }else + if( is_numeric( $token ) ){ + // The token is a NUMBER so I store it + $msg_nro = $token; + $this->_parseSpace( $str , __LINE__ , __FILE__ ); + + // I get the command + $this->_getNextToken( $str , $command ); + + if( ( $ext_arr = $this->_retrParsedResponse( $str , $command, $msg_nro ) ) == false ){ + // if this bogus response cis a FLAGS () or EXPUNGE response + // the ignore it + if( $command != 'FLAGS' && $command != 'EXPUNGE' ){ + $this->_prot_error("bogus response!!!!" , __LINE__ , __FILE__, false); + } + } + $result_array[] = array( "COMMAND"=>$command , "NRO"=>$msg_nro , "EXT"=>$ext_arr ); + }else{ + // OK the token is not a NUMBER so it MUST be a COMMAND + $command = $token; + + /* Call the parser return the array + take care of bogus responses! + */ + + if( ( $ext_arr = $this->_retrParsedResponse( $str , $command ) ) == false ){ + $this->_prot_error( "bogus response!!!! (COMMAND:$command)" , __LINE__ , __FILE__ ); + } + $result_array[] = array( "COMMAND"=>$command , "EXT"=>$ext_arr ); + + + } + + + $this->_getNextToken( $str , $token ); + + $token = strtoupper( $token ); + if( $token != "\r\n" && $token != '' ){ + $this->_prot_error("PARSE ERROR!!! must be a '\\r\\n' here but is a '$token'!!!! (getting the next line)|STR:|$str|" , __LINE__ , __FILE__ ); + } + $this->_getNextToken( $str , $token ); + + if($token == "+" ){ + //if the token is + ignore the line + // TODO: verify that this is correct!!! + $this->_getToEOL( $str ); + $this->_getNextToken( $str , $token ); + } + }//While + // OK we finish the UNTAGGED Response now we must parse the FINAL TAGGED RESPONSE + //TODO: make this a litle more elegant! + + $this->_parseSpace( $str , __LINE__ , __FILE__, false ); + + $this->_getNextToken( $str , $cmd_status ); + + $str_line = rtrim (substr( $this->_getToEOL( $str ) , 1 ) ); + + + $response["RESPONSE"]=array( "CODE"=>$cmd_status , "STR_CODE"=>$str_line , "CMDID"=>$cmdid ); + + $ret=$response; + if( !empty($result_array)){ + $ret=array_merge($ret,array("PARSED"=>$result_array) ); + } + + if( $this->_unParsedReturn ){ + $unparsed["UNPARSED"]=$unparsed_str; + $ret=array_merge($ret,$unparsed); + } + + + if( isset($status_arr) ){ + $status["STATUS"]=$status_arr; + $ret=array_merge($ret,$status); + } + return $ret; + +} + + + + + function _genericCommand($command, $params = '') + { + if( !$this->_connected ){ + return new PEAR_Error( "not connected! (CMD:$command)" ); + } + $cmdid = $this->_getCmdId(); + $this->_putCMD( $cmdid , $command , $params ); + $args=$this->_getRawResponse( $cmdid ); + return $this->_genericImapResponseParser( $args , $cmdid ); + } + + + + function utf_7_encode($str) + { + if($this->_useUTF_7 == false ){ + return $str; + } + //return imap_utf7_encode($str); + + $encoded_utf7 = ''; + $base64_part = ''; + if(is_array($str)){ + return new PEAR_Error('error'); + } + + + for ($i = 0; $i < strlen($str); $i++) { + //those chars should be base64 encoded + if ( ((ord($str[$i]) >= 39 ) and (ord($str[$i]) <= 126 )) or ((ord($str[$i]) >= 32 ) and (ord($str[$i]) <= 37 )) ) { + if ($base64_part) { + $encoded_utf7 = sprintf("%s&%s-", $encoded_utf7, str_replace('=', '',base64_encode($base64_part)) ); + $base64_part = ''; + } + $encoded_utf7 = sprintf("%s%s",$encoded_utf7 , $str[$i]); + } else { + //handle & + if (ord($str[$i]) == 38 ) { + if ($base64_part) { + $encoded_utf7 = sprintf("%s&%s-", $encoded_utf7, str_replace('=', '',base64_encode($base64_part)) ); + $base64_part = ''; + } + $encoded_utf7 = sprintf("%s&-", $encoded_utf7 ); + } else { + $base64_part = sprintf("%s%s",$base64_part , $str[$i]); + //$base64_part = sprintf("%s%s%s",$base64_part , chr(0) , $str[$i]); + } + } + } + if ($base64_part) { + $encoded_utf7 = sprintf("%s&%s-", $encoded_utf7, str_replace('=', '',base64_encode($base64_part)) ); + $base64_part = ''; + } + return $encoded_utf7; + } + + + function utf_7_decode($str) + { + + if($this->_useUTF_7 == false ){ + return $str; + } + + //return imap_utf7_decode($str); + + $base64_part = ''; + $decoded_utf7 = ''; + + for ($i = 0; $i < strlen($str); $i++) { + if ( strlen($base64_part) > 0 ) { + if ($str[$i] == '-') { + if ($base64_part == '&') { + $decoded_utf7 = sprintf("%s&" , $decoded_utf7 ); + } else { + $next_part_decoded= base64_decode( substr( $base64_part, 1 ) ) ; + $decoded_utf7 = sprintf("%s%s", $decoded_utf7 , $next_part_decoded ); + } + $base64_part = ''; + + } else { + $base64_part = sprintf("%s%s", $base64_part , $str[$i] ); + } + } else { + if ($str[$i] == '&') { + $base64_part = '&'; + } else { + $decoded_utf7 = sprintf("%s%s", $decoded_utf7 , $str[$i] ); + } + } + } + return $decoded_utf7; + } + + + +}//Class +?> diff --git a/thirdparty/pear/Net/IPv4.php b/thirdparty/pear/Net/IPv4.php new file mode 100644 index 0000000..68de17e --- /dev/null +++ b/thirdparty/pear/Net/IPv4.php @@ -0,0 +1,458 @@ + +* @author Marco Kaiser +* @author Florian Anderiasch +* @copyright 1997-2005 The PHP Group +* @license http://www.php.net/license/3_01.txt PHP License 3.01 +* @version CVS: $Id: IPv4.php,v 1.11 2005/11/29 12:56:35 fa Exp $ +* @link http://pear.php.net/package/Net_IPv4 +*/ + +require_once 'PEAR.php'; + +// {{{ GLOBALS +/** + * Map of bitmasks to subnets + * + * This array contains every valid netmask. The index of the dot quad + * netmask value is the corresponding CIDR notation (bitmask). + * + * @global array $GLOBALS['Net_IPv4_Netmask_Map'] + */ +$GLOBALS['Net_IPv4_Netmask_Map'] = array( + 0 => "0.0.0.0", + 1 => "128.0.0.0", + 2 => "192.0.0.0", + 3 => "224.0.0.0", + 4 => "240.0.0.0", + 5 => "248.0.0.0", + 6 => "252.0.0.0", + 7 => "254.0.0.0", + 8 => "255.0.0.0", + 9 => "255.128.0.0", + 10 => "255.192.0.0", + 11 => "255.224.0.0", + 12 => "255.240.0.0", + 13 => "255.248.0.0", + 14 => "255.252.0.0", + 15 => "255.254.0.0", + 16 => "255.255.0.0", + 17 => "255.255.128.0", + 18 => "255.255.192.0", + 19 => "255.255.224.0", + 20 => "255.255.240.0", + 21 => "255.255.248.0", + 22 => "255.255.252.0", + 23 => "255.255.254.0", + 24 => "255.255.255.0", + 25 => "255.255.255.128", + 26 => "255.255.255.192", + 27 => "255.255.255.224", + 28 => "255.255.255.240", + 29 => "255.255.255.248", + 30 => "255.255.255.252", + 31 => "255.255.255.254", + 32 => "255.255.255.255" + ); +// }}} +// {{{ Net_IPv4 + +/** +* Class to provide IPv4 calculations +* +* Provides methods for validating IP addresses, calculating netmasks, +* broadcast addresses, network addresses, conversion routines, etc. +* +* @category Net +* @package Net_IPv4 +* @author Eric Kilfoil +* @author Marco Kaiser +* @author Florian Anderiasch +* @copyright 1997-2005 The PHP Group +* @license http://www.php.net/license/3_01.txt PHP License 3.01 +* @version CVS: 1.3.0 +* @link http://pear.php.net/package/Net_IPv4 +* @access public +*/ +class Net_IPv4 +{ + // {{{ properties + var $ip = ""; + var $bitmask = false; + var $netmask = ""; + var $network = ""; + var $broadcast = ""; + var $long = 0; + + // }}} + // {{{ validateIP() + + /** + * Validate the syntax of the given IP adress + * + * Using the PHP long2ip() and ip2long() functions, convert the IP + * address from a string to a long and back. If the original still + * matches the converted IP address, it's a valid address. This + * function does not allow for IP addresses to be formatted as long + * integers. + * + * @param string $ip IP address in the format x.x.x.x + * @return bool true if syntax is valid, otherwise false + */ + function validateIP($ip) + { + if ($ip == long2ip(ip2long($ip))) { + return true; + } else { + return false; + } + } + + // }}} + // {{{ check_ip() + + /** + * Validate the syntax of the given IP address (compatibility) + * + * This function is identical to Net_IPv4::validateIP(). It is included + * merely for compatibility reasons. + * + * @param string $ip IP address + * @return bool true if syntax is valid, otherwise false + */ + function check_ip($ip) + { + return $this->validateIP($ip); + } + + // }}} + // {{{ validateNetmask() + + /** + * Validate the syntax of a four octet netmask + * + * There are 33 valid netmask values. This function will compare the + * string passed as $netmask to the predefined 33 values and return + * true or false. This is most likely much faster than performing the + * calculation to determine the validity of the netmask. + * + * @param string $netmask Netmask + * @return bool true if syntax is valid, otherwise false + */ + function validateNetmask($netmask) + { + if (! in_array($netmask, $GLOBALS['Net_IPv4_Netmask_Map'])) { + return false; + } + return true; + } + + // }}} + // {{{ parseAddress() + + /** + * Parse a formatted IP address + * + * Given a network qualified IP address, attempt to parse out the parts + * and calculate qualities of the address. + * + * The following formats are possible: + * + * [dot quad ip]/[ bitmask ] + * [dot quad ip]/[ dot quad netmask ] + * [dot quad ip]/[ hex string netmask ] + * + * The first would be [IP Address]/[BitMask]: + * 192.168.0.0/16 + * + * The second would be [IP Address] [Subnet Mask in dot quad notation]: + * 192.168.0.0/255.255.0.0 + * + * The third would be [IP Address] [Subnet Mask as Hex string] + * 192.168.0.0/ffff0000 + * + * Usage: + * + * $cidr = '192.168.0.50/16'; + * $net = Net_IPv4::parseAddress($cidr); + * echo $net->network; // 192.168.0.0 + * echo $net->ip; // 192.168.0.50 + * echo $net->broadcast; // 192.168.255.255 + * echo $net->bitmask; // 16 + * echo $net->long; // 3232235520 (long/double version of 192.168.0.50) + * echo $net->netmask; // 255.255.0.0 + * + * @param string $ip IP address netmask combination + * @return object true if syntax is valid, otherwise false + */ + function parseAddress($address) + { + $myself = new Net_IPv4; + if (strchr($address, "/")) { + $parts = explode("/", $address); + if (! $myself->validateIP($parts[0])) { + return PEAR::raiseError("invalid IP address"); + } + $myself->ip = $parts[0]; + + // Check the style of netmask that was entered + /* + * a hexadecimal string was entered + */ + if (eregi("^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$", $parts[1], $regs)) { + // hexadecimal string + $myself->netmask = hexdec($regs[1]) . "." . hexdec($regs[2]) . "." . + hexdec($regs[3]) . "." . hexdec($regs[4]); + + /* + * a standard dot quad netmask was entered. + */ + } else if (strchr($parts[1], ".")) { + if (! $myself->validateNetmask($parts[1])) { + return PEAR::raiseError("invalid netmask value"); + } + $myself->netmask = $parts[1]; + + /* + * a CIDR bitmask type was entered + */ + } else if ($parts[1] >= 0 && $parts[1] <= 32) { + // bitmask was entered + $myself->bitmask = $parts[1]; + + /* + * Some unknown format of netmask was entered + */ + } else { + return PEAR::raiseError("invalid netmask value"); + } + $myself->calculate(); + return $myself; + } else if ($myself->validateIP($address)) { + $myself->ip = $address; + return $myself; + } else { + return PEAR::raiseError("invalid IP address"); + } + } + + // }}} + // {{{ calculate() + + /** + * Calculates network information based on an IP address and netmask. + * + * Fully populates the object properties based on the IP address and + * netmask/bitmask properties. Once these two fields are populated, + * calculate() will perform calculations to determine the network and + * broadcast address of the network. + * + * @return mixed true if no errors occured, otherwise PEAR_Error object + */ + function calculate() + { + $validNM = $GLOBALS['Net_IPv4_Netmask_Map']; + + if (! is_a($this, "net_ipv4")) { + $myself = new Net_IPv4; + return PEAR::raiseError("cannot calculate on uninstantiated Net_IPv4 class"); + } + + /* Find out if we were given an ip address in dot quad notation or + * a network long ip address. Whichever was given, populate the + * other field + */ + if (strlen($this->ip)) { + if (! $this->validateIP($this->ip)) { + return PEAR::raiseError("invalid IP address"); + } + $this->long = $this->ip2double($this->ip); + } else if (is_numeric($this->long)) { + $this->ip = long2ip($this->long); + } else { + return PEAR::raiseError("ip address not specified"); + } + + /* + * Check to see if we were supplied with a bitmask or a netmask. + * Populate the other field as needed. + */ + if (strlen($this->bitmask)) { + $this->netmask = $validNM[$this->bitmask]; + } else if (strlen($this->netmask)) { + $validNM_rev = array_flip($validNM); + $this->bitmask = $validNM_rev[$this->netmask]; + } else { + return PEAR::raiseError("netmask or bitmask are required for calculation"); + } + $this->network = long2ip(ip2long($this->ip) & ip2long($this->netmask)); + $this->broadcast = long2ip(ip2long($this->ip) | + (ip2long($this->netmask) ^ ip2long("255.255.255.255"))); + return true; + } + + // }}} + // {{{ getNetmask() + + function getNetmask($length) + { + if (! PEAR::isError($ipobj = Net_IPv4::parseAddress("0.0.0.0/" . $length))) { + $mask = $ipobj->netmask; + unset($ipobj); + return $mask; + } + return false; + } + + // }}} + // {{{ getNetLength() + + function getNetLength($netmask) + { + if (! PEAR::isError($ipobj = Net_IPv4::parseAddress("0.0.0.0/" . $netmask))) { + $bitmask = $ipobj->bitmask; + unset($ipobj); + return $bitmask; + } + return false; + } + + // }}} + // {{{ getSubnet() + + function getSubnet($ip, $netmask) + { + if (! PEAR::isError($ipobj = Net_IPv4::parseAddress($ip . "/" . $netmask))) { + $net = $ipobj->network; + unset($ipobj); + return $net; + } + return false; + } + + // }}} + // {{{ inSameSubnet() + + function inSameSubnet($ip1, $ip2) + { + if (! is_object($ip1) || strcasecmp(get_class($ip1), 'net_ipv4') <> 0) { + $ipobj1 = Net_IPv4::parseAddress($ip1); + if (PEAR::isError($ipobj)) { + return PEAR::raiseError("IP addresses must be an understood format or a Net_IPv4 object"); + } + } + if (! is_object($ip2) || strcasecmp(get_class($ip2), 'net_ipv4') <> 0) { + $ipobj2 = Net_IPv4::parseAddress($ip2); + if (PEAR::isError($ipobj)) { + return PEAR::raiseError("IP addresses must be an understood format or a Net_IPv4 object"); + } + } + if ($ipobj1->network == $ipobj2->network && + $ipobj1->bitmask == $ipobj2->bitmask) { + return true; + } + return false; + } + + // }}} + // {{{ atoh() + + /** + * Converts a dot-quad formatted IP address into a hexadecimal string + * @param string $addr IP-adress in dot-quad format + * @return mixed false if invalid IP and hexadecimal representation as string if valid + */ + function atoh($addr) + { + if (! Net_IPv4::validateIP($addr)) { + return false; + } + $ap = explode(".", $addr); + return sprintf("%02x%02x%02x%02x", $ap[0], $ap[1], $ap[2], $ap[3]); + } + + // }}} + // {{{ htoa() + + /** + * Converts a hexadecimal string into a dot-quad formatted IP address + * @param string $addr IP-adress in hexadecimal format + * @return mixed false if invalid IP and dot-quad formatted IP as string if valid + */ + function htoa($addr) + { + if (eregi("^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$", + $addr, $regs)) { + return hexdec($regs[1]) . "." . hexdec($regs[2]) . "." . + hexdec($regs[3]) . "." . hexdec($regs[4]); + } + return false; + } + + // }}} + // {{{ ip2double() + + /** + * Converts an IP address to a PHP double. Better than ip2long because + * a long in PHP is a signed integer. + * @param string $ip dot-quad formatted IP adress + * @return float IP adress as double - positive value unlike ip2long + */ + function ip2double($ip) + { + return (double)(sprintf("%u", ip2long($ip))); + } + + // }}} + // {{{ ipInNetwork() + + /** + * Determines whether or not the supplied IP is within the supplied network. + * + * This function determines whether an IP address is within a network. + * The IP address ($ip) must be supplied in dot-quad format, and the + * network ($network) may be either a string containing a CIDR + * formatted network definition, or a Net_IPv4 object. + * + * @param string $ip A dot quad representation of an IP address + * @param string $network A string representing the network in CIDR format or a Net_IPv4 object. + * @return bool true if the IP address exists within the network + */ + function ipInNetwork($ip, $network) + { + if (! is_object($network) || strcasecmp(get_class($network), 'net_ipv4') <> 0) { + $network = Net_IPv4::parseAddress($network); + } + + $net = Net_IPv4::ip2double($network->network); + $bcast = Net_IPv4::ip2double($network->broadcast); + $ip = Net_IPv4::ip2double($ip); + unset($network); + if ($ip >= $net && $ip <= $bcast) { + return true; + } + return false; + } + + // }}} +} + +// }}} + +/* + * vim: sts=4 ts=4 sw=4 cindent fdm=marker + */ +?> diff --git a/thirdparty/pear/Net/IPv6.php b/thirdparty/pear/Net/IPv6.php new file mode 100644 index 0000000..296abe6 --- /dev/null +++ b/thirdparty/pear/Net/IPv6.php @@ -0,0 +1,218 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: IPv6.php,v 1.12 2005/09/01 12:42:00 alexmerz Exp $ + +/** +* Class to validate and to work with IPv6 +* +* @author Alexander Merz +* @author elfrink at introweb dot nl +* @package Net_IPv6 +* @version $Id: IPv6.php,v 1.12 2005/09/01 12:42:00 alexmerz Exp $ +* @access public +*/ +class Net_IPv6 { + + // {{{ Uncompress() + + /** + * Uncompresses an IPv6 adress + * + * RFC 2373 allows you to compress zeros in an adress to '::'. This + * function expects an valid IPv6 adress and expands the '::' to + * the required zeros. + * + * Example: FF01::101 -> FF01:0:0:0:0:0:0:101 + * ::1 -> 0:0:0:0:0:0:0:1 + * + * @access public + * @see Compress() + * @static + * @param string $ip a valid IPv6-adress (hex format) + * @return string the uncompressed IPv6-adress (hex format) + */ + function Uncompress($ip) { + $uip = $ip; + $c1 = -1; + $c2 = -1; + if (false !== strpos($ip, '::') ) { + list($ip1, $ip2) = explode('::', $ip); + if(""==$ip1) { + $c1 = -1; + } else { + $pos = 0; + if(0 < ($pos = substr_count($ip1, ':'))) { + $c1 = $pos; + } else { + $c1 = 0; + } + } + if(""==$ip2) { + $c2 = -1; + } else { + $pos = 0; + if(0 < ($pos = substr_count($ip2, ':'))) { + $c2 = $pos; + } else { + $c2 = 0; + } + } + if(strstr($ip2, '.')) { + $c2++; + } + if(-1 == $c1 && -1 == $c2) { // :: + $uip = "0:0:0:0:0:0:0:0"; + } else if(-1==$c1) { // ::xxx + $fill = str_repeat('0:', 7-$c2); + $uip = str_replace('::', $fill, $uip); + } else if(-1==$c2) { // xxx:: + $fill = str_repeat(':0', 7-$c1); + $uip = str_replace('::', $fill, $uip); + } else { // xxx::xxx + $fill = str_repeat(':0:', 6-$c2-$c1); + $uip = str_replace('::', $fill, $uip); + $uip = str_replace('::', ':', $uip); + } + } + return $uip; + } + + // }}} + // {{{ Compress() + + /** + * Compresses an IPv6 adress + * + * RFC 2373 allows you to compress zeros in an adress to '::'. This + * function expects an valid IPv6 adress and compresses successive zeros + * to '::' + * + * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 + * 0:0:0:0:0:0:0:1 -> ::1 + * + * @access public + * @see Uncompress() + * @static + * @param string $ip a valid IPv6-adress (hex format) + * @return string the compressed IPv6-adress (hex format) + * @author elfrink at introweb dot nl + */ + function Compress($ip) { + $cip = $ip; + + if (!strstr($ip, '::')) { + $ipp = explode(':',$ip); + for($i=0; $i0) { + $match = ''; + foreach($zeros[0] as $zero) { + if (strlen($zero) > strlen($match)) + $match = $zero; + } + $cip = preg_replace('/' . $match . '/', ':', $cip, 1); + } + $cip = preg_replace('/((^:)|(:$))/', '' ,$cip); + $cip = preg_replace('/((^:)|(:$))/', '::' ,$cip); + } + return $cip; + } + + // }}} + // {{{ SplitV64() + + /** + * Splits an IPv6 adress into the IPv6 and a possible IPv4 part + * + * RFC 2373 allows you to note the last two parts of an IPv6 adress as + * an IPv4 compatible adress + * + * Example: 0:0:0:0:0:0:13.1.68.3 + * 0:0:0:0:0:FFFF:129.144.52.38 + * + * @access public + * @static + * @param string $ip a valid IPv6-adress (hex format) + * @return array [0] contains the IPv6 part, [1] the IPv4 part (hex format) + */ + function SplitV64($ip) { + $ip = Net_IPv6::Uncompress($ip); + if (strstr($ip, '.')) { + $pos = strrpos($ip, ':'); + $ip{$pos} = '_'; + $ipPart = explode('_', $ip); + return $ipPart; + } else { + return array($ip, ""); + } + } + + // }}} + // {{{ checkIPv6 + + /** + * Checks an IPv6 adress + * + * Checks if the given IP is IPv6-compatible + * + * @access public + * @static + * @param string $ip a valid IPv6-adress + * @return boolean true if $ip is an IPv6 adress + */ + function checkIPv6($ip) { + + $ipPart = Net_IPv6::SplitV64($ip); + $count = 0; + if (!empty($ipPart[0])) { + $ipv6 =explode(':', $ipPart[0]); + for ($i = 0; $i < count($ipv6); $i++) { + $dec = hexdec($ipv6[$i]); + $hex = strtoupper(preg_replace("/^[0]{1,3}(.*[0-9a-fA-F])$/", "\\1", $ipv6[$i])); + if ($ipv6[$i] >= 0 && $dec <= 65535 && $hex == strtoupper(dechex($dec))) { + $count++; + } + } + if (8 == $count) { + return true; + } elseif (6 == $count and !empty($ipPart[1])) { + $ipv4 = explode('.',$ipPart[1]); + $count = 0; + for ($i = 0; $i < count($ipv4); $i++) { + if ($ipv4[$i] >= 0 && (integer)$ipv4[$i] <= 255 && preg_match("/^\d{1,3}$/", $ipv4[$i])) { + $count++; + } + } + if (4 == $count) { + return true; + } + } else { + return false; + } + + } else { + return false; + } + } + // }}} +} +?> diff --git a/thirdparty/pear/Net/Ident.php b/thirdparty/pear/Net/Ident.php new file mode 100644 index 0000000..df5c4f5 --- /dev/null +++ b/thirdparty/pear/Net/Ident.php @@ -0,0 +1,331 @@ + | +// | Original author: Gavin Brown | +// +----------------------------------------------------------------------+ +// +// $Id: Ident.php,v 1.7 2005/03/07 11:23:26 nepto Exp $ +// +// Identification Protocol implementation +// + +require_once 'PEAR.php'; + +/** + * Net_Ident default values + * @const NET_IDENT_DEFAULT_TIMEOUT Default ident query network timeout + * @const NET_IDENT_DEFAULT_PORT Default ident protocol port + */ +define('NET_IDENT_DEFAULT_TIMEOUT', 30); +define('NET_IDENT_DEFAULT_PORT', 113); + +/** + * Net_Ident object states + * @const NET_IDENT_STATUS_UNDEF Undefined Net_Ident object state + * @const NET_IDENT_STATUS_OK Net_Ident object did successful query + * @const NET_IDENT_STATUS_ERROR Net_Ident object query failed + */ +define('NET_IDENT_STATUS_UNDEF', 0); +define('NET_IDENT_STATUS_OK', 1); +define('NET_IDENT_STATUS_ERROR', 2); + +/** + * Net_Ident - Identification Protocol implementation according to RFC 1413 + * + * The Identification Protocol (a.k.a., "ident", a.k.a., "the Ident Protocol") + * provides a means to determine the identity of a user of a particular TCP + * connection. Given a TCP port number pair, it returns a character string + * which identifies the owner of that connection on the server's system. + * + * You can find out more about the Ident protocol at + * + * http://www.ietf.org/rfc/rfc1413.txt + * + * Usage: + * getUser(); + * $os_type = $ident->getOsType(); + * echo "user: $user, operating system: $os_type\n"; + * ?> + * + * @author Ondrej Jombik + * @package Net_Ident + * @version 1.1.0 + * @access public + */ +class Net_Ident +{ + /** + * Current object state (undef, ok, error) + * + * @var enum + * @access private + */ + var $_status; + + /** + * Error message string; + * if $_status is "error", $_error contains system error message; + * if $_status is "ok", $_error contains ident error message + * or it is empty + * + * @var string + * @access private + */ + var $_error; + + /** + * Properties array (contains remote host, remote port, ident port, etc.) + * + * @var array + * @access private + */ + var $_props; + + /** + * Data array (contains ident username, ident operating system type, and + * raw line returned from ident server) + * + * @var array + * @access private + */ + var $_data; + + /** + * Net_Ident object constructor + * + * Initializes class properties. Use empty string '' for any string + * parameter and value of 0 for any int parameter to set default value. + * + * @param string $remote_addr Remote host address (IP or hostname) + * @param int $remote_port Remote port (default $REMOTE_PORT) + * @param int $local_port Local port (default $SERVER_PORT) + * @param int $ident_port Ident port (default 113) + * @param int $timeout Socket timeout (default 30 seconds) + * @return none + * @access public + */ + function Net_Ident( + $remote_addr = '', + $remote_port = 0, + $local_port = 0, + $ident_port = 0, + $timeout = 0) + { + $this->_status = NET_IDENT_STATUS_UNDEF; + $this->setRemoteAddr($remote_addr); + $this->setRemotePort($remote_port); + $this->setLocalPort($local_port); + $this->setIdentPort($ident_port); + $this->setTimeout($timeout); + } + + /** + * Sets remote host address (IP or hostname) + * + * @param string $remote_addr Remote host address (IP or hostname) + * @return none + * @access public + */ + function setRemoteAddr($remote_addr) + { + strlen($remote_addr) <= 0 && $remote_addr = $_SERVER['REMOTE_ADDR']; + $this->_props['remote_addr'] = $remote_addr; + } + + /** + * Sets remote port + * + * @param int $remote_port Remote port (default $REMOTE_PORT) + * @return none + * @access public + */ + function setRemotePort($remote_port) + { + $remote_port = intval($remote_port); + $remote_port <= 0 && $remote_port = $_SERVER['REMOTE_PORT']; + $this->_props['remote_port'] = $remote_port; + } + + /** + * Sets local port + * + * @param int $local_port Local port (default $SERVER_PORT) + * @return none + * @access public + */ + function setLocalPort($local_port) + { + $local_port = intval($local_port); + $local_port <= 0 && $local_port = $_SERVER['SERVER_PORT']; + $this->_props['local_port'] = $local_port; + } + + /** + * Sets ident port + * + * @param int $ident_port Ident port (default 113) + * @return none + * @access public + */ + function setIdentPort($ident_port) + { + $ident_port = intval($ident_port); + $ident_port <= 0 && $ident_port = NET_IDENT_DEFAULT_PORT; + $this->_props['ident_port'] = $ident_port; + } + + /** + * Sets socket timeout + * + * @param int $timeout Socket timeout (default 30 seconds) + * @return none + * @access public + */ + function setTimeout($timeout) + { + $timeout = intval($timeout); + $timeout <= 0 && $timeout = NET_IDENT_DEFAULT_TIMEOUT; + $this->_props['timeout'] = $timeout; + } + + /** + * Performs network socket ident query + * + * @return mixed PEAR_Error on connection error + * rawdata read from socket on success + * @access public + */ + function query() + { + // query forced, clean current result + if ($this->_status == NET_IDENT_STATUS_OK) { + unset($this->_data['username']); + unset($this->_data['os_type']); + $this->_status = NET_IDENT_STATUS_UNDEF; + } + while (1) { + if ($this->_status == NET_IDENT_STATUS_ERROR) { + return new PEAR_Error($this->_error); + } + + if ($socket = @fsockopen( + $this->_props['remote_addr'], + $this->_props['ident_port'], + $errno, $errstr, + $this->_props['timeout'])) { + break; + } + $this->_status = NET_IDENT_STATUS_ERROR; + $this->_error = 'Error connecting to ident server (' + .$this->_props['remote_addr'].':' + .$this->_props['ident_port']."): $errstr ($errno)"; // ) + } + + $line = $this->_props['remote_port'].',' + .$this->_props['local_port']."\r\n"; + @fwrite($socket, $line); + $line = @fgets($socket, 1000); // 1000 octets according to RFC 1413 + fclose($socket); + + $this->_status = NET_IDENT_STATUS_OK; + $this->_data['rawdata'] = $line; + $this->_parseIdentResponse($line); + + return $line; + } + + /** + * Returns ident username + * + * @return mixed PEAR_Error on connection error + * false boolean on ident protocol error + * username string on success + * @access public + */ + function getUser() + { + $this->_status == NET_IDENT_STATUS_UNDEF && $this->query(); + if ($this->_status == NET_IDENT_STATUS_ERROR) { + return new PEAR_Error($this->_error); + } + return $this->_data['username']; + } + + /** + * Returns ident operating system type + * + * @return mixed PEAR_Error on connection error + * false boolean on ident protocol error + * operating system type string on success + * @access public + */ + function getOsType() + { + $this->_status == NET_IDENT_STATUS_UNDEF && $this->query(); + if ($this->_status == NET_IDENT_STATUS_ERROR) { + return new PEAR_Error($this->_error); + } + return $this->_data['os_type']; + } + + /** + * Returns ident protocol error + * + * @return mixed error string if ident protocol error had occured + * false otherwise + * @access public + */ + function identError() + { + if ($this->_status == NET_IDENT_STATUS_OK + && isset($this->_error)) { + return $this->_error; + } + return false; + } + + /** + * Parses response from indent server and sets internal data structures + * with ident username and ident operating system type + * + * @param string $string ident server response + * @return boolean true if no ident protocol error had occured + * false otherwise + * @access private + */ + function _parseIdentResponse($string) + { + $this->_data['username'] = false; + $this->_data['os_type'] = false; + $array = explode(':', $string, 4); + if (count($array) > 1 && ! strcasecmp(trim($array[1]), 'USERID')) { + isset($array[2]) && $this->_data['os_type'] = trim($array[2]); + isset($array[3]) && $this->_data['username'] = trim($array[3]); + return true; + } elseif (count($array) > 1 && ! strcasecmp(trim($array[1]), 'ERROR')) { + isset($array[2]) && $this->_error = trim($array[2]); + } else { + $this->_error = 'Invalid ident server response'; + } + return false; + } +} + +?> diff --git a/thirdparty/pear/Net/LMTP.php b/thirdparty/pear/Net/LMTP.php new file mode 100644 index 0000000..e5fd17a --- /dev/null +++ b/thirdparty/pear/Net/LMTP.php @@ -0,0 +1,729 @@ + | +// | Chuck Hagenbuch | +// | Jon Parise | +// | | +// +----------------------------------------------------------------------+ + +require_once 'Net/Socket.php'; + +/** + * Provides an implementation of the LMTP protocol using PEAR's + * Net_Socket:: class. + * + * @package Net_LMTP + * @author Chuck Hagenbuch + * @author Jon Parise + * @author Damian Alejandro Fernandez Sosa + */ +class Net_LMTP { + + + /** + * The server to connect to. + * @var string + */ + var $_host; + + /** + * The port to connect to. + * @var int + */ + var $_port; + + /** + * The value to give when sending LHLO. + * @var string + */ + var $_localhost; + + /** + * Should debugging output be enabled? + * @var boolean + * @access private + */ + var $_debug = false; + + /** + * The socket resource being used to connect to the LMTP 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 LMTP server. + * @var array + * @access private + */ + var $_esmtp = array(); + + + /** + * The auth methods this class support + * @var array + */ + var $supportedAuthMethods=array('DIGEST-MD5', 'CRAM-MD5', 'LOGIN'); + + + /** + * The auth methods this class support + * @var array + */ + var $supportedSASLAuthMethods=array('DIGEST-MD5', 'CRAM-MD5'); + + + + + /** + * Instantiates a new Net_LMTP object, overriding any defaults + * with parameters that are passed in. + * + * @param string The server to connect to. + * @param int The port to connect to. + * @param string The value to give when sending LHLO. + * + * @access public + * @since 1.0 + */ + function Net_LMTP($host = 'localhost', $port = 2003, $localhost = 'localhost') + { + $this->_host = $host; + $this->_port = $port; + $this->_localhost = $localhost; + $this->_socket = new Net_Socket(); + + if ((@include_once 'Auth/SASL.php') == false) { + foreach($this->supportedSASLAuthMethods as $SASLMethod){ + $pos = array_search( $SASLMethod , $this->supportedAuthMethods); + unset($this->supportedAuthMethods[$pos]); + } + } + + + } + + /** + * Set the value of the debugging flag. + * + * @param boolean $debug New value for the debugging flag. + * + * @access public + * @since 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.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 LMTP server. + * + * @param string $command The LMTP 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.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 LMTP server. The reply consists of a response + * code and a response message. + * + * @param mixed $valid The set of valid response codes. These + * may be specified as an array of integer + * values or as a single integer value. + * + * @return mixed True if the server returned a valid response code or + * a PEAR_Error object is an error condition is reached. + * + * @access private + * @since 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 LMTP server. + * + * @return array A two-element array: the first element contains the + * response code as an integer and the second element + * contains the response's arguments as a string. + * + * @access public + * @since 1.0 + */ + function getResponse() + { + return array($this->_code, join("\n", $this->_arguments)); + } + + /** + * Attempt to connect to the LMTP server. + * + * @param int $timeout The timeout value (in seconds) for the + * socket connection. + * + * @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) + { + $result = $this->_socket->connect($this->_host, $this->_port, + false, $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 LMTP 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 LHLO command and obtain a list of ESMTP + * extensions available + * + * @return mixed Returns a PEAR_Error with an error message on any + * kind of failure, or true on success. + * + * @access private + * @since 1.0 + */ + function _negotiate() + { + if (PEAR::isError($error = $this->_put('LHLO', $this->_localhost))) { + return $error; + } + + if (PEAR::isError($this->_parseResponse(250))) { + return new PEAR_Error('LHLO 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.0 + */ + function _getBestAuthMethod() + { + $available_methods = explode(' ', $this->_esmtp['AUTH']); + + foreach ($this->supportedAuthMethods as $method) { + if (in_array($method, $available_methods)) { + return $method; + } + } + + return new PEAR_Error('No supported authentication methods'); + } + + /** + * Attempt to do LMTP authentication. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * @param string The requested authentication method. If none is + * specified, the best supported method will be used. + * + * @return mixed Returns a PEAR_Error with an error message on any + * kind of failure, or true on success. + * @access public + * @since 1.0 + */ + function auth($uid, $pwd , $method = '') + { + if (!array_key_exists('AUTH', $this->_esmtp)) { + return new PEAR_Error('LMTP server does no support authentication'); + } + + /* + * If no method has been specified, get the name of the best supported + * method advertised by the LMTP server. + */ + if (empty($method) || $method === true ) { + if (PEAR::isError($method = $this->_getBestAuthMethod())) { + /* Return the PEAR_Error object from _getBestAuthMethod(). */ + return $method; + } + } else { + $method = strtoupper($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.0 + */ + function _authDigest_MD5($uid, $pwd) + { + + + if (PEAR::isError($error = $this->_put('AUTH', 'DIGEST-MD5'))) { + return $error; + } + if (PEAR::isError($error = $this->_parseResponse(334))) { + 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; + } + if (PEAR::isError($error = $this->_parseResponse(334))) { + return $error; + } + + /* + * We don't use the protocol's third step because LMTP doesn't allow + * subsequent authentication, so we just silently ignore it. + */ + if (PEAR::isError($error = $this->_put(' '))) { + return $error; + } + 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.0 + */ + function _authCRAM_MD5($uid, $pwd) + { + + + if (PEAR::isError($error = $this->_put('AUTH', 'CRAM-MD5'))) { + return $error; + } + if (PEAR::isError($error = $this->_parseResponse(334))) { + 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; + } + 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.0 + */ + function _authLogin($uid, $pwd) + { + if (PEAR::isError($error = $this->_put('AUTH', 'LOGIN'))) { + return $error; + } + if (PEAR::isError($error = $this->_parseResponse(334))) { + return $error; + } + + if (PEAR::isError($error = $this->_put(base64_encode($uid)))) { + return $error; + } + if (PEAR::isError($error = $this->_parseResponse(334))) { + return $error; + } + + if (PEAR::isError($error = $this->_put(base64_encode($pwd)))) { + return $error; + } + 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.0 + */ + function _authPlain($uid, $pwd) + { + if (PEAR::isError($error = $this->_put('AUTH', 'PLAIN'))) { + return $error; + } + if (PEAR::isError($error = $this->_parseResponse(334))) { + return $error; + } + + $auth_str = base64_encode(chr(0) . $uid . chr(0) . $pwd); + + if (PEAR::isError($error = $this->_put($auth_str))) { + return $error; + } + if (PEAR::isError($error = $this->_parseResponse(235))) { + 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; + } + + /** + * 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) + { + + 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'); + } + } + + + /* + * 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); + + 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 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 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; + } +} + +?> diff --git a/thirdparty/pear/Net/POP3.php b/thirdparty/pear/Net/POP3.php new file mode 100644 index 0000000..316a142 --- /dev/null +++ b/thirdparty/pear/Net/POP3.php @@ -0,0 +1,1226 @@ + | +// | Co-Author: Damian Fernandez Sosa | +// +-----------------------------------------------------------------------+ +// +// $Id: POP3.php,v 1.2 2004/12/05 16:34:39 damian Exp $ + +require_once('Net/Socket.php'); + + + +/** +* +----------------------------- IMPORTANT ------------------------------+ +* | Usage of this class compared to native php extensions such as IMAP | +* | is slow and may be feature deficient. If available you are STRONGLY | +* | recommended to use the php extensions. | +* +----------------------------------------------------------------------+ +* +* POP3 Access Class +* +* For usage see the example script +*/ + +define('NET_POP3_STATE_DISCONNECTED', 1, true); +define('NET_POP3_STATE_AUTHORISATION', 2, true); +define('NET_POP3_STATE_TRANSACTION', 4, true); + +class Net_POP3 { + + /* + * Some basic information about the mail drop + * garnered from the STAT command + * + * @var array + */ + var $_maildrop; + + /* + * Used for APOP to store the timestamp + * + * @var string + */ + var $_timestamp; + + /* + * Timeout that is passed to the socket object + * + * @var integer + */ + var $_timeout; + + /* + * Socket object + * + * @var object + */ + var $_socket; + + /* + * Current state of the connection. Used with the + * constants defined above. + * + * @var integer + */ + var $_state; + + /* + * Hostname to connect to + * + * @var string + */ + var $_host; + + /* + * Port to connect to + * + * @var integer + */ + var $_port; + + /** + * To allow class debuging + * @var boolean + */ + var $_debug = false; + + + /** + * The auth methods this class support + * @var array + */ + //var $supportedAuthMethods=array('DIGEST-MD5', 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER'); + //Disabling DIGEST-MD5 for now + var $supportedAuthMethods=array( 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER'); + //var $supportedAuthMethods=array( 'CRAM-MD5', 'PLAIN' , 'LOGIN'); + //var $supportedAuthMethods=array( 'PLAIN' , 'LOGIN'); + + + /** + * The auth methods this class support + * @var array + */ + var $supportedSASLAuthMethods=array('DIGEST-MD5', 'CRAM-MD5'); + + + /** + * The capability response + * @var array + */ + var $_capability; + + /* + * Constructor. Sets up the object variables, and instantiates + * the socket object. + * + */ + + + function Net_POP3() + { + $this->_timestamp = ''; // Used for APOP + $this->_maildrop = array(); + $this->_timeout = 3; + $this->_state = NET_POP3_STATE_DISCONNECTED; + $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) { + if($this->_debug){ + echo "AUTH_SASL NOT PRESENT!\n"; + } + foreach($this->supportedSASLAuthMethods as $SASLMethod){ + $pos = array_search( $SASLMethod, $this->supportedAuthMethods ); + if($this->_debug){ + echo "DISABLING METHOD $SASLMethod\n"; + } + unset($this->supportedAuthMethods[$pos]); + } + } + + + + } + + + /** + * Handles the errors the class can find + * on the server + * + * @access private + * @return PEAR_Error + */ + + function _raiseError($msg, $code =-1) + { + include_once 'PEAR.php'; + return PEAR::raiseError($msg, $code); + } + + + + /* + * Connects to the given host on the given port. + * Also looks for the timestamp in the greeting + * needed for APOP authentication + * + * @param string $host Hostname/IP address to connect to + * @param string $port Port to use to connect to on host + * @return bool Success/Failure + */ + function connect($host = 'localhost', $port = 110) + { + $this->_host = $host; + $this->_port = $port; + + $result = $this->_socket->connect($host, $port, false, $this->_timeout); + if ($result === true) { + $data = $this->_recvLn(); + + if( $this->_checkResponse($data) ){ + // if the response begins with '+OK' ... +// if (@substr(strtoupper($data), 0, 3) == '+OK') { + // Check for string matching apop timestamp + if (preg_match('/<.+@.+>/U', $data, $matches)) { + $this->_timestamp = $matches[0]; + } + $this->_maildrop = array(); + $this->_state = NET_POP3_STATE_AUTHORISATION; + + return true; + } + } + + $this->_socket->disconnect(); + return false; + } + + /* + * Disconnect function. Sends the QUIT command + * and closes the socket. + * + * @return bool Success/Failure + */ + function disconnect() + { + return $this->_cmdQuit(); + } + + /* + * Performs the login procedure. If there is a timestamp + * stored, APOP will be tried first, then basic USER/PASS. + * + * @param string $user Username to use + * @param string $pass Password to use + * @param mixed $apop Whether to try APOP first, if used as string you can select the auth methd to use ( $pop3->login('validlogin', 'validpass', "CRAM-MD5"); + * Valid methods are: 'DIGEST-MD5','CRAM-MD5','LOGIN','PLAIN','APOP','USER' + * @return mixed true on Success/ PEAR_ERROR on error + */ + function login($user, $pass, $apop = true) + { + if ($this->_state == NET_POP3_STATE_AUTHORISATION) { + + if(PEAR::isError($ret= $this->_cmdAuthenticate($user , $pass , $apop ) ) ){ + return $ret; + } + if( ! PEAR::isError($ret)){ + $this->_state = NET_POP3_STATE_TRANSACTION; + return true; + } + + } + return $this->_raiseError('Generic login error' , 1); + } + + + + /** + * Parses the response from the capability command. Stores + * the result in $this->_capability + * + * @access private + */ + function _parseCapability() + { + + if(!PEAR::isError($data = $this->_sendCmd('CAPA'))){ + $data = $this->_getMultiline(); + }else { + // CAPA command not supported, reset data var + // to avoid Notice errors of preg_split on an object + $data = ''; + } + $data = preg_split('/\r?\n/', $data, -1, PREG_SPLIT_NO_EMPTY); + + for ($i = 0; $i < count($data); $i++) { + + $capa=''; + if (preg_match('/^([a-z,\-]+)( ((.*))|$)$/i', $data[$i], $matches)) { + + $capa=strtolower($matches[1]); + switch ($capa) { + case 'implementation': + $this->_capability['implementation'] = $matches[3]; + break; + case 'sasl': + $this->_capability['sasl'] = preg_split('/\s+/', $matches[3]); + break; + default : + $this->_capability[$capa] = $matches[2]; + break; + } + } + } + } + + + + + /** + * Returns the name of the best authentication method that the server + * has advertised. + * + * @param string if !=null,authenticate with this method ($userMethod). + * + * @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.0 + */ + function _getBestAuthMethod($userMethod = null) + { + +/* + return 'USER'; + return 'APOP'; + return 'DIGEST-MD5'; + return 'CRAM-MD5'; +*/ + + + $this->_parseCapability(); + + //unset($this->_capability['sasl']); + + if( isset($this->_capability['sasl']) ){ + $serverMethods=$this->_capability['sasl']; + }else{ + $serverMethods=array('USER'); + // Check for timestamp before attempting APOP + if ($this->_timestamp != null) + { + $serverMethods[] = 'APOP'; + } + } + + if($userMethod !== null && $userMethod !== true ){ + $methods = array(); + $methods[] = $userMethod; + return $userMethod; + }else{ + $methods = $this->supportedAuthMethods; + } + + if( ($methods != null) && ($serverMethods != null)){ + + foreach ( $methods as $method ) { + + if ( in_array( $method , $serverMethods ) ) { + return $method; + } + } + $serverMethods=implode(',' , $serverMethods ); + $myMethods=implode(',' ,$this->supportedAuthMethods); + return $this->_raiseError("$method NOT supported authentication method!. This server " . + "supports these methods: $serverMethods, but I support $myMethods"); + }else{ + return $this->_raiseError("This server don't support any Auth methods"); + } + } + + + + + + + /* Handles the authentication using any known method + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * @param string The method to use ( if $usermethod == '' then the class chooses the best method (the stronger is the best ) ) + * + * @return mixed string or PEAR_Error + * + * @access private + * @since 1.0 + */ + function _cmdAuthenticate($uid , $pwd , $userMethod = null ) + { + + + if ( PEAR::isError( $method = $this->_getBestAuthMethod($userMethod) ) ) { + return $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; + case 'APOP': + $result = $this->_cmdApop( $uid , $pwd ); + // if APOP fails fallback to USER auth + if( PEAR::isError( $result ) ){ + //echo "APOP FAILED!!!\n"; + $result=$this->_authUSER( $uid , $pwd ); + } + break; + case 'USER': + $result = $this->_authUSER( $uid , $pwd ); + break; + + + default : + $result = $this->_raiseError( "$method is not a supported authentication method" ); + break; + } + return $result; + } + + + + + /* Authenticates the user using the USER-PASS method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * + * @return mixed true on success or PEAR_Error on failure + * + * @access private + * @since 1.0 + */ + function _authUSER($user, $pass ) + { + if ( PEAR::isError($ret=$this->_cmdUser($user) ) ){ + return $ret; + } + if ( PEAR::isError($ret=$this->_cmdPass($pass) ) ){ + return $ret; + } + return true; + } + + + + + + + + + /* Authenticates the user using the PLAIN method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * + * @return array Returns an array containing the response + * + * @access private + * @since 1.0 + */ + function _authPLAIN($user, $pass ) + { + $cmd=sprintf('AUTH PLAIN %s', base64_encode( chr(0) . $user . chr(0) . $pass ) ); + + if ( PEAR::isError( $ret = $this->_send($cmd) ) ) { + return $ret; + } + if ( PEAR::isError( $challenge = $this->_recvLn() ) ){ + return $challenge; + } + if( PEAR::isError($ret=$this->_checkResponse($challenge) )){ + return $ret; + } + + return true; + } + + + + /* Authenticates the user using the PLAIN method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * + * @return array Returns an array containing the response + * + * @access private + * @since 1.0 + */ + function _authLOGIN($user, $pass ) + { + $this->_send('AUTH LOGIN'); + + if ( PEAR::isError( $challenge = $this->_recvLn() ) ) { + return $challenge; + } + if( PEAR::isError($ret=$this->_checkResponse($challenge) )){ + return $ret; + } + + + if ( PEAR::isError( $ret = $this->_send(sprintf('%s', base64_encode($user))) ) ) { + return $ret; + } + + if ( PEAR::isError( $challenge = $this->_recvLn() ) ) { + return $challenge; + } + if( PEAR::isError($ret=$this->_checkResponse($challenge) )){ + return $ret; + } + + if ( PEAR::isError( $ret = $this->_send(sprintf('%s', base64_encode($pass))) ) ) { + return $ret; + } + + if ( PEAR::isError( $challenge = $this->_recvLn() ) ) { + return $challenge; + } + return $this->_checkResponse($challenge); + } + + + + + + /* Authenticates the user using the CRAM-MD5 method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * + * @return array Returns an array containing the response + * + * @access private + * @since 1.0 + */ + function _authCRAM_MD5($uid, $pwd ) + { + if ( PEAR::isError( $ret = $this->_send( 'AUTH CRAM-MD5' ) ) ) { + return $ret; + } + + if ( PEAR::isError( $challenge = $this->_recvLn() ) ) { + return $challenge; + } + if( PEAR::isError($ret=$this->_checkResponse($challenge) )){ + return $ret; + } + + // remove '+ ' + + $challenge=substr($challenge,2); + + $challenge = base64_decode( $challenge ); + + $cram = &Auth_SASL::factory('crammd5'); + $auth_str = base64_encode( $cram->getResponse( $uid , $pwd , $challenge ) ); + + + if ( PEAR::isError($error = $this->_send( $auth_str ) ) ) { + return $error; + } + if ( PEAR::isError( $ret = $this->_recvLn() ) ) { + return $ret; + } + //echo "RET:$ret\n"; + return $this->_checkResponse($ret); + } + + + + /* Authenticates the user using the DIGEST-MD5 method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * @param string The efective user + * + * @return array Returns an array containing the response + * + * @access private + * @since 1.0 + */ + function _authDigest_MD5($uid, $pwd) + { + if ( PEAR::isError( $ret = $this->_send( 'AUTH DIGEST-MD5' ) ) ) { + return $ret; + } + + if ( PEAR::isError( $challenge = $this->_recvLn() ) ) { + return $challenge; + } + if( PEAR::isError($ret=$this->_checkResponse($challenge) )){ + return $ret; + } + + // remove '+ ' + $challenge=substr($challenge,2); + + $challenge = base64_decode( $challenge ); + $digest = &Auth_SASL::factory('digestmd5'); + $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge, "localhost", "pop3" )); + + if ( PEAR::isError($error = $this->_send( $auth_str ) ) ) { + return $error; + } + + if ( PEAR::isError( $challenge = $this->_recvLn() ) ) { + return $challenge; + } + if( PEAR::isError($ret=$this->_checkResponse($challenge) )){ + return $ret; + } + /* + * We don't use the protocol's third step because POP3 doesn't allow + * subsequent authentication, so we just silently ignore it. + */ + + if ( PEAR::isError( $challenge = $this->_send("\r\n") ) ) { + return $challenge ; + } + + if ( PEAR::isError( $challenge = $this->_recvLn() ) ) { + return $challenge; + } + + return $this->_checkResponse($challenge); + + + } + + + + + + + + + + + /* + * Sends the APOP command + * + * @param $user Username to send + * @param $pass Password to send + * @return bool Success/Failure + */ + function _cmdApop($user, $pass) + { + if ($this->_state == NET_POP3_STATE_AUTHORISATION) { + + if (!empty($this->_timestamp)) { + if(PEAR::isError($data = $this->_sendCmd('APOP ' . $user . ' ' . md5($this->_timestamp . $pass)) ) ){ + return $data; + } + $this->_state = NET_POP3_STATE_TRANSACTION; + return true; + } + } + return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State1'); + } + + + + + + + + + + + + + + + + /* + * Returns the raw headers of the specified message. + * + * @param integer $msg_id Message number + * @return mixed Either raw headers or false on error + */ + function getRawHeaders($msg_id) + { + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + return $this->_cmdTop($msg_id, 0); + } + + return false; + } + + /* + * Returns the headers of the specified message in an + * associative array. Array keys are the header names, array + * values are the header values. In the case of multiple headers + * having the same names, eg Received:, the array value will be + * an indexed array of all the header values. + * + * @param integer $msg_id Message number + * @return mixed Either array of headers or false on error + */ + function getParsedHeaders($msg_id) + { + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + + $raw_headers = rtrim($this->getRawHeaders($msg_id)); + + $raw_headers = preg_replace("/\r\n[ \t]+/", ' ', $raw_headers); // Unfold headers + $raw_headers = explode("\r\n", $raw_headers); + foreach ($raw_headers as $value) { + $name = substr($value, 0, $pos = strpos($value, ':')); + $value = ltrim(substr($value, $pos + 1)); + if (isset($headers[$name]) AND is_array($headers[$name])) { + $headers[$name][] = $value; + } elseif (isset($headers[$name])) { + $headers[$name] = array($headers[$name], $value); + } else { + $headers[$name] = $value; + } + } + + return $headers; + } + + return false; + } + + /* + * Returns the body of the message with given message number. + * + * @param integer $msg_id Message number + * @return mixed Either message body or false on error + */ + function getBody($msg_id) + { + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + $msg = $this->_cmdRetr($msg_id); + return substr($msg, strpos($msg, "\r\n\r\n")+4); + } + + return false; + } + + /* + * Returns the entire message with given message number. + * + * @param integer $msg_id Message number + * @return mixed Either entire message or false on error + */ + function getMsg($msg_id) + { + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + return $this->_cmdRetr($msg_id); + } + + return false; + } + + /* + * Returns the size of the maildrop + * + * @return mixed Either size of maildrop or false on error + */ + function getSize() + { + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + if (isset($this->_maildrop['size'])) { + return $this->_maildrop['size']; + } else { + list(, $size) = $this->_cmdStat(); + return $size; + } + } + + return false; + } + + /* + * Returns number of messages in this maildrop + * + * @return mixed Either number of messages or false on error + */ + function numMsg() + { + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + if (isset($this->_maildrop['num_msg'])) { + return $this->_maildrop['num_msg']; + } else { + list($num_msg, ) = $this->_cmdStat(); + return $num_msg; + } + } + + return false; + } + + /* + * Marks a message for deletion. Only will be deleted if the + * disconnect() method is called. + * + * @param integer $msg_id Message to delete + * @return bool Success/Failure + */ + function deleteMsg($msg_id) + { + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + return $this->_cmdDele($msg_id); + } + + return false; + } + + /* + * Combination of LIST/UIDL commands, returns an array + * of data + * + * @param integer $msg_id Optional message number + * @return mixed Array of data or false on error + */ + function getListing($msg_id = null) + { + + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + if (!isset($msg_id)){ + + $list=array(); + if ($list = $this->_cmdList()) { + if ($uidl = $this->_cmdUidl()) { + foreach ($uidl as $i => $value) { + $list[$i]['uidl'] = $value['uidl']; + } + } + return $list; + }else{ + return array(); + } + } else { + if ($list = $this->_cmdList($msg_id) AND $uidl = $this->_cmdUidl($msg_id)) { + return array_merge($list, $uidl); + } + } + } + + return false; + } + + /* + * Sends the USER command + * + * @param string $user Username to send + * @return bool Success/Failure + */ + function _cmdUser($user) + { + if ($this->_state == NET_POP3_STATE_AUTHORISATION) { + return $this->_sendCmd('USER ' . $user); + } + return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State'); + } + + + /* + * Sends the PASS command + * + * @param string $pass Password to send + * @return bool Success/Failure + */ + function _cmdPass($pass) + { + if ($this->_state == NET_POP3_STATE_AUTHORISATION) { + return $this->_sendCmd('PASS ' . $pass); + } + return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State'); + } + + + /* + * Sends the STAT command + * + * @return mixed Indexed array of number of messages and + * maildrop size, or false on error. + */ + function _cmdStat() + { + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + if(!PEAR::isError($data = $this->_sendCmd('STAT'))){ + sscanf($data, '+OK %d %d', $msg_num, $size); + $this->_maildrop['num_msg'] = $msg_num; + $this->_maildrop['size'] = $size; + + return array($msg_num, $size); + } + } + return false; + } + + + /* + * Sends the LIST command + * + * @param integer $msg_id Optional message number + * @return mixed Indexed array of msg_id/msg size or + * false on error + */ + function _cmdList($msg_id = null) + { + $return=array(); + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + if (!isset($msg_id)) { + if(!PEAR::isError($data = $this->_sendCmd('LIST') )){ + $data = $this->_getMultiline(); + $data = explode("\r\n", $data); + foreach ($data as $line) { + if($line !=''){ + sscanf($line, '%s %s', $msg_id, $size); + $return[] = array('msg_id' => $msg_id, 'size' => $size); + } + } + return $return; + } + } else { + if(!PEAR::isError($data = $this->_sendCmd('LIST ' . $msg_id))){ + if($data!=''){ + sscanf($data, '+OK %d %d', $msg_id, $size); + return array('msg_id' => $msg_id, 'size' => $size); + } + return array(); + } + } + } + + + return false; + } + + + /* + * Sends the RETR command + * + * @param integer $msg_id The message number to retrieve + * @return mixed The message or false on error + */ + function _cmdRetr($msg_id) + { + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + if(!PEAR::isError($data = $this->_sendCmd('RETR ' . $msg_id) )){ + $data = $this->_getMultiline(); + return $data; + } + } + + return false; + } + + + /* + * Sends the DELE command + * + * @param integer $msg_id Message number to mark as deleted + * @return bool Success/Failure + */ + function _cmdDele($msg_id) + { + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + return $this->_sendCmd('DELE ' . $msg_id); + } + + return false; + } + + + /* + * Sends the NOOP command + * + * @return bool Success/Failure + */ + function _cmdNoop() + { + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + if(!PEAR::isError($data = $this->_sendCmd('NOOP'))){ + return true; + } + } + + return false; + } + + /* + * Sends the RSET command + * + * @return bool Success/Failure + */ + function _cmdRset() + { + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + if(!PEAR::isError($data = $this->_sendCmd('RSET'))){ + return true; + } + } + + return false; + } + + /* + * Sends the QUIT command + * + * @return bool Success/Failure + */ + function _cmdQuit() + { + $data = $this->_sendCmd('QUIT'); + $this->_state = NET_POP3_STATE_DISCONNECTED; + $this->_socket->disconnect(); + + return (bool)$data; + } + + + /* + * Sends the TOP command + * + * @param integer $msg_id Message number + * @param integer $num_lines Number of lines to retrieve + * @return mixed Message data or false on error + */ + function _cmdTop($msg_id, $num_lines) + { + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + + if(!PEAR::isError($data = $this->_sendCmd('TOP ' . $msg_id . ' ' . $num_lines))){ + return $this->_getMultiline(); + } + } + + return false; + } + + /* + * Sends the UIDL command + * + * @param integer $msg_id Message number + * @return mixed indexed array of msg_id/uidl or false on error + */ + function _cmdUidl($msg_id = null) + { + if ($this->_state == NET_POP3_STATE_TRANSACTION) { + + if (!isset($msg_id)) { + if(!PEAR::isError($data = $this->_sendCmd('UIDL') )){ + $data = $this->_getMultiline(); + $data = explode("\r\n", $data); + foreach ($data as $line) { + sscanf($line, '%d %s', $msg_id, $uidl); + $return[] = array('msg_id' => $msg_id, 'uidl' => $uidl); + } + + return $return; + } + } else { + + $data = $this->_sendCmd('UIDL ' . $msg_id); + sscanf($data, '+OK %d %s', $msg_id, $uidl); + return array('msg_id' => $msg_id, 'uidl' => $uidl); + } + } + + return false; + } + + + + + + + + + + /* + * Sends a command, checks the reponse, and + * if good returns the reponse, other wise + * returns false. + * + * @param string $cmd Command to send (\r\n will be appended) + * @return mixed First line of response if successful, otherwise false + */ + function _sendCmd($cmd) + { + if (PEAR::isError($result = $this->_send($cmd) )){ + return $result ; + } + + if (PEAR::isError($data = $this->_recvLn() )){ + return $data; + } + + if ( strtoupper(substr($data, 0, 3)) == '+OK') { + return $data; + } + + + return $this->_raiseError($data); + } + + /* + * Reads a multiline reponse and returns the data + * + * @return string The reponse. + */ + function _getMultiline() + { + $data = ''; + while(!PEAR::isError($tmp = $this->_recvLn() ) ) { + if($tmp == '.'){ + return substr($data, 0, -2); + } + if (substr($tmp, 0, 2) == '..') { + $tmp = substr($tmp, 1); + } + $data .= $tmp . "\r\n"; + } + return substr($data, 0, -2); + } + + + /** + * Sets the bebug state + * + * @param bool $debug + * @access public + * @return void + */ + function setDebug($debug=true) + { + $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.0 + */ + function _send($data) + { + if ($this->_debug) { + echo "C: $data\n"; + } + + if (PEAR::isError($error = $this->_socket->writeLine($data))) { + return $this->_raiseError('Failed to write to socket: ' . $error->getMessage()); + } + return true; + } + + + + /** + * Receive the given string of data from the server. + * + * @return mixed a line of response on success or a PEAR_Error object on failure. + * + * @access private + * @since 1.0 + */ + function _recvLn() + { + if (PEAR::isError( $lastline = $this->_socket->readLine( 8192 ) ) ) { + return $this->_raiseError('Failed to write to socket: ' . $this->lastline->getMessage() ); + } + if($this->_debug){ + // S: means this data was sent by the POP3 Server + echo "S:$lastline\n" ; + } + return $lastline; + } + + /** + * Checks de server Response + * + * @param string $response the response + * @return mixed true on success or a PEAR_Error object on failure. + * + * @access private + * @since 1.3.3 + */ + + function _checkResponse($response) + { + if (@substr(strtoupper($response), 0, 3) == '+OK') { + return true; + }else{ + if (@substr(strtoupper($response), 0, 4) == '-ERR') { + return $this->_raiseError($response); + }else{ + if (@substr(strtoupper($response), 0, 2) == '+ ') { + return true; + } + } + + } + return $this->_raiseError("Unknown Response ($response)"); + } + + + +} + +?> diff --git a/thirdparty/pear/Net/Ping.php b/thirdparty/pear/Net/Ping.php new file mode 100644 index 0000000..21f9fda --- /dev/null +++ b/thirdparty/pear/Net/Ping.php @@ -0,0 +1,1098 @@ + | +// | Tomas V.V.Cox | +// | Jan Lehnardt | +// | Kai Schröder | +// +----------------------------------------------------------------------+ +// +// $Id: Ping.php,v 1.36 2004/02/08 23:17:22 jan Exp $ + +require_once "PEAR.php"; +require_once "OS/Guess.php"; + +define('NET_PING_FAILED_MSG', 'execution of ping failed' ); +define('NET_PING_HOST_NOT_FOUND_MSG', 'unknown host' ); +define('NET_PING_INVALID_ARGUMENTS_MSG', 'invalid argument array' ); +define('NET_PING_CANT_LOCATE_PING_BINARY_MSG', 'unable to locate the ping binary'); +define('NET_PING_RESULT_UNSUPPORTED_BACKEND_MSG', 'Backend not Supported' ); + +define('NET_PING_FAILED', 0); +define('NET_PING_HOST_NOT_FOUND', 1); +define('NET_PING_INVALID_ARGUMENTS', 2); +define('NET_PING_CANT_LOCATE_PING_BINARY', 3); +define('NET_PING_RESULT_UNSUPPORTED_BACKEND', 4); + +/**************************TODO*******************************************/ +/* + * + * - add Net_Ping_Result parser for: + * + IRIX64 + * + OSF1 + * + BSD/OS + * + OpenBSD + * - fix Net_Ping::checkHost() + * - reset result buffer + */ + +/** +* Wrapper class for ping calls +* +* Usage: +* +* getMessage(); +* } else { +* $ping->setArgs(array('count' => 2)); +* var_dump($ping->ping('example.com')); +* } +* ?> +* +* @author Jan Lehnardt +* @version $Revision: 1.36 $ +* @package Net +* @access public +*/ +class Net_Ping +{ + /** + * Location where the ping program is stored + * + * @var string + * @access private + */ + var $_ping_path = ""; + + /** + * Array with the result from the ping execution + * + * @var array + * @access private + */ + var $_result = array(); + + /** + * OS_Guess instance + * + * @var object + * @access private + */ + var $_OS_Guess = ""; + + /** + * OS_Guess->getSysname result + * + * @var string + * @access private + */ + var $_sysname = ""; + + /** + * Ping command arguments + * + * @var array + * @access private + */ + var $_args = array(); + + /** + * Indicates if an empty array was given to setArgs + * + * @var boolean + * @access private + */ + var $_noArgs = true; + + /** + * Contains the argument->option relation + * + * @var array + * @access private + */ + var $_argRelation = array(); + + /** + * Constructor for the Class + * + * @access private + */ + function Net_Ping($ping_path, $sysname) + { + $this->_ping_path = $ping_path; + $this->_sysname = $sysname; + $this->_initArgRelation(); + } /* function Net_Ping() */ + + /** + * Factory for Net_Ping + * + * @access public + */ + function factory() + { + $ping_path = ''; + + $sysname = Net_Ping::_setSystemName(); + + if (($ping_path = Net_Ping::_setPingPath($sysname)) == NET_PING_CANT_LOCATE_PING_BINARY) { + return PEAR::throwError(NET_PING_CANT_LOCATE_PING_BINARY_MSG, NET_PING_CANT_LOCATE_PING_BINARY); + } else { + return new Net_Ping($ping_path, $sysname); + } + } /* function factory() */ + + /** + * Resolve the system name + * + * @access private + */ + function _setSystemName() + { + $OS_Guess = new OS_Guess; + $sysname = $OS_Guess->getSysname(); + + /* Nasty hack for Debian, as it uses a custom ping version */ + if ('linux' == $sysname) { + if (file_exists('/etc/debian_version')) { + $sysname = 'debian'; + } + } + + return $sysname; + + } /* function _setSystemName */ + + /** + * Set the arguments array + * + * @param array $args Hash with options + * @return mixed true or PEAR_error + * @access public + */ + function setArgs($args) + { + if (!is_array($args)) { + return PEAR::throwError(NET_PING_INVALID_ARGUMENTS_MSG, NET_PING_INVALID_ARGUMENTS); + } + + $this->_setNoArgs($args); + + $this->_args = $args; + + return true; + } /* function setArgs() */ + + /** + * Set the noArgs flag + * + * @param array $args Hash with options + * @return void + * @access private + */ + function _setNoArgs($args) + { + if (0 == count($args)) { + $this->_noArgs = true; + } else { + $this->_noArgs = false; + } + } /* function _setNoArgs() */ + + /** + * Sets the system's path to the ping binary + * + * @access private + */ + function _setPingPath($sysname) + { + $status = ''; + $output = array(); + $ping_path = ''; + + if ("windows" == $sysname) { + return "ping"; + } else { + $ping_path = exec("which ping", $output, $status); + if (0 != $status) { + return NET_PING_CANT_LOCATE_PING_BINARY; + } else { + return $ping_path; + } + } + } /* function _setPingPath() */ + + /** + * Creates the argument list according to platform differences + * + * @return string Argument line + * @access private + */ + function _createArgList() + { + $retval = array(); + + $timeout = ""; + $iface = ""; + $ttl = ""; + $count = ""; + $quiet = ""; + $size = ""; + $seq = ""; + $deadline = ""; + + foreach($this->_args AS $option => $value) { + if(!empty($option) && isset($this->_argRelation[$this->_sysname][$option]) && NULL != $this->_argRelation[$this->_sysname][$option]) { + ${$option} = $this->_argRelation[$this->_sysname][$option]." ".$value." "; + } + } + + switch($this->_sysname) { + + case "sunos": + if ($size || $count || $iface) { + /* $size and $count must be _both_ defined */ + $seq = " -s "; + if ($size == "") { + $size = " 56 "; + } + if ($count == "") { + $count = " 5 "; + } + } + $retval['pre'] = $iface.$seq.$ttl; + $retval['post'] = $size.$count; + break; + + case "freebsd": + $retval['pre'] = $quiet.$count.$ttl.$timeout; + $retval['post'] = ""; + break; + + case "darwin": + $retval['pre'] = $count.$timeout.$size; + $retval['post'] = ""; + break; + + case "netbsd": + $retval['pre'] = $quiet.$count.$iface.$size.$ttl.$timeout; + $retval['post'] = ""; + break; + + case "linux": + $retval['pre'] = $quiet.$deadline.$count.$ttl.$size.$timeout; + $retval['post'] = ""; + break; + + case "debian": + $retval['pre'] = $quiet.$count.$ttl.$size.$timeout; + $retval['post'] = ""; + + /* undo nasty debian hack*/ + $this->_sysname = 'linux'; + break; + + case "windows": + $retval['pre'] = $count.$ttl.$timeout; + $retval['post'] = ""; + break; + + case "hpux": + $retval['pre'] = $ttl; + $retval['post'] = $size.$count; + break; + + case "aix": + $retval['pre'] = $count.$timeout.$ttl.$size; + $retval['post'] = ""; + break; + + default: + $retval['pre'] = ""; + $retval['post'] = ""; + break; + } + return($retval); + } /* function _createArgList() */ + + /** + * Execute ping + * + * @param string $host hostname + * @return mixed String on error or array with the result + * @access public + */ + function ping($host) + { + + if($this->_noArgs) { + $this->setArgs(array('count' => 3)); + } + + $argList = $this->_createArgList(); + $cmd = $this->_ping_path." ".$argList['pre']." ".$host." ".$argList['post']; + exec($cmd, $this->_result); + + if (!is_array($this->_result)) { + return PEAR::throwError(NET_PING_FAILED_MSG, NET_PING_FAILED); + } + + if (count($this->_result) == 0) { + return PEAR::throwError(NET_PING_HOST_NOT_FOUND_MSG, NET_PING_HOST_NOT_FOUND); + } else { + return Net_Ping_Result::factory($this->_result, $this->_sysname); + } + } /* function ping() */ + + /** + * Check if a host is up by pinging it + * + * @param string $host The host to test + * @param bool $severely If some of the packages did reach the host + * and severely is false the function will return true + * @return bool True on success or false otherwise + * + */ + function checkHost($host, $severely = true) + { + $matches = array(); + + $this->setArgs(array("count" => 10, + "size" => 32, + "quiet" => null, + "deadline" => 10 + ) + ); + $res = $this->ping($host); + if (PEAR::isError($res)) { + return false; + } + if (!preg_match_all('|\d+|', $res[3], $matches) || count($matches[0]) < 3) { + ob_start(); + $rep = ob_get_contents(); + ob_end_clean(); + trigger_error("Output format seems not to be supported, please report ". + "the following to pear-dev@lists.php.net, including your ". + "version of ping:\n $rep"); + return false; + } + if ($matches[0][1] == 0) { + return false; + } + // [0] => transmitted, [1] => received + if ($matches[0][0] != $matches[0][1] && $severely) { + return false; + } + return true; + } /* function checkHost() */ + + /** + * Output errors with PHP trigger_error(). You can silence the errors + * with prefixing a "@" sign to the function call: @Net_Ping::ping(..); + * + * @param mixed $error a PEAR error or a string with the error message + * @return bool false + * @access private + * @author Kai Schröder + */ + function _raiseError($error) + { + if (PEAR::isError($error)) { + $error = $error->getMessage(); + } + trigger_error($error, E_USER_WARNING); + return false; + } /* function _raiseError() */ + + /** + * Creates the argument list according to platform differences + * + * @return string Argument line + * @access private + */ + function _initArgRelation() + { + $this->_argRelation["sunos"] = array( + "timeout" => NULL, + "ttl" => "-t", + "count" => " ", + "quiet" => "-q", + "size" => " ", + "iface" => "-i" + ); + + $this->_argRelation["freebsd"] = array ( + "timeout" => "-t", + "ttl" => "-m", + "count" => "-c", + "quiet" => "-q", + "size" => NULL, + "iface" => NULL + ); + + $this->_argRelation["netbsd"] = array ( + "timeout" => "-w", + "iface" => "-I", + "ttl" => "-T", + "count" => "-c", + "quiet" => "-q", + "size" => "-s" + ); + + $this->_argRelation["openbsd"] = array ( + "timeout" => "-w", + "iface" => "-I", + "ttl" => "-t", + "count" => "-c", + "quiet" => "-q", + "size" => "-s" + ); + + $this->_argRelation["darwin"] = array ( + "timeout" => "-t", + "iface" => NULL, + "ttl" => NULL, + "count" => "-c", + "quiet" => "-q", + "size" => NULL + ); + + $this->_argRelation["linux"] = array ( + "timeout" => "-t", + "iface" => NULL, + "ttl" => "-m", + "count" => "-c", + "quiet" => "-q", + "size" => "-s", + "deadline" => "-w" + ); + + $this->_argRelation["debian"] = array ( + "timeout" => "-t", + "iface" => NULL, + "ttl" => "-m", + "count" => "-c", + "quiet" => "-q", + "size" => "-s", + ); + + $this->_argRelation["windows"] = array ( + "timeout" => "-w", + "iface" => NULL, + "ttl" => "-i", + "count" => "-n", + "quiet" => NULL, + "size" => "-l" + ); + + $this->_argRelation["hpux"] = array ( + "timeout" => NULL, + "iface" => NULL, + "ttl" => "-t", + "count" => "-n", + "quiet" => NULL, + "size" => " " + ); + + $this->_argRelation["aix"] = array ( + "timeout" => "-i", + "iface" => NULL, + "ttl" => "-T", + "count" => "-c", + "quiet" => NULL, + "size" => "-s" + ); + } /* function _initArgRelation() */ +} /* class Net_Ping */ + +/** +* Container class for Net_Ping results +* +* @author Jan Lehnardt +* @version $Revision: 1.36 $ +* @package Net +* @access private +*/ +class Net_Ping_Result +{ + /** + * ICMP sequence number and associated time in ms + * + * @var array + * @access private + */ + var $_icmp_sequence = array(); /* array($sequence_number => $time ) */ + + /** + * The target's IP Address + * + * @var string + * @access private + */ + var $_target_ip; + + /** + * Number of bytes that are sent with each ICMP request + * + * @var int + * @access private + */ + var $_bytes_per_request; + + /** + * The total number of bytes that are sent with all ICMP requests + * + * @var int + * @access private + */ + var $_bytes_total; + + /** + * The ICMP request's TTL + * + * @var int + * @access private + */ + var $_ttl; + + /** + * The raw Net_Ping::result + * + * @var array + * @access private + */ + var $_raw_data = array(); + + /** + * The Net_Ping::_sysname + * + * @var int + * @access private + */ + var $_sysname; + + /** + * Statistical information about the ping + * + * @var int + * @access private + */ + var $_round_trip = array(); /* array('min' => xxx, 'avg' => yyy, 'max' => zzz) */ + + + /** + * Constructor for the Class + * + * @access private + */ + function Net_Ping_Result($result, $sysname) + { + $this->_raw_data = $result; + $this->_sysname = $sysname; + + $this->_parseResult(); + } /* function Net_Ping_Result() */ + + /** + * Factory for Net_Ping_Result + * + * @access public + * @param array $result Net_Ping result + * @param string $sysname OS_Guess::sysname + */ + function factory($result, $sysname) + { + if (!Net_Ping_Result::_prepareParseResult($sysname)) { + return PEAR::throwError(NET_PING_RESULT_UNSUPPORTED_BACKEND_MSG, NET_PING_RESULT_UNSUPPORTED_BACKEND); + } else { + return new Net_Ping_Result($result, $sysname); + } + } /* function factory() */ + + /** + * Preparation method for _parseResult + * + * @access private + * @param string $sysname OS_Guess::sysname + * $return bool + */ + function _prepareParseResult($sysname) + { + $parse_methods = array_values(get_class_methods('Net_Ping_Result')); + + return in_array('_parseresult'.$sysname, $parse_methods); + } /* function _prepareParseResult() */ + + /** + * Delegates the parsing routine according to $this->_sysname + * + * @access private + */ + function _parseResult() + { + call_user_func(array(&$this, '_parseResult'.$this->_sysname)); + } /* function _parseResult() */ + + /** + * Parses the output of Linux' ping command + * + * @access private + * @see _parseResultlinux + */ + function _parseResultlinux() + { + $raw_data_len = count($this->_raw_data); + $icmp_seq_count = $raw_data_len - 4; + + /* loop from second elment to the fifths last */ + for($idx = 1; $idx < $icmp_seq_count; $idx++) { + $parts = explode(' ', $this->_raw_data[$idx]); + $this->_icmp_sequence[substr(@$parts[4], 9, strlen(@$parts[4]))] = substr(@$parts[6], 5, strlen(@$parts[6])); + } + $this->_bytes_per_request = $parts[0]; + $this->_bytes_total = (int)$parts[0] * $icmp_seq_count; + $this->_target_ip = substr($parts[3], 0, -1); + $this->_ttl = substr($parts[5], 4, strlen($parts[3])); + + $stats = explode(',', $this->_raw_data[$raw_data_len - 2]); + $transmitted = explode(' ', $stats[0]); + $this->_transmitted = $transmitted[0]; + + $received = explode(' ', $stats[1]); + $this->_received = $received[1]; + + $loss = explode(' ', $stats[2]); + $this->_loss = (int)$loss[1]; + + $round_trip = explode('/', str_replace('=', '/', substr($this->_raw_data[$raw_data_len - 1], 0, -3))); + + /* if mdev field exists, shift input one unit left */ + if (strpos($this->_raw_data[$raw_data_len - 1], 'mdev')) { + /* do not forget the rtt field */ + $this->_round_trip['min'] = ltrim($round_trip[5]); + $this->_round_trip['avg'] = $round_trip[6]; + $this->_round_trip['max'] = $round_trip[7]; + } else { + $this->_round_trip['min'] = ltrim($round_trip[4]); + $this->_round_trip['avg'] = $round_trip[5]; + $this->_round_trip['max'] = $round_trip[6]; + } + } /* function _parseResultlinux() */ + + /** + * Parses the output of NetBSD's ping command + * + * @access private + * @see _parseResultfreebsd + */ + function _parseResultnetbsd() + { + $this->_parseResultfreebsd(); + } /* function _parseResultnetbsd() */ + + /** + * Parses the output of Darwin's ping command + * + * @access private + */ + function _parseResultdarwin() + { + $raw_data_len = count($this->_raw_data); + $icmp_seq_count = $raw_data_len - 5; + + /* loop from second elment to the fifths last */ + for($idx = 1; $idx < $icmp_seq_count; $idx++) { + $parts = explode(' ', $this->_raw_data[$idx]); + $this->_icmp_sequence[substr($parts[4], 9, strlen($parts[4]))] = substr($parts[6], 5, strlen($parts[6])); + } + + $this->_bytes_per_request = (int)$parts[0]; + $this->_bytes_total = (int)($this->_bytes_per_request * $icmp_seq_count); + $this->_target_ip = substr($parts[3], 0, -1); + $this->_ttl = (int)substr($parts[5], 4, strlen($parts[3])); + + $stats = explode(',', $this->_raw_data[$raw_data_len - 2]); + $transmitted = explode(' ', $stats[0]); + $this->_transmitted = (int)$transmitted[0]; + + $received = explode(' ', $stats[1]); + $this->_received = (int)$received[1]; + + $loss = explode(' ', $stats[2]); + $this->_loss = (int)$loss[1]; + + $round_trip = explode('/', str_replace('=', '/', substr($this->_raw_data[$raw_data_len - 1], 0, -3))); + + $this->_round_trip['min'] = (float)ltrim($round_trip[3]); + $this->_round_trip['avg'] = (float)$round_trip[4]; + $this->_round_trip['max'] = (float)$round_trip[5]; + $this->_round_trip['stddev'] = NULL; /* no stddev */ + } /* function _parseResultdarwin() */ + + /** + * Parses the output of HP-UX' ping command + * + * @access private + */ + function _parseResulthpux() + { + $parts = array(); + $raw_data_len = count($this->_raw_data); + $icmp_seq_count = $raw_data_len - 5; + + /* loop from second elment to the fifths last */ + for($idx = 1; $idx <= $icmp_seq_count; $idx++) { + $parts = explode(' ', $this->_raw_data[$idx]); + $this->_icmp_sequence[(int)substr($parts[4], 9, strlen($parts[4]))] = (int)substr($parts[5], 5, strlen($parts[5])); + } + $this->_bytes_per_request = (int)$parts[0]; + $this->_bytes_total = (int)($parts[0] * $icmp_seq_count); + $this->_target_ip = NULL; /* no target ip */ + $this->_ttl = NULL; /* no ttl */ + + $stats = explode(',', $this->_raw_data[$raw_data_len - 2]); + $transmitted = explode(' ', $stats[0]); + $this->_transmitted = (int)$transmitted[0]; + + $received = explode(' ', $stats[1]); + $this->_received = (int)$received[1]; + + $loss = explode(' ', $stats[2]); + $this->_loss = (int)$loss[1]; + + $round_trip = explode('/', str_replace('=', '/',$this->_raw_data[$raw_data_len - 1])); + + $this->_round_trip['min'] = (int)ltrim($round_trip[3]); + $this->_round_trip['avg'] = (int)$round_trip[4]; + $this->_round_trip['max'] = (int)$round_trip[5]; + $this->_round_trip['stddev'] = NULL; /* no stddev */ + } /* function _parseResulthpux() */ + + /** + * Parses the output of AIX' ping command + * + * @access private + */ + function _parseResultaix() + { + $parts = array(); + $raw_data_len = count($this->_raw_data); + $icmp_seq_count = $raw_data_len - 5; + + /* loop from second elment to the fifths last */ + for($idx = 1; $idx <= $icmp_seq_count; $idx++) { + $parts = explode(' ', $this->_raw_data[$idx]); + $this->_icmp_sequence[(int)substr($parts[4], 9, strlen($parts[4]))] = (int)substr($parts[6], 5, strlen($parts[6])); + } + $this->_bytes_per_request = (int)$parts[0]; + $this->_bytes_total = (int)($parts[0] * $icmp_seq_count); + $this->_target_ip = substr($parts[3], 0, -1); + $this->_ttl = (int)substr($parts[5], 4, strlen($parts[3])); + + $stats = explode(',', $this->_raw_data[$raw_data_len - 2]); + $transmitted = explode(' ', $stats[0]); + $this->_transmitted = (int)$transmitted[0]; + + $received = explode(' ', $stats[1]); + $this->_received = (int)$received[1]; + + $loss = explode(' ', $stats[2]); + $this->_loss = (int)$loss[1]; + + $round_trip = explode('/', str_replace('=', '/',$this->_raw_data[$raw_data_len - 1])); + + $this->_round_trip['min'] = (int)ltrim($round_trip[3]); + $this->_round_trip['avg'] = (int)$round_trip[4]; + $this->_round_trip['max'] = (int)$round_trip[5]; + $this->_round_trip['stddev'] = NULL; /* no stddev */ + } /* function _parseResultaix() */ + + /** + * Parses the output of FreeBSD's ping command + * + * @access private + */ + function _parseResultfreebsd() + { + $raw_data_len = count($this->_raw_data); + $icmp_seq_count = $raw_data_len - 5; + + /* loop from second elment to the fifths last */ + for($idx = 1; $idx < $icmp_seq_count; $idx++) { + $parts = explode(' ', $this->_raw_data[$idx]); + $this->_icmp_sequence[substr($parts[4], 9, strlen($parts[4]))] = substr($parts[6], 5, strlen($parts[6])); + } + + $this->_bytes_per_request = (int)$parts[0]; + $this->_bytes_total = (int)($parts[0] * $icmp_seq_count); + $this->_target_ip = substr($parts[3], 0, -1); + $this->_ttl = (int)substr($parts[5], 4, strlen($parts[3])); + + $stats = explode(',', $this->_raw_data[$raw_data_len - 2]); + $transmitted = explode(' ', $stats[0]); + $this->_transmitted = (int)$transmitted[0]; + + $received = explode(' ', $stats[1]); + $this->_received = (int)$received[1]; + + $loss = explode(' ', $stats[2]); + $this->_loss = (int)$loss[1]; + + $round_trip = explode('/', str_replace('=', '/', substr($this->_raw_data[$raw_data_len - 1], 0, -3))); + + $this->_round_trip['min'] = (float)ltrim($round_trip[4]); + $this->_round_trip['avg'] = (float)$round_trip[5]; + $this->_round_trip['max'] = (float)$round_trip[6]; + $this->_round_trip['stddev'] = (float)$round_trip[7]; + } /* function _parseResultfreebsd() */ + + /** + * Parses the output of Windows' ping command + * + * @author Kai Schröder + * @access private + */ + function _parseResultwindows() + { + $raw_data_len = count($this->_raw_data); + $icmp_seq_count = $raw_data_len - 8; + + /* loop from fourth elment to the sixths last */ + for($idx = 1; $idx <= $icmp_seq_count; $idx++) { + $parts = explode(' ', $this->_raw_data[$idx + 2]); + $this->_icmp_sequence[$idx - 1] = (int)substr(end(split('=', $parts[4])), 0, -2); + + $ttl = (int)substr($parts[5], 4, strlen($parts[3])); + if ($ttl > 0 && $this->_ttl == 0) { + $this->_ttl = $ttl; + } + } + + + $parts = explode(' ', $this->_raw_data[1]); + $this->_bytes_per_request = (int)$parts[4]; + $this->_bytes_total = $this->_bytes_per_request * $icmp_seq_count; + $this->_target_ip = substr(trim($parts[2]), 1, -1); + + $stats = explode(',', $this->_raw_data[$raw_data_len - 3]); + $transmitted = explode('=', $stats[0]); + $this->_transmitted = (int)$transmitted[1]; + + $received = explode('=', $stats[1]); + $this->_received = (int)$received[1]; + + $loss = explode('=', $stats[2]); + $this->_loss = (int)$loss[1]; + + $round_trip = explode(',', str_replace('=', ',', $this->_raw_data[$raw_data_len - 1])); + $this->_round_trip['min'] = (int)substr(trim($round_trip[1]), 0, -2); + $this->_round_trip['avg'] = (int)substr(trim($round_trip[3]), 0, -2); + $this->_round_trip['max'] = (int)substr(trim($round_trip[5]), 0, -2); + } /* function _parseResultwindows() */ + + /** + * Returns a Ping_Result property + * + * @param string $name property name + * @return mixed property value + * @access public + */ + function getValue($name) + { + return isset($this->$name)?$this->$name:''; + } /* function getValue() */ + + /** + * Accessor for $this->_target_ip; + * + * @return string IP address + * @access public + * @see Ping_Result::_target_ip + */ + function getTargetIp() + { + return $this->_target_ip; + } /* function getTargetIp() */ + + /** + * Accessor for $this->_icmp_sequence; + * + * @return array ICMP sequence + * @access private + * @see Ping_Result::_icmp_sequence + */ + function getICMPSequence() + { + return $this->_icmp_sequence; + } /* function getICMPSequencs() */ + + /** + * Accessor for $this->_bytes_per_request; + * + * @return int bytes per request + * @access private + * @see Ping_Result::_bytes_per_request + */ + function getBytesPerRequest() + { + return $this->_bytes_per_request; + } /* function getBytesPerRequest() */ + + /** + * Accessor for $this->_bytes_total; + * + * @return int total bytes + * @access private + * @see Ping_Result::_bytes_total + */ + function getBytesTotal() + { + return $this->_bytes_total; + } /* function getBytesTotal() */ + + /** + * Accessor for $this->_ttl; + * + * @return int TTL + * @access private + * @see Ping_Result::_ttl + */ + function getTTL() + { + return $this->_ttl; + } /* function getTTL() */ + + /** + * Accessor for $this->_raw_data; + * + * @return array raw data + * @access private + * @see Ping_Result::_raw_data + */ + function getRawData() + { + return $this->_raw_data; + } /* function getRawData() */ + + /** + * Accessor for $this->_sysname; + * + * @return string OS_Guess::sysname + * @access private + * @see Ping_Result::_sysname + */ + function getSystemName() + { + return $this->_sysname; + } /* function getSystemName() */ + + /** + * Accessor for $this->_round_trip; + * + * @return array statistical information + * @access private + * @see Ping_Result::_round_trip + */ + function getRoundTrip() + { + return $this->_round_trip; + } /* function getRoundTrip() */ + + /** + * Accessor for $this->_round_trip['min']; + * + * @return array statistical information + * @access private + * @see Ping_Result::_round_trip + */ + function getMin() + { + return $this->_round_trip['min']; + } /* function getMin() */ + + /** + * Accessor for $this->_round_trip['max']; + * + * @return array statistical information + * @access private + * @see Ping_Result::_round_trip + */ + function getMax() + { + return $this->_round_trip['max']; + } /* function getMax() */ + + /** + * Accessor for $this->_round_trip['stddev']; + * + * @return array statistical information + * @access private + * @see Ping_Result::_round_trip + */ + function getStddev() + { + return $this->_round_trip['stddev']; + } /* function getStddev() */ + + /** + * Accessor for $this->_round_tripp['avg']; + * + * @return array statistical information + * @access private + * @see Ping_Result::_round_trip + */ + function getAvg() + { + return $this->_round_trip['avg']; + } /* function getAvg() */ + + /** + * Accessor for $this->_transmitted; + * + * @return array statistical information + * @access private + */ + function getTransmitted() + { + return $this->_transmitted; + } /* function getTransmitted() */ + + /** + * Accessor for $this->_received; + * + * @return array statistical information + * @access private + */ + function getReceived() + { + return $this->_received; + } /* function getReceived() */ + + /** + * Accessor for $this->_loss; + * + * @return array statistical information + * @access private + */ + function getLoss() + { + return $this->_loss; + } /* function getLoss() */ + +} /* class Net_Ping_Result */ +?> diff --git a/thirdparty/pear/Net/SMTP.php b/thirdparty/pear/Net/SMTP.php new file mode 100644 index 0000000..049a92f --- /dev/null +++ b/thirdparty/pear/Net/SMTP.php @@ -0,0 +1,991 @@ + | +// | Jon Parise | +// | Damian Alejandro Fernandez Sosa | +// +----------------------------------------------------------------------+ +// +// $Id: SMTP.php,v 1.54 2005/08/16 03:44:15 jon Exp $ + +require_once 'PEAR.php'; +require_once 'Net/Socket.php'; + +/** + * Provides an implementation of the SMTP protocol using PEAR's + * Net_Socket:: class. + * + * @package Net_SMTP + * @author Chuck Hagenbuch + * @author Jon Parise + * @author Damian Alejandro Fernandez Sosa + * + * @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('/(?_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; + } + +} diff --git a/thirdparty/pear/Net/Sieve.php b/thirdparty/pear/Net/Sieve.php new file mode 100644 index 0000000..aefd60d --- /dev/null +++ b/thirdparty/pear/Net/Sieve.php @@ -0,0 +1,1165 @@ + | +// | Co-Author: Damian Fernandez Sosa | +// +-----------------------------------------------------------------------+ + +require_once('Net/Socket.php'); + +/** +* TODO +* +* o supportsAuthMech() +*/ + +/** +* Disconnected state +* @const NET_SIEVE_STATE_DISCONNECTED +*/ +define('NET_SIEVE_STATE_DISCONNECTED', 1, true); + +/** +* Authorisation state +* @const NET_SIEVE_STATE_AUTHORISATION +*/ +define('NET_SIEVE_STATE_AUTHORISATION', 2, true); + +/** +* Transaction state +* @const NET_SIEVE_STATE_TRANSACTION +*/ +define('NET_SIEVE_STATE_TRANSACTION', 3, true); + +/** +* A class for talking to the timsieved server which +* comes with Cyrus IMAP. the HAVESPACE +* command which appears to be broken (Cyrus 2.0.16). +* +* @author Richard Heyes +* @author Damian Fernandez Sosa +* @access public +* @version 0.9.1 +* @package Net_Sieve +*/ + +class Net_Sieve +{ + /** + * The socket object + * @var object + */ + var $_sock; + + /** + * Info about the connect + * @var array + */ + var $_data; + + /** + * Current state of the connection + * @var integer + */ + var $_state; + + /** + * Constructor error is any + * @var object + */ + var $_error; + + + /** + * To allow class debuging + * @var boolean + */ + var $_debug = false; + + + /** + * The auth methods this class support + * @var array + */ + + var $supportedAuthMethods=array('DIGEST-MD5', 'CRAM-MD5', 'PLAIN' , 'LOGIN'); + //if you have problems using DIGEST-MD5 authentication please commente the line above and discomment the following line + //var $supportedAuthMethods=array( 'CRAM-MD5', 'PLAIN' , 'LOGIN'); + + //var $supportedAuthMethods=array( 'PLAIN' , 'LOGIN'); + + + /** + * The auth methods this class support + * @var array + */ + var $supportedSASLAuthMethods=array('DIGEST-MD5', 'CRAM-MD5'); + + + + /** + * Handles posible referral loops + * @var array + */ + var $_maxReferralCount = 15; + + /** + * Constructor + * Sets up the object, connects to the server and logs in. stores + * any generated error in $this->_error, which can be retrieved + * using the getError() method. + * + * @access public + * @param string $user Login username + * @param string $pass Login password + * @param string $host Hostname of server + * @param string $port Port of server + * @param string $logintype Type of login to perform + * @param string $euser Effective User (if $user=admin, login as $euser) + */ + function Net_Sieve($user = null , $pass = null , $host = 'localhost', $port = 2000, $logintype = '', $euser = '', $debug = false) + { + $this->_state = NET_SIEVE_STATE_DISCONNECTED; + $this->_data['user'] = $user; + $this->_data['pass'] = $pass; + $this->_data['host'] = $host; + $this->_data['port'] = $port; + $this->_data['logintype'] = $logintype; + $this->_data['euser'] = $euser; + $this->_sock = &new Net_Socket(); + $this->_debug = $debug; + /* + * 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) { + if($this->_debug){ + echo "AUTH_SASL NOT PRESENT!\n"; + } + foreach($this->supportedSASLAuthMethods as $SASLMethod){ + $pos = array_search( $SASLMethod, $this->supportedAuthMethods ); + if($this->_debug){ + echo "DISABLING METHOD $SASLMethod\n"; + } + unset($this->supportedAuthMethods[$pos]); + } + } + if( ($user != null) && ($pass != null) ){ + $this->_error = $this->_handleConnectAndLogin(); + } + } + + + + /** + * Handles the errors the class can find + * on the server + * + * @access private + * @return PEAR_Error + */ + + function _raiseError($msg, $code) + { + include_once 'PEAR.php'; + return PEAR::raiseError($msg, $code); + } + + + + + + /** + * Handles connect and login. + * on the server + * + * @access private + * @return mixed Indexed array of scriptnames or PEAR_Error on failure + */ + function _handleConnectAndLogin(){ + if (PEAR::isError($res = $this->connect($this->_data['host'] , $this->_data['port'] ))) { + return $res; + } + if (PEAR::isError($res = $this->login($this->_data['user'], $this->_data['pass'], $this->_data['logintype'] , $this->_data['euser'] ) ) ) { + return $res; + } + return true; + + } + + + + + /** + * Returns an indexed array of scripts currently + * on the server + * + * @access public + * @return mixed Indexed array of scriptnames or PEAR_Error on failure + */ + function listScripts() + { + if (is_array($scripts = $this->_cmdListScripts())) { + $this->_active = $scripts[1]; + return $scripts[0]; + } else { + return $scripts; + } + } + + /** + * Returns the active script + * + * @access public + * @return mixed The active scriptname or PEAR_Error on failure + */ + function getActive() + { + if (!empty($this->_active)) { + return $this->_active; + + } elseif (is_array($scripts = $this->_cmdListScripts())) { + $this->_active = $scripts[1]; + return $scripts[1]; + } + } + + /** + * Sets the active script + * + * @access public + * @param string $scriptname The name of the script to be set as active + * @return mixed true on success, PEAR_Error on failure + */ + function setActive($scriptname) + { + return $this->_cmdSetActive($scriptname); + } + + /** + * Retrieves a script + * + * @access public + * @param string $scriptname The name of the script to be retrieved + * @return mixed The script on success, PEAR_Error on failure + */ + function getScript($scriptname) + { + return $this->_cmdGetScript($scriptname); + } + + /** + * Adds a script to the server + * + * @access public + * @param string $scriptname Name of the script + * @param string $script The script + * @param bool $makeactive Whether to make this the active script + * @return mixed true on success, PEAR_Error on failure + */ + function installScript($scriptname, $script, $makeactive = false) + { + if (PEAR::isError($res = $this->_cmdPutScript($scriptname, $script))) { + return $res; + + } elseif ($makeactive) { + return $this->_cmdSetActive($scriptname); + + } else { + return true; + } + } + + /** + * Removes a script from the server + * + * @access public + * @param string $scriptname Name of the script + * @return mixed True on success, PEAR_Error on failure + */ + function removeScript($scriptname) + { + return $this->_cmdDeleteScript($scriptname); + } + + /** + * Returns any error that may have been generated in the + * constructor + * + * @access public + * @return mixed False if no error, PEAR_Error otherwise + */ + function getError() + { + return PEAR::isError($this->_error) ? $this->_error : false; + } + + /** + * Handles connecting to the server and checking the + * response is valid. + * + * @access private + * @param string $host Hostname of server + * @param string $port Port of server + * @return mixed True on success, PEAR_Error otherwise + */ + function connect($host, $port) + { + if (NET_SIEVE_STATE_DISCONNECTED != $this->_state) { + $msg='Not currently in DISCONNECTED state'; + $code=1; + return $this->_raiseError($msg,$code); + } + + if (PEAR::isError($res = $this->_sock->connect($host, $port, null, 5))) { + return $res; + } + + + $this->_state = NET_SIEVE_STATE_AUTHORISATION; + if (PEAR::isError($res = $this->_doCmd())) { + return $res; + } + /* + if(PEAR::isError($res = $this->_cmdCapability() )) { + $msg='Failed to connect, server said: ' . $res->getMessage(); + $code=2; + return $this->_raiseError($msg,$code); + } +*/ + // Get logon greeting/capability and parse + $this->_parseCapability($res); + + return true; + } + + /** + * Logs into server. + * + * @access public + * @param string $user Login username + * @param string $pass Login password + * @param string $logintype Type of login method to use + * @param string $euser Effective UID (perform on behalf of $euser) + * @param boolean $bypassAuth dont perform auth + * @return mixed True on success, PEAR_Error otherwise + */ + function login($user, $pass, $logintype = null , $euser = '', $bypassAuth=false) + { + if (NET_SIEVE_STATE_AUTHORISATION != $this->_state) { + $msg='Not currently in AUTHORISATION state'; + $code=1; + return $this->_raiseError($msg,$code); + } + + + + if( $bypassAuth === false ){ + if(PEAR::isError($res=$this->_cmdAuthenticate($user , $pass , $logintype, $euser ) ) ){ + return $res; + } + } + $this->_state = NET_SIEVE_STATE_TRANSACTION; + return true; + } + + + + /* Handles the authentication using any known method + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * @param string The method to use ( if $usermethod == '' then the class chooses the best method (the stronger is the best ) ) + * @param string The effective uid to authenticate as. + * + * @return mixed string or PEAR_Error + * + * @access private + * @since 1.0 + */ + function _cmdAuthenticate($uid , $pwd , $userMethod = null , $euser = '' ) + { + + + if ( PEAR::isError( $method = $this->_getBestAuthMethod($userMethod) ) ) { + return $method; + } + switch ($method) { + case 'DIGEST-MD5': + $result = $this->_authDigest_MD5( $uid , $pwd , $euser ); + return $result; + break; + case 'CRAM-MD5': + $result = $this->_authCRAM_MD5( $uid , $pwd, $euser); + break; + case 'LOGIN': + $result = $this->_authLOGIN( $uid , $pwd , $euser ); + break; + case 'PLAIN': + $result = $this->_authPLAIN( $uid , $pwd , $euser ); + break; + default : + $result = new PEAR_Error( "$method is not a supported authentication method" ); + break; + } + + + if (PEAR::isError($res = $this->_doCmd() )) { + return $res; + } + return $result; + } + + + + + + + + + + /* Authenticates the user using the PLAIN method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * @param string The effective uid to authenticate as. + * + * @return array Returns an array containing the response + * + * @access private + * @since 1.0 + */ + function _authPLAIN($user, $pass , $euser ) + { + + if ($euser != '') { + $cmd=sprintf('AUTHENTICATE "PLAIN" "%s"', base64_encode($euser . chr(0) . $user . chr(0) . $pass ) ) ; + } else { + $cmd=sprintf('AUTHENTICATE "PLAIN" "%s"', base64_encode( chr(0) . $user . chr(0) . $pass ) ); + } + return $this->_sendCmd( $cmd ) ; + + } + + + + /* Authenticates the user using the PLAIN method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * @param string The effective uid to authenticate as. + * + * @return array Returns an array containing the response + * + * @access private + * @since 1.0 + */ + function _authLOGIN($user, $pass , $euser ) + { + $this->_sendCmd('AUTHENTICATE "LOGIN"'); + $this->_doCmd(sprintf('"%s"', base64_encode($user))); + $this->_doCmd(sprintf('"%s"', base64_encode($pass))); + + } + + + + + /* Authenticates the user using the CRAM-MD5 method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * @param string The cmdID. + * + * @return array Returns an array containing the response + * + * @access private + * @since 1.0 + */ + function _authCRAM_MD5($uid, $pwd, $euser) + { + + if ( PEAR::isError( $challenge = $this->_doCmd( 'AUTHENTICATE "CRAM-MD5"' ) ) ) { + $this->_error=challenge ; + return challenge ; + } + $challenge=trim($challenge); + $challenge = base64_decode( trim($challenge) ); + $cram = &Auth_SASL::factory('crammd5'); + if ( PEAR::isError($resp=$cram->getResponse( $uid , $pwd , $challenge ) ) ) { + $this->_error=$resp; + return $resp; + } + $auth_str = base64_encode( $resp ); + if ( PEAR::isError($error = $this->_sendStringResponse( $auth_str ) ) ) { + $this->_error=$error; + return $error; + } + + } + + + + /* Authenticates the user using the DIGEST-MD5 method. + * + * @param string The userid to authenticate as. + * @param string The password to authenticate with. + * @param string The efective user + * + * @return array Returns an array containing the response + * + * @access private + * @since 1.0 + */ + function _authDigest_MD5($uid, $pwd, $euser) + { + + if ( PEAR::isError( $challenge = $this->_doCmd('AUTHENTICATE "DIGEST-MD5"') ) ) { + $this->_error=challenge ; + return challenge ; + } + $challenge = base64_decode( $challenge ); + $digest = &Auth_SASL::factory('digestmd5'); + + + if(PEAR::isError($param=$digest->getResponse($uid, $pwd, $challenge, "localhost", "sieve" , $euser) )) { + return $param; + } + $auth_str = base64_encode($param); + + if ( PEAR::isError($error = $this->_sendStringResponse( $auth_str ) ) ) { + $this->_error=$error; + return $error; + } + + + + if ( PEAR::isError( $challenge = $this->_doCmd() ) ) { + $this->_error=$challenge ; + return $challenge ; + } + + if( strtoupper(substr($challenge,0,2))== 'OK' ){ + return true; + } + + + /* + * We don't use the protocol's third step because SIEVE doesn't allow + * subsequent authentication, so we just silently ignore it. + */ + + + if ( PEAR::isError($error = $this->_sendStringResponse( '' ) ) ) { + $this->_error=$error; + return $error; + } + + if (PEAR::isError($res = $this->_doCmd() )) { + return $res; + } + + + } + + + + + /** + * Removes a script from the server + * + * @access private + * @param string $scriptname Name of the script to delete + * @return mixed True on success, PEAR_Error otherwise + */ + function _cmdDeleteScript($scriptname) + { + if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { + $msg='Not currently in AUTHORISATION state'; + $code=1; + return $this->_raiseError($msg,$code); + } + if (PEAR::isError($res = $this->_doCmd(sprintf('DELETESCRIPT "%s"', $scriptname) ) )) { + return $res; + } + return true; + } + + /** + * Retrieves the contents of the named script + * + * @access private + * @param string $scriptname Name of the script to retrieve + * @return mixed The script if successful, PEAR_Error otherwise + */ + function _cmdGetScript($scriptname) + { + if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { + $msg='Not currently in AUTHORISATION state'; + $code=1; + return $this->_raiseError($msg,$code); + } + + if (PEAR::isError($res = $this->_doCmd(sprintf('GETSCRIPT "%s"', $scriptname) ) ) ) { + return $res; + } + + return preg_replace('/{[0-9]+}\r\n/', '', $res); + } + + /** + * Sets the ACTIVE script, ie the one that gets run on new mail + * by the server + * + * @access private + * @param string $scriptname The name of the script to mark as active + * @return mixed True on success, PEAR_Error otherwise + */ + function _cmdSetActive($scriptname) + { + if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { + $msg='Not currently in AUTHORISATION state'; + $code=1; + return $this->_raiseError($msg,$code); + } + + if (PEAR::isError($res = $this->_doCmd(sprintf('SETACTIVE "%s"', $scriptname) ) ) ) { + return $res; + } + + $this->_activeScript = $scriptname; + return true; + } + + /** + * Sends the LISTSCRIPTS command + * + * @access private + * @return mixed Two item array of scripts, and active script on success, + * PEAR_Error otherwise. + */ + function _cmdListScripts() + { + + if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { + $msg='Not currently in AUTHORISATION state'; + $code=1; + return $this->_raiseError($msg,$code); + } + + $scripts = array(); + $activescript = null; + + if (PEAR::isError($res = $this->_doCmd('LISTSCRIPTS'))) { + return $res; + } + + $res = explode("\r\n", $res); + + foreach ($res as $value) { + if (preg_match('/^"(.*)"( ACTIVE)?$/i', $value, $matches)) { + $scripts[] = $matches[1]; + if (!empty($matches[2])) { + $activescript = $matches[1]; + } + } + } + + return array($scripts, $activescript); + } + + /** + * Sends the PUTSCRIPT command to add a script to + * the server. + * + * @access private + * @param string $scriptname Name of the new script + * @param string $scriptdata The new script + * @return mixed True on success, PEAR_Error otherwise + */ + function _cmdPutScript($scriptname, $scriptdata) + { + if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { + $msg='Not currently in TRANSACTION state'; + $code=1; + return $this->_raiseError($msg,$code); + } + + if (PEAR::isError($res = $this->_doCmd(sprintf("PUTSCRIPT \"%s\" {%d+}\r\n%s", $scriptname, strlen($scriptdata),$scriptdata ) ))) { + return $res; + } + + return true; + } + + /** + * Sends the LOGOUT command and terminates the connection + * + * @access private + * @return mixed True on success, PEAR_Error otherwise + */ + function _cmdLogout($sendLogoutCMD=true) + { + if (NET_SIEVE_STATE_DISCONNECTED === $this->_state) { + $msg='Not currently connected'; + $code=1; + return $this->_raiseError($msg,$code); + //return PEAR::raiseError('Not currently connected'); + } + + if($sendLogoutCMD){ + if (PEAR::isError($res = $this->_doCmd('LOGOUT'))) { + return $res; + } + } + + $this->_sock->disconnect(); + $this->_state = NET_SIEVE_STATE_DISCONNECTED; + return true; + } + + /** + * Sends the CAPABILITY command + * + * @access private + * @return mixed True on success, PEAR_Error otherwise + */ + function _cmdCapability() + { + if (NET_SIEVE_STATE_DISCONNECTED === $this->_state) { + $msg='Not currently connected'; + $code=1; + return $this->_raiseError($msg,$code); + } + + if (PEAR::isError($res = $this->_doCmd('CAPABILITY'))) { + return $res; + } + $this->_parseCapability($res); + return true; + } + + + /** + * Checks if the server has space to store the script + * by the server + * + * @access public + * @param string $scriptname The name of the script to mark as active + * @return mixed True on success, PEAR_Error otherwise + */ + function haveSpace($scriptname,$quota) + { + if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { + $msg='Not currently in TRANSACTION state'; + $code=1; + return $this->_raiseError($msg,$code); + } + + if (PEAR::isError($res = $this->_doCmd(sprintf('HAVESPACE "%s" %s', $scriptname, $quota) ) ) ) { + //if (PEAR::isError($res = $this->_doCmd(sprintf('HAVESPACE %d "%s"', $quota,$scriptname ) ) ) ) { + return $res; + } + + return true; + } + + + + + /** + * Parses the response from the capability command. Storesq + * the result in $this->_capability + * + * @access private + */ + function _parseCapability($data) + { + $data = preg_split('/\r?\n/', $data, -1, PREG_SPLIT_NO_EMPTY); + + for ($i = 0; $i < count($data); $i++) { + if (preg_match('/^"([a-z]+)" ("(.*)")?$/i', $data[$i], $matches)) { + switch (strtolower($matches[1])) { + case 'implementation': + $this->_capability['implementation'] = $matches[3]; + break; + + case 'sasl': + $this->_capability['sasl'] = preg_split('/\s+/', $matches[3]); + break; + + case 'sieve': + $this->_capability['extensions'] = preg_split('/\s+/', $matches[3]); + break; + + case 'starttls': + $this->_capability['starttls'] = true; + } + } + } + } + + /** + * Sends a command to the server + * + * @access private + * @param string $cmd The command to send + */ + function _sendCmd($cmd) + { + $status = $this->_sock->getStatus(); + if (PEAR::isError($status) || $status['eof']) { + return new PEAR_Error( 'Failed to write to socket: (connection lost!) ' ); + } + if ( PEAR::isError( $error = $this->_sock->write( $cmd . "\r\n" ) ) ) { + return new PEAR_Error( 'Failed to write to socket: ' . $error->getMessage() ); + } + + if( $this->_debug ){ + // C: means this data was sent by the client (this class) + echo "C:$cmd\n"; + } + return true; + + + } + + + + /** + * Sends a string response to the server + * + * @access private + * @param string $cmd The command to send + */ + function _sendStringResponse($str) + { + $response='{' . strlen($str) . "+}\r\n" . $str ; + return $this->_sendCmd($response); + } + + + + + function _recvLn() + { + $lastline=''; + if (PEAR::isError( $lastline = $this->_sock->gets( 8192 ) ) ) { + return new PEAR_Error('Failed to write to socket: ' . $lastline->getMessage() ); + } + $lastline=rtrim($lastline); + if($this->_debug){ + // S: means this data was sent by the IMAP Server + echo "S:$lastline\n" ; + } + +/* if( $lastline === '' ){ + return new PEAR_Error('Failed to receive from the socket: ' ); + } +*/ + return $lastline; + } + + + + + + /** + * Send a command and retrieves a response from the server. + * + * + * @access private + * @param string $cmd The command to send + * @return mixed Reponse string if an OK response, PEAR_Error if a NO response + */ + function _doCmd($cmd = '' ) + { + + $referralCount=0; + while($referralCount < $this->_maxReferralCount ){ + + + if($cmd != '' ){ + if(PEAR::isError($error = $this->_sendCmd($cmd) )) { + return $error; + } + } + $response = ''; + + while (true) { + if(PEAR::isError( $line=$this->_recvLn() )){ + return $line; + } + if ('ok' === strtolower(substr($line, 0, 2))) { + $response .= $line; + return rtrim($response); + + } elseif ('no' === strtolower(substr($line, 0, 2))) { + // Check for string literal error message + if (preg_match('/^no {([0-9]+)\+?}/i', $line, $matches)) { + $line .= str_replace("\r\n", ' ', $this->_sock->read($matches[1] + 2 )); + if($this->_debug){ + echo "S:$line\n"; + } + } + $msg=trim($response . substr($line, 2)); + $code=3; + return $this->_raiseError($msg,$code); + //return PEAR::raiseError(trim($response . substr($line, 2))); + } elseif ('bye' === strtolower(substr($line, 0, 3))) { + + if(PEAR::isError($error = $this->disconnect(false) ) ){ + $msg="Can't handle bye, The error was= " . $error->getMessage() ; + $code=4; + return $this->_raiseError($msg,$code); + //return PEAR::raiseError("Can't handle bye, The error was= " . $error->getMessage() ); + } + //if (preg_match('/^bye \(referral "([^"]+)/i', $line, $matches)) { + if (preg_match('/^bye \(referral "(sieve:\/\/)?([^"]+)/i', $line, $matches)) { + // Check for referral, then follow it. Otherwise, carp an error. + //$this->_data['host'] = $matches[1]; + $this->_data['host'] = $matches[2]; + if (PEAR::isError($error = $this->_handleConnectAndLogin() ) ){ + $msg="Can't follow referral to " . $this->_data['host'] . ", The error was= " . $error->getMessage() ; + $code=5; + return $this->_raiseError($msg,$code); + //return PEAR::raiseError("Can't follow referral to " . $this->_data['host'] . ", The error was= " . $error->getMessage() ); + } + break; + // Retry the command + if(PEAR::isError($error = $this->_sendCmd($cmd) )) { + return $error; + } + continue; + } + $msg=trim($response . $line); + $code=6; + return $this->_raiseError($msg,$code); + //return PEAR::raiseError(trim($response . $line)); + } elseif (preg_match('/^{([0-9]+)\+?}/i', $line, $matches)) { + // Matches String Responses. + //$line = str_replace("\r\n", ' ', $this->_sock->read($matches[1] + 2 )); + $line = $this->_sock->read($matches[1] + 2 ); + if($this->_debug){ + echo "S:$line\n"; + } + return $line; + } + $response .= $line . "\r\n"; + $referralCount++; + } + } + $msg="Max referral count reached ($referralCount times) Cyrus murder loop error?"; + $code=7; + return $this->_raiseError($msg,$code); + //return PEAR::raiseError("Max referral count reached ($referralCount times) Cyrus murder loop error?" ); + } + + + + + /** + * Sets the bebug state + * + * @access public + * @return void + */ + function setDebug($debug=true) + { + $this->_debug=$debug; + } + + /** + * Disconnect from the Sieve server + * + * @access public + * @param string $scriptname The name of the script to be set as active + * @return mixed true on success, PEAR_Error on failure + */ + function disconnect($sendLogoutCMD=true) + { + return $this->_cmdLogout($sendLogoutCMD); + } + + + /** + * Returns the name of the best authentication method that the server + * has advertised. + * + * @param string if !=null,authenticate with this method ($userMethod). + * + * @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.0 + */ + function _getBestAuthMethod($userMethod = null) + { + + if( isset($this->_capability['sasl']) ){ + $serverMethods=$this->_capability['sasl']; + }else{ + // if the server don't send an sasl capability fallback to login auth + //return 'LOGIN'; + return new PEAR_Error("This server don't support any Auth methods SASL problem?"); + } + + if($userMethod != null ){ + $methods = array(); + $methods[] = $userMethod; + }else{ + + $methods = $this->supportedAuthMethods; + } + if( ($methods != null) && ($serverMethods != null)){ + foreach ( $methods as $method ) { + if ( in_array( $method , $serverMethods ) ) { + return $method; + } + } + $serverMethods=implode(',' , $serverMethods ); + $myMethods=implode(',' ,$this->supportedAuthMethods); + return new PEAR_Error("$method NOT supported authentication method!. This server " . + "supports these methods= $serverMethods, but I support $myMethods"); + }else{ + return new PEAR_Error("This server don't support any Auth methods"); + } + } + + + + + + /** + * Return the list of extensions the server supports + * + * @access public + * @return mixed array on success, PEAR_Error on failure + */ + function getExtensions() + { + if (NET_SIEVE_STATE_DISCONNECTED === $this->_state) { + $msg='Not currently connected'; + $code=7; + return $this->_raiseError($msg,$code); + //return PEAR::raiseError('Not currently connected'); + } + + return $this->_capability['extensions']; + } + + + + + + /** + * Return true if tyhe server has that extension + * + * @access public + * @param string the extension to compare + * @return mixed array on success, PEAR_Error on failure + */ + function hasExtension($extension) + { + if (NET_SIEVE_STATE_DISCONNECTED === $this->_state) { + $msg='Not currently connected'; + $code=7; + return $this->_raiseError($msg,$code); + //return PEAR::raiseError('Not currently connected'); + } + + if(is_array($this->_capability['extensions'] ) ){ + foreach( $this->_capability['extensions'] as $ext){ + if( trim( strtolower( $ext ) ) === trim( strtolower( $extension ) ) ) + return true; + } + } + return false; + } + + + + /** + * Return the list of auth methods the server supports + * + * @access public + * @return mixed array on success, PEAR_Error on failure + */ + function getAuthMechs() + { + if (NET_SIEVE_STATE_DISCONNECTED === $this->_state) { + $msg='Not currently connected'; + $code=7; + return $this->_raiseError($msg,$code); + //return PEAR::raiseError('Not currently connected'); + } + if(!isset($this->_capability['sasl']) ){ + $this->_capability['sasl']=array(); + } + return $this->_capability['sasl']; + } + + + + + + /** + * Return true if tyhe server has that extension + * + * @access public + * @param string the extension to compare + * @return mixed array on success, PEAR_Error on failure + */ + function hasAuthMech($method) + { + if (NET_SIEVE_STATE_DISCONNECTED === $this->_state) { + $msg='Not currently connected'; + $code=7; + return $this->_raiseError($msg,$code); + //return PEAR::raiseError('Not currently connected'); + } + + if(is_array($this->_capability['sasl'] ) ){ + foreach( $this->_capability['sasl'] as $ext){ + if( trim( strtolower( $ext ) ) === trim( strtolower( $method ) ) ) + return true; + } + } + return false; + } + + + + + +} +?> \ No newline at end of file diff --git a/thirdparty/pear/Net/SmartIRC.php b/thirdparty/pear/Net/SmartIRC.php new file mode 100644 index 0000000..e0167a8 --- /dev/null +++ b/thirdparty/pear/Net/SmartIRC.php @@ -0,0 +1,2553 @@ + and + * Latest versions of Net_SmartIRC you will find on the project homepage + * or get it through PEAR since SmartIRC is an official PEAR package. + * See . + * + * Official Projet Homepage: + * + * Net_SmartIRC conforms to RFC 2812 (Internet Relay Chat: Client Protocol) + * + * Copyright (c) 2002-2003 Mirco 'meebey' Bauer + * + * Full LGPL License: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +// ------- PHP code ---------- +include_once('SmartIRC/defines.php'); +include_once('SmartIRC/irccommands.php'); +include_once('SmartIRC/messagehandler.php'); +define('SMARTIRC_VERSION', '1.0.0 ($Revision: 1.54.2.14 $)'); +define('SMARTIRC_VERSIONSTRING', 'Net_SmartIRC '.SMARTIRC_VERSION); + +/** + * main SmartIRC class + * + * @package Net_SmartIRC + * @version 0.5.5 + * @author Mirco 'meebey' Bauer + * @access public + */ +class Net_SmartIRC_base +{ + /** + * @var resource + * @access private + */ + var $_socket; + + /** + * @var string + * @access private + */ + var $_address; + + /** + * @var integer + * @access private + */ + var $_port; + + /** + * @var string + * @access private + */ + var $_nick; + + /** + * @var string + * @access private + */ + var $_username; + + /** + * @var string + * @access private + */ + var $_realname; + + /** + * @var string + * @access private + */ + var $_usermode; + + /** + * @var string + * @access private + */ + var $_password; + + /** + * @var boolean + * @access private + */ + var $_state = false; + + /** + * @var array + * @access private + */ + var $_actionhandler = array(); + + /** + * @var array + * @access private + */ + var $_timehandler = array(); + + /** + * @var integer + * @access private + */ + var $_debug = SMARTIRC_DEBUG_NOTICE; + + /** + * @var array + * @access private + */ + var $_messagebuffer = array(); + + /** + * @var integer + * @access private + */ + var $_messagebuffersize; + + /** + * @var boolean + * @access private + */ + var $_usesockets = false; + + /** + * @var integer + * @access private + */ + var $_receivedelay = 100; + + /** + * @var integer + * @access private + */ + var $_senddelay = 250; + + /** + * @var integer + * @access private + */ + var $_logdestination = SMARTIRC_STDOUT; + + /** + * @var resource + * @access private + */ + var $_logfilefp = 0; + + /** + * @var string + * @access private + */ + var $_logfile = 'Net_SmartIRC.log'; + + /** + * @var integer + * @access private + */ + var $_disconnecttime = 1000; + + /** + * @var boolean + * @access private + */ + var $_loggedin = false; + + /** + * @var boolean + * @access private + */ + var $_benchmark = false; + + /** + * @var integer + * @access private + */ + var $_benchmark_starttime; + + /** + * @var integer + * @access private + */ + var $_benchmark_stoptime; + + /** + * @var integer + * @access private + */ + var $_actionhandlerid = 0; + + /** + * @var integer + * @access private + */ + var $_timehandlerid = 0; + + /** + * @var array + * @access private + */ + var $_motd = array(); + + /** + * @var array + * @access private + */ + var $_channels = array(); + + /** + * @var boolean + * @access private + */ + var $_channelsyncing = false; + + /** + * @var string + * @access private + */ + var $_ctcpversion; + + /** + * @var mixed + * @access private + */ + var $_mintimer = false; + + /** + * @var integer + * @access private + */ + var $_maxtimer = 300000; + + /** + * @var integer + * @access private + */ + var $_txtimeout = 300; + + /** + * @var integer + * @access private + */ + var $_rxtimeout = 300; + + /** + * @var integer + * @access private + */ + var $_selecttimeout; + + /** + * @var integer + * @access private + */ + var $_lastrx; + + /** + * @var integer + * @access private + */ + var $_lasttx; + + /** + * @var boolean + * @access private + */ + var $_autoreconnect = false; + + /** + * @var boolean + * @access private + */ + var $_autoretry = false; + + /** + * All IRC replycodes, the index is the replycode name. + * + * @see $SMARTIRC_replycodes + * @var array + * @access public + */ + var $replycodes; + + /** + * All numeric IRC replycodes, the index is the numeric replycode. + * + * @see $SMARTIRC_nreplycodes + * @var array + * @access public + */ + var $nreplycodes; + + /** + * Stores all channels in this array where we are joined, works only if channelsyncing is activated. + * Eg. for accessing a user, use it like this: (in this example the SmartIRC object is stored in $irc) + * $irc->channel['#test']->users['meebey']->nick; + * + * @see setChannelSyncing() + * @see Net_SmartIRC_channel + * @see Net_SmartIRC_channeluser + * @var array + * @access public + */ + var $channel; + + /** + * Constructor. Initiales the messagebuffer and "links" the replycodes from + * global into properties. Also some PHP runtime settings are configured. + * + * @access public + * @return void + */ + function Net_SmartIRC_base() + { + // precheck + $this->_checkPHPVersion(); + + ob_implicit_flush(true); + @set_time_limit(0); + ignore_user_abort(true); + $this->_messagebuffer[SMARTIRC_CRITICAL] = array(); + $this->_messagebuffer[SMARTIRC_HIGH] = array(); + $this->_messagebuffer[SMARTIRC_MEDIUM] = array(); + $this->_messagebuffer[SMARTIRC_LOW] = array(); + $this->replycodes = &$GLOBALS['SMARTIRC_replycodes']; + $this->nreplycodes = &$GLOBALS['SMARTIRC_nreplycodes']; + + // hack till PHP allows (PHP5) $object->method($param)->$object + $this->channel = &$this->_channels; + // another hack + $this->user = &$this->_users; + + if (isset($_SERVER['REQUEST_METHOD'])) { + // the script is called from a browser, lets set default log destination + // to SMARTIRC_BROWSEROUT (makes browser friendly output) + $this->setLogdestination(SMARTIRC_BROWSEROUT); + } + } + + /** + * Enables/disables the usage of real sockets. + * + * Enables/disables the usage of real sockets instead of fsocks + * (works only if your PHP build has loaded the PHP socket extension) + * Default: false + * + * @param bool $boolean + * @return void + * @access public + */ + function setUseSockets($boolean) + { + if ($boolean === true) { + if (@extension_loaded('sockets')) { + $this->_usesockets = true; + } else { + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: socket extension not loaded, trying to load it...', __FILE__, __LINE__); + + if (strtoupper(substr(PHP_OS, 0,3) == 'WIN')) { + $load_status = @dl('php_sockets.dll'); + } else { + $load_status = @dl('sockets.so'); + } + + if ($load_status) { + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: socket extension succesfully loaded', __FILE__, __LINE__); + $this->_usesockets = true; + } else { + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: couldn\'t load the socket extension', __FILE__, __LINE__); + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: your PHP build doesn\'t support real sockets, will use fsocks instead', __FILE__, __LINE__); + $this->_usesockets = false; + } + } + } else { + $this->_usesockets = false; + } + } + + /** + * Sets the level of debug messages. + * + * Sets the debug level (bitwise), useful for testing/developing your code. + * Here the list of all possible debug levels: + * SMARTIRC_DEBUG_NONE + * SMARTIRC_DEBUG_NOTICE + * SMARTIRC_DEBUG_CONNECTION + * SMARTIRC_DEBUG_SOCKET + * SMARTIRC_DEBUG_IRCMESSAGES + * SMARTIRC_DEBUG_MESSAGETYPES + * SMARTIRC_DEBUG_ACTIONHANDLER + * SMARTIRC_DEBUG_TIMEHANDLER + * SMARTIRC_DEBUG_MESSAGEHANDLER + * SMARTIRC_DEBUG_CHANNELSYNCING + * SMARTIRC_DEBUG_MODULES + * SMARTIRC_DEBUG_USERSYNCING + * SMARTIRC_DEBUG_ALL + * + * Default: SMARTIRC_DEBUG_NOTICE + * + * @see DOCUMENTATION + * @see SMARTIRC_DEBUG_NOTICE + * @param integer $level + * @return void + * @access public + */ + function setDebug($level) + { + $this->_debug = $level; + } + + /** + * Enables/disables the benchmark engine. + * + * @param boolean $boolean + * @return void + * @access public + */ + function setBenchmark($boolean) + { + if (is_bool($boolean)) { + $this->_benchmark = $boolean; + } else { + $this->_benchmark = false; + } + } + + /** + * Deprecated, use setChannelSyncing() instead! + * + * @deprecated + * @param boolean $boolean + * @return void + * @access public + */ + function setChannelSynching($boolean) + { + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: you are using setChannelSynching() which is a deprecated method, use setChannelSyncing() instead!', __FILE__, __LINE__); + $this->setChannelSyncing($boolean); + } + + /** + * Enables/disables channel syncing. + * + * Channel syncing means, all users on all channel we are joined are tracked in the + * channel array. This makes it very handy for botcoding. + * + * @param boolean $boolean + * @return void + * @access public + */ + function setChannelSyncing($boolean) + { + if (is_bool($boolean)) { + $this->_channelsyncing = $boolean; + } else { + $this->_channelsyncing = false; + } + + if ($this->_channelsyncing == true) { + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: Channel syncing enabled', __FILE__, __LINE__); + } else { + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: Channel syncing disabled', __FILE__, __LINE__); + } + } + + /** + * Sets the CTCP version reply string. + * + * @param string $versionstring + * @return void + * @access public + */ + function setCtcpVersion($versionstring) + { + $this->_ctcpversion = $versionstring; + } + + /** + * Sets the destination of all log messages. + * + * Sets the destination of log messages. + * $type can be: + * SMARTIRC_FILE for saving the log into a file + * SMARTIRC_STDOUT for echoing the log to stdout + * SMARTIRC_SYSLOG for sending the log to the syslog + * Default: SMARTIRC_STDOUT + * + * @see SMARTIRC_STDOUT + * @param integer $type must be on of the constants + * @return void + * @access public + */ + function setLogdestination($type) + { + switch ($type) { + case (SMARTIRC_FILE || + SMARTIRC_STDOUT || + SMARTIRC_SYSLOG || + SMARTIRC_BROWSEROUT || + SMARTIRC_NONE): + $this->_logdestination = $type; + break; + default: + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: unknown logdestination type ('.$type.'), will use STDOUT instead', __FILE__, __LINE__); + $this->_logdestination = SMARTIRC_STDOUT; + } + } + + /** + * Sets the file for the log if the destination is set to file. + * + * Sets the logfile, if {@link setLogdestination logdestination} is set to SMARTIRC_FILE. + * This should be only used with full path! + * + * @param string $file + * @return void + * @access public + */ + function setLogfile($file) + { + $this->_logfile = $file; + } + + /** + * Sets the delaytime before closing the socket when disconnect. + * + * @param integer $milliseconds + * @return void + * @access public + */ + function setDisconnecttime($milliseconds) + { + if (is_integer($milliseconds) && $milliseconds >= 100) { + $this->_disconnecttime = $milliseconds; + } else { + $this->_disconnecttime = 100; + } + } + + /** + * Sets the delay for receiving data from the IRC server. + * + * Sets the delaytime between messages that are received, this reduces your CPU load. + * Don't set this too low (min 100ms). + * Default: 100 + * + * @param integer $milliseconds + * @return void + * @access public + */ + function setReceivedelay($milliseconds) + { + if (is_integer($milliseconds) && $milliseconds >= 100) { + $this->_receivedelay = $milliseconds; + } else { + $this->_receivedelay = 100; + } + } + + /** + * Sets the delay for sending data to the IRC server. + * + * Sets the delaytime between messages that are sent, because IRC servers doesn't like floods. + * This will avoid sending your messages too fast to the IRC server. + * Default: 250 + * + * @param integer $milliseconds + * @return void + * @access public + */ + function setSenddelay($milliseconds) + { + if (is_integer($milliseconds)) { + $this->_senddelay = $milliseconds; + } else { + $this->_senddelay = 250; + } + } + + /** + * Enables/disables autoreconnecting. + * + * @param boolean $boolean + * @return void + * @access public + */ + function setAutoReconnect($boolean) + { + if (is_bool($boolean)) { + $this->_autoreconnect = $boolean; + } else { + $this->_autoreconnect = false; + } + } + + /** + * Enables/disables autoretry for connecting to a server. + * + * @param boolean $boolean + * @return void + * @access public + */ + function setAutoRetry($boolean) + { + if (is_bool($boolean)) { + $this->_autoretry = $boolean; + } else { + $this->_autoretry = false; + } + } + + /** + * Sets the receive timeout. + * + * If the timeout occurs, the connection will be reinitialized + * Default: 300 seconds + * + * @param integer $seconds + * @return void + * @access public + */ + function setReceiveTimeout($seconds) + { + if (is_integer($seconds)) { + $this->_rxtimeout = $seconds; + } else { + $this->_rxtimeout = 300; + } + } + + /** + * Sets the transmit timeout. + * + * If the timeout occurs, the connection will be reinitialized + * Default: 300 seconds + * + * @param integer $seconds + * @return void + * @access public + */ + function setTransmitTimeout($seconds) + { + if (is_integer($seconds)) { + $this->_txtimeout = $seconds; + } else { + $this->_txtimeout = 300; + } + } + + /** + * Starts the benchmark (sets the counters). + * + * @return void + * @access public + */ + function startBenchmark() + { + $this->_benchmark_starttime = $this->_microint(); + $this->log(SMARTIRC_DEBUG_NOTICE, 'benchmark started', __FILE__, __LINE__); + } + + /** + * Stops the benchmark and displays the result. + * + * @return void + * @access public + */ + function stopBenchmark() + { + $this->_benchmark_stoptime = $this->_microint(); + $this->log(SMARTIRC_DEBUG_NOTICE, 'benchmark stopped', __FILE__, __LINE__); + + if ($this->_benchmark) { + $this->showBenchmark(); + } + } + + /** + * Shows the benchmark result. + * + * @return void + * @access public + */ + function showBenchmark() + { + $this->log(SMARTIRC_DEBUG_NOTICE, 'benchmark time: '.((float)$this->_benchmark_stoptime-(float)$this->_benchmark_starttime), __FILE__, __LINE__); + } + + /** + * Adds an entry to the log. + * + * Adds an entry to the log with Linux style log format. + * Possible $level constants (can also be combined with "|"s) + * SMARTIRC_DEBUG_NONE + * SMARTIRC_DEBUG_NOTICE + * SMARTIRC_DEBUG_CONNECTION + * SMARTIRC_DEBUG_SOCKET + * SMARTIRC_DEBUG_IRCMESSAGES + * SMARTIRC_DEBUG_MESSAGETYPES + * SMARTIRC_DEBUG_ACTIONHANDLER + * SMARTIRC_DEBUG_TIMEHANDLER + * SMARTIRC_DEBUG_MESSAGEHANDLER + * SMARTIRC_DEBUG_CHANNELSYNCING + * SMARTIRC_DEBUG_MODULES + * SMARTIRC_DEBUG_USERSYNCING + * SMARTIRC_DEBUG_ALL + * + * @see SMARTIRC_DEBUG_NOTICE + * @param integer $level bit constants (SMARTIRC_DEBUG_*) + * @param string $entry the new log entry + * @return void + * @access public + */ + function log($level, $entry, $file = null, $line = null) + { + // prechecks + if (!(is_integer($level)) || + !($level & SMARTIRC_DEBUG_ALL)) { + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: invalid log level passed to log() ('.$level.')', __FILE__, __LINE__); + return; + } + + if (!($level & $this->_debug) || + ($this->_logdestination == SMARTIRC_NONE)) { + return; + } + + if (substr($entry, -1) != "\n") { + $entry .= "\n"; + } + + if ($file !== null && + $line !== null) { + $file = basename($file); + $entry = $file.'('.$line.') '.$entry; + } else { + $entry = 'unknown(0) '.$entry; + } + + $formatedentry = date('M d H:i:s ').$entry; + switch ($this->_logdestination) { + case SMARTIRC_STDOUT: + echo $formatedentry; + flush(); + break; + case SMARTIRC_BROWSEROUT: + echo '
'.htmlentities($formatedentry).'
'; + break; + case SMARTIRC_FILE: + if (!is_resource($this->_logfilefp)) { + if ($this->_logfilefp === null) { + // we reconncted and don't want to destroy the old log entries + $this->_logfilefp = @fopen($this->_logfile,'a'); + } else { + $this->_logfilefp = @fopen($this->_logfile,'w'); + } + } + @fwrite($this->_logfilefp, $formatedentry); + fflush($this->_logfilefp); + break; + case SMARTIRC_SYSLOG: + define_syslog_variables(); + if (!is_int($this->_logfilefp)) { + $this->_logfilefp = openlog('Net_SmartIRC', LOG_NDELAY, LOG_DAEMON); + } + syslog(LOG_INFO, $entry); + break; + } + } + + /** + * Returns the full motd. + * + * @return array + * @access public + */ + function getMotd() + { + return $this->_motd; + } + + /** + * Returns the usermode. + * + * @return string + * @access public + */ + function getUsermode() + { + return $this->_usermode; + } + + /** + * Creates the sockets and connects to the IRC server on the given port. + * + * @param string $address + * @param integer $port + * @return void + * @access public + */ + function connect($address, $port) + { + $this->log(SMARTIRC_DEBUG_CONNECTION, 'DEBUG_CONNECTION: connecting', __FILE__, __LINE__); + $this->_address = $address; + $this->_port = $port; + + if ($this->_usesockets == true) { + $this->log(SMARTIRC_DEBUG_SOCKET, 'DEBUG_SOCKET: using real sockets', __FILE__, __LINE__); + $this->_socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + $result = @socket_connect($this->_socket, $this->_address, $this->_port); + } else { + $this->log(SMARTIRC_DEBUG_SOCKET, 'DEBUG_SOCKET: using fsockets', __FILE__, __LINE__); + $result = @fsockopen($this->_address, $this->_port, $errno, $errstr); + } + + if ($result === false) { + if ($this->_usesockets == true) { + $error = socket_strerror(socket_last_error($this->_socket)); + } else { + $error = $errstr.' ('.$errno.')'; + } + + $error_msg = 'couldn\'t connect to "'.$address.'" reason: "'.$error.'"'; + $this->log(SMARTIRC_DEBUG_NOTICE, 'DEBUG_NOTICE: '.$error_msg, __FILE__, __LINE__); + // TODO! muss return wert sein + $this->throwError($error_msg); + + // doesn't work somehow.... I only want to retry 4 times! no endless loop (causes segfault) + static $tries = 0; + if ($this->_autoretry == true && $tries < 5) { + $this->reconnect(); + $tries++; + } else { + die(); + } + } else { + $this->log(SMARTIRC_DEBUG_CONNECTION, 'DEBUG_CONNECTION: connected', __FILE__, __LINE__); + + if ($this->_usesockets != true) { + $this->_socket = $result; + $this->log(SMARTIRC_DEBUG_SOCKET, 'DEBUG_SOCKET: activating nonblocking fsocket mode', __FILE__, __LINE__); + socket_set_blocking($this->_socket, false); + } + } + + $this->_lastrx = time(); + $this->_lasttx = $this->_lastrx; + $this->_updatestate(); + + if ($result !== false) { + return true; + } else { + return false; + } + } + + /** + * Disconnects from the IRC server nicely with a QUIT or just destroys the socket. + * + * Disconnects from the IRC server in the given quickness mode. + * $quickdisconnect: + * true, just close the socket + * false, send QUIT and wait {@link $_disconnectime $_disconnectime} before closing the socket + * + * @param boolean $quickdisconnect default: false + * @return boolean + * @access public + */ + function disconnect($quickdisconnect = false) + { + if ($this->_state() == SMARTIRC_STATE_CONNECTED) { + if ($quickdisconnect == false) { + $this->_send('QUIT', SMARTIRC_CRITICAL); + usleep($this->_disconnecttime*1000); + } + + if ($this->_usesockets == true) { + @socket_shutdown($this->_socket); + @socket_close($this->_socket); + } else { + fclose($this->_socket); + } + + $this->_updatestate(); + $this->log(SMARTIRC_DEBUG_CONNECTION, 'DEBUG_CONNECTION: disconnected', __FILE__, __LINE__); + } else { + return false; + } + + if ($this->_channelsyncing == true) { + // let's clean our channel array + $this->_channels = array(); + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: cleaned channel array', __FILE__, __LINE__); + } + + if ($this->_logdestination == SMARTIRC_FILE) { + fclose($this->_logfilefp); + $this->_logfilefp = null; + } else if ($this->_logdestination == SMARTIRC_SYSLOG) { + closelog(); + } + + return true; + } + + /** + * Reconnects to the IRC server with the same login info, + * it also rejoins the channels + * + * @return void + * @access public + */ + function reconnect() + { + $this->log(SMARTIRC_DEBUG_CONNECTION, 'DEBUG_CONNECTION: reconnecting...', __FILE__, __LINE__); + + // remember in which channels we are joined + $channels = array(); + foreach ($this->_channels as $value) { + if (empty($value->key)) { + $channels[] = array('name' => $value->name); + } else { + $channels[] = array('name' => $value->name, 'key' => $value->key); + } + } + + $this->disconnect(true); + $this->connect($this->_address, $this->_port); + $this->login($this->_nick, $this->_realname, $this->_usermode, $this->_username, $this->_password); + + // rejoin the channels + foreach ($channels as $value) { + if (isset($value['key'])) { + $this->join($value['name'], $value['key']); + } else { + $this->join($value['name']); + } + } + } + + /** + * login and register nickname on the IRC network + * + * Registers the nickname and user information on the IRC network. + * + * @param string $nick + * @param string $realname + * @param integer $usermode + * @param string $username + * @param string $password + * @return void + * @access public + */ + function login($nick, $realname, $usermode = 0, $username = null, $password = null) + { + $this->log(SMARTIRC_DEBUG_CONNECTION, 'DEBUG_CONNECTION: logging in', __FILE__, __LINE__); + + $this->_nick = str_replace(' ', '', $nick); + $this->_realname = $realname; + + if ($username !== null) { + $this->_username = str_replace(' ', '', $username); + } else { + $this->_username = str_replace(' ', '', exec('whoami')); + } + + if ($password !== null) { + $this->_password = $password; + $this->_send('PASS '.$this->_password, SMARTIRC_CRITICAL); + } + + if (!is_numeric($usermode)) { + $this->log(SMARTIRC_DEBUG_NOTICE, 'DEBUG_NOTICE: login() usermode ('.$usermode.') is not valid, will use 0 instead', __FILE__, __LINE__); + $usermode = 0; + } + + $this->_send('NICK '.$this->_nick, SMARTIRC_CRITICAL); + $this->_send('USER '.$this->_username.' '.$usermode.' '.SMARTIRC_UNUSED.' :'.$this->_realname, SMARTIRC_CRITICAL); + } + + // + + /** + * checks if we or the given user is joined to the specified channel and returns the result + * ChannelSyncing is required for this. + * + * @see setChannelSyncing + * @param string $channel + * @param string $nickname + * @return boolean + * @access public + */ + function isJoined($channel, $nickname = null) + { + if ($this->_channelsyncing != true) { + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: isJoined() is called and the required Channel Syncing is not activated!', __FILE__, __LINE__); + return false; + } + + if ($nickname === null) { + $nickname = $this->_nick; + } + + if (isset($this->_channels[strtolower($channel)]->users[strtolower($nickname)])) { + return true; + } + + return false; + } + + /** + * Checks if we or the given user is opped on the specified channel and returns the result. + * ChannelSyncing is required for this. + * + * @see setChannelSyncing + * @param string $channel + * @param string $nickname + * @return boolean + * @access public + */ + function isOpped($channel, $nickname = null) + { + if ($this->_channelsyncing != true) { + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: isOpped() is called and the required Channel Syncing is not activated!', __FILE__, __LINE__); + return false; + } + + if ($nickname === null) { + $nickname = $this->_nick; + } + + if ($this->isJoined($channel, $nickname)) { + if ($this->_channels[strtolower($channel)]->users[strtolower($nickname)]->op) { + return true; + } + } + + return false; + } + + /** + * Checks if we or the given user is voiced on the specified channel and returns the result. + * ChannelSyncing is required for this. + * + * @see setChannelSyncing + * @param string $channel + * @param string $nickname + * @return boolean + * @access public + */ + function isVoiced($channel, $nickname = null) + { + if ($this->_channelsyncing != true) { + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: isVoiced() is called and the required Channel Syncing is not activated!', __FILE__, __LINE__); + return false; + } + + if ($nickname === null) { + $nickname = $this->_nick; + } + + if ($this->isJoined($channel, $nickname)) { + if ($this->_channels[strtolower($channel)]->users[strtolower($nickname)]->voice) { + return true; + } + } + + return false; + } + + /** + * Checks if the hostmask is on the specified channel banned and returns the result. + * ChannelSyncing is required for this. + * + * @see setChannelSyncing + * @param string $channel + * @param string $hostmask + * @return boolean + * @access public + */ + function isBanned($channel, $hostmask) + { + if ($this->_channelsyncing != true) { + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: isBanned() is called and the required Channel Syncing is not activated!', __FILE__, __LINE__); + return false; + } + + if ($this->isJoined($channel)) { + $result = array_search($hostmask, $this->_channels[strtolower($channel)]->bans); + + if ($result !== false) { + return true; + } + } + + return false; + } + + /** + * goes into receive mode + * + * Goes into receive and idle mode. Only call this if you want to "spawn" the bot. + * No further lines of PHP code will be processed after this call, only the bot methods! + * + * @return boolean + * @access public + */ + function listen() + { + if ($this->_state() == SMARTIRC_STATE_CONNECTED) { + $this->_rawreceive(); + return true; + } else { + return false; + } + } + + /** + * waits for a special message type and puts the answer in $result + * + * Creates a special actionhandler for that given TYPE and returns the answer. + * This will only receive the requested type, immediately quit and disconnect from the IRC server. + * Made for showing IRC statistics on your homepage, or other IRC related information. + * + * @param integer $messagetype see in the documentation 'Message Types' + * @return array answer from the IRC server for this $messagetype + * @access public + */ + function listenFor($messagetype) + { + $listenfor = &new Net_SmartIRC_listenfor(); + $this->registerActionhandler($messagetype, '.*', $listenfor, 'handler'); + $this->listen(); + $result = $listenfor->result; + + if (isset($listenfor)) { + unset($listenfor); + } + + return $result; + } + + /** + * waits for a special message type and puts the answer in $result + * + * Creates a special actionhandler for that given TYPE and returns the answer. + * This will only receive the requested type, immediately quit and disconnect from the IRC server. + * Made for showing IRC statistics on your homepage, or other IRC related information. + * This special version of listenFor() stores the whole ircdata object, not just the message! + * + * @param integer $messagetype see in the documentation 'Message Types' + * @return array answer from the IRC server for this $messagetype + * @access public + */ + function objListenFor($messagetype) + { + $objlistenfor = &new Net_SmartIRC_objListenFor(); + $this->registerActionhandler($messagetype, '.*', $objlistenfor, 'handler'); + $this->listen(); + $result = $objlistenfor->result; + + if (isset($objlistenfor)) { + unset($objlistenfor); + } + + return $result; + } + + /** + * registers a new actionhandler and returns the assigned id + * + * Registers an actionhandler in Net_SmartIRC for calling it later. + * The actionhandler id is needed for unregistering the actionhandler. + * + * @see example.php + * @param integer $handlertype bits constants, see in this documentation Message Types + * @param string $regexhandler the message that has to be in the IRC message in regex syntax + * @param object $object a reference to the objects of the method + * @param string $methodname the methodname that will be called when the handler happens + * @return integer assigned actionhandler id + * @access public + */ + function registerActionhandler($handlertype, $regexhandler, &$object, $methodname) + { + // precheck + if (!$this->_isValidType($handlertype)) { + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: passed invalid handlertype to registerActionhandler()', __FILE__, __LINE__); + return false; + } + + $id = $this->_actionhandlerid++; + $newactionhandler = &new Net_SmartIRC_actionhandler(); + + $newactionhandler->id = $id; + $newactionhandler->type = $handlertype; + $newactionhandler->message = $regexhandler; + $newactionhandler->object = &$object; + $newactionhandler->method = $methodname; + + $this->_actionhandler[] = &$newactionhandler; + $this->log(SMARTIRC_DEBUG_ACTIONHANDLER, 'DEBUG_ACTIONHANDLER: actionhandler('.$id.') registered', __FILE__, __LINE__); + return $id; + } + + /** + * unregisters an existing actionhandler + * + * @param integer $handlertype + * @param string $regexhandler + * @param object $object + * @param string $methodname + * @return boolean + * @access public + */ + function unregisterActionhandler($handlertype, $regexhandler, &$object, $methodname) + { + // precheck + if (!$this->_isValidType($handlertype)) { + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: passed invalid handlertype to unregisterActionhandler()', __FILE__, __LINE__); + return false; + } + + $handler = &$this->_actionhandler; + $handlercount = count($handler); + + for ($i = 0; $i < $handlercount; $i++) { + $handlerobject = &$handler[$i]; + + if ($handlerobject->type == $handlertype && + $handlerobject->message == $regexhandler && + $handlerobject->method == $methodname) { + + $id = $handlerobject->id; + + if (isset($this->_actionhandler[$i])) { + unset($this->_actionhandler[$i]); + } + + $this->log(SMARTIRC_DEBUG_ACTIONHANDLER, 'DEBUG_ACTIONHANDLER: actionhandler('.$id.') unregistered', __FILE__, __LINE__); + $this->_reorderactionhandler(); + return true; + } + } + + $this->log(SMARTIRC_DEBUG_ACTIONHANDLER, 'DEBUG_ACTIONHANDLER: could not find actionhandler type: "'.$handlertype.'" message: "'.$regexhandler.'" method: "'.$methodname.'" from object "'.get_class($object).'" _not_ unregistered', __FILE__, __LINE__); + return false; + } + + /** + * unregisters an existing actionhandler via the id + * + * @param integer $id + * @return boolean + * @access public + */ + function unregisterActionid($id) + { + $handler = &$this->_actionhandler; + $handlercount = count($handler); + for ($i = 0; $i < $handlercount; $i++) { + $handlerobject = &$handler[$i]; + + if ($handlerobject->id == $id) { + if (isset($this->_actionhandler[$i])) { + unset($this->_actionhandler[$i]); + } + + $this->log(SMARTIRC_DEBUG_ACTIONHANDLER, 'DEBUG_ACTIONHANDLER: actionhandler('.$id.') unregistered', __FILE__, __LINE__); + $this->_reorderactionhandler(); + return true; + } + } + + $this->log(SMARTIRC_DEBUG_ACTIONHANDLER, 'DEBUG_ACTIONHANDLER: could not find actionhandler id: '.$id.' _not_ unregistered', __FILE__, __LINE__); + return false; + } + + /** + * registers a timehandler and returns the assigned id + * + * Registers a timehandler in Net_SmartIRC, which will be called in the specified interval. + * The timehandler id is needed for unregistering the timehandler. + * + * @see example7.php + * @param integer $interval interval time in milliseconds + * @param object $object a reference to the objects of the method + * @param string $methodname the methodname that will be called when the handler happens + * @return integer assigned timehandler id + * @access public + */ + function registerTimehandler($interval, &$object, $methodname) + { + $id = $this->_timehandlerid++; + $newtimehandler = &new Net_SmartIRC_timehandler(); + + $newtimehandler->id = $id; + $newtimehandler->interval = $interval; + $newtimehandler->object = &$object; + $newtimehandler->method = $methodname; + $newtimehandler->lastmicrotimestamp = $this->_microint(); + + $this->_timehandler[] = &$newtimehandler; + $this->log(SMARTIRC_DEBUG_TIMEHANDLER, 'DEBUG_TIMEHANDLER: timehandler('.$id.') registered', __FILE__, __LINE__); + + if (($interval < $this->_mintimer) || ($this->_mintimer == false)) { + $this->_mintimer = $interval; + } + + return $id; + } + + /** + * unregisters an existing timehandler via the id + * + * @see example7.php + * @param integer $id + * @return boolean + * @access public + */ + function unregisterTimeid($id) + { + $handler = &$this->_timehandler; + $handlercount = count($handler); + for ($i = 0; $i < $handlercount; $i++) { + $handlerobject = &$handler[$i]; + + if ($handlerobject->id == $id) { + if (isset($this->_timehandler[$i])) { + unset($this->_timehandler[$i]); + } + + $this->log(SMARTIRC_DEBUG_TIMEHANDLER, 'DEBUG_TIMEHANDLER: timehandler('.$id.') unregistered', __FILE__, __LINE__); + $this->_reordertimehandler(); + $this->_updatemintimer(); + return true; + } + } + + $this->log(SMARTIRC_DEBUG_TIMEHANDLER, 'DEBUG_TIMEHANDLER: could not find timehandler id: '.$id.' _not_ unregistered', __FILE__, __LINE__); + return false; + } + + // + /** + * changes a already used nickname to a new nickname plus 3 random digits + * + * @return void + * @access private + */ + function _nicknameinuse() + { + $newnickname = substr($this->_nick, 0, 5).rand(0, 999); + $this->changeNick($newnickname, SMARTIRC_CRITICAL); + } + + /** + * sends an IRC message + * + * Adds a message to the messagequeue, with the optional priority. + * $priority: + * SMARTIRC_CRITICAL + * SMARTIRC_HIGH + * SMARTIRC_MEDIUM + * SMARTIRC_LOW + * + * @param string $data + * @param integer $priority must be one of the priority constants + * @return boolean + * @access private + */ + function _send($data, $priority = SMARTIRC_MEDIUM) + { + switch ($priority) { + case SMARTIRC_CRITICAL: + $this->_rawsend($data); + return true; + break; + case (SMARTIRC_HIGH|| + SMARTIRC_MEDIUM|| + SMARTIRC_LOW): + $this->_messagebuffer[$priority][] = $data; + return true; + break; + default: + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: message ('.$data.') with an invalid priority passed ('.$priority.'), message is ignored!', __FILE__, __LINE__); + return false; + } + } + + /** + * checks the buffer if there are messages to send + * + * @return void + * @access private + */ + function _checkbuffer() + { + if (!$this->_loggedin) { + return; + } + + static $highsent = 0; + static $lastmicrotimestamp = 0; + + if ($lastmicrotimestamp == 0) { + $lastmicrotimestamp = $this->_microint(); + } + + $highcount = count($this->_messagebuffer[SMARTIRC_HIGH]); + $mediumcount = count($this->_messagebuffer[SMARTIRC_MEDIUM]); + $lowcount = count($this->_messagebuffer[SMARTIRC_LOW]); + $this->_messagebuffersize = $highcount+$mediumcount+$lowcount; + + // don't send them too fast + if ($this->_microint() >= ($lastmicrotimestamp+($this->_senddelay/1000))) { + if ($highcount > 0 && $highsent <= 2) { + $this->_rawsend(array_shift($this->_messagebuffer[SMARTIRC_HIGH])); + $lastmicrotimestamp = $this->_microint(); + $highsent++; + } else if ($mediumcount > 0) { + $this->_rawsend(array_shift($this->_messagebuffer[SMARTIRC_MEDIUM])); + $lastmicrotimestamp = $this->_microint(); + $highsent = 0; + } else if ($lowcount > 0) { + $this->_rawsend(array_shift($this->_messagebuffer[SMARTIRC_LOW])); + $lastmicrotimestamp = $this->_microint(); + } + } + } + + /** + * Checks the running timers and calls the registered timehandler, + * when the interval is reached. + * + * @return void + * @access private + */ + function _checktimer() + { + if (!$this->_loggedin) { + return; + } + + // has to be count() because the array may change during the loop! + for ($i = 0; $i < count($this->_timehandler); $i++) { + $handlerobject = &$this->_timehandler[$i]; + $microtimestamp = $this->_microint(); + if ($microtimestamp >= ($handlerobject->lastmicrotimestamp+($handlerobject->interval/1000))) { + $methodobject = &$handlerobject->object; + $method = $handlerobject->method; + $handlerobject->lastmicrotimestamp = $microtimestamp; + + if (@method_exists($methodobject, $method)) { + $this->log(SMARTIRC_DEBUG_TIMEHANDLER, 'DEBUG_TIMEHANDLER: calling method "'.get_class($methodobject).'->'.$method.'"', __FILE__, __LINE__); + $methodobject->$method($this); + } + } + } + } + + /** + * Checks if a receive or transmit timeout occured and reconnects if configured + * + * @return void + * @access private + */ + function _checktimeout() + { + if ($this->_autoreconnect == true) { + $timestamp = time(); + if ($this->_lastrx < ($timestamp - $this->_rxtimeout)) { + $this->log(SMARTIRC_DEBUG_CONNECTION, 'DEBUG_CONNECTION: receive timeout detected, doing reconnect...', __FILE__, __LINE__); + $this->reconnect(); + } else if ($this->_lasttx < ($timestamp - $this->_txtimeout)) { + $this->log(SMARTIRC_DEBUG_CONNECTION, 'DEBUG_CONNECTION: transmit timeout detected, doing reconnect...', __FILE__, __LINE__); + $this->reconnect(); + } + } + } + + /** + * sends a raw message to the IRC server (don't use this!!) + * + * Use message() or _send() instead. + * + * @param string $data + * @return boolean + * @access private + */ + function _rawsend($data) + { + if ($this->_state() == SMARTIRC_STATE_CONNECTED) { + $this->log(SMARTIRC_DEBUG_IRCMESSAGES, 'DEBUG_IRCMESSAGES: sent: "'.$data.'"', __FILE__, __LINE__); + + if ($this->_usesockets == true) { + $result = @socket_write($this->_socket, $data.SMARTIRC_CRLF); + } else { + $result = @fwrite($this->_socket, $data.SMARTIRC_CRLF); + } + + + if ($result === false) { + return false; + } else { + $this->_lasttx = time(); + return true; + } + } else { + return false; + } + } + + /** + * goes into main idle loop for waiting messages from the IRC server + * + * @return void + * @access private + */ + function _rawreceive() + { + $lastpart = ''; + $rawdataar = array(); + + while ($this->_state() == SMARTIRC_STATE_CONNECTED) { + $this->_checkbuffer(); + + $timeout = $this->_selecttimeout(); + if ($this->_usesockets == true) { + $sread = array($this->_socket); + $result = @socket_select($sread, $w = null, $e = null, 0, $timeout*1000); + + if ($result == 1) { + // the socket got data to read + $rawdata = @socket_read($this->_socket, 10240); + } else if ($result === false) { + // panic! panic! something went wrong! + $this->log(SMARTIRC_DEBUG_NOTICE, 'WARNING: socket_select() returned false, something went wrong! Reason: '.socket_strerror(socket_last_error()), __FILE__, __LINE__); + exit; + } else { + // no data + $rawdata = null; + } + } else { + usleep($this->_receivedelay*1000); + $rawdata = @fread($this->_socket, 10240); + } + + $this->_checktimer(); + $this->_checktimeout(); + + if ($rawdata !== null && !empty($rawdata)) { + $this->_lastrx = time(); + $rawdata = str_replace("\r", '', $rawdata); + $rawdata = $lastpart.$rawdata; + + $lastpart = substr($rawdata, strrpos($rawdata ,"\n")+1); + $rawdata = substr($rawdata, 0, strrpos($rawdata ,"\n")); + $rawdataar = explode("\n", $rawdata); + } + + // loop through our received messages + while (count($rawdataar) > 0) { + $rawline = array_shift($rawdataar); + $validmessage = false; + + $this->log(SMARTIRC_DEBUG_IRCMESSAGES, 'DEBUG_IRCMESSAGES: received: "'.$rawline.'"', __FILE__, __LINE__); + + // building our data packet + $ircdata = &new Net_SmartIRC_data(); + $ircdata->rawmessage = $rawline; + $lineex = explode(' ', $rawline); + $ircdata->rawmessageex = $lineex; + $messagecode = $lineex[0]; + + if (substr($rawline, 0, 1) == ':') { + $validmessage = true; + $line = substr($rawline, 1); + $lineex = explode(' ', $line); + + // conform to RFC 2812 + $from = $lineex[0]; + $messagecode = $lineex[1]; + $exclamationpos = strpos($from, '!'); + $atpos = strpos($from, '@'); + $colonpos = strpos($line, ' :'); + if ($colonpos !== false) { + // we want the exact position of ":" not beginning from the space + $colonpos += 1; + } + $ircdata->nick = substr($from, 0, $exclamationpos); + $ircdata->ident = substr($from, $exclamationpos+1, ($atpos-$exclamationpos)-1); + $ircdata->host = substr($from, $atpos+1); + $ircdata->type = $this->_gettype($rawline); + $ircdata->from = $from; + if ($colonpos !== false) { + $ircdata->message = substr($line, $colonpos+1); + $ircdata->messageex = explode(' ', $ircdata->message); + } + + if ($ircdata->type & (SMARTIRC_TYPE_CHANNEL| + SMARTIRC_TYPE_ACTION| + SMARTIRC_TYPE_MODECHANGE| + SMARTIRC_TYPE_KICK| + SMARTIRC_TYPE_PART| + SMARTIRC_TYPE_JOIN)) { + $ircdata->channel = $lineex[2]; + } else if ($ircdata->type & (SMARTIRC_TYPE_WHO| + SMARTIRC_TYPE_BANLIST| + SMARTIRC_TYPE_TOPIC| + SMARTIRC_TYPE_CHANNELMODE)) { + $ircdata->channel = $lineex[3]; + } else if ($ircdata->type & SMARTIRC_TYPE_NAME) { + $ircdata->channel = $lineex[4]; + } + + if ($ircdata->channel !== null) { + if (substr($ircdata->channel, 0, 1) == ':') { + $ircdata->channel = substr($ircdata->channel, 1); + } + } + + $this->log(SMARTIRC_DEBUG_MESSAGEPARSER, 'DEBUG_MESSAGEPARSER: ircdata nick: "'.$ircdata->nick. + '" ident: "'.$ircdata->ident. + '" host: "'.$ircdata->host. + '" type: "'.$ircdata->type. + '" from: "'.$ircdata->from. + '" channel: "'.$ircdata->channel. + '" message: "'.$ircdata->message. + '"', __FILE__, __LINE__); + } + + // lets see if we have a messagehandler for it + $this->_handlemessage($messagecode, $ircdata); + + if ($validmessage == true) { + // now the actionhandlers are comming + $this->_handleactionhandler($ircdata); + } + + if (isset($ircdata)) { + unset($ircdata); + } + } + } + } + + /** + * sends the pong for keeping alive + * + * Sends the PONG signal as reply of the PING from the IRC server. + * + * @param string $data + * @return void + * @access private + */ + function _pong($data) + { + $this->log(SMARTIRC_DEBUG_CONNECTION, 'DEBUG_CONNECTION: Ping? Pong!', __FILE__, __LINE__); + $this->_send('PONG '.$data, SMARTIRC_CRITICAL); + } + + /** + * returns the calculated selecttimeout value + * + * @return integer selecttimeout in microseconds + * @access private + */ + function _selecttimeout() { + if ($this->_messagebuffersize == 0) { + $this->_selecttimeout = null; + + if ($this->_mintimer != false) { + $this->_calculateselecttimeout($this->_mintimer); + } + + if ($this->_autoreconnect == true) { + $this->_calculateselecttimeout($this->_rxtimeout*1000); + } + + $this->_calculateselecttimeout($this->_maxtimer); + return $this->_selecttimeout; + } else { + return $this->_senddelay; + } + } + + /** + * calculates the selecttimeout value + * + * @return void + * @access private + */ + function _calculateselecttimeout($microseconds) + { + if (($this->_selecttimeout > $microseconds) || $this->_selecttimeout === null) { + $this->_selecttimeout = $microseconds; + } + } + + /** + * updates _mintimer to the smallest timer interval + * + * @return void + * @access private + */ + function _updatemintimer() + { + $timerarray = array(); + foreach ($this->_timehandler as $values) { + $timerarray[] = $values->interval; + } + + $result = array_multisort($timerarray, SORT_NUMERIC, SORT_ASC); + if ($result == true && isset($timerarray[0])) { + $this->_mintimer = $timerarray[0]; + } else { + $this->_mintimer = false; + } + } + + /** + * reorders the actionhandler array, needed after removing one + * + * @return void + * @access private + */ + function _reorderactionhandler() + { + $orderedactionhandler = array(); + foreach ($this->_actionhandler as $value) { + $orderedactionhandler[] = $value; + } + $this->_actionhandler = &$orderedactionhandler; + } + + /** + * reorders the timehandler array, needed after removing one + * + * @return void + * @access private + */ + function _reordertimehandler() + { + $orderedtimehandler = array(); + foreach ($this->_timehandler as $value) { + $orderedtimehandler[] = $value; + } + $this->_timehandler = &$orderedtimehandler; + } + + /** + * reorders the modules array, needed after removing one + * + * @return void + * @access private + */ + function _reordermodules() + { + $orderedmodules = array(); + foreach ($this->_modules as $value) { + $orderedmodules[] = $value; + } + $this->_modules = &$orderedmodules; + } + + /** + * determines the messagetype of $line + * + * Analyses the type of an IRC message and returns the type. + * + * @param string $line + * @return integer SMARTIRC_TYPE_* constant + * @access private + */ + function _gettype($line) + { + if (preg_match('/^:[^ ]+? [0-9]{3} .+$/', $line) == 1) { + $lineex = explode(' ', $line); + $code = $lineex[1]; + + switch ($code) { + case SMARTIRC_RPL_WELCOME: + case SMARTIRC_RPL_YOURHOST: + case SMARTIRC_RPL_CREATED: + case SMARTIRC_RPL_MYINFO: + case SMARTIRC_RPL_BOUNCE: + return SMARTIRC_TYPE_LOGIN; + case SMARTIRC_RPL_LUSERCLIENT: + case SMARTIRC_RPL_LUSEROP: + case SMARTIRC_RPL_LUSERUNKNOWN: + case SMARTIRC_RPL_LUSERME: + case SMARTIRC_RPL_LUSERCHANNELS: + return SMARTIRC_TYPE_INFO; + case SMARTIRC_RPL_MOTDSTART: + case SMARTIRC_RPL_MOTD: + case SMARTIRC_RPL_ENDOFMOTD: + return SMARTIRC_TYPE_MOTD; + case SMARTIRC_RPL_NAMREPLY: + case SMARTIRC_RPL_ENDOFNAMES: + return SMARTIRC_TYPE_NAME; + case SMARTIRC_RPL_WHOREPLY: + case SMARTIRC_RPL_ENDOFWHO: + return SMARTIRC_TYPE_WHO; + case SMARTIRC_RPL_LISTSTART: + return SMARTIRC_TYPE_NONRELEVANT; + case SMARTIRC_RPL_LIST: + case SMARTIRC_RPL_LISTEND: + return SMARTIRC_TYPE_LIST; + case SMARTIRC_RPL_BANLIST: + case SMARTIRC_RPL_ENDOFBANLIST: + return SMARTIRC_TYPE_BANLIST; + case SMARTIRC_RPL_TOPIC: + return SMARTIRC_TYPE_TOPIC; + case SMARTIRC_RPL_WHOISUSER: + case SMARTIRC_RPL_WHOISSERVER: + case SMARTIRC_RPL_WHOISOPERATOR: + case SMARTIRC_RPL_WHOISIDLE: + case SMARTIRC_RPL_ENDOFWHOIS: + case SMARTIRC_RPL_WHOISCHANNELS: + return SMARTIRC_TYPE_WHOIS; + case SMARTIRC_RPL_WHOWASUSER: + case SMARTIRC_RPL_ENDOFWHOWAS: + return SMARTIRC_TYPE_WHOWAS; + case SMARTIRC_RPL_UMODEIS: + return SMARTIRC_TYPE_USERMODE; + case SMARTIRC_RPL_CHANNELMODEIS: + return SMARTIRC_TYPE_CHANNELMODE; + case SMARTIRC_ERR_NICKNAMEINUSE: + case SMARTIRC_ERR_NOTREGISTERED: + return SMARTIRC_TYPE_ERROR; + default: + $this->log(SMARTIRC_DEBUG_IRCMESSAGES, 'DEBUG_IRCMESSAGES: replycode UNKNOWN ('.$code.'): "'.$line.'"', __FILE__, __LINE__); + } + } + + if (preg_match('/^:.*? PRIVMSG .* :'.chr(1).'ACTION .*'.chr(1).'$/', $line) == 1) { + return SMARTIRC_TYPE_ACTION; + } else if (preg_match('/^:.*? PRIVMSG .* :'.chr(1).'.*'.chr(1).'$/', $line) == 1) { + return SMARTIRC_TYPE_CTCP; + } else if (preg_match('/^:.*? PRIVMSG (\&|\#|\+|\!).* :.*$/', $line) == 1) { + return SMARTIRC_TYPE_CHANNEL; + } else if (preg_match('/^:.*? PRIVMSG .*:.*$/', $line) == 1) { + return SMARTIRC_TYPE_QUERY; + } else if (preg_match('/^:.*? NOTICE .* :.*$/', $line) == 1) { + return SMARTIRC_TYPE_NOTICE; + } else if (preg_match('/^:.*? INVITE .* .*$/', $line) == 1) { + return SMARTIRC_TYPE_INVITE; + } else if (preg_match('/^:.*? JOIN .*$/', $line) == 1) { + return SMARTIRC_TYPE_JOIN; + } else if (preg_match('/^:.*? TOPIC .* :.*$/', $line) == 1) { + return SMARTIRC_TYPE_TOPICCHANGE; + } else if (preg_match('/^:.*? NICK .*$/', $line) == 1) { + return SMARTIRC_TYPE_NICKCHANGE; + } else if (preg_match('/^:.*? KICK .* .*$/', $line) == 1) { + return SMARTIRC_TYPE_KICK; + } else if (preg_match('/^:.*? PART .*$/', $line) == 1) { + return SMARTIRC_TYPE_PART; + } else if (preg_match('/^:.*? MODE .* .*$/', $line) == 1) { + return SMARTIRC_TYPE_MODECHANGE; + } else if (preg_match('/^:.*? QUIT :.*$/', $line) == 1) { + return SMARTIRC_TYPE_QUIT; + } else { + $this->log(SMARTIRC_DEBUG_MESSAGETYPES, 'DEBUG_MESSAGETYPES: SMARTIRC_TYPE_UNKNOWN!: "'.$line.'"', __FILE__, __LINE__); + return SMARTIRC_TYPE_UNKNOWN; + } + } + + /** + * updates the current connection state + * + * @return boolean + * @access private + */ + function _updatestate() + { + $rtype = get_resource_type($this->_socket); + if ((is_resource($this->_socket)) && + ($this->_socket !== false) && + ($rtype == 'socket' || $rtype == 'Socket' || $rtype == 'stream')) { + + $this->_state = true; + return true; + } else { + $this->_state = false; + $this->_loggedin = false; + return false; + } + } + + /** + * returns the current connection state + * + * @return integer SMARTIRC_STATE_CONNECTED or SMARTIRC_STATE_DISCONNECTED + * @access private + */ + function _state() + { + $result = $this->_updatestate(); + + if ($result == true) { + return SMARTIRC_STATE_CONNECTED; + } else { + return SMARTIRC_STATE_DISCONNECTED; + } + } + + /** + * tries to find a messagehandler for the received message ($ircdata) and calls it + * + * @param string $messagecode + * @param object $ircdata + * @return void + * @access private + */ + function _handlemessage($messagecode, &$ircdata) + { + $found = false; + + if (is_numeric($messagecode)) { + if (!array_key_exists($messagecode, $this->nreplycodes)) { + $this->log(SMARTIRC_DEBUG_MESSAGEHANDLER, 'DEBUG_MESSAGEHANDLER: ignoring unreconzied messagecode! "'.$messagecode.'"', __FILE__, __LINE__); + $this->log(SMARTIRC_DEBUG_MESSAGEHANDLER, 'DEBUG_MESSAGEHANDLER: this IRC server ('.$this->_address.') doesn\'t conform to the RFC 2812!', __FILE__, __LINE__); + return; + } + + $methodname = 'event_'.strtolower($this->nreplycodes[$messagecode]); + $_methodname = '_'.$methodname; + $_codetype = 'by numeric'; + } else if (is_string($messagecode)) { // its not numericcode so already a name/string + $methodname = 'event_'.strtolower($messagecode); + $_methodname = '_'.$methodname; + $_codetype = 'by string'; + } + + // if exists call internal method for the handling + if (@method_exists($this, $_methodname)) { + $this->log(SMARTIRC_DEBUG_MESSAGEHANDLER, 'DEBUG_MESSAGEHANDLER: calling internal method "'.get_class($this).'->'.$_methodname.'" ('.$_codetype.')', __FILE__, __LINE__); + $this->$_methodname($ircdata); + $found = true; + } + + // if exist, call user defined method for the handling + if (@method_exists($this, $methodname)) { + $this->log(SMARTIRC_DEBUG_MESSAGEHANDLER, 'DEBUG_MESSAGEHANDLER: calling user defined method "'.get_class($this).'->'.$methodname.'" ('.$_codetype.')', __FILE__, __LINE__); + $this->$methodname($ircdata); + $found = true; + } + + if ($found == false) { + $this->log(SMARTIRC_DEBUG_MESSAGEHANDLER, 'DEBUG_MESSAGEHANDLER: no method found for "'.$messagecode.'" ('.$methodname.')', __FILE__, __LINE__); + } + } + + /** + * tries to find a actionhandler for the received message ($ircdata) and calls it + * + * @param object $ircdata + * @return void + * @access private + */ + function _handleactionhandler(&$ircdata) + { + $handler = &$this->_actionhandler; + $handlercount = count($handler); + for ($i = 0; $i < $handlercount; $i++) { + $handlerobject = &$handler[$i]; + + if (substr($handlerobject->message, 0, 1) == '/') { + $regex = $handlerobject->message; + } else { + $regex = '/'.$handlerobject->message.'/'; + } + + if (($handlerobject->type & $ircdata->type) && + (preg_match($regex, $ircdata->message) == 1)) { + + $this->log(SMARTIRC_DEBUG_ACTIONHANDLER, 'DEBUG_ACTIONHANDLER: actionhandler match found for id: '.$i.' type: '.$ircdata->type.' message: "'.$ircdata->message.'" regex: "'.$regex.'"', __FILE__, __LINE__); + + $methodobject = &$handlerobject->object; + $method = $handlerobject->method; + + if (@method_exists($methodobject, $method)) { + $this->log(SMARTIRC_DEBUG_ACTIONHANDLER, 'DEBUG_ACTIONHANDLER: calling method "'.get_class($methodobject).'->'.$method.'"', __FILE__, __LINE__); + $methodobject->$method($this, $ircdata); + } else { + $this->log(SMARTIRC_DEBUG_ACTIONHANDLER, 'DEBUG_ACTIONHANDLER: method doesn\'t exist! "'.get_class($methodobject).'->'.$method.'"', __FILE__, __LINE__); + } + + break; + } + } + } + + /** + * getting current microtime, needed for benchmarks + * + * @return float + * @access private + */ + function _microint() + { + $tmp = microtime(); + $parts = explode(' ', $tmp); + $floattime = (float)$parts[0] + (float)$parts[1]; + return $floattime; + } + + /** + * adds an user to the channelobject or updates his info + * + * @param object $channel + * @param object $newuser + * @return void + * @access private + */ + function _adduser(&$channel, &$newuser) + { + $lowerednick = strtolower($newuser->nick); + if (isset($channel->users[$lowerednick])) { + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: updating user: '.$newuser->nick.' on channel: '.$channel->name, __FILE__, __LINE__); + + // lets update the existing user + $currentuser = &$channel->users[$lowerednick]; + + if ($newuser->ident !== null) { + $currentuser->ident = $newuser->ident; + } + if ($newuser->host !== null) { + $currentuser->host = $newuser->host; + } + if ($newuser->realname !== null) { + $currentuser->realname = $newuser->realname; + } + if ($newuser->op !== null) { + $currentuser->op = $newuser->op; + } + if ($newuser->voice !== null) { + $currentuser->voice = $newuser->voice; + } + if ($newuser->ircop !== null) { + $currentuser->ircop = $newuser->ircop; + } + if ($newuser->away !== null) { + $currentuser->away = $newuser->away; + } + if ($newuser->server !== null) { + $currentuser->server = $newuser->server; + } + if ($newuser->hopcount !== null) { + $currentuser->hopcount = $newuser->hopcount; + } + } else { + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: adding user: '.$newuser->nick.' to channel: '.$channel->name, __FILE__, __LINE__); + + // he is new just add the reference to him + $channel->users[$lowerednick] = &$newuser; + } + + $user = &$channel->users[$lowerednick]; + if ($user->op) { + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: adding op: '.$user->nick.' to channel: '.$channel->name, __FILE__, __LINE__); + $channel->ops[$user->nick] = true; + } + if ($user->voice) { + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: adding voice: '.$user->nick.' to channel: '.$channel->name, __FILE__, __LINE__); + $channel->voices[$user->nick] = true; + } + } + + /** + * removes an user from one channel or all if he quits + * + * @param object $ircdata + * @return void + * @access private + */ + function _removeuser(&$ircdata) + { + if ($ircdata->type & (SMARTIRC_TYPE_PART|SMARTIRC_TYPE_QUIT)) { + $nick = $ircdata->nick; + } else if ($ircdata->type & SMARTIRC_TYPE_KICK) { + $nick = $ircdata->rawmessageex[3]; + } else { + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: unknown TYPE ('.$ircdata->type.') in _removeuser(), trying default', __FILE__, __LINE__); + $nick = $ircdata->nick; + } + + $lowerednick = strtolower($nick); + + if ($this->_nick == $nick) { + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: we left channel: '.$ircdata->channel.' destroying...', __FILE__, __LINE__); + unset($this->_channels[strtolower($ircdata->channel)]); + } else { + if ($ircdata->type & SMARTIRC_TYPE_QUIT) { + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: user '.$nick.' quit, removing him from all channels', __FILE__, __LINE__); + // remove the user from all channels + foreach ($this->_channels as $channelkey => $channelvalue) { + // loop through all channels + $channel = &$this->_channels[$channelkey]; + foreach ($channel->users as $userkey => $uservalue) { + // loop through all user in this channel + + if ($nick == $uservalue->nick) { + // found him + // kill him + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: found him on channel: '.$channel->name.' destroying...', __FILE__, __LINE__); + unset($channel->users[$lowerednick]); + + if (isset($channel->ops[$nick])) { + // die! + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: removing him from op list', __FILE__, __LINE__); + unset($channel->ops[$nick]); + } + + if (isset($channel->voices[$nick])) { + // die!! + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: removing him from voice list', __FILE__, __LINE__); + unset($channel->voices[$nick]); + } + + // ups this was not DukeNukem 3D + } + } + } + } else { + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: removing user: '.$nick.' from channel: '.$ircdata->channel, __FILE__, __LINE__); + $channel = &$this->_channels[strtolower($ircdata->channel)]; + unset($channel->users[$lowerednick]); + + if (isset($channel->ops[$nick])) { + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: removing him from op list', __FILE__, __LINE__); + unset($channel->ops[$nick]); + } + + if (isset($channel->voices[$nick])) { + $this->log(SMARTIRC_DEBUG_CHANNELSYNCING, 'DEBUG_CHANNELSYNCING: removing him from voice list', __FILE__, __LINE__); + unset($channel->voices[$nick]); + } + } + } + } + + /** + * @return void + * @access private + */ + function _checkPHPVersion() + { + // doing nothing at the moment + } + + /** + * checks if the passed handlertype is valid + * + * @param integer $handlertype + * @return boolean + * @access private + */ + function _isValidType($handlertype) { + if ($handlertype & SMARTIRC_TYPE_ALL ) { + return true; + } else { + return false; + } + } + + // + + function isError($object) { + return (bool)(is_object($object) && (strtolower(get_class($object)) == 'net_smartirc_error')); + } + + function &throwError($message) { + return new Net_SmartIRC_Error($message); + } +} + +class Net_SmartIRC extends Net_SmartIRC_messagehandler +{ + // empty +} + +/** + * @access public + */ +class Net_SmartIRC_data +{ + /** + * @var string + * @access public + */ + var $from; + + /** + * @var string + * @access public + */ + var $nick; + + /** + * @var string + * @access public + */ + var $ident; + + /** + * @var string + * @access public + */ + var $host; + + /** + * @var string + * @access public + */ + var $channel; + + /** + * @var string + * @access public + */ + var $message; + + /** + * @var array + * @access public + */ + var $messageex = array(); + + /** + * @var integer + * @access public + */ + var $type; + + /** + * @var string + * @access public + */ + var $rawmessage; + + /** + * @var array + * @access public + */ + var $rawmessageex = array(); +} + +/** + * @access public + */ +class Net_SmartIRC_actionhandler +{ + /** + * @var integer + * @access public + */ + var $id; + + /** + * @var integer + * @access public + */ + var $type; + + /** + * @var string + * @access public + */ + var $message; + + /** + * @var object + * @access public + */ + var $object; + + /** + * @var string + * @access public + */ + var $method; +} + +/** + * @access public + */ +class Net_SmartIRC_timehandler +{ + /** + * @var integer + * @access public + */ + var $id; + + /** + * @var integer + * @access public + */ + var $interval; + + /** + * @var integer + * @access public + */ + var $lastmicrotimestamp; + + /** + * @var object + * @access public + */ + var $object; + + /** + * @var string + * @access public + */ + var $method; +} + +/** + * @access public + */ +class Net_SmartIRC_channel +{ + /** + * @var string + * @access public + */ + var $name; + + /** + * @var string + * @access public + */ + var $key; + + /** + * @var array + * @access public + */ + var $users = array(); + + /** + * @var array + * @access public + */ + var $ops = array(); + + /** + * @var array + * @access public + */ + var $voices = array(); + + /** + * @var array + * @access public + */ + var $bans = array(); + + /** + * @var string + * @access public + */ + var $topic; + + /** + * @var string + * @access public + */ + var $mode; +} + +/** + * @access public + */ +class Net_SmartIRC_user +{ + /** + * @var string + * @access public + */ + var $nick; + + /** + * @var string + * @access public + */ + var $ident; + + /** + * @var string + * @access public + */ + var $host; + + /** + * @var string + * @access public + */ + var $realname; + + /** + * @var boolean + * @access public + */ + var $ircop; + + /** + * @var boolean + * @access public + */ + var $away; + + /** + * @var string + * @access public + */ + var $server; + + /** + * @var integer + * @access public + */ + var $hopcount; +} + +/** + * @access public + */ +class Net_SmartIRC_channeluser extends Net_SmartIRC_user +{ + /** + * @var boolean + * @access public + */ + var $op; + + /** + * @var boolean + * @access public + */ + var $voice; +} + +/** + * @access public + */ +class Net_SmartIRC_ircuser extends Net_SmartIRC_user +{ + /** + * @var array + * @access public + */ + var $joinedchannels = array(); +} + +/** + * @access public + */ +class Net_SmartIRC_listenfor +{ + /** + * @var array + * @access public + */ + var $result = array(); + + /** + * stores the received answer into the result array + * + * @param object $irc + * @param object $ircdata + * @return void + */ + function handler(&$irc, &$ircdata) + { + $irc->log(SMARTIRC_DEBUG_ACTIONHANDLER, 'DEBUG_ACTIONHANDLER: listenfor handler called', __FILE__, __LINE__); + $this->result[] = $ircdata->message; + $irc->disconnect(true); + } +} + +// so we don't break BC! +/** + * @access public + */ +class Net_SmartIRC_objListenFor +{ + /** + * @var array + * @access public + */ + var $result = array(); + + /** + * stores the received answer into the result array + * + * @param object $irc + * @param object $ircdata + * @return void + */ + function handler(&$irc, &$ircdata) + { + $irc->log(SMARTIRC_DEBUG_ACTIONHANDLER, 'DEBUG_ACTIONHANDLER: objListenFor handler called', __FILE__, __LINE__); + $this->result[] = $ircdata; + $irc->disconnect(true); + } +} + +class Net_SmartIRC_Error +{ + var $error_msg; + + function Net_SmartIRC_Error($message) + { + $this->error_msg = $message; + } + + function getMessage() + { + return $this->error_msg; + } +} +?> \ No newline at end of file diff --git a/thirdparty/pear/Net/Socket.php b/thirdparty/pear/Net/Socket.php new file mode 100644 index 0000000..7c4ff1d --- /dev/null +++ b/thirdparty/pear/Net/Socket.php @@ -0,0 +1,528 @@ + | +// | Chuck Hagenbuch | +// +----------------------------------------------------------------------+ +// +// $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 + * @author Chuck Hagenbuch + */ +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: + * + *

+ * timed_out (bool) - The socket timed out waiting for data
+ * blocked (bool) - The socket was blocked
+ * eof (bool) - Indicates EOF event
+ * unread_bytes (int) - Number of bytes left in the socket buffer
+ *

+ * + * @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; + } + +} diff --git a/thirdparty/pear/Net/Whois.php b/thirdparty/pear/Net/Whois.php new file mode 100644 index 0000000..aac9997 --- /dev/null +++ b/thirdparty/pear/Net/Whois.php @@ -0,0 +1,271 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Whois.php,v 1.11 2003/01/04 11:55:54 mj Exp $ +// +// Whois Class +// +require_once('PEAR.php'); + +/** + * Looks up records in the databases maintained by several Network Information + * Centres (NICs). This class uses PEAR's Net_Socket:: class. + * + * @version 1.0 + * @author Seamus Venasse + * @package Net + * @access public + */ +class Net_Whois extends PEAR { + + // {{{ properties + + /** + * List of NICs to query + * + * @var array + * @access private + */ + var $_nicServers = array ( + "NICHOST" => "whois.crsnic.net", + "INICHOST" => "whois.networksolutions.com", + "DNICHOST" => "whois.nic.mil", + "GNICHOST" => "whois.nic.gov", + "ANICHOST" => "whois.arin.net", + "RNICHOST" => "whois.ripe.net", + "PNICHOST" => "whois.apnic.net", + "RUNICHOST" => "whois.ripn.net", + "MNICHOST" => "whois.ra.net", + "QNICHOST_TAIL" => ".whois-servers.net", + "SNICHOST" => "whois.6bone.net", + "BNICHOST" => "whois.registro.br" + ); + + /** + * Search string of server to search on + * + * @var string + * @access private + */ + var $_whoisServerID = "Whois Server: "; + + /** + * Server to search for IP address lookups + * + * @var array + * @access private + */ + var $_ipNicServers = array ("RNICHOST", "PNICHOST", "BNICHOST"); + + /** + * List of error codes and text + * + * @var array + * @access private + */ + var $_errorCodes = array ( + 010 => 'Unable to create a socket object', + 011 => 'Unable to open socket', + 012 => 'Write to socket failed', + 013 => 'Read from socket failed' + ); + // }}} + + // {{{ constructor + /** + * Constructs a new Net_Whois object + * + * @access public + */ + function Net_Whois() { + $this->PEAR(); + } + // }}} + + // {{{ query() + /** + * Connect to the necessary servers to perform a domain whois query. Prefix + * queries with a "!" to lookup information in InterNIC handle database. + * Add a "-arin" suffix to queries to lookup information in ARIN handle + * database. + * + * @param $domain string IP address or host name + * @param $userWhoisServer string server to query (optional) + * @access public + * @return mixed returns a PEAR_Error on failure, or a string on success + */ + function query($domain, $userWhoisServer = null) { + $domain = trim($domain); + + if (isset($userWhoisServer)) { + $whoisServer = $userWhoisServer; + } elseif (preg_match("/^!.*/", $domain)) { + $whoisServer = $this->_nicServers["INICHOST"]; + } elseif (preg_match("/.*?-arin/i", $domain)) { + $whoisServer = $this->_nicServers["ANICHOST"]; + } elseif (preg_match('/\.gov$/i', $domain)) { + $whoisServer = $this->_nicServers["GNICHOST"]; + } elseif (preg_match('/\.mil$/i', $domain)) { + $whoisServer = $this->_nicServers["DNICHOST"]; + } else { + $whoisServer = $this->_chooseServer($domain); + } + + $whoisData = $this->_connect($whoisServer, $domain); + if (PEAR::isError($whoisData)) { + return $whoisData; + } + + return $whoisData; + } + // }}} + + // {{{ queryAPNIC() + /** + * Use the Asia/Pacific Network Information Center (APNIC) database. + * It contains network numbers used in East Asia, Australia, New + * Zealand, and the Pacific islands. + * + * @param $ipAddress string IP address + * @access public + * @return mixed returns a PEAR_Error on failure, or a string on success + */ + function queryAPNIC($domain) { + return $this->query($domain, $this->_nicServers["PNICHOST"]); + } + // }}} + + // {{{ queryIPv6() + /** + * Use the IPv6 Resource Center (6bone) database. It contains network + * names and addresses for the IPv6 network. + * + * @param $domain string IP address or host name + * @access public + * @return mixed returns a PEAR_Error on failure, or a string on success + */ + function queryIPv6($domain) { + return $this->query($domain, $thiis->_nicServers["SNICHOST"]); + } + // }}} + + // {{{ queryRADB() + /** + * Use the Route Arbiter Database (RADB) database. It contains + * route policy specifications for a large number of operators' + * networks. + * + * @param $ipAddress string IP address + * @access public + * @return mixed returns a PEAR_Error on failure, or a string on success + */ + function queryRADB($ipAddress) { + return $this->query($ipAddress, $this->_nicServers["MNICHOST"]); + } + // }}} + + // {{{ _chooseServer() + /** + * Determines the correct server to connect to based upon the domin + * + * @param $domain string IP address or host name + * @access private + * @return string whois server host name + */ + function _chooseServer($domain) { + if (!strpos($domain, ".")) { + return $this->_nicServers["NICHOST"]; + } + + $pieces = explode(".", $domain); + $TLD = $pieces[count($pieces)-1]; + if (is_numeric($TLD)) { + $whoisServer = $this->_nicServers["ANICHOST"]; + } else { + $whoisServer = $TLD . $this->_nicServers["QNICHOST_TAIL"]; + } + + return $whoisServer; + } + // }}} + + // {{{ _connect() + /** + * Connects to the whois server and retrieves domain information + * + * @param $nicServer string FQDN of whois server to query + * @param $domain string domain name to query + * @access private + * @return mixed returns a PEAR_Error on failure, string of whois data on success + */ + function _connect($nicServer, $domain) { + include_once 'Net/Socket.php'; + + if (PEAR::isError($socket = new Net_Socket())) { + return new PEAR_Error($this->_errorCodes[010]); + } + if (PEAR::isError($socket->connect($nicServer, getservbyname('whois', 'tcp')))) { + return new PEAR_Error($this->_errorCodes[011]); + } + $socket->setBlocking(false); + if (PEAR::isError($socket->writeLine($domain))) { + return new PEAR_Error($this->_errorCodes[012]); + } + + $nHost = null; + $whoisData = $socket->readAll(); + if (PEAR::isError($whoisData)) { + return new PEAR_Error($this->_errorCodes[013]); + } + + $data = explode("\n", $whoisData); + foreach ($data as $line) { + $line = rtrim($line); + + // check for whois server redirection + if (!isset($nHost)) { + if (preg_match("/" . $this->_whoisServerID . "(.*)/", $line, $matches)) { + $nHost = $matches[1]; + } elseif ($nicServer == $this->_nicServers["ANICHOST"]) { + foreach ($this->_ipNicServers as $ipNicServer) { + if (strstr($line, $ipNicServer)) { + $nHost = $ipNicServer; + } + } + } + } + } + + // this should fail, but we'll call it anyway and ignore the error + $socket->disconnect(); + + if ($nHost) { + $tmpBuffer = $this->_connect($nHost, $domain); + if (PEAR::isError($tmpBuffer)) { + return $tmpBuffer; + } + $whoisData .= $tmpBuffer; + } + + return $whoisData; + } + // }}} +} +?> \ No newline at end of file