| | |
| | | * @copyright 2002-2003 Richard Heyes |
| | | * @copyright 2006-2008 Anish Mistry |
| | | * @license http://www.opensource.org/licenses/bsd-license.php BSD |
| | | * @version SVN: $Id: Sieve.php 289313 2009-10-07 22:26:33Z yunosh $ |
| | | * @version SVN: $Id: Sieve.php 300898 2010-07-01 09:49:02Z yunosh $ |
| | | * @link http://pear.php.net/package/Net_Sieve |
| | | */ |
| | | |
| | |
| | | * @copyright 2002-2003 Richard Heyes |
| | | * @copyright 2006-2008 Anish Mistry |
| | | * @license http://www.opensource.org/licenses/bsd-license.php BSD |
| | | * @version Release: @package_version@ |
| | | * @version Release: 1.3.0 |
| | | * @link http://pear.php.net/package/Net_Sieve |
| | | * @link http://www.ietf.org/rfc/rfc3028.txt RFC 3028 (Sieve: A Mail |
| | | * Filtering Language) |
| | |
| | | * |
| | | * @var array |
| | | */ |
| | | var $_supportedSASLAuthMethods = array('DIGEST-MD5', 'CRAM-MD5'); |
| | | var $supportedSASLAuthMethods = array('DIGEST-MD5', 'CRAM-MD5'); |
| | | |
| | | /** |
| | | * The socket handle. |
| | |
| | | * @param boolean $useTLS Use TLS if available. |
| | | * @param array $options Additional options for |
| | | * stream_context_create(). |
| | | * @param mixed $handler A callback handler for the debug output. |
| | | */ |
| | | function Net_Sieve($user = null, $pass = null, $host = 'localhost', |
| | | $port = 2000, $logintype = '', $euser = '', $debug = false, |
| | | $bypassAuth = false, $useTLS = true, $options = null |
| | | ) { |
| | | $port = 2000, $logintype = '', $euser = '', |
| | | $debug = false, $bypassAuth = false, $useTLS = true, |
| | | $options = null, $handler = null) |
| | | { |
| | | $this->_state = NET_SIEVE_STATE_DISCONNECTED; |
| | | $this->_data['user'] = $user; |
| | | $this->_data['pass'] = $pass; |
| | |
| | | $this->_data['logintype'] = $logintype; |
| | | $this->_data['euser'] = $euser; |
| | | $this->_sock = new Net_Socket(); |
| | | $this->_debug = $debug; |
| | | $this->_bypassAuth = $bypassAuth; |
| | | $this->_useTLS = $useTLS; |
| | | $this->_options = $options; |
| | | $this->setDebug($debug, $handler); |
| | | |
| | | /* Try to include the Auth_SASL package. If the package is not |
| | | * available, we disable the authentication methods that depend upon |
| | |
| | | */ |
| | | function connect($host, $port, $options = null, $useTLS = true) |
| | | { |
| | | $this->_data['host'] = $host; |
| | | $this->_data['port'] = $port; |
| | | $this->_useTLS = $useTLS; |
| | | if (!empty($options) && is_array($options)) { |
| | | $this->_options = array_merge($this->_options, $options); |
| | | } |
| | | |
| | | if (NET_SIEVE_STATE_DISCONNECTED != $this->_state) { |
| | | return PEAR::raiseError('Not currently in DISCONNECTED state', 1); |
| | | } |
| | |
| | | */ |
| | | function login($user, $pass, $logintype = null, $euser = '', $bypassAuth = false) |
| | | { |
| | | $this->_data['user'] = $user; |
| | | $this->_data['pass'] = $pass; |
| | | $this->_data['logintype'] = $logintype; |
| | | $this->_data['euser'] = $euser; |
| | | $this->_bypassAuth = $bypassAuth; |
| | | |
| | | if (NET_SIEVE_STATE_AUTHORISATION != $this->_state) { |
| | | return PEAR::raiseError('Not currently in AUTHORISATION state', 1); |
| | | } |
| | |
| | | if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { |
| | | return PEAR::raiseError('Not currently in TRANSACTION state', 1); |
| | | } |
| | | if (PEAR::isError($res = $this->_doCmd(sprintf('HAVESPACE "%s" %d', $scriptname, $size)))) { |
| | | |
| | | $command = sprintf('HAVESPACE %s %d', $this->_escape($scriptname), $size); |
| | | if (PEAR::isError($res = $this->_doCmd($command))) { |
| | | return $res; |
| | | } |
| | | return true; |
| | |
| | | if (PEAR::isError($result = $this->_sendCmd('AUTHENTICATE "LOGIN"'))) { |
| | | return $result; |
| | | } |
| | | if (PEAR::isError($result = $this->_doCmd('"' . base64_encode($user) . '"'))) { |
| | | if (PEAR::isError($result = $this->_doCmd('"' . base64_encode($user) . '"', true))) { |
| | | return $result; |
| | | } |
| | | return $this->_doCmd('"' . base64_encode($pass) . '"'); |
| | | return $this->_doCmd('"' . base64_encode($pass) . '"', true); |
| | | } |
| | | |
| | | /** |
| | |
| | | return $response; |
| | | } |
| | | |
| | | if (PEAR::isError($result = $this->_sendStringResponse(base64_encode($param)))) { |
| | | if (PEAR::isError($result = $this->_sendStringResponse(base64_encode($response)))) { |
| | | return $result; |
| | | } |
| | | if (PEAR::isError($result = $this->_doCmd())) { |
| | | if (PEAR::isError($result = $this->_doCmd('', true))) { |
| | | return $result; |
| | | } |
| | | if ($this->_toUpper(substr($result, 0, 2)) == 'OK') { |
| | |
| | | if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { |
| | | return PEAR::raiseError('Not currently in AUTHORISATION state', 1); |
| | | } |
| | | if (PEAR::isError($res = $this->_doCmd(sprintf('DELETESCRIPT "%s"', $scriptname)))) { |
| | | |
| | | $command = sprintf('DELETESCRIPT %s', $this->_escape($scriptname)); |
| | | if (PEAR::isError($res = $this->_doCmd($command))) { |
| | | return $res; |
| | | } |
| | | return true; |
| | |
| | | return PEAR::raiseError('Not currently in AUTHORISATION state', 1); |
| | | } |
| | | |
| | | if (PEAR::isError($res = $this->_doCmd(sprintf('GETSCRIPT "%s"', $scriptname)))) { |
| | | $command = sprintf('GETSCRIPT %s', $this->_escape($scriptname)); |
| | | if (PEAR::isError($res = $this->_doCmd($command))) { |
| | | return $res; |
| | | } |
| | | |
| | | return preg_replace('/{[0-9]+}\r\n/', '', $res); |
| | | return preg_replace('/^{[0-9]+}\r\n/', '', $res); |
| | | } |
| | | |
| | | /** |
| | |
| | | if (NET_SIEVE_STATE_TRANSACTION != $this->_state) { |
| | | return PEAR::raiseError('Not currently in AUTHORISATION state', 1); |
| | | } |
| | | if (PEAR::isError($res = $this->_doCmd(sprintf('SETACTIVE "%s"', $scriptname)))) { |
| | | |
| | | $command = sprintf('SETACTIVE %s', $this->_escape($scriptname)); |
| | | if (PEAR::isError($res = $this->_doCmd($command))) { |
| | | return $res; |
| | | } |
| | | |
| | | $this->_activeScript = $scriptname; |
| | | return true; |
| | | } |
| | |
| | | $res = explode("\r\n", $res); |
| | | foreach ($res as $value) { |
| | | if (preg_match('/^"(.*)"( ACTIVE)?$/i', $value, $matches)) { |
| | | $scripts[] = $matches[1]; |
| | | $script_name = stripslashes($matches[1]); |
| | | $scripts[] = $script_name; |
| | | if (!empty($matches[2])) { |
| | | $activescript = $matches[1]; |
| | | $activescript = $script_name; |
| | | } |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | $stringLength = $this->_getLineLength($scriptdata); |
| | | $command = sprintf("PUTSCRIPT %s {%d+}\r\n%s", |
| | | $this->_escape($scriptname), $stringLength, $scriptdata); |
| | | |
| | | if (PEAR::isError($res = $this->_doCmd(sprintf("PUTSCRIPT \"%s\" {%d+}\r\n%s", $scriptname, $stringLength, $scriptdata)))) { |
| | | if (PEAR::isError($res = $this->_doCmd($command))) { |
| | | return $res; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * Receives x bytes from the server. |
| | | * |
| | | * @param int $length Number of bytes to read |
| | | * |
| | | * @return string The server response. |
| | | */ |
| | | function _recvBytes($length) |
| | | { |
| | | $response = ''; |
| | | $response_length = 0; |
| | | |
| | | while ($response_length < $length) { |
| | | $response .= $this->_sock->read($length - $response_length); |
| | | $response_length = $this->_getLineLength($response); |
| | | } |
| | | |
| | | $this->_debug("S: " . rtrim($response)); |
| | | |
| | | return $response; |
| | | } |
| | | |
| | | /** |
| | | * Send a command and retrieves a response from the server. |
| | | * |
| | | * @param string $cmd The command to send. |
| | |
| | | |
| | | if ('NO' == substr($uc_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) |
| | | ); |
| | | $this->_debug("S: $line"); |
| | | if (preg_match('/{([0-9]+)}$/i', $line, $matches)) { |
| | | $line = substr($line, 0, -(strlen($matches[1])+2)) |
| | | . str_replace( |
| | | "\r\n", ' ', $this->_recvBytes($matches[1] + 2) |
| | | ); |
| | | } |
| | | return PEAR::raiseError(trim($response . substr($line, 2)), 3); |
| | | } |
| | |
| | | return PEAR::raiseError(trim($response . $line), 6); |
| | | } |
| | | |
| | | // "\+?" is added in the regexp to workaround DBMail bug |
| | | // http://dbmail.org/mantis/view.php?id=963 |
| | | if (preg_match('/^{([0-9]+)\+?}/i', $line, $matches)) { |
| | | // Matches String Responses. |
| | | $str_size = $matches[1] + 2; |
| | | $line = ''; |
| | | $line_length = 0; |
| | | while ($line_length < $str_size) { |
| | | $line .= $this->_sock->read($str_size - $line_length); |
| | | $line_length = $this->_getLineLength($line); |
| | | } |
| | | $this->_debug("S: $line"); |
| | | // Matches literal string responses. |
| | | $line = $this->_recvBytes($matches[1] + 2); |
| | | |
| | | if (!$auth) { |
| | | // Receive the pending OK only if we aren't |
| | |
| | | if (!isset($this->_capability['sasl'])) { |
| | | return PEAR::raiseError('This server doesn\'t support any authentication methods. SASL problem?'); |
| | | } |
| | | |
| | | $serverMethods = $this->_capability['sasl']; |
| | | if (!$this->_capability['sasl']) { |
| | | return PEAR::raiseError('This server doesn\'t support any authentication methods.'); |
| | | } |
| | | |
| | | if ($userMethod) { |
| | | $methods = array($userMethod); |
| | | } else { |
| | | $methods = $this->supportedAuthMethods; |
| | | } |
| | | |
| | | if (!$methods || !$serverMethods) { |
| | | if (in_array($userMethod, $this->_capability['sasl'])) { |
| | | return $userMethod; |
| | | } |
| | | return PEAR::raiseError( |
| | | 'This server doesn\'t support any authentication methods.' |
| | | ); |
| | | sprintf('No supported authentication method found. The server supports these methods: %s, but we want to use: %s', |
| | | implode(', ', $this->_capability['sasl']), |
| | | $userMethod)); |
| | | } |
| | | |
| | | foreach ($methods as $method) { |
| | | if (in_array($method, $serverMethods)) { |
| | | foreach ($this->supportedAuthMethods as $method) { |
| | | if (in_array($method, $this->_capability['sasl'])) { |
| | | return $method; |
| | | } |
| | | } |
| | | |
| | | return PEAR::raiseError( |
| | | 'No supported authentication method found. The server supports these methods: ' |
| | | . implode(',', $serverMethods) |
| | | . ', but we only support: ' |
| | | . implode(',', $this->supportedAuthMethods) |
| | | ); |
| | | sprintf('No supported authentication method found. The server supports these methods: %s, but we only support: %s', |
| | | implode(', ', $this->_capability['sasl']), |
| | | implode(', ', $this->supportedAuthMethods))); |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | // The server should be sending a CAPABILITY response after |
| | | // negotiating TLS. Read it, and ignore if it doesn't. |
| | | $this->_doCmd(); |
| | | // Doesn't work with older timsieved versions |
| | | $regexp = '/^CYRUS TIMSIEVED V([0-9.]+)/'; |
| | | if (!preg_match($regexp, $this->_capability['implementation'], $matches) |
| | | || version_compare($matches[1], '2.3.10', '>=') |
| | | ) { |
| | | $this->_doCmd(); |
| | | } |
| | | |
| | | // RFC says we need to query the server capabilities again now that we |
| | | // are under encryption. |
| | |
| | | */ |
| | | function _getLineLength($string) |
| | | { |
| | | if (extension_loaded('mbstring') |
| | | || @dl(PHP_SHLIB_PREFIX . 'mbstring.' . PHP_SHLIB_SUFFIX) |
| | | ) { |
| | | if (extension_loaded('mbstring')) { |
| | | return mb_strlen($string, 'latin1'); |
| | | } else { |
| | | return strlen($string); |
| | |
| | | } |
| | | |
| | | /** |
| | | * Convert string into RFC's quoted-string or literal-c2s form |
| | | * |
| | | * @param string $string The string to convert. |
| | | * |
| | | * @return string Result string |
| | | */ |
| | | function _escape($string) |
| | | { |
| | | // Some implementations doesn't allow UTF-8 characters in quoted-string |
| | | // It's safe to use literal-c2s |
| | | if (preg_match('/[^\x01-\x09\x0B-\x0C\x0E-\x7F]/', $string)) { |
| | | return sprintf("{%d+}\r\n%s", $this->_getLineLength($string), $string); |
| | | } |
| | | |
| | | return '"' . addcslashes($string, '\\"') . '"'; |
| | | } |
| | | |
| | | /** |
| | | * Write debug text to the current debug output handler. |
| | | * |
| | | * @param string $message Debug message text. |