From: Michael M Slusarz Date: Fri, 26 Mar 2010 22:19:05 +0000 (-0600) Subject: Ticket #8936: Add sequence sorting to search(). X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=c5d8fa67cd3937f6e596a7e722c7612074332094;p=horde.git Ticket #8936: Add sequence sorting to search(). --- diff --git a/framework/Imap_Client/lib/Horde/Imap/Client.php b/framework/Imap_Client/lib/Horde/Imap/Client.php index 968b385e2..00a212df2 100644 --- a/framework/Imap_Client/lib/Horde/Imap/Client.php +++ b/framework/Imap_Client/lib/Horde/Imap/Client.php @@ -118,6 +118,9 @@ class Horde_Imap_Client /* Sort criteria defined in draft-ietf-morg-sortdisplay-02 */ const SORT_DISPLAYFROM = 10; const SORT_DISPLAYTO = 11; + /* SORT_SEQUENCE does a simple numerical sort on the returned + * UIDs/sequence numbers. */ + const SORT_SEQUENCE = 12; const SORT_RESULTS_COUNT = 1; const SORT_RESULTS_MATCH = 2; diff --git a/framework/Imap_Client/lib/Horde/Imap/Client/Base.php b/framework/Imap_Client/lib/Horde/Imap/Client/Base.php index 56cb701fd..58088c65a 100644 --- a/framework/Imap_Client/lib/Horde/Imap/Client/Base.php +++ b/framework/Imap_Client/lib/Horde/Imap/Client/Base.php @@ -1364,6 +1364,7 @@ abstract class Horde_Imap_Client_Base * Horde_Imap_Client::SORT_CC * Horde_Imap_Client::SORT_DATE * Horde_Imap_Client::SORT_FROM + * Horde_Imap_Client::SORT_SEQUENCE * Horde_Imap_Client::SORT_SIZE * Horde_Imap_Client::SORT_SUBJECT * Horde_Imap_Client::SORT_TO @@ -1378,7 +1379,7 @@ abstract class Horde_Imap_Client_Base * entire list to be sorted in reverse order, use the 'reverse' * option instead. If this option is set, the 'results' option * is ignored. - * DEFAULT: Arrival sort (Horde_Imap_Client::SORT_ARRIVAL) + * DEFAULT: None. * * * @return array An array with the following keys: @@ -2803,7 +2804,7 @@ abstract class Horde_Imap_Client_Base $search = new Horde_Imap_Client_Search_Query(); $search->sequence($ids, $seq); } - $res = $this->search($this->_selected, $search, array('sort' => array(Horde_Imap_Client::SORT_ARRIVAL))); + $res = $this->search($this->_selected, $search, array('sort' => array(Horde_Imap_Client::SORT_SEQUENCE))); $ret = array('uids' => $res['sort']); if ($seq) { if (!$res['count']) { diff --git a/framework/Imap_Client/lib/Horde/Imap/Client/Cclient.php b/framework/Imap_Client/lib/Horde/Imap/Client/Cclient.php index dfa46d3aa..186f4dc39 100644 --- a/framework/Imap_Client/lib/Horde/Imap/Client/Cclient.php +++ b/framework/Imap_Client/lib/Horde/Imap/Client/Cclient.php @@ -807,9 +807,15 @@ class Horde_Imap_Client_Cclient extends Horde_Imap_Client_Base return $this->_getSocket()->search($this->_selected, $query, $options); } - $old_error = error_reporting(0); - if (empty($options['sort'])) { - $res = imap_search($this->_stream, $options['_query']['query'], empty($options['sequence']) ? SE_UID : 0, $options['_query']['charset']); + $sort = empty($options['sort']) + ? null + : reset($options['sort']); + + if (!$sort || ($sort == Horde_Imap_Client::SORT_SEQUENCE)) { + $res = @imap_search($this->_stream, $options['_query']['query'], empty($options['sequence']) ? SE_UID : 0, $options['_query']['charset']); + if ($sort && ($res !== false)) { + sort($res, SORT_NUMERIC); + } } else { $sort_criteria = array( Horde_Imap_Client::SORT_ARRIVAL => SORTARRIVAL, @@ -821,10 +827,9 @@ class Horde_Imap_Client_Cclient extends Horde_Imap_Client_Base Horde_Imap_Client::SORT_TO => SORTTO ); - $res = imap_sort($this->_stream, $sort_criteria[reset($options['sort'])], 0, empty($options['sequence']) ? SE_UID : 0, $options['_query']['query'], $options['_query']['charset']); + $res = @imap_sort($this->_stream, $sort_criteria[$sort], 0, empty($options['sequence']) ? SE_UID : 0, $options['_query']['query'], $options['_query']['charset']); } $res = ($res === false) ? array() : $res; - error_reporting($old_error); $ret = array(); foreach ($options['results'] as $val) { @@ -834,7 +839,7 @@ class Horde_Imap_Client_Cclient extends Horde_Imap_Client_Base break; case Horde_Imap_Client::SORT_RESULTS_MATCH: - $ret[empty($options['sort']) ? 'match' : 'sort'] = $res; + $ret[$sort ? 'sort' : 'match'] = $res; break; case Horde_Imap_Client::SORT_RESULTS_MAX: diff --git a/framework/Imap_Client/lib/Horde/Imap/Client/Socket.php b/framework/Imap_Client/lib/Horde/Imap/Client/Socket.php index 430b7691d..648362a22 100644 --- a/framework/Imap_Client/lib/Horde/Imap/Client/Socket.php +++ b/framework/Imap_Client/lib/Horde/Imap/Client/Socket.php @@ -1641,6 +1641,9 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base Horde_Imap_Client::SORT_DISPLAYTO => 'DISPLAYTO', Horde_Imap_Client::SORT_FROM => 'FROM', Horde_Imap_Client::SORT_REVERSE => 'REVERSE', + // This is a bogus entry to allow the sort options check to + // correctly work below. + Horde_Imap_Client::SORT_SEQUENCE => 'SEQUENCE', Horde_Imap_Client::SORT_SIZE => 'SIZE', Horde_Imap_Client::SORT_SUBJECT => 'SUBJECT', Horde_Imap_Client::SORT_TO => 'TO' @@ -1655,22 +1658,40 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base ); // Check if the server supports server-side sorting (RFC 5256). - $esearch = $server_sort = $return_sort = false; + $esearch = $return_sort = $server_seq_sort = $server_sort = false; if (!empty($options['sort'])) { - $return_sort = true; - $server_sort = $this->queryCapability('SORT'); - - /* Make sure sort options are correct. If not, default to ARRIVAL + /* Make sure sort options are correct. If not, default to no * sort. */ if (count(array_intersect($options['sort'], array_keys($sort_criteria))) === 0) { - $options['sort'] = array(Horde_Imap_Client::SORT_ARRIVAL); - } + unset($options['sort']); + } else { + $return_sort = true; + + $server_sort = + $this->queryCapability('SORT') && + /* Make sure server supports DISPLAYFROM & DISPLAYTO. */ + !((in_array(Horde_Imap_Client::SORT_DISPLAYFROM, $options['sort']) || + in_array(Horde_Imap_Client::SORT_DISPLAYTO, $options['sort'])) && + (!is_array($server_sort) || !in_array('DISPLAY', $server_sort))); + + /* If doing a sequence sort, need to do this on the client + * side. */ + if ($server_sort && + in_array(Horde_Imap_Client::SORT_SEQUENCE, $options['sort'])) { + $server_sort = false; + + /* Optimization: If doing only a sequence sort, just do a + * simple search and sort UIDs/sequences on client side. */ + switch (count($options['sort'])) { + case 1: + $server_seq_sort = true; + break; - /* Make sure server supports DISPLAYFROM & DISPLAYTO. */ - if ((in_array(Horde_Imap_Client::SORT_DISPLAYFROM, $options['sort']) || - in_array(Horde_Imap_Client::SORT_DISPLAYTO, $options['sort'])) && - (!is_array($server_sort) || !in_array('DISPLAY', $server_sort))) { - $server_sort = false; + case 2: + $server_seq_sort = (reset($options['sort']) == Horde_Imap_Client::SORT_REVERSE); + break; + } + } } } @@ -1724,7 +1745,14 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base $this->_sendLine($cmd . $options['_query']['charset'] . ' ' . $options['_query']['query']); if ($return_sort && !$server_sort) { - $sr = array_values($this->_clientSort($sr, $options)); + if ($server_seq_sort) { + sort($sr, SORT_NUMERIC); + if (reset($options['sort']) == Horde_Imap_Client::SORT_REVERSE) { + $sr = array_reverse($sr); + } + } else { + $sr = array_values($this->_clientSort($sr, $options)); + } } $ret = array(); @@ -1840,9 +1868,14 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base $criteria = array(); foreach ($opts['sort'] as $val) { switch ($val) { + case Horde_Imap_Client::SORT_ARRIVAL: + $criteria[Horde_Imap_Client::FETCH_DATE] = true; + break; + case Horde_Imap_Client::SORT_DATE: $criteria[Horde_Imap_Client::FETCH_DATE] = true; - // Fall through + $criteria[Horde_Imap_Client::FETCH_ENVELOPE] = true; + break; case Horde_Imap_Client::SORT_CC: case Horde_Imap_Client::SORT_FROM: @@ -1884,7 +1917,7 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base } switch ($val) { - case Horde_Imap_Client::SORT_ARRIVAL: + case Horde_Imap_Client::SORT_SEQUENCE: /* There is no requirement that IDs be returned in * sequence order (see RFC 4549 [4.3.1]). So we must sort * ourselves. */ @@ -1928,6 +1961,11 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base asort($sorted, SORT_LOCALE_STRING); break; + case Horde_Imap_Client::SORT_ARRIVAL: + $sorted = $this->_getSentDates($fetch_res, $slice, true); + asort($sorted, SORT_NUMERIC); + break; + case Horde_Imap_Client::SORT_DATE: // Date sorting rules in RFC 5256 [2.2] $sorted = $this->_getSentDates($fetch_res, $slice); @@ -1986,24 +2024,24 @@ class Horde_Imap_Client_Socket extends Horde_Imap_Client_Base * Get the sent dates for purposes of SORT/THREAD sorting under RFC 5256 * [2.2]. * - * @param array $data Data returned from fetch() that includes both the - * 'envelope' and 'date' keys. - * @param array $ids The IDs to process. + * @param array $data Data returned from fetch() that includes both + * the 'envelope' and 'date' keys. + * @param array $ids The IDs to process. + * @param boolean $internal Only use internal date? * * @return array A mapping of IDs -> UNIX timestamps. */ - protected function _getSentDates($data, $ids) + protected function _getSentDates($data, $ids, $internal = false) { $dates = array(); $tz = new DateTimeZone('UTC'); foreach ($ids as $num) { - if (empty($data[$num]['envelope']['date'])) { - $dt = $data[$num]['date']; - $dt->setTimezone($tz); - } else { - $dt = new DateTime($data[$num]['envelope']['date'], $tz); - } + $dt = ($internal || empty($data[$num]['envelope']['date'])) + // RFC 5256 [3] & 3501 [6.4.4]: disregard timezone when + // using internaldate. + ? $data[$num]['date'] + : new DateTime($data[$num]['envelope']['date'], $tz); $dates[$num] = $dt->format('U'); } diff --git a/framework/Imap_Client/lib/Horde/Imap/Client/Socket/Pop3.php b/framework/Imap_Client/lib/Horde/Imap/Client/Socket/Pop3.php index 40fba7e1c..732b96918 100644 --- a/framework/Imap_Client/lib/Horde/Imap/Client/Socket/Pop3.php +++ b/framework/Imap_Client/lib/Horde/Imap/Client/Socket/Pop3.php @@ -645,13 +645,15 @@ class Horde_Imap_Client_Socket_Pop3 extends Horde_Imap_Client_Base */ protected function _search($query, $options) { - // Only support a single query: an ALL search sorted by arrival. + $sort = empty($options['sort']) + ? null + : reset($options['sort']); + + // Only support a single query: an ALL search sorted by sequence. if (($options['_query']['query'] != 'ALL') || - (!empty($options['sort']) && + ($sort && ((count($options['sort']) > 1) || - in_array(Horde_Imap_Client::SORT_ARRIVAL, $options['sort']) || - in_array(Horde_Imap_Client::SORT_DISPLAYFROM, $options['sort']) || - in_array(Horde_Imap_Client::SORT_DISPLAYTO, $options['sort'])))) { + ($sort != Horde_Imap_Client::SORT_SEQUENCE)))) { throw new Horde_Imap_Client_Exception('Server search not supported on POP3 server.', Horde_Imap_Client_Exception::POP3_NOTSUPPORTED); } diff --git a/framework/Imap_Client/package.xml b/framework/Imap_Client/package.xml index b7d5b750a..7dcb47000 100644 --- a/framework/Imap_Client/package.xml +++ b/framework/Imap_Client/package.xml @@ -31,7 +31,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> alpha LGPL - * Add ability to disable server capabilities. + * Add sequence sort (numeric sort by UID/sequence number). + * Add ability to disable server capabilities. * Add Horde_Imap_Client_Base::parseCacheId(). * Added STATUS_LASTMODSEQ and STATUS_LASTMODSEQUIDS status() entries. * Add support for LIST-STATUS (draft-ietf-morg-status-in-list-01).