add class_exists checks for Auth_SASL that can autoload it and avoid fatal errors if it's not available
remove require statements that should be autoloadable
split a few classes into their own files
<?php
-
-require_once dirname(__FILE__) . '/Client/Base.php';
-require_once dirname(__FILE__) . '/Client/Exception.php';
-require_once dirname(__FILE__) . '/Client/Utf7imap.php';
-
/**
* Horde_Imap_Client:: provides an abstracted API interface to various IMAP
* backends (RFC 3501).
static public $encryptKey = null;
/**
- * Autoload handler.
- */
- static public function autoload($classname)
- {
- $res = false;
-
- $old_error = error_reporting(0);
- switch ($classname) {
- case 'Horde_Mime':
- $res = require_once 'Horde/Mime.php';
- break;
-
- case 'Horde_Mime_Headers':
- $res = require_once 'Horde/Mime/Headers.php';
- break;
-
- case 'Horde_Mime_Part':
- $res = require_once 'Horde/Mime/Part.php';
- break;
-
- case 'Secret':
- $res = require_once 'Horde/Secret.php';
- break;
- }
- error_reporting($old_error);
-
- return $res;
- }
-
- /**
* Attempts to return a concrete Horde_Imap_Client instance based on
* $driver.
* Throws a Horde_Imap_Client_Exception on error.
{
$class = 'Horde_Imap_Client_' . strtr(basename($driver), '-', '_');
if (!class_exists($class)) {
- $fname = dirname(__FILE__) . '/Client/' . $driver . '.php';
- if (is_file($fname)) {
- require_once $fname;
- }
- }
- if (!class_exists($class)) {
throw new Horde_Imap_Client_Exception('Driver ' . $driver . ' not found', Horde_Imap_Client_Exception::DRIVER_NOT_FOUND);
}
return new $class($params);
return $ret_array;
}
-}
-spl_autoload_register(array('Horde_Imap_Client_Base', 'autoload'));
+}
if (is_null($this->_cacheOb)) {
$p = $this->_params;
- require_once dirname(__FILE__) . '/Cache.php';
- $this->_cacheOb = &Horde_Imap_Client_Cache::singleton(array_merge($p['cache'], array(
+ $this->_cacheOb = Horde_Imap_Client_Cache::singleton(array_merge($p['cache'], array(
'debug' => $this->_debug,
'hostspec' => $p['hostspec'],
'username' => $p['username']
$ret = $this->_listMailboxes(Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($pattern), $mode, $options);
if (!empty($options['sort'])) {
- require_once dirname(__FILE__) . '/Sort.php';
Horde_Imap_Client_Sort::sortMailboxes($ret, array('delimiter' => empty($options['sort_delimiter']) ? '.' : $options['sort_delimiter'], 'index' => false, 'keysort' => empty($options['flat'])));
}
}
}
}
-}
-
-/*
- * Abstraction of the IMAP4rev1 search criteria (see RFC 3501 [6.4.4]). This
- * class allows translation between abstracted search criteria and a
- * generated IMAP search criteria string suitable for sending to a remote
- * IMAP server.
- */
-class Horde_Imap_Client_Search_Query
-{
- /* Constants for dateSearch() */
- const DATE_BEFORE = 'BEFORE';
- const DATE_ON = 'ON';
- const DATE_SINCE = 'SINCE';
-
- /* Constants for intervalSearch() */
- const INTERVAL_OLDER = 'OLDER';
- const INTERVAL_YOUNGER = 'YOUNGER';
- /**
- * The charset of the search strings. All text strings must be in
- * this charset.
- *
- * @var string
- */
- protected $_charset = 'US-ASCII';
-
- /**
- * The list of defined system flags (see RFC 3501 [2.3.2]).
- *
- * @var array
- */
- protected $_systemflags = array(
- 'ANSWERED', 'DELETED', 'DRAFT', 'FLAGGED', 'RECENT', 'SEEN'
- );
-
- /**
- * The list of 'system' headers that have a specific search query.
- *
- * @var array
- */
- protected $_systemheaders = array(
- 'BCC', 'CC', 'FROM', 'SUBJECT', 'TO'
- );
-
- /**
- * The list of search params.
- *
- * @var array
- */
- protected $_search = array();
-
- /**
- * List of extensions needed for advanced queries.
- *
- * @var array
- */
- protected $_exts = array();
-
- /**
- * Sets the charset of the search text.
- *
- * @param string $charset The charset to use for the search.
- */
- public function charset($charset)
- {
- $this->_charset = strtoupper($charset);
- }
-
- /**
- * Builds an IMAP4rev1 compliant search string.
- *
- * @return array An array with 3 elements:
- * <pre>
- * 'charset' - (string) The charset of the search string.
- * 'imap4' - (boolean) True if the search uses IMAP4 criteria (as opposed
- * to IMAP2 search criteria)
- * 'query' - (string) The IMAP search string
- * </pre>
- */
- public function build()
- {
- $cmds = array();
- $imap4 = false;
- $ptr = &$this->_search;
-
- if (isset($ptr['new'])) {
- if ($ptr['new']) {
- $cmds[] = 'NEW';
- unset($ptr['flag']['UNSEEN']);
- } else {
- $cmds[] = 'OLD';
- }
- unset($ptr['flag']['RECENT']);
- }
-
- if (!empty($ptr['flag'])) {
- foreach ($ptr['flag'] as $key => $val) {
- if ($key == 'draft') {
- // DRAFT flag was not in IMAP2
- $imap4 = true;
- }
-
- $tmp = '';
- if (!$val['set']) {
- // This is a 'NOT' search. All system flags but \Recent
- // have 'UN' equivalents.
- if ($key == 'RECENT') {
- $tmp = 'NOT ';
- // NOT searches were not in IMAP2
- $imap4 = true;
- } else {
- $tmp = 'UN';
- }
- }
-
- $cmds[] = $tmp . ($val['type'] == 'keyword' ? 'KEYWORD ' : '') . $key;
- }
- }
-
- if (!empty($ptr['header'])) {
- foreach ($ptr['header'] as $val) {
- $tmp = '';
- if ($val['not']) {
- $tmp = 'NOT ';
- // NOT searches were not in IMAP2
- $imap4 = true;
- }
-
- if (!in_array($val['header'], $this->_systemheaders)) {
- // HEADER searches were not in IMAP2
- $tmp .= 'HEADER ';
- $imap4 = true;
- }
- $cmds[] = $tmp . $val['header'] . ' ' . Horde_Imap_Client::escape($val['text']);
- }
- }
-
- if (!empty($ptr['text'])) {
- foreach ($ptr['text'] as $val) {
- $tmp = '';
- if ($val['not']) {
- $tmp = 'NOT ';
- // NOT searches were not in IMAP2
- $imap4 = true;
- }
- $cmds[] = $tmp . $val['type'] . ' ' . Horde_Imap_Client::escape($val['text']);
- }
- }
-
- if (!empty($ptr['size'])) {
- foreach ($ptr['size'] as $key => $val) {
- $cmds[] = ($val['not'] ? 'NOT ' : '' ) . $key . ' ' . $val['size'];
- // LARGER/SMALLER searches were not in IMAP2
- $imap4 = true;
- }
- }
-
- if (isset($ptr['sequence'])) {
- $cmds[] = ($ptr['sequence']['not'] ? 'NOT ' : '') . ($ptr['sequence']['sequence'] ? '' : 'UID ') . $ptr['sequence']['ids'];
-
- // sequence searches were not in IMAP2
- $imap4 = true;
- }
-
- if (!empty($ptr['date'])) {
- foreach ($ptr['date'] as $key => $val) {
- $tmp = '';
- if ($val['not']) {
- $tmp = 'NOT ';
- // NOT searches were not in IMAP2
- $imap4 = true;
- }
-
- if ($key == 'header') {
- $tmp .= 'SENT';
- // 'SENT*' searches were not in IMAP2
- $imap4 = true;
- }
- $cmds[] = $tmp . $val['range'] . ' ' . $val['date'];
- }
- }
-
- if (!empty($ptr['within'])) {
- $imap4 = true;
- $this->_exts['WITHIN'] = true;
-
- foreach ($ptr['within'] as $key => $val) {
- $cmds[] = ($val['not'] ? 'NOT ' : '') . $key . ' ' . $val['interval'];
- }
- }
-
- if (!empty($ptr['modseq'])) {
- $imap4 = true;
- $this->_exts['CONDSTORE'] = true;
- $cmds[] = ($ptr['modseq']['not'] ? 'NOT ' : '') .
- 'MODSEQ ' .
- (is_null($ptr['modseq']['name'])
- ? ''
- : Horde_Imap_Client::escape($ptr['modseq']['name']) . ' ' . $ptr['modseq']['type'] . ' ') .
- $ptr['modseq']['value'];
- }
-
- if (isset($ptr['prevsearch'])) {
- $imap4 = true;
- $this->_exts['SEARCHRES'] = true;
- $cmds[] = ($ptr['prevsearch'] ? '' : 'NOT ') . '$';
- }
-
- $query = '';
-
- // Add OR'ed queries
- if (!empty($ptr['or'])) {
- foreach ($ptr['or'] as $key => $val) {
- // OR queries were not in IMAP 2
- $imap4 = true;
-
- if ($key == 0) {
- $query = '(' . $query . ')';
- }
-
- $ret = $val->build();
- $query = 'OR (' . $ret['query'] . ') ' . $query;
- }
- }
-
- // Add AND'ed queries
- if (!empty($ptr['and'])) {
- foreach ($ptr['and'] as $key => $val) {
- $ret = $val->build();
- $query .= ' ' . $ret['query'];
- }
- }
-
- // Default search is 'ALL'
- if (empty($cmds)) {
- $query .= empty($query) ? 'ALL' : '';
- } else {
- $query .= implode(' ', $cmds);
- }
-
- return array(
- 'charset' => $this->_charset,
- 'imap4' => $imap4,
- 'query' => trim($query)
- );
- }
-
- /**
- * Return the list of any IMAP extensions needed to perform the query.
- *
- * @return array The list of extensions (CAPABILITY responses) needed to
- * perform the query.
- */
- public function extensionsNeeded()
- {
- return $this->_exts;
- }
-
- /**
- * Search for a flag/keywords.
- *
- * @param string $name The flag or keyword name.
- * @param boolean $set If true, search for messages that have the flag
- * set. If false, search for messages that do not
- * have the flag set.
- */
- public function flag($name, $set = true)
- {
- $name = strtoupper(ltrim($name, '\\'));
- if (!isset($this->_search['flag'])) {
- $this->_search['flag'] = array();
- }
- $this->_search['flag'][$name] = array(
- 'set' => $set,
- 'type' => in_array($name, $this->_systemflags) ? 'flag' : 'keyword'
- );
- }
-
- /**
- * Search for either new messages (messages that have the '\Recent' flag
- * but not the '\Seen' flag) or old messages (messages that do not have
- * the '\Recent' flag). If new messages are searched, this will clear
- * any '\Recent' or '\Unseen' flag searches. If old messages are searched,
- * this will clear any '\Recent' flag search.
- *
- * @param boolean $newmsgs If true, searches for new messages. Else,
- * search for old messages.
- */
- public function newMsgs($newmsgs = true)
- {
- $this->_search['new'] = $newmsgs;
- }
-
- /**
- * Search for text in the header of a message.
- *
- * @param string $header The header field.
- * @param string $text The search text.
- * @param boolean $not If true, do a 'NOT' search of $text.
- */
- public function headerText($header, $text, $not = false)
- {
- if (!isset($this->_search['header'])) {
- $this->_search['header'] = array();
- }
- $this->_search['header'][] = array(
- 'header' => strtoupper($header),
- 'text' => $text,
- 'not' => $not
- );
- }
-
- /**
- * Search for text in either the entire message, or just the body.
- *
- * @param string $text The search text.
- * @param string $bodyonly If true, only search in the body of the
- * message. If false, also search in the headers.
- * @param boolean $not If true, do a 'NOT' search of $text.
- */
- public function text($text, $bodyonly = true, $not = false)
- {
- if (!isset($this->_search['text'])) {
- $this->_search['text'] = array();
- }
- $this->_search['text'][] = array(
- 'text' => $text,
- 'not' => $not,
- 'type' => $bodyonly ? 'BODY' : 'TEXT'
- );
- }
-
- /**
- * Search for messages smaller/larger than a certain size.
- *
- * @param integer $size The size (in bytes).
- * @param boolean $larger Search for messages larger than $size?
- * @param boolean $not If true, do a 'NOT' search of $text.
- */
- public function size($size, $larger = false, $not = false)
- {
- if (!isset($this->_search['size'])) {
- $this->_search['size'] = array();
- }
- $this->_search['size'][$larger ? 'LARGER' : 'SMALLER'] = array(
- 'size' => (float)$size,
- 'not' => $not
- );
- }
-
- /**
- * Search for messages within a given message range. Only one message
- * range can be specified per query.
- *
- * @param array $ids The list of messages to search.
- * @param boolean $sequence By default, $ids is assumed to be UIDs. If
- * this param is true, $ids are taken to be
- * message sequence numbers instead.
- * @param boolean $not If true, do a 'NOT' search of the sequence.
- */
- public function sequence($ids, $sequence = false, $not = false)
- {
- if (empty($ids)) {
- $ids = '1:*';
- } else {
- $ids = Horde_Imap_Client::toSequenceString($ids);
- }
- $this->_search['sequence'] = array(
- 'ids' => $ids,
- 'not' => $not,
- 'sequence' => $sequence
- );
- }
-
- /**
- * Search for messages within a date range. Only one internal date and
- * one RFC 2822 date can be specified per query.
- *
- * @param integer $month Month (from 1-12).
- * @param integer $day Day of month (from 1-31).
- * @param integer $year Year (4-digit year).
- * @param string $range Either:
- * <pre>
- * Horde_Imap_Client_Search_Query::DATE_BEFORE,
- * Horde_Imap_Client_Search_Query::DATE_ON, or
- * Horde_Imap_Client_Search_Query::DATE_SINCE.
- * </pre>
- * @param boolean $header If true, search using the date in the message
- * headers. If false, search using the internal
- * IMAP date (usually arrival time).
- * @param boolean $not If true, do a 'NOT' search of the range.
- */
- public function dateSearch($month, $day, $year, $range, $header = true,
- $not = false)
- {
- $type = $header ? 'header' : 'internal';
- if (!isset($this->_search['date'])) {
- $this->_search['date'] = array();
- }
- $this->_search['date'][$header ? 'header' : 'internal'] = array(
- 'date' => date("d-M-y", mktime(0, 0, 0, $month, $day, $year)),
- 'range' => $range,
- 'not' => $not
- );
- }
-
- /**
- * Search for messages within a given interval. Only one interval of each
- * type can be specified per search query. The IMAP server must support
- * the WITHIN extension (RFC 5032) for this query to be used.
- *
- * @param integer $interval Seconds from the present.
- * @param string $range Either:
- * <pre>
- * Horde_Imap_Client_Search_Query::INTERVAL_OLDER, or
- * Horde_Imap_Client_Search_Query::INTERVAL_YOUNGER
- * </pre>
- * @param boolean $not If true, do a 'NOT' search.
- */
- public function intervalSearch($interval, $range, $not = false)
- {
- if (!isset($this->_search['within'])) {
- $this->_search['within'] = array();
- }
- $this->_search['within'][$range] = array(
- 'interval' => $interval,
- 'not' => $not
- );
- }
-
- /**
- * AND queries - the contents of this query will be AND'ed (in its
- * entirety) with the contents of each of the queries passed in. All
- * AND'd queries must share the same charset as this query.
- *
- * @param array $queries An array of queries to AND with this one. Each
- * query is a Horde_Imap_Client_Search_Query
- * object.
- */
- public function andSearch($queries)
- {
- if (!isset($this->_search['and'])) {
- $this->_search['and'] = array();
- }
- $this->_search['and'] = array_merge($this->_search['and'], $queries);
- }
-
- /**
- * OR a query - the contents of this query will be OR'ed (in its entirety)
- * with the contents of each of the queries passed in. All OR'd queries
- * must share the same charset as this query. All contents of any single
- * query will be AND'ed together.
- *
- * @param array $queries An array of queries to OR with this one. Each
- * query is a Horde_Imap_Client_Search_Query
- * object.
- */
- public function orSearch($queries)
- {
- if (!isset($this->_search['or'])) {
- $this->_search['or'] = array();
- }
- $this->_search['or'] = array_merge($this->_search['or'], $queries);
- }
-
- /**
- * Search for messages modified since a specific moment. The IMAP server
- * must support the CONDSTORE extension (RFC 4551) for this query to be
- * used.
- *
- * @param integer $value The mod-sequence value.
- * @param string $name The entry-name string.
- * @param string $type Either 'shared', 'priv', or 'all'. Defaults to
- * 'all'
- * @param boolean $not If true, do a 'NOT' search.
- */
- public function modseq($value, $name = null, $type = null, $not = false)
- {
- if (!is_null($type)) {
- $type = strtolower($type);
- if (!in_array($type, array('shared', 'priv', 'all'))) {
- $type = 'all';
- }
- }
-
- $this->_search['modseq'] = array(
- 'value' => $value,
- 'name' => $name,
- 'not' => $not,
- 'type' => (!is_null($name) && is_null($type)) ? 'all' : $type
- );
- }
-
- /**
- * Use the results from the previous SEARCH command. The IMAP server must
- * support the SEARCHRES extension (RFC 5032) for this query to be used.
- *
- * @param boolean $not If true, don't match the previous query.
- */
- public function previousSearch($not = false)
- {
- $this->_search['prevsearch'] = $not;
- }
-}
-
-/*
- * A class allowing easy access to threaded sort results from
- * Horde_Imap_Client::thread().
- */
-class Horde_Imap_Client_Thread
-{
- /**
- * Internal thread data structure.
- *
- * @var array
- */
- protected $_thread = array();
-
- /**
- * The index type.
- *
- * @var string
- */
- protected $_type;
-
- /**
- * Constructor.
- *
- * @param array $data The data as returned by
- * Horde_Imap_Client_Base::_thread().
- * @param string $type Either 'uid' or 'sequence'.
- */
- function __construct($data, $type)
- {
- $this->_thread = $data;
- $this->_type = $type;
- }
-
- /**
- * Return the raw thread data array.
- *
- * @return array See Horde_Imap_Client_Base::_thread().
- */
- public function getRawData()
- {
- return $this->_thread;
- }
-
- /**
- * Gets the indention level for an index.
- *
- * @param integer $index The index.
- *
- * @return mixed Returns the thread indent level if $index found.
- * Returns false on failure.
- */
- public function getThreadIndent($index)
- {
- return isset($this->_thread[$index]['level'])
- ? $this->_thread[$index]['level']
- : false;
- }
-
- /**
- * Gets the base thread index for an index.
- *
- * @param integer $index The index.
- *
- * @return mixed Returns the base index if $index is part of a thread.
- * Returns false on failure.
- */
- public function getThreadBase($index)
- {
- return !empty($this->_thread[$index]['base'])
- ? $this->_thread[$index]['base']
- : false;
- }
-
- /**
- * Is this index the last in the current level?
- *
- * @param integer $index The index.
- *
- * @return boolean Returns true if $index is the last element in the
- * current thread level.
- * Returns false if not, or on failure.
- */
- public function lastInLevel($index)
- {
- return !empty($this->_thread[$index]['last'])
- ? $this->_thread[$index]['last']
- : false;
- }
-
- /**
- * Return the sorted list of messages indices.
- *
- * @param boolean $new True for newest first, false for oldest first.
- *
- * @return array The sorted list of messages.
- */
- public function messageList($new)
- {
- return ($new) ? array_reverse(array_keys($this->_thread)) : array_keys($this->_thread);
- }
-
- /**
- * Returns the list of messages in the current thread.
- *
- * @param integer $index The index of the current message.
- *
- * @return array A list of message indices.
- */
- public function getThread($index)
- {
- /* Find the beginning of the thread. */
- if (($begin = $this->getThreadBase($index)) === false) {
- return array($index);
- }
-
- /* Work forward from the first thread element to find the end of the
- * thread. */
- $in_thread = false;
- $thread_list = array();
- reset($this->_thread);
- while (list($k, $v) = each($this->_thread)) {
- if ($k == $begin) {
- $in_thread = true;
- } elseif ($in_thread && ($v['base'] != $begin)) {
- break;
- }
-
- if ($in_thread) {
- $thread_list[] = $k;
- }
- }
-
- return $thread_list;
- }
}
<?php
-
-require_once 'Horde/Cache.php';
-require_once 'Horde/Serialize.php';
-
/**
* Horde_Imap_Client_Cache:: provides an interface to cache various data
* retrieved from the IMAP server.
/**
* Return a reference to a concrete Horde_Imap_Client_Cache instance.
*
- * This method must be invoked as:
- * $var = &IMP_MessageCache::singleton();
- *
* @param array $params The configuration parameters.
*
* @return Horde_Imap_Client_Cache The global instance.
*/
- static public function &singleton($params = array())
+ static public function singleton($params = array())
{
static $instance = array();
$sig = md5(serialize($params));
-
if (!isset($instance[$sig])) {
$instance[$sig] = new Horde_Imap_Client_Cache($params);
}
}
/* Initialize the Cache object. */
- $this->_cacheOb = &Horde_Cache::singleton($params['driver'], $params['driver_params']);
+ $this->_cacheOb = Horde_Cache::singleton($params['driver'], $params['driver_params']);
if (is_a($this->_cacheOb, 'PEAR_Error')) {
throw new Horde_Imap_Client_Exception($this->_cacheOb->getMessage());
}
--- /dev/null
+<?php
+/**
+ * Abstraction of the IMAP4rev1 search criteria (see RFC 3501 [6.4.4]). This
+ * class allows translation between abstracted search criteria and a
+ * generated IMAP search criteria string suitable for sending to a remote
+ * IMAP server.
+ */
+class Horde_Imap_Client_Search_Query
+{
+ /* Constants for dateSearch() */
+ const DATE_BEFORE = 'BEFORE';
+ const DATE_ON = 'ON';
+ const DATE_SINCE = 'SINCE';
+
+ /* Constants for intervalSearch() */
+ const INTERVAL_OLDER = 'OLDER';
+ const INTERVAL_YOUNGER = 'YOUNGER';
+
+ /**
+ * The charset of the search strings. All text strings must be in
+ * this charset.
+ *
+ * @var string
+ */
+ protected $_charset = 'US-ASCII';
+
+ /**
+ * The list of defined system flags (see RFC 3501 [2.3.2]).
+ *
+ * @var array
+ */
+ protected $_systemflags = array(
+ 'ANSWERED', 'DELETED', 'DRAFT', 'FLAGGED', 'RECENT', 'SEEN'
+ );
+
+ /**
+ * The list of 'system' headers that have a specific search query.
+ *
+ * @var array
+ */
+ protected $_systemheaders = array(
+ 'BCC', 'CC', 'FROM', 'SUBJECT', 'TO'
+ );
+
+ /**
+ * The list of search params.
+ *
+ * @var array
+ */
+ protected $_search = array();
+
+ /**
+ * List of extensions needed for advanced queries.
+ *
+ * @var array
+ */
+ protected $_exts = array();
+
+ /**
+ * Sets the charset of the search text.
+ *
+ * @param string $charset The charset to use for the search.
+ */
+ public function charset($charset)
+ {
+ $this->_charset = strtoupper($charset);
+ }
+
+ /**
+ * Builds an IMAP4rev1 compliant search string.
+ *
+ * @return array An array with 3 elements:
+ * <pre>
+ * 'charset' - (string) The charset of the search string.
+ * 'imap4' - (boolean) True if the search uses IMAP4 criteria (as opposed
+ * to IMAP2 search criteria)
+ * 'query' - (string) The IMAP search string
+ * </pre>
+ */
+ public function build()
+ {
+ $cmds = array();
+ $imap4 = false;
+ $ptr = &$this->_search;
+
+ if (isset($ptr['new'])) {
+ if ($ptr['new']) {
+ $cmds[] = 'NEW';
+ unset($ptr['flag']['UNSEEN']);
+ } else {
+ $cmds[] = 'OLD';
+ }
+ unset($ptr['flag']['RECENT']);
+ }
+
+ if (!empty($ptr['flag'])) {
+ foreach ($ptr['flag'] as $key => $val) {
+ if ($key == 'draft') {
+ // DRAFT flag was not in IMAP2
+ $imap4 = true;
+ }
+
+ $tmp = '';
+ if (!$val['set']) {
+ // This is a 'NOT' search. All system flags but \Recent
+ // have 'UN' equivalents.
+ if ($key == 'RECENT') {
+ $tmp = 'NOT ';
+ // NOT searches were not in IMAP2
+ $imap4 = true;
+ } else {
+ $tmp = 'UN';
+ }
+ }
+
+ $cmds[] = $tmp . ($val['type'] == 'keyword' ? 'KEYWORD ' : '') . $key;
+ }
+ }
+
+ if (!empty($ptr['header'])) {
+ foreach ($ptr['header'] as $val) {
+ $tmp = '';
+ if ($val['not']) {
+ $tmp = 'NOT ';
+ // NOT searches were not in IMAP2
+ $imap4 = true;
+ }
+
+ if (!in_array($val['header'], $this->_systemheaders)) {
+ // HEADER searches were not in IMAP2
+ $tmp .= 'HEADER ';
+ $imap4 = true;
+ }
+ $cmds[] = $tmp . $val['header'] . ' ' . Horde_Imap_Client::escape($val['text']);
+ }
+ }
+
+ if (!empty($ptr['text'])) {
+ foreach ($ptr['text'] as $val) {
+ $tmp = '';
+ if ($val['not']) {
+ $tmp = 'NOT ';
+ // NOT searches were not in IMAP2
+ $imap4 = true;
+ }
+ $cmds[] = $tmp . $val['type'] . ' ' . Horde_Imap_Client::escape($val['text']);
+ }
+ }
+
+ if (!empty($ptr['size'])) {
+ foreach ($ptr['size'] as $key => $val) {
+ $cmds[] = ($val['not'] ? 'NOT ' : '' ) . $key . ' ' . $val['size'];
+ // LARGER/SMALLER searches were not in IMAP2
+ $imap4 = true;
+ }
+ }
+
+ if (isset($ptr['sequence'])) {
+ $cmds[] = ($ptr['sequence']['not'] ? 'NOT ' : '') . ($ptr['sequence']['sequence'] ? '' : 'UID ') . $ptr['sequence']['ids'];
+
+ // sequence searches were not in IMAP2
+ $imap4 = true;
+ }
+
+ if (!empty($ptr['date'])) {
+ foreach ($ptr['date'] as $key => $val) {
+ $tmp = '';
+ if ($val['not']) {
+ $tmp = 'NOT ';
+ // NOT searches were not in IMAP2
+ $imap4 = true;
+ }
+
+ if ($key == 'header') {
+ $tmp .= 'SENT';
+ // 'SENT*' searches were not in IMAP2
+ $imap4 = true;
+ }
+ $cmds[] = $tmp . $val['range'] . ' ' . $val['date'];
+ }
+ }
+
+ if (!empty($ptr['within'])) {
+ $imap4 = true;
+ $this->_exts['WITHIN'] = true;
+
+ foreach ($ptr['within'] as $key => $val) {
+ $cmds[] = ($val['not'] ? 'NOT ' : '') . $key . ' ' . $val['interval'];
+ }
+ }
+
+ if (!empty($ptr['modseq'])) {
+ $imap4 = true;
+ $this->_exts['CONDSTORE'] = true;
+ $cmds[] = ($ptr['modseq']['not'] ? 'NOT ' : '') .
+ 'MODSEQ ' .
+ (is_null($ptr['modseq']['name'])
+ ? ''
+ : Horde_Imap_Client::escape($ptr['modseq']['name']) . ' ' . $ptr['modseq']['type'] . ' ') .
+ $ptr['modseq']['value'];
+ }
+
+ if (isset($ptr['prevsearch'])) {
+ $imap4 = true;
+ $this->_exts['SEARCHRES'] = true;
+ $cmds[] = ($ptr['prevsearch'] ? '' : 'NOT ') . '$';
+ }
+
+ $query = '';
+
+ // Add OR'ed queries
+ if (!empty($ptr['or'])) {
+ foreach ($ptr['or'] as $key => $val) {
+ // OR queries were not in IMAP 2
+ $imap4 = true;
+
+ if ($key == 0) {
+ $query = '(' . $query . ')';
+ }
+
+ $ret = $val->build();
+ $query = 'OR (' . $ret['query'] . ') ' . $query;
+ }
+ }
+
+ // Add AND'ed queries
+ if (!empty($ptr['and'])) {
+ foreach ($ptr['and'] as $key => $val) {
+ $ret = $val->build();
+ $query .= ' ' . $ret['query'];
+ }
+ }
+
+ // Default search is 'ALL'
+ if (empty($cmds)) {
+ $query .= empty($query) ? 'ALL' : '';
+ } else {
+ $query .= implode(' ', $cmds);
+ }
+
+ return array(
+ 'charset' => $this->_charset,
+ 'imap4' => $imap4,
+ 'query' => trim($query)
+ );
+ }
+
+ /**
+ * Return the list of any IMAP extensions needed to perform the query.
+ *
+ * @return array The list of extensions (CAPABILITY responses) needed to
+ * perform the query.
+ */
+ public function extensionsNeeded()
+ {
+ return $this->_exts;
+ }
+
+ /**
+ * Search for a flag/keywords.
+ *
+ * @param string $name The flag or keyword name.
+ * @param boolean $set If true, search for messages that have the flag
+ * set. If false, search for messages that do not
+ * have the flag set.
+ */
+ public function flag($name, $set = true)
+ {
+ $name = strtoupper(ltrim($name, '\\'));
+ if (!isset($this->_search['flag'])) {
+ $this->_search['flag'] = array();
+ }
+ $this->_search['flag'][$name] = array(
+ 'set' => $set,
+ 'type' => in_array($name, $this->_systemflags) ? 'flag' : 'keyword'
+ );
+ }
+
+ /**
+ * Search for either new messages (messages that have the '\Recent' flag
+ * but not the '\Seen' flag) or old messages (messages that do not have
+ * the '\Recent' flag). If new messages are searched, this will clear
+ * any '\Recent' or '\Unseen' flag searches. If old messages are searched,
+ * this will clear any '\Recent' flag search.
+ *
+ * @param boolean $newmsgs If true, searches for new messages. Else,
+ * search for old messages.
+ */
+ public function newMsgs($newmsgs = true)
+ {
+ $this->_search['new'] = $newmsgs;
+ }
+
+ /**
+ * Search for text in the header of a message.
+ *
+ * @param string $header The header field.
+ * @param string $text The search text.
+ * @param boolean $not If true, do a 'NOT' search of $text.
+ */
+ public function headerText($header, $text, $not = false)
+ {
+ if (!isset($this->_search['header'])) {
+ $this->_search['header'] = array();
+ }
+ $this->_search['header'][] = array(
+ 'header' => strtoupper($header),
+ 'text' => $text,
+ 'not' => $not
+ );
+ }
+
+ /**
+ * Search for text in either the entire message, or just the body.
+ *
+ * @param string $text The search text.
+ * @param string $bodyonly If true, only search in the body of the
+ * message. If false, also search in the headers.
+ * @param boolean $not If true, do a 'NOT' search of $text.
+ */
+ public function text($text, $bodyonly = true, $not = false)
+ {
+ if (!isset($this->_search['text'])) {
+ $this->_search['text'] = array();
+ }
+ $this->_search['text'][] = array(
+ 'text' => $text,
+ 'not' => $not,
+ 'type' => $bodyonly ? 'BODY' : 'TEXT'
+ );
+ }
+
+ /**
+ * Search for messages smaller/larger than a certain size.
+ *
+ * @param integer $size The size (in bytes).
+ * @param boolean $larger Search for messages larger than $size?
+ * @param boolean $not If true, do a 'NOT' search of $text.
+ */
+ public function size($size, $larger = false, $not = false)
+ {
+ if (!isset($this->_search['size'])) {
+ $this->_search['size'] = array();
+ }
+ $this->_search['size'][$larger ? 'LARGER' : 'SMALLER'] = array(
+ 'size' => (float)$size,
+ 'not' => $not
+ );
+ }
+
+ /**
+ * Search for messages within a given message range. Only one message
+ * range can be specified per query.
+ *
+ * @param array $ids The list of messages to search.
+ * @param boolean $sequence By default, $ids is assumed to be UIDs. If
+ * this param is true, $ids are taken to be
+ * message sequence numbers instead.
+ * @param boolean $not If true, do a 'NOT' search of the sequence.
+ */
+ public function sequence($ids, $sequence = false, $not = false)
+ {
+ if (empty($ids)) {
+ $ids = '1:*';
+ } else {
+ $ids = Horde_Imap_Client::toSequenceString($ids);
+ }
+ $this->_search['sequence'] = array(
+ 'ids' => $ids,
+ 'not' => $not,
+ 'sequence' => $sequence
+ );
+ }
+
+ /**
+ * Search for messages within a date range. Only one internal date and
+ * one RFC 2822 date can be specified per query.
+ *
+ * @param integer $month Month (from 1-12).
+ * @param integer $day Day of month (from 1-31).
+ * @param integer $year Year (4-digit year).
+ * @param string $range Either:
+ * <pre>
+ * Horde_Imap_Client_Search_Query::DATE_BEFORE,
+ * Horde_Imap_Client_Search_Query::DATE_ON, or
+ * Horde_Imap_Client_Search_Query::DATE_SINCE.
+ * </pre>
+ * @param boolean $header If true, search using the date in the message
+ * headers. If false, search using the internal
+ * IMAP date (usually arrival time).
+ * @param boolean $not If true, do a 'NOT' search of the range.
+ */
+ public function dateSearch($month, $day, $year, $range, $header = true,
+ $not = false)
+ {
+ $type = $header ? 'header' : 'internal';
+ if (!isset($this->_search['date'])) {
+ $this->_search['date'] = array();
+ }
+ $this->_search['date'][$header ? 'header' : 'internal'] = array(
+ 'date' => date("d-M-y", mktime(0, 0, 0, $month, $day, $year)),
+ 'range' => $range,
+ 'not' => $not
+ );
+ }
+
+ /**
+ * Search for messages within a given interval. Only one interval of each
+ * type can be specified per search query. The IMAP server must support
+ * the WITHIN extension (RFC 5032) for this query to be used.
+ *
+ * @param integer $interval Seconds from the present.
+ * @param string $range Either:
+ * <pre>
+ * Horde_Imap_Client_Search_Query::INTERVAL_OLDER, or
+ * Horde_Imap_Client_Search_Query::INTERVAL_YOUNGER
+ * </pre>
+ * @param boolean $not If true, do a 'NOT' search.
+ */
+ public function intervalSearch($interval, $range, $not = false)
+ {
+ if (!isset($this->_search['within'])) {
+ $this->_search['within'] = array();
+ }
+ $this->_search['within'][$range] = array(
+ 'interval' => $interval,
+ 'not' => $not
+ );
+ }
+
+ /**
+ * AND queries - the contents of this query will be AND'ed (in its
+ * entirety) with the contents of each of the queries passed in. All
+ * AND'd queries must share the same charset as this query.
+ *
+ * @param array $queries An array of queries to AND with this one. Each
+ * query is a Horde_Imap_Client_Search_Query
+ * object.
+ */
+ public function andSearch($queries)
+ {
+ if (!isset($this->_search['and'])) {
+ $this->_search['and'] = array();
+ }
+ $this->_search['and'] = array_merge($this->_search['and'], $queries);
+ }
+
+ /**
+ * OR a query - the contents of this query will be OR'ed (in its entirety)
+ * with the contents of each of the queries passed in. All OR'd queries
+ * must share the same charset as this query. All contents of any single
+ * query will be AND'ed together.
+ *
+ * @param array $queries An array of queries to OR with this one. Each
+ * query is a Horde_Imap_Client_Search_Query
+ * object.
+ */
+ public function orSearch($queries)
+ {
+ if (!isset($this->_search['or'])) {
+ $this->_search['or'] = array();
+ }
+ $this->_search['or'] = array_merge($this->_search['or'], $queries);
+ }
+
+ /**
+ * Search for messages modified since a specific moment. The IMAP server
+ * must support the CONDSTORE extension (RFC 4551) for this query to be
+ * used.
+ *
+ * @param integer $value The mod-sequence value.
+ * @param string $name The entry-name string.
+ * @param string $type Either 'shared', 'priv', or 'all'. Defaults to
+ * 'all'
+ * @param boolean $not If true, do a 'NOT' search.
+ */
+ public function modseq($value, $name = null, $type = null, $not = false)
+ {
+ if (!is_null($type)) {
+ $type = strtolower($type);
+ if (!in_array($type, array('shared', 'priv', 'all'))) {
+ $type = 'all';
+ }
+ }
+
+ $this->_search['modseq'] = array(
+ 'value' => $value,
+ 'name' => $name,
+ 'not' => $not,
+ 'type' => (!is_null($name) && is_null($type)) ? 'all' : $type
+ );
+ }
+
+ /**
+ * Use the results from the previous SEARCH command. The IMAP server must
+ * support the SEARCHRES extension (RFC 5032) for this query to be used.
+ *
+ * @param boolean $not If true, don't match the previous query.
+ */
+ public function previousSearch($not = false)
+ {
+ $this->_search['prevsearch'] = $not;
+ }
+
+}
switch ($method) {
case 'CRAM-MD5':
// RFC 2195
+ if (class_exists('Auth_SASL')) {
+ throw new Horde_Imap_Client_Exception('The Auth_SASL package is required for CRAM-MD5 authentication');
+ }
$auth_sasl = Auth_SASL::factory('crammd5');
$response = base64_encode($auth_sasl->getResponse($this->_params['username'], $this->_params['password'], base64_decode($ob['line'])));
$this->_sendLine($response, array('debug' => '[CRAM-MD5 Response]', 'notag' => true));
break;
case 'DIGEST-MD5':
+ if (class_exists('Auth_SASL')) {
+ throw new Horde_Imap_Client_Exception('The Auth_SASL package is required for DIGEST-MD5 authentication');
+ }
$auth_sasl = Auth_SASL::factory('digestmd5');
$response = base64_encode($auth_sasl->getResponse($this->_params['username'], $this->_params['password'], base64_decode($ob['line']), $this->_params['hostspec'], 'imap'));
$ob = $this->_sendLine($response, array('debug' => '[DIGEST-MD5 Response]', 'noparse' => true, 'notag' => true));
--- /dev/null
+<?php
+/**
+ * A class allowing easy access to threaded sort results from
+ * Horde_Imap_Client::thread().
+ */
+class Horde_Imap_Client_Thread
+{
+ /**
+ * Internal thread data structure.
+ *
+ * @var array
+ */
+ protected $_thread = array();
+
+ /**
+ * The index type.
+ *
+ * @var string
+ */
+ protected $_type;
+
+ /**
+ * Constructor.
+ *
+ * @param array $data The data as returned by
+ * Horde_Imap_Client_Base::_thread().
+ * @param string $type Either 'uid' or 'sequence'.
+ */
+ function __construct($data, $type)
+ {
+ $this->_thread = $data;
+ $this->_type = $type;
+ }
+
+ /**
+ * Return the raw thread data array.
+ *
+ * @return array See Horde_Imap_Client_Base::_thread().
+ */
+ public function getRawData()
+ {
+ return $this->_thread;
+ }
+
+ /**
+ * Gets the indention level for an index.
+ *
+ * @param integer $index The index.
+ *
+ * @return mixed Returns the thread indent level if $index found.
+ * Returns false on failure.
+ */
+ public function getThreadIndent($index)
+ {
+ return isset($this->_thread[$index]['level'])
+ ? $this->_thread[$index]['level']
+ : false;
+ }
+
+ /**
+ * Gets the base thread index for an index.
+ *
+ * @param integer $index The index.
+ *
+ * @return mixed Returns the base index if $index is part of a thread.
+ * Returns false on failure.
+ */
+ public function getThreadBase($index)
+ {
+ return !empty($this->_thread[$index]['base'])
+ ? $this->_thread[$index]['base']
+ : false;
+ }
+
+ /**
+ * Is this index the last in the current level?
+ *
+ * @param integer $index The index.
+ *
+ * @return boolean Returns true if $index is the last element in the
+ * current thread level.
+ * Returns false if not, or on failure.
+ */
+ public function lastInLevel($index)
+ {
+ return !empty($this->_thread[$index]['last'])
+ ? $this->_thread[$index]['last']
+ : false;
+ }
+
+ /**
+ * Return the sorted list of messages indices.
+ *
+ * @param boolean $new True for newest first, false for oldest first.
+ *
+ * @return array The sorted list of messages.
+ */
+ public function messageList($new)
+ {
+ return ($new) ? array_reverse(array_keys($this->_thread)) : array_keys($this->_thread);
+ }
+
+ /**
+ * Returns the list of messages in the current thread.
+ *
+ * @param integer $index The index of the current message.
+ *
+ * @return array A list of message indices.
+ */
+ public function getThread($index)
+ {
+ /* Find the beginning of the thread. */
+ if (($begin = $this->getThreadBase($index)) === false) {
+ return array($index);
+ }
+
+ /* Work forward from the first thread element to find the end of the
+ * thread. */
+ $in_thread = false;
+ $thread_list = array();
+ reset($this->_thread);
+ while (list($k, $v) = each($this->_thread)) {
+ if ($k == $begin) {
+ $in_thread = true;
+ } elseif ($in_thread && ($v['base'] != $begin)) {
+ break;
+ }
+
+ if ($in_thread) {
+ $thread_list[] = $k;
+ }
+ }
+
+ return $thread_list;
+ }
+
+}