Copyright 2012 Horde LLC (http://www.horde.org/)
See the enclosed file COPYING for license information (LGPL). If you
did not receive this file, see http://www.horde.org/licenses/lgpl21.
/** * Process/send a command to the remote server. * * @param Horde_Imap_Client_Interaction_Pipeline $pipeline The pipeline * object. * @param Horde_Imap_Client_Interaction_Command $cmd The master command. * @param Horde_Imap_Client_Data_Format_List $data Commands to send. * * @return boolean True if EOL needed to finish command. * @throws Horde_Imap_Client_Exception * @throws Horde_Imap_Client_Exception_NoSupport */ protected function _processCmd($pipeline, $cmd, $data) { if ($this->_debug->debug && $data instanceof Horde_Imap_Client_Interaction_Command) { $data->startTimer(); } foreach ($data as $key => $val) { if ($val instanceof Horde_Imap_Client_Interaction_Command_Continuation) { $this->_connection->write('', true); /* Check for optional continuation responses when the command * has already finished. */ if (!($cmd_continuation = $this->_processCmdContinuation($pipeline, $val->optional))) { return false; } $this->_processCmd($pipeline, $cmd, $val->getCommands($cmd_continuation)); continue; } if ($key) { $this->_connection->write(' '); } if ($val instanceof Horde_Imap_Client_Data_Format_List) { $this->_connection->write('('); $this->_processCmd($pipeline, $cmd, $val); $this->_connection->write(')'); } elseif ($val instanceof Horde_Imap_Client_Data_Format_String && $val->literal()) { /* RFC 3516/4466: Send literal8 if we have binary data. */ if ($cmd->literal8 && $val->binary() && $this->queryCapability('BINARY')) { $binary = true; $this->_connection->write('~'); } else { $binary = false; } $literal_len = $val->length(); $this->_connection->write('{' . $literal_len); /* RFC 2088 - If LITERAL+ is available, saves a roundtrip from * the server. */ if ($cmd->literalplus && $this->queryCapability('LITERAL+')) { $this->_connection->write('+}', true); } else { $this->_connection->write('}', true); $this->_processCmdContinuation($pipeline); } $this->_connection->writeLiteral($val->getStream(), $literal_len, $binary); } else { $this->_connection->write($val->escape()); } } return true; }
/** */ protected function _setMetadata(Horde_Imap_Client_Mailbox $mailbox, $data) { if ($this->queryCapability('METADATA') || strlen($mailbox) == 0 && $this->queryCapability('METADATA-SERVER')) { $data_elts = new Horde_Imap_Client_Data_Format_List(); foreach ($data as $key => $value) { $data_elts->add(array(new Horde_Imap_Client_Data_Format_Astring($key), new Horde_Imap_Client_Data_Format_Nstring($value))); } $cmd = $this->_clientCommand(array('SETMETADATA', new Horde_Imap_Client_Data_Format_Mailbox($mailbox), $data_elts)); $this->_sendLine($cmd); return; } if (!$this->queryCapability('ANNOTATEMORE') && !$this->queryCapability('ANNOTATEMORE2')) { throw new Horde_Imap_Client_Exception_NoSupportExtension('METADATA'); } foreach ($data as $md_entry => $value) { list($entry, $type) = $this->_getAnnotateMoreEntry($md_entry); $cmd = $this->_clientCommand(array('SETANNOTATION', new Horde_Imap_Client_Data_Format_Mailbox($mailbox), new Horde_Imap_Client_Data_Format_String($entry), new Horde_Imap_Client_Data_Format_List(array(new Horde_Imap_Client_Data_Format_String($type), new Horde_Imap_Client_Data_Format_Nstring($value))))); $this->_sendLine($cmd); } }
/** * Builds the AND/OR query. * * @param string $type 'AND' or 'OR'. * @param array $data Query data. * @param string &$charset Search charset. * @param array &$exts_used IMAP extensions used. * @param Horde_Imap_Client_Data_Format_List &$cmds Command list. * * @return boolean True if query might return results. */ protected function _buildAndOr($type, $data, &$charset, &$exts_used, &$cmds) { $results = false; foreach ($data as $val) { $ret = $val->build(); /* Empty sub-query. */ if (!count($ret['query'])) { switch ($type) { case 'AND': /* Any empty sub-query means that the query MUST return * no results. */ $cmds = new Horde_Imap_Client_Data_Format_List(); $exts_used = array(); return false; case 'OR': /* Skip this query. */ continue 2; } } $results = true; if (!is_null($ret['charset']) && $ret['charset'] != 'US-ASCII') { if (!is_null($charset) && $charset != 'US-ASCII' && $charset != $ret['charset']) { throw new InvalidArgumentException('AND/OR queries must all have the same charset.'); } $charset = $ret['charset']; } $exts_used = array_merge($exts_used, $ret['exts']); switch ($type) { case 'AND': $cmds->add($ret['query'], true); break; case 'OR': // First OR'd query if (count($cmds)) { $new_cmds = new Horde_Imap_Client_Data_Format_List(); $new_cmds->add(array('OR', $ret['query'], $cmds)); $cmds = $new_cmds; } else { $cmds = $ret['query']; } break; } } return $results; }
/** * Builds an IMAP4rev1 compliant search string. * * @param array $exts The list of extensions supported by the server. * This determines whether certain criteria can be * used, and determines whether workarounds are used * for other criteria. In the format returned by * Horde_Imap_Client_Base::capability(). If this value * is null, all extensions are assumed to be * available. * * @return array An array with these elements: * - charset: (string) The charset of the search string. If null, no * text strings appear in query. * - exts: (array) The list of IMAP extensions used to create the * string. * - query: (Horde_Imap_Client_Data_Format_List) The IMAP search * command. * * @throws Horde_Imap_Client_Exception_NoSupportExtension */ public function build($exts = array()) { $temp = array('cmds' => new Horde_Imap_Client_Data_Format_List(), 'exts' => $exts, 'exts_used' => array()); $cmds =& $temp['cmds']; $charset = null; $exts_used =& $temp['exts_used']; $ptr =& $this->_search; if (isset($ptr['new'])) { $this->_addFuzzy(!empty($ptr['newfuzzy']), $temp); if ($ptr['new']) { $cmds->add('NEW'); unset($ptr['flag']['UNSEEN']); } else { $cmds->add('OLD'); } unset($ptr['flag']['RECENT']); } if (!empty($ptr['flag'])) { foreach ($ptr['flag'] as $key => $val) { $this->_addFuzzy(!empty($val['fuzzy']), $temp); $tmp = ''; if (empty($val['set'])) { // This is a 'NOT' search. All system flags but \Recent // have 'UN' equivalents. if ($key == 'RECENT') { $cmds->add('NOT'); } else { $tmp = 'UN'; } } if ($val['type'] == 'keyword') { $cmds->add(array($tmp . 'KEYWORD', $key)); } else { $cmds->add($tmp . $key); } } } if (!empty($ptr['header'])) { /* The list of 'system' headers that have a specific search * query. */ $systemheaders = array('BCC', 'CC', 'FROM', 'SUBJECT', 'TO'); foreach ($ptr['header'] as $val) { $this->_addFuzzy(!empty($val['fuzzy']), $temp); if (!empty($val['not'])) { $cmds->add('NOT'); } if (in_array($val['header'], $systemheaders)) { $cmds->add($val['header']); } else { $cmds->add(array('HEADER', new Horde_Imap_Client_Data_Format_Astring($val['header']))); } $cmds->add(new Horde_Imap_Client_Data_Format_Astring(isset($val['text']) ? $val['text'] : '')); $charset = is_null($this->_charset) ? 'US-ASCII' : $this->_charset; } } if (!empty($ptr['text'])) { foreach ($ptr['text'] as $val) { $this->_addFuzzy(!empty($val['fuzzy']), $temp); if (!empty($val['not'])) { $cmds->add('NOT'); } $cmds->add(array($val['type'], new Horde_Imap_Client_Data_Format_Astring($val['text']))); if (is_null($charset)) { $charset = is_null($this->_charset) ? 'US-ASCII' : $this->_charset; } } } if (!empty($ptr['size'])) { foreach ($ptr['size'] as $key => $val) { $this->_addFuzzy(!empty($val['fuzzy']), $temp); if (!empty($val['not'])) { $cmds->add('NOT'); } $cmds->add(array($key, new Horde_Imap_Client_Data_Format_Number($val['size']))); } } if (isset($ptr['ids']) && (count($ptr['ids']['ids']) || $ptr['ids']['ids']->special)) { $this->_addFuzzy(!empty($val['fuzzy']), $temp); if (!empty($ptr['ids']['not'])) { $cmds->add('NOT'); } if (!$ptr['ids']['ids']->sequence) { $cmds->add('UID'); } $cmds->add(strval($ptr['ids']['ids'])); } if (!empty($ptr['date'])) { foreach ($ptr['date'] as $val) { $this->_addFuzzy(!empty($val['fuzzy']), $temp); if (!empty($val['not'])) { $cmds->add('NOT'); } if (empty($val['header'])) { $cmds->add($val['range']); } else { $cmds->add('SENT' . $val['range']); } $cmds->add($val['date']); } } if (!empty($ptr['within'])) { if (is_null($exts) || isset($exts['WITHIN'])) { $exts_used[] = 'WITHIN'; } foreach ($ptr['within'] as $key => $val) { $this->_addFuzzy(!empty($val['fuzzy']), $temp); if (!empty($val['not'])) { $cmds->add('NOT'); } if (is_null($exts) || isset($exts['WITHIN'])) { $cmds->add(array($key, new Horde_Imap_Client_Data_Format_Number($val['interval']))); } else { // This workaround is only accurate to within 1 day, due // to limitations with the IMAP4rev1 search commands. $cmds->add(array($key == self::INTERVAL_OLDER ? self::DATE_BEFORE : self::DATE_SINCE, new Horde_Imap_Client_Data_Format_Date('now -' . $val['interval'] . ' seconds'))); } } } if (!empty($ptr['modseq'])) { if (!is_null($exts) && !isset($exts['CONDSTORE'])) { throw new Horde_Imap_Client_Exception_NoSupportExtension('IMAP Server does not support CONDSTORE.'); } $exts_used[] = 'CONDSTORE'; $this->_addFuzzy(!empty($ptr['modseq']['fuzzy']), $temp); if (!empty($ptr['modseq']['not'])) { $cmds->add('NOT'); } $cmds->add('MODSEQ'); if (isset($ptr['modseq']['name'])) { $cmds->add(array(new Horde_Imap_Client_Data_Format_String($ptr['modseq']['name']), $ptr['modseq']['type'])); } $cmds->add(new Horde_Imap_Client_Data_Format_Number($ptr['modseq']['value'])); } if (isset($ptr['prevsearch'])) { if (!is_null($exts) && !isset($exts['SEARCHRES'])) { throw new Horde_Imap_Client_Exception_NoSupportExtension('IMAP Server does not support SEARCHRES.'); } $exts_used[] = 'SEARCHRES'; $this->_addFuzzy(!empty($ptr['prevsearchfuzzy']), $temp); if (!$ptr['prevsearch']) { $cmds->add('NOT'); } $cmds->add('$'); } // Add AND'ed queries if (!empty($ptr['and'])) { foreach ($ptr['and'] as $val) { $ret = $val->build(); if ($ret['charset'] != 'US-ASCII') { $charset = $ret['charset']; } $exts_used = array_merge($exts_used, $ret['exts']); $cmds->add($ret['query'], true); } } // Add OR'ed queries if (!empty($ptr['or'])) { foreach ($ptr['or'] as $val) { $ret = $val->build(); if ($ret['charset'] != 'US-ASCII') { $charset = $ret['charset']; } $exts_used = array_merge($exts_used, $ret['exts']); // First OR'd query if (count($cmds)) { $new_cmds = new Horde_Imap_Client_Data_Format_List(); $new_cmds->add(array('OR', $ret['query'], $cmds)); $cmds = $new_cmds; } else { $cmds = $ret['query']; } } } // Default search is 'ALL' if (!count($cmds)) { $cmds->add('ALL'); } return array('charset' => $charset, 'exts' => array_keys(array_flip($exts_used)), 'query' => $cmds); }
/** * Constructor. * * @param string $cmd The IMAP command. * @param string $tag The tag to use. If not set, will be automatically * generated. */ public function __construct($cmd, $tag = null) { $this->tag = is_null($tag) ? substr(new Horde_Support_Randomid(), 0, 10) : strval($tag); parent::__construct($this->tag); $this->add($cmd); }
/** * Process/send a command to the remote server. * * @param Horde_Imap_Client_Interaction_Pipeline $pipeline The pipeline * object. * @param Horde_Imap_Client_Interaction_Command $cmd The master command. * @param Horde_Imap_Client_Data_Format_List $data Commands to send. * * @return boolean True if EOL needed to finish command. * @throws Horde_Imap_Client_Exception * @throws Horde_Imap_Client_Exception_NoSupport */ protected function _processCmd($pipeline, $cmd, $data) { if ($this->_debug->debug && $data instanceof Horde_Imap_Client_Interaction_Command) { $data->startTimer(); } foreach ($data as $key => $val) { if ($val instanceof Horde_Imap_Client_Interaction_Command_Continuation) { $this->_connection->write('', true); /* Check for optional continuation responses when the command * has already finished. */ if (!($cmd_continuation = $this->_processCmdContinuation($pipeline, $val->optional))) { return false; } $this->_processCmd($pipeline, $cmd, $val->getCommands($cmd_continuation)); continue; } if (!is_null($debug_msg = array_shift($cmd->debug))) { $this->_debug->client(($cmd == $data ? $cmd->tag . ' ' : '') . $debug_msg); $this->_connection->client_debug = false; } if ($key) { $this->_connection->write(' '); } if ($val instanceof Horde_Imap_Client_Data_Format_List) { $this->_connection->write('('); $this->_processCmd($pipeline, $cmd, $val); $this->_connection->write(')'); } elseif ($val instanceof Horde_Imap_Client_Data_Format_String && $val->literal()) { $c = $this->_capability(); /* RFC 6855: If UTF8 extension is available, quote short * strings instead of sending as literal. */ if ($c->isEnabled('UTF8=ACCEPT') && $val->length() < 100) { $val->forceQuoted(); $this->_connection->write($val->escape()); } else { /* RFC 3516/4466: Send literal8 if we have binary data. */ if ($cmd->literal8 && $val->binary() && ($c->query('BINARY') || $c->query('UTF8=ACCEPT'))) { $binary = true; $this->_connection->write('~'); } else { $binary = false; } $literal_len = $val->length(); $this->_connection->write('{' . $literal_len); /* RFC 2088 - If LITERAL+ is available, saves a roundtrip * from the server. */ if ($cmd->literalplus && $c->query('LITERAL+')) { $this->_connection->write('+}', true); } else { $this->_connection->write('}', true); $this->_processCmdContinuation($pipeline); } if ($debug_msg) { $this->_connection->client_debug = false; } $this->_connection->writeLiteral($val->getStream(), $literal_len, $binary); } } else { $this->_connection->write($val->escape()); } } return true; }