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,
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) {
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:
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'
);
// 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;
+ }
+ }
}
}
$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();
$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:
}
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. */
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);
* 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');
}