mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-15 04:24:41 +01:00
3416 lines
98 KiB
PHP
3416 lines
98 KiB
PHP
<?php
|
|
//
|
|
// +----------------------------------------------------------------------+
|
|
// | PHP Version 4 |
|
|
// +----------------------------------------------------------------------+
|
|
// | Copyright (c) 1997-2003 The PHP Group |
|
|
// +----------------------------------------------------------------------+
|
|
// | This source file is subject to version 2.02 of the PHP license, |
|
|
// | that is bundled with this package in the file LICENSE, and is |
|
|
// | available at through the world-wide-web at |
|
|
// | http://www.php.net/license/2_02.txt. |
|
|
// | If you did not receive a copy of the PHP license and are unable to |
|
|
// | obtain it through the world-wide-web, please send a note to |
|
|
// | license@php.net so we can mail you a copy immediately. |
|
|
// +----------------------------------------------------------------------+
|
|
// | Author: Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar> |
|
|
// +----------------------------------------------------------------------+
|
|
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 <damlists@cnba.uba.ar>
|
|
*/
|
|
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;
|
|
|
|
|
|
/**
|
|
* The timeout for the connection to the IMAP server.
|
|
* @var int
|
|
*/
|
|
var $_timeout = null;
|
|
|
|
|
|
/**
|
|
* The options for SSL/TLS connection
|
|
* (see documentation for stream_context_create)
|
|
* @var array
|
|
*/
|
|
var $_streamContextOptions = null;
|
|
|
|
|
|
/**
|
|
* To allow class debuging
|
|
* @var boolean
|
|
*/
|
|
var $_debug = false;
|
|
var $dbgDialog = '';
|
|
|
|
|
|
/**
|
|
* Print error messages
|
|
* @var boolean
|
|
*/
|
|
var $_printErrors = false;
|
|
|
|
|
|
/**
|
|
* 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 = 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($error = $this->_socket->connect($host, $port, null, $this->_timeout, $this->_streamContextOptions))) {
|
|
return $error;
|
|
}
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Sets printed output of errors on or of
|
|
*
|
|
* @param boolean true or false
|
|
*
|
|
* @return nothing
|
|
* @access public
|
|
* @since 1.1
|
|
*/
|
|
function setPrintErrors($printErrors = true)
|
|
{
|
|
$this->_printErrors = $printErrors;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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 options for a SSL/TLS connection
|
|
* (see documentation for stream_context_create)
|
|
*
|
|
* @param array $options the options for the SSL/TLS connection
|
|
* @return nothing
|
|
*
|
|
* @access public
|
|
* @since 1.1
|
|
*/
|
|
function setStreamContextOptions($options)
|
|
{
|
|
$this->_streamContextOptions = $options;
|
|
}
|
|
|
|
/**
|
|
* set the the timeout for the connection to the IMAP server.
|
|
*
|
|
* @param int $timeout the timeout
|
|
* @return nothing
|
|
*
|
|
* @access public
|
|
* @since 1.1
|
|
*/
|
|
function setTimeout($timeout)
|
|
{
|
|
$this->_timeout = $timeout;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
class_exists('Auth_SASL') || require_once 'Auth/SASL.php';
|
|
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)
|
|
{
|
|
|
|
class_exists('Auth_SASL') || require_once 'Auth/SASL.php';
|
|
|
|
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);
|
|
if (!empty($userMethod) && !in_array($userMethod,$this->_serverAuthMethods))
|
|
{
|
|
foreach ( $this->supportedAuthMethods as $method ) {
|
|
if ( in_array( $method , $this->_serverAuthMethods ) ) {
|
|
if ($this->_debug) error_log(__METHOD__." UserMethod $userMethod not supported by server; trying best ServerMethod $method");
|
|
return $method;
|
|
}
|
|
}
|
|
}
|
|
|
|
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!' );
|
|
}
|
|
|
|
if ( PEAR::isError( $args = $this->_genericCommand( 'LOGOUT' ) ) ) {
|
|
error_log(__METHOD__.$args->message);
|
|
return $args;
|
|
}
|
|
if (PEAR::isError( $reval = $this->_socket->disconnect() ) ) {
|
|
error_log(__METHOD__.$reval->message);
|
|
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=$this->_createQuotedString($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=$this->_createQuotedString($mailbox);
|
|
$ret=$this->_genericCommand('EXAMINE', $mailbox_name);
|
|
$parsed='';
|
|
if(isset( $ret["PARSED"] ) ){
|
|
for($i=0;$i<count($ret["PARSED"]); $i++){
|
|
if (array_key_exists("EXT",$ret["PARSED"][$i]) && is_array($ret["PARSED"][$i]["EXT"])) {
|
|
$command=$ret["PARSED"][$i]["EXT"];
|
|
$parsed[key($command)]=$command[key($command)];
|
|
}
|
|
}
|
|
}
|
|
return array("PARSED"=>$parsed,"RESPONSE"=>$ret["RESPONSE"]);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Send the CREATE Mailbox Command
|
|
*
|
|
* @param string $mailbox The mailbox to create.
|
|
* @param array $options options to pass to create
|
|
* @return array Returns an array containing the response
|
|
*
|
|
* @access public
|
|
* @since 1.0
|
|
*/
|
|
function cmdCreate($mailbox, $options = null)
|
|
{
|
|
$args = "";
|
|
$mailbox_name=$this->_createQuotedString($mailbox);
|
|
$args = $this->_getCreateParams($options);
|
|
return $this->_genericCommand('CREATE', $mailbox_name.$args);
|
|
}
|
|
|
|
/**
|
|
* Send the RENAME Mailbox Command
|
|
*
|
|
* @param string $mailbox The old mailbox name.
|
|
* @param string $new_mailbox The new (renamed) mailbox name.
|
|
* @param array $options options to pass to create
|
|
*
|
|
* @return array Returns an array containing the response
|
|
*
|
|
* @access public
|
|
* @since 1.0
|
|
*/
|
|
function cmdRename($mailbox, $new_mailbox, $options = null)
|
|
{
|
|
$mailbox_name=$this->_createQuotedString($mailbox);
|
|
$new_mailbox_name=$this->_createQuotedString($new_mailbox);
|
|
$args = $this->_getCreateParams($options);
|
|
return $this->_genericCommand('RENAME', "$mailbox_name $new_mailbox_name".$args );
|
|
}
|
|
|
|
/**
|
|
* 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=$this->_createQuotedString($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=$this->_createQuotedString($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=$this->_createQuotedString($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"] ) ){
|
|
foreach($ret["PARSED"] as $substruct) {
|
|
if ($substruct["COMMAND"] == "CAPABILITY") {
|
|
$subrv=$substruct["EXT"]["CAPABILITY"];
|
|
break;
|
|
}
|
|
}
|
|
$ret["PARSED"]=$subrv;
|
|
//fill the $this->_serverAuthMethods and $this->_serverSupportedCapabilities arrays
|
|
foreach( (array)$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 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 cmdNamespace()
|
|
{
|
|
$ret = $this->_genericCommand( 'NAMESPACE' );
|
|
|
|
if(isset( $ret["PARSED"] ) ){
|
|
$ret["PARSED"]=$ret["PARSED"][0]["EXT"]["NAMESPACE"];
|
|
// Keep the namespace response for later use
|
|
$this->_namespace = $ret["PARSED"]["NAMESPACES"];
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* Send the STATUS Mailbox Command
|
|
*
|
|
* @param string $mailbox the mailbox name
|
|
* @param mixed $request the request status
|
|
* it could be an array or space separated string of
|
|
* MESSAGES | RECENT | UIDNEXT
|
|
* UIDVALIDITY | UNSEEN
|
|
* @return array Returns a Parsed Response
|
|
*
|
|
* @access public
|
|
* @since 1.0
|
|
*/
|
|
function cmdStatus($mailbox, $request)
|
|
{
|
|
$mailbox_name=$this->_createQuotedString($mailbox);
|
|
|
|
// make array from $request if it is none
|
|
if (!is_array($request)) {
|
|
$request = explode(' ', $request);
|
|
}
|
|
|
|
// see RFC 3501
|
|
$valid_status_data = array('MESSAGES', 'RECENT', 'UIDNEXT', 'UIDVALIDITY', 'UNSEEN');
|
|
|
|
foreach ($request as $status_data) {
|
|
if (!in_array($status_data, $valid_status_data)) {
|
|
$this->_prot_error("request '$status_data' is invalid! see RFC 3501!!!!" , __LINE__ , __FILE__);
|
|
}
|
|
}
|
|
|
|
// back to space separated string
|
|
$request = implode(' ', $request);
|
|
|
|
$ret = $this->_genericCommand('STATUS', $mailbox_name.' ('.$request.')');
|
|
|
|
if (isset($ret['PARSED'])) {
|
|
foreach ($ret['PARSED'] as &$parsed)
|
|
{
|
|
if (!empty($parsed['EXT']))
|
|
{
|
|
if(empty($ret['RESPONSE']['CODE'])) $ret['RESPONSE']['CODE'] ='OK';
|
|
$ret['PARSED'] = $parsed['EXT'];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
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=$this->_createQuotedString($mailbox);
|
|
$mailbox_base=$this->_createQuotedString($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=$this->_createQuotedString($mailbox);
|
|
$mailbox_base=$this->_createQuotedString($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=$this->_getLineLength($msg);
|
|
|
|
$mailbox_name=$this->_createQuotedString($mailbox);
|
|
if($flags_list != '') {
|
|
$flags_list = " ($flags_list)";
|
|
}
|
|
// 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}",$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."\r\n" ) ) ) {
|
|
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 (PEAR::isError($ret)) return new PEAR_Error('could not 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 SORT command.
|
|
*
|
|
* @return mixed Returns a PEAR_Error with an error message on any
|
|
* kind of failure, or true on success.
|
|
* @access public
|
|
* @since 1.1
|
|
*/
|
|
function cmdSort($sort_cmd)
|
|
{
|
|
/*
|
|
if ($_charset != '' )
|
|
$_charset = "[$_charset] ";
|
|
$param = sprintf("%s%s",$charset,$search_cmd);
|
|
*/
|
|
$ret = $this->_genericCommand('SORT', $sort_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=$this->_createQuotedString($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=$this->_createQuotedString($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 UID SORT command.
|
|
*
|
|
* @return mixed Returns a PEAR_Error with an error message on any
|
|
* kind of failure, or true on success.
|
|
* @access public
|
|
* @since 1.1
|
|
*/
|
|
function cmdUidSort($sort_cmd)
|
|
{
|
|
$ret=$this->_genericCommand('UID SORT', sprintf("%s",$sort_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"][1];
|
|
}
|
|
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=$this->_createQuotedString($mailbox_name);
|
|
//Make the command request
|
|
$param=sprintf("%s (",$mailbox_name);
|
|
if($storageQuota != null ){
|
|
if ($storageQuota == -1) {
|
|
// set -1 to remove a quota
|
|
$param = sprintf("%s", $param);
|
|
} elseif ($storageQuota == strtolower('remove')) {
|
|
// this is a cyrus rmquota specific feature
|
|
// see http://email.uoa.gr/projects/cyrus/quota-patches/rmquota/
|
|
$param = sprintf("%sREMOVE 1", $param);
|
|
} else {
|
|
$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=$this->_createQuotedString($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=$this->_createQuotedString($mailbox_name);
|
|
$user_name=$this->_createQuotedString($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=$this->_createQuotedString($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=$this->_createQuotedString($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=$this->_createQuotedString($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=$this->_createQuotedString($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');
|
|
}
|
|
|
|
|
|
|
|
function _createQuotedString($mailbox)
|
|
{
|
|
$search = array('\\', '"');
|
|
$replace = array('\\\\', '\\"');
|
|
|
|
$mailbox_name = str_replace($search, $replace, $mailbox);
|
|
$mailbox_name=sprintf("\"%s\"",$this->utf_7_encode($mailbox_name) );
|
|
|
|
return $mailbox_name;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* 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=$this->_getSubstr($params,1,$this->_getLineLength($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);
|
|
}
|
|
$this->arrayfy_content_level--;
|
|
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) ){
|
|
foreach ($params_arr as $index => $address_arr) {
|
|
$personal_name = $address_arr[0];
|
|
$at_domain_list = $address_arr[1];
|
|
$mailbox_name = $address_arr[2];
|
|
$host_name = $address_arr[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 = $this->_getLineLength( $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 '".$startDelim."' 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] == '"') {
|
|
$this->_advanceOverStr($str_line,$pos,$len,$startDelim,$stopDelim);
|
|
}
|
|
if ( $str_line[$pos] == $startDelim ) {
|
|
#$str_line_aux = substr( $str_line , $pos );
|
|
$str_line_aux = $this->_getSubstr( $str_line , $pos );
|
|
$pos_aux = $this->_getClosingBracesPos( $str_line_aux );
|
|
$pos+=$pos_aux;
|
|
if ($pos == $len-1) break;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
|
|
/**
|
|
* Advances the position $pos in $str over an correcty escaped string
|
|
*
|
|
* Examples: $str='"\\\"First Last\\\""', $pos=0
|
|
* --> returns true and $pos=strlen($str)-1
|
|
*
|
|
* @param string $str
|
|
* @param int &$pos current position in $str pointing to a double quote ("), on return pointing on the closing double quote
|
|
* @param int $len length of $str in bytes(!)
|
|
* @return boolean true if we advanced over a correct string, false otherwise
|
|
*/
|
|
function _advanceOverStr($str,&$pos,$len,$startDelim ='(', $stopDelim = ')' )
|
|
{
|
|
#error_log(__METHOD__. $len . "#".$str."\n");
|
|
$startingpos = $pos;
|
|
if ($str[$pos] !== '"') return false; // start condition failed
|
|
$pos++;
|
|
$delimCount=0;
|
|
while($str[$pos] !== '"' && $pos < $len) {
|
|
// this is a fix to stop before the delimiter, in broken string messages containing an odd number of double quotes
|
|
// the idea is to check for a stopDelimited followed by eiter a new startDelimiter or an other stopDelimiter
|
|
// that allows to have something like '"Name (Nick)" <email>' containing one delimiter
|
|
// if you have something like "Name ((something))" we must count the delimiters (and hope that they are not unbalanced too)
|
|
// and check if we have a negative amount of delimiters or no delimiters to meet the stop condition, before we run into a closing double quote
|
|
if ($str[$pos] === $startDelim) $delimCount++;
|
|
if ($str[$pos] === $stopDelim) $delimCount--;
|
|
if ($str[$pos] === $stopDelim && ($str[$pos+1] === $startDelim || ($str[$pos+1] === $stopDelim && $delimCount<=0))) {
|
|
$encoding =mb_internal_encoding();
|
|
//if (mb_check_encoding($str)) {
|
|
// error_log(__METHOD__.' Encoding used:'.mb_internal_encoding().' Detected:'.mb_detect_encoding($str));
|
|
// $encoding = mb_detect_encoding($str);
|
|
//}
|
|
// beware as mb_substr_count does not support the offset parameter.
|
|
$numOfQuotes = substr_count(substr($str,$startingpos),'"');
|
|
$numOfMaskedQuotes = substr_count(substr($str,$startingpos),'\"');
|
|
if ((($numOfQuotes - $numOfMaskedQuotes) % 2 ) == 0) {
|
|
// quotes are balanced, so its unlikely that we meet a stop condition here as strings may contain )(
|
|
//error_log(__METHOD__. "->Length: $len; NumOfQuotes: $numOfQuotes - NumofMaskedQuotes:$numOfMaskedQuotes #".$str."\n");
|
|
error_log(__METHOD__." problem at $pos with:".$str[$pos]."(last character) Numberof delimiters ('".$startDelim."','".$stopDelim."') found:".$delimCount.' String:'.substr($str,$startingpos,$pos).' called from:'.function_backtrace());
|
|
//return false;
|
|
} else {
|
|
$pos--; // stopDelimited need to be parsed outside!
|
|
return false;
|
|
}
|
|
}
|
|
if ($str[$pos] === '\\') $pos++; // all escaped chars are overread (eg. \\, \", \x)
|
|
$pos++;
|
|
}
|
|
return $pos < $len && $str[$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 = $this->_getLineLength( $str );
|
|
if( $including ){
|
|
for($i=0;$i<$len;$i++){
|
|
if( $str[$i] =="\n" )
|
|
break;
|
|
}
|
|
$content=$this->_getSubstr($str,0,$i + 1);
|
|
$str=$this->_getSubstr($str,$i + 1);
|
|
return $content;
|
|
|
|
}else{
|
|
for( $i = 0 ; $i < $len ; $i++ ){
|
|
if( $str[$i] =="\n" || $str[$i] == "\r")
|
|
break;
|
|
}
|
|
$content = $this->_getSubstr( $str ,0 , $i );
|
|
$str = $this->_getSubstr( $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 = $this->_getLineLength($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 = $this->_getSubstr( $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 = $this->_getSubstr( $str , $posClosingBraces + 3 , $strBytes );
|
|
if( $this->_getLineLength( $content ) != $strBytes ){
|
|
$this->_prot_error("content size is ". $this->_getLineLength($content) . " but the string reports a size of $strBytes!!!\n" , __LINE__ , __FILE__ );
|
|
}
|
|
$content_size = $strBytes;
|
|
//Advance the string
|
|
$str = $this->_getSubstr( $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 = $this->_getSubstr( $str , 1 , $pos - 1 );
|
|
//Advance the string
|
|
$str = $this->_getSubstr( $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 = $this->_getSubstr( $str , 0 , $pos + 1 );
|
|
//Advance the string
|
|
$str = $this->_getSubstr( $str , $pos + 1 );
|
|
|
|
}
|
|
# we need to strip slashes for a quoted string
|
|
$content = stripslashes($content);
|
|
break;
|
|
|
|
case "\r":
|
|
$pos = 1;
|
|
if( $str[1] == "\n")
|
|
$pos++;
|
|
$content_size = $pos;
|
|
$content = $this->_getSubstr( $str , 0 , $pos );
|
|
$str = $this->_getSubstr( $str , $pos );
|
|
break;
|
|
case "\n":
|
|
$pos = 1;
|
|
$content_size = $pos;
|
|
$content = $this->_getSubstr( $str , 0 , $pos );
|
|
$str = $this->_getSubstr( $str , $pos );
|
|
break;
|
|
case '(':
|
|
if( $parenthesisIsToken == false ){
|
|
$pos = $this->_getClosingBracesPos( $str );
|
|
$content_size = $pos + 1;
|
|
$content = $this->_getSubstr( $str , 0 , $pos + 1 );
|
|
$str = $this->_getSubstr( $str , $pos + 1 );
|
|
}else{
|
|
$pos = 1;
|
|
$content_size = $pos;
|
|
$content = $this->_getSubstr( $str , 0 , $pos );
|
|
$str = $this->_getSubstr( $str , $pos );
|
|
}
|
|
break;
|
|
case ')':
|
|
$pos = 1;
|
|
$content_size = $pos;
|
|
$content = $this->_getSubstr( $str , 0 , $pos );
|
|
$str = $this->_getSubstr( $str , $pos );
|
|
break;
|
|
case ' ':
|
|
$pos = 1;
|
|
$content_size = $pos;
|
|
$content = $this->_getSubstr( $str , 0 , $pos );
|
|
$str = $this->_getSubstr( $str , $pos );
|
|
break;
|
|
default:
|
|
for( $pos = 0 ; $pos < $len ; $pos++ ){
|
|
if($this->_getSubstr( $str , 0 , 5 ) == 'BODY[' || $this->_getSubstr( $str , 0 , 5 ) == 'BODY.') {
|
|
if($str[$pos] == ']') {
|
|
$pos++;
|
|
break;
|
|
}
|
|
} elseif ( $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 = $this->_getSubstr( $str , 0 , 1 );
|
|
$str = $this->_getSubstr( $str , 1 );
|
|
}else{
|
|
$content_size = $pos;
|
|
$content = $this->_getSubstr( $str , 0 , $pos );
|
|
if($pos < $len){
|
|
$str = $this->_getSubstr( $str , $pos );
|
|
}else{
|
|
//if this is the end of the string... exit the switch
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
break;
|
|
}
|
|
#error_log("egw-pear::NET::IMAPProtocoll:_getNextToken:".$str);
|
|
return $content_size;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ToDo: all real errors should be returned as PEAR error, others hidden by default
|
|
// NO extra output from this class!
|
|
/**
|
|
* Utility funcion to display to console the protocol errors
|
|
* printErrors() additionally has to be set to true
|
|
*
|
|
* @param string $str the error message
|
|
* @param int $line the line producing the error
|
|
* @param string $file file where the error was produced
|
|
*
|
|
* @return nothing
|
|
* @access private
|
|
* @since 1.0
|
|
*/
|
|
function _prot_error($str , $line , $file, $printError = true)
|
|
{
|
|
if ($this->_printErrors && $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";
|
|
$token = strtoupper($token);
|
|
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 'NOMODSEQ':
|
|
// ToDo: implement RFC 4551
|
|
return array($token=>'');
|
|
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
|
|
|
|
another example of QUOTA response from GETQUOTAROOT:
|
|
C: A0008 GETQUOTAROOT INBOX
|
|
S: * QUOTAROOT INBOX ""
|
|
S: * QUOTA "" (STORAGE 0 1024000 MESSAGE 0 40000)
|
|
S: A0008 OK GETQUOTAROOT finished.
|
|
|
|
RFC 2087 section 5.1 says the list could be empty:
|
|
|
|
C: A0004 GETQUOTA user.damian
|
|
S: * QUOTA user.damian ()
|
|
S: A0004 OK Completed
|
|
|
|
quota_list ::= "(" #quota_resource ")"
|
|
quota_resource ::= atom SP number SP number
|
|
quota_response ::= "QUOTA" SP astring SP quota_list
|
|
*/
|
|
$mailbox = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ );
|
|
$ret_aux = array('MAILBOX'=>$this->utf_7_decode($mailbox));
|
|
|
|
// courier fix
|
|
if ($str[0].$str[1] == "\r\n") {
|
|
return array($token => $ret_aux);
|
|
}
|
|
// end courier fix
|
|
|
|
$this->_parseSpace( $str , __LINE__ , __FILE__ );
|
|
$this->_parseString( $str , '(' , __LINE__ , __FILE__ );
|
|
|
|
// fetching quota resources ( BNF ::= #quota_resource but spce separated instead of comma)
|
|
$this->_getNextToken($str, $quota_resp );
|
|
while ($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, $quota_resp);
|
|
if ($quota_resp == ' ') {
|
|
$this->_getNextToken($str, $quota_resp);
|
|
}
|
|
}
|
|
|
|
// if empty list, apparently no STORAGE or MESSAGE quota set
|
|
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 ) );
|
|
if(empty($str_line)) {
|
|
$ret = @array( "MAILBOX"=>$this->utf_7_decode($mailbox));
|
|
} else {
|
|
$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 "LEVEL" :
|
|
$lused = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ );
|
|
$lmax = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ );
|
|
return array($token=>array("LUSED"=> $lused, "LMAX" => $lmax));
|
|
break;
|
|
case "MAILBOX" :
|
|
$mbused = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ );
|
|
$mbmax = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ );
|
|
return array(); //throw away this information as this info seems quite useless with a QUOTA Response, AND we use the KEYWORD otherwise
|
|
break;
|
|
case "EXPIRATION" :
|
|
$expused = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ );
|
|
$expmax = $this->_parseOneStringResponse( $str,__LINE__ , __FILE__ );
|
|
return array(); //throw away this information as this info seems quite useless
|
|
break;
|
|
case "FETCH" :
|
|
$this->_parseSpace( $str ,__LINE__ ,__FILE__ );
|
|
// Get the parsed pathenthesis
|
|
$struct_arr = $this->_getEXTarray( $str );
|
|
return $struct_arr;
|
|
break;
|
|
case "NAMESPACE" :
|
|
$this->_parseSpace( $str , __LINE__ , __FILE__ );
|
|
$this->_getNextToken($str , $personal, false);
|
|
$struct_arr['NAMESPACES']['personal'] = $this->_arrayfy_content($personal);
|
|
|
|
$this->_parseSpace( $str , __LINE__ , __FILE__ );
|
|
$this->_getNextToken($str , $others, false);
|
|
$struct_arr['NAMESPACES']['others'] = $this->_arrayfy_content($others);
|
|
|
|
$this->_parseSpace( $str , __LINE__ , __FILE__ );
|
|
$this->_getNextToken($str , $shared, false);
|
|
$struct_arr['NAMESPACES']['shared'] = $this->_arrayfy_content($shared);
|
|
|
|
return array($token=>$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" :
|
|
case 'SORT':
|
|
$str_line = rtrim( substr( $this->_getToEOL( $str , false ) , 1) );
|
|
$struct_arr[$token.'_LIST'] = explode(' ', $str_line);
|
|
if (count($struct_arr[$token.'_LIST']) == 1 && $struct_arr[$token.'_LIST'][0]=='') {
|
|
$struct_arr[$token.'_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"=>$token , "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 );
|
|
// Patch to handle the alternate MYRIGHTS response from Courier-IMAP
|
|
if ($str==')'){
|
|
$granted = $mailbox;
|
|
$mailbox = $this->currentMailbox;
|
|
}else{
|
|
$this->_parseSpace( $str , __LINE__ , __FILE__ );
|
|
$this->_getNextToken( $str , $granted );
|
|
}
|
|
// End Patch
|
|
|
|
$result_array = array( "MAILBOX"=>$this->utf_7_decode($mailbox) , "GRANTED"=>$granted );
|
|
return $result_array;
|
|
break;
|
|
|
|
case 'ACL':
|
|
/*
|
|
RFC 4314:
|
|
acl-data = "ACL" SP mailbox *(SP identifier SP rights)
|
|
identifier = astring
|
|
rights = astring ;; only lowercase ASCII letters and digits are allowed.
|
|
*/
|
|
//$str = " INBOX\r\nA0006 OK Completed\r\n";
|
|
$this->_parseSpace($str, __LINE__, __FILE__);
|
|
$this->_getNextToken($str, $mailbox);
|
|
|
|
$arr = array();
|
|
while (substr($str, 0, 2) != "\r\n") {
|
|
$this->_parseSpace($str, __LINE__, __FILE__);
|
|
$this->_getNextToken($str, $acl_user);
|
|
$this->_parseSpace($str, __LINE__, __FILE__);
|
|
$this->_getNextToken($str, $acl_rights);
|
|
$arr[] = array('USER'=>$acl_user, 'RIGHTS'=>$acl_rights);
|
|
}
|
|
|
|
$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));
|
|
$rfc822_token=strtoupper(substr($token,0,7));
|
|
|
|
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);
|
|
#error_log(" egw-pear::NET::IMAPProtocoll:_genericIMAPResponseParser: After retrieving the first token:".$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 );
|
|
#error_log("egw-pear::NET::IMAPProtocoll:_genericCommand:".$command." result:".print_r($args,TRUE));
|
|
return $this->_genericImapResponseParser( $args , $cmdid );
|
|
}
|
|
|
|
|
|
|
|
function utf_7_encode($str)
|
|
{
|
|
if($this->_useUTF_7 == false ){
|
|
return $str;
|
|
}
|
|
|
|
if(function_exists('mb_convert_encoding')) {
|
|
return mb_convert_encoding($str, "UTF7-IMAP", "ISO-8859-1");
|
|
}
|
|
|
|
$encoded_utf7 = '';
|
|
$base64_part = '';
|
|
if(is_array($str)){
|
|
return new PEAR_Error('error');
|
|
}
|
|
|
|
for ($i = 0; $i < $this->_getLineLength($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);
|
|
|
|
if(function_exists('mb_convert_encoding')) {
|
|
return mb_convert_encoding($str, "ISO-8859-1", "UTF7-IMAP");
|
|
}
|
|
|
|
$base64_part = '';
|
|
$decoded_utf7 = '';
|
|
|
|
for ($i = 0; $i < $this->_getLineLength($str); $i++) {
|
|
if ( $this->_getLineLength($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;
|
|
}
|
|
|
|
/**
|
|
* Make CREATE/RENAME compatible option params
|
|
*
|
|
* @param array $options options to format
|
|
* @return string Returns a string for formatted parameters
|
|
*
|
|
* @access private
|
|
* @since 1.1
|
|
*/
|
|
function _getCreateParams($options)
|
|
{
|
|
$args = "";
|
|
if(is_null($options) === false && is_array($options) === true) {
|
|
foreach($options as $opt => $data) {
|
|
switch(strtoupper($opt)) {
|
|
case "PARTITION":
|
|
$args .= sprintf(" %s",$this->utf_7_encode($data));
|
|
break;
|
|
default:
|
|
// ignore any unknown options
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return $args;
|
|
}
|
|
|
|
/**
|
|
* Return true if the TLS negotiation was successful
|
|
*
|
|
* @access public
|
|
* @return mixed true on success, PEAR_Error on failure
|
|
*/
|
|
function cmdStartTLS()
|
|
{
|
|
if (PEAR::isError($res = $this->_genericCommand("STARTTLS"))) {
|
|
return $res;
|
|
}
|
|
|
|
if(stream_socket_enable_crypto($this->_socket->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT) == false) {
|
|
$msg = 'Failed to establish TLS connection';
|
|
return new PEAR_Error($msg);
|
|
}
|
|
|
|
if($this->_debug === true) {
|
|
echo "STARTTLS Negotiation Successful\n";
|
|
}
|
|
|
|
// RFC says we need to query the server capabilities again
|
|
if(PEAR::isError($res = $this->cmdCapability() )) {
|
|
$msg = 'Failed to connect, server said: ' . $res->getMessage();
|
|
return new PEAR_Error($msg);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function _getLineLength($string)
|
|
{
|
|
if (extension_loaded('mbstring')) {
|
|
return mb_strlen($string,'latin1');
|
|
} else {
|
|
return strlen($string);
|
|
}
|
|
}
|
|
|
|
function _getSubstr($string, $start, $length = false)
|
|
{
|
|
if (extension_loaded('mbstring')) {
|
|
if($length !== false) {
|
|
return mb_substr($string, $start, $length, 'latin1');
|
|
} else {
|
|
$strlen = mb_strlen($string,'latin1');
|
|
return mb_substr($string, $start, $strlen, 'latin1');
|
|
}
|
|
} else {
|
|
if($length !== false) {
|
|
return substr($string, $start, $length);
|
|
} else {
|
|
return substr($string, $start);
|
|
}
|
|
}
|
|
}
|
|
|
|
}//Class
|
|
?>
|