build() public method

Builds an IMAP4rev1 compliant search string.
public build ( Horde_Imap_Client_Base $exts = [] ) : array
$exts Horde_Imap_Client_Base The server object this query will be run on (@since 2.24.0), a Horde_Imap_Client_Data_Capability object (@since 2.24.0), or the list of extensions present on the server (@deprecated). If 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.
Beispiel #1
0
 public function testHeaderTextUtf8Query()
 {
     $ob = new Horde_Imap_Client_Search_Query();
     $ob->headerText('Foo', 'EëE');
     try {
         $ob->build();
         $this->fail();
     } catch (Horde_Imap_Client_Data_Format_Exception $e) {
         // Expected
     }
     $ob->charset('UTF-8', false);
     $this->assertNotEmpty($ob->build());
 }
Beispiel #2
0
 /**
  * Search a mailbox.
  *
  * @param mixed $mailbox                         The mailbox to search.
  *                                               Either a
  *                                               Horde_Imap_Client_Mailbox
  *                                               object or a string
  *                                               (UTF-8).
  * @param Horde_Imap_Client_Search_Query $query  The search query.
  *                                               Defaults to an ALL
  *                                               search.
  * @param array $options                         Additional options:
  * <pre>
  *   - nocache: (boolean) Don't cache the results.
  *              DEFAULT: false (results cached, if possible)
  *   - partial: (mixed) The range of results to return (message sequence
  *              numbers) Only a single range is supported (represented by
  *              the minimum and maximum values contained in the range
  *              given).
  *              DEFAULT: All messages are returned.
  *   - results: (array) The data to return. Consists of zero or more of
  *              the following flags:
  *     - Horde_Imap_Client::SEARCH_RESULTS_COUNT
  *     - Horde_Imap_Client::SEARCH_RESULTS_MATCH (DEFAULT)
  *     - Horde_Imap_Client::SEARCH_RESULTS_MAX
  *     - Horde_Imap_Client::SEARCH_RESULTS_MIN
  *     - Horde_Imap_Client::SEARCH_RESULTS_SAVE
  *     - Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY
  *   - sequence: (boolean) If true, returns an array of sequence numbers.
  *               DEFAULT: Returns an array of UIDs
  *   - sort: (array) Sort the returned list of messages. Multiple sort
  *           criteria can be specified. Any sort criteria can be sorted in
  *           reverse order (instead of the default ascending order) by
  *           adding a Horde_Imap_Client::SORT_REVERSE element to the array
  *           directly before adding the sort element. The following sort
  *           criteria are available:
  *     - Horde_Imap_Client::SORT_ARRIVAL
  *     - Horde_Imap_Client::SORT_CC
  *     - Horde_Imap_Client::SORT_DATE
  *     - Horde_Imap_Client::SORT_DISPLAYFROM
  *       On servers that don't support SORT=DISPLAY, this criteria will
  *       fallback to doing client-side sorting.
  *     - Horde_Imap_Client::SORT_DISPLAYFROM_FALLBACK
  *       On servers that don't support SORT=DISPLAY, this criteria will
  *       fallback to Horde_Imap_Client::SORT_FROM [since 2.4.0].
  *     - Horde_Imap_Client::SORT_DISPLAYTO
  *       On servers that don't support SORT=DISPLAY, this criteria will
  *       fallback to doing client-side sorting.
  *     - Horde_Imap_Client::SORT_DISPLAYTO_FALLBACK
  *       On servers that don't support SORT=DISPLAY, this criteria will
  *       fallback to Horde_Imap_Client::SORT_TO [since 2.4.0].
  *     - 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
  *
  *     [On servers that support SEARCH=FUZZY, this criteria is also
  *     available:]
  *     - Horde_Imap_Client::SORT_RELEVANCY
  * </pre>
  *
  * @return array  An array with the following keys:
  * <pre>
  *   - count: (integer) The number of messages that match the search
  *            criteria. Always returned.
  *   - match: (Horde_Imap_Client_Ids) The IDs that match $criteria, sorted
  *            if the 'sort' modifier was set. Returned if
  *            Horde_Imap_Client::SEARCH_RESULTS_MATCH is set.
  *   - max: (integer) The UID (default) or message sequence number (if
  *          'sequence' is true) of the highest message that satisifies
  *          $criteria. Returns null if no matches found. Returned if
  *          Horde_Imap_Client::SEARCH_RESULTS_MAX is set.
  *   - min: (integer) The UID (default) or message sequence number (if
  *          'sequence' is true) of the lowest message that satisifies
  *          $criteria. Returns null if no matches found. Returned if
  *          Horde_Imap_Client::SEARCH_RESULTS_MIN is set.
  *   - modseq: (integer) The highest mod-sequence for all messages being
  *            returned. Returned if 'sort' is false, the search query
  *            includes a MODSEQ command, and the server supports the
  *            CONDSTORE IMAP extension.
  *   - relevancy: (array) The list of relevancy scores. Returned if
  *                Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY is set and
  *                the server supports FUZZY search matching.
  *   - save: (boolean) Whether the search results were saved. Returned if
  *           Horde_Imap_Client::SEARCH_RESULTS_SAVE is set.
  * </pre>
  *
  * @throws Horde_Imap_Client_Exception
  */
 public function search($mailbox, $query = null, array $options = array())
 {
     $this->login();
     if (empty($options['results'])) {
         $options['results'] = array(Horde_Imap_Client::SEARCH_RESULTS_MATCH, Horde_Imap_Client::SEARCH_RESULTS_COUNT);
     } elseif (!in_array(Horde_Imap_Client::SEARCH_RESULTS_COUNT, $options['results'])) {
         $options['results'][] = Horde_Imap_Client::SEARCH_RESULTS_COUNT;
     }
     // Default to an ALL search.
     if (is_null($query)) {
         $query = new Horde_Imap_Client_Search_Query();
     }
     // Check for SEARCHRES support.
     if (($pos = array_search(Horde_Imap_Client::SEARCH_RESULTS_SAVE, $options['results'])) !== false && !$this->_capability('SEARCHRES')) {
         unset($options['results'][$pos]);
     }
     // Check for SORT-related options.
     if (!empty($options['sort'])) {
         foreach ($options['sort'] as $key => $val) {
             switch ($val) {
                 case Horde_Imap_Client::SORT_DISPLAYFROM_FALLBACK:
                     $options['sort'][$key] = $this->_capability('SORT', 'DISPLAY') ? Horde_Imap_Client::SORT_DISPLAYFROM : Horde_Imap_Client::SORT_FROM;
                     break;
                 case Horde_Imap_Client::SORT_DISPLAYTO_FALLBACK:
                     $options['sort'][$key] = $this->_capability('SORT', 'DISPLAY') ? Horde_Imap_Client::SORT_DISPLAYTO : Horde_Imap_Client::SORT_TO;
                     break;
             }
         }
     }
     /* Default search results. */
     $default_ret = array('count' => 0, 'match' => $this->getIdsOb(), 'max' => null, 'min' => null, 'relevancy' => array());
     /* Build search query. */
     $squery = $query->build($this);
     /* Check for query contents. If empty, this means that the query
      * object has identified that this query can NEVER return any results.
      * Immediately return now. */
     if (empty($squery['query'])) {
         return $default_ret;
     }
     // Check for supported charset.
     if (!is_null($squery['charset']) && $this->search_charset->query($squery['charset'], true) === false) {
         foreach ($this->search_charset->charsets as $val) {
             try {
                 $new_query = clone $query;
                 $new_query->charset($val);
                 break;
             } catch (Horde_Imap_Client_Exception_SearchCharset $e) {
                 unset($new_query);
             }
         }
         if (!isset($new_query)) {
             throw $e;
         }
         $query = $new_query;
         $squery = $query->build($this);
     }
     // Store query in $options array to pass to child method.
     $options['_query'] = $squery;
     /* RFC 6203: MUST NOT request relevancy results if we are not using
      * FUZZY searching. */
     if (in_array(Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY, $options['results']) && !in_array('SEARCH=FUZZY', $squery['exts_used'])) {
         throw new InvalidArgumentException('Cannot specify RELEVANCY results if not doing a FUZZY search.');
     }
     /* Check for partial matching. */
     if (!empty($options['partial'])) {
         $pids = $this->getIdsOb($options['partial'], true)->range_string;
         if (!strlen($pids)) {
             throw new InvalidArgumentException('Cannot specify empty sequence range for a PARTIAL search.');
         }
         if (strpos($pids, ':') === false) {
             $pids .= ':' . $pids;
         }
         $options['partial'] = $pids;
     }
     /* Optimization - if query is just for a count of either RECENT or
      * ALL messages, we can send status information instead. Can't
      * optimize with unseen queries because we may cause an infinite loop
      * between here and the status() call. */
     if (count($options['results']) === 1 && reset($options['results']) == Horde_Imap_Client::SEARCH_RESULTS_COUNT) {
         switch ($squery['query']) {
             case 'ALL':
                 $ret = $this->status($mailbox, Horde_Imap_Client::STATUS_MESSAGES);
                 return array('count' => $ret['messages']);
             case 'RECENT':
                 $ret = $this->status($mailbox, Horde_Imap_Client::STATUS_RECENT);
                 return array('count' => $ret['recent']);
         }
     }
     $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO);
     /* Take advantage of search result caching.  If CONDSTORE available,
      * we can cache all queries and invalidate the cache when the MODSEQ
      * changes. If CONDSTORE not available, we can only store queries
      * that don't involve flags. We store results by hashing the options
      * array. */
     $cache = null;
     if (empty($options['nocache']) && $this->_initCache(true) && ($this->_capability()->isEnabled('CONDSTORE') || !$query->flagSearch())) {
         $cache = $this->_getSearchCache('search', $options);
         if (isset($cache['data'])) {
             if (isset($cache['data']['match'])) {
                 $cache['data']['match'] = $this->getIdsOb($cache['data']['match']);
             }
             return $cache['data'];
         }
     }
     /* Optimization: Catch when there are no messages in a mailbox. */
     $status_res = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES | Horde_Imap_Client::STATUS_HIGHESTMODSEQ);
     if ($status_res['messages'] || in_array(Horde_Imap_Client::SEARCH_RESULTS_SAVE, $options['results'])) {
         /* RFC 7162 [3.1.2.2] - trying to do a MODSEQ SEARCH on a mailbox
          * that doesn't support it will return BAD. */
         if (in_array('CONDSTORE', $squery['exts']) && !$this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_HIGHESTMODSEQ)) {
             throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Mailbox does not support mod-sequences."), Horde_Imap_Client_Exception::MBOXNOMODSEQ);
         }
         $ret = $this->_search($query, $options);
     } else {
         $ret = $default_ret;
         if (isset($status_res['highestmodseq'])) {
             $ret['modseq'] = $status_res['highestmodseq'];
         }
     }
     if ($cache) {
         $save = $ret;
         if (isset($save['match'])) {
             $save['match'] = strval($ret['match']);
         }
         $this->_setSearchCache($save, $cache);
     }
     return $ret;
 }
Beispiel #3
0
 /**
  * Search a mailbox.
  *
  * @param mixed $mailbox                         The mailbox to search.
  *                                               Either a
  *                                               Horde_Imap_Client_Mailbox
  *                                               object or a string
  *                                               (UTF-8).
  * @param Horde_Imap_Client_Search_Query $query  The search query.
  *                                               Defaults to an ALL
  *                                               search.
  * @param array $options                         Additional options:
  * <ul>
  *  <li>
  *   nocache: (boolean) Don't cache the results.
  *   DEFAULT: false (results cached, if possible)
  *  </li>
  *  <li>
  *   partial: (mixed) The range of results to return (message sequence
  *   numbers).
  *   DEFAULT: All messages are returned.
  *  </li>
  *  <li>
  *   results: (array) The data to return. Consists of zero or more of
  *   the following flags:
  *   <ul>
  *    <li>Horde_Imap_Client::SEARCH_RESULTS_COUNT</li>
  *    <li>Horde_Imap_Client::SEARCH_RESULTS_MATCH (DEFAULT)</li>
  *    <li>Horde_Imap_Client::SEARCH_RESULTS_MAX</li>
  *    <li>Horde_Imap_Client::SEARCH_RESULTS_MIN</li>
  *    <li>
  *     Horde_Imap_Client::SEARCH_RESULTS_SAVE (This option is currently
  *     meant for internal use only)
  *    </li>
  *    <li>Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY</li>
  *   </ul>
  *  </li>
  *  <li>
  *   sequence: (boolean) If true, returns an array of sequence numbers.
  *   DEFAULT: Returns an array of UIDs
  *  </li>
  *  <li>
  *   sort: (array) Sort the returned list of messages. Multiple sort
  *   criteria can be specified. Any sort criteria can be sorted in reverse
  *   order (instead of the default ascending order) by adding a
  *   Horde_Imap_Client::SORT_REVERSE element to the array directly before
  *   adding the sort element. The following sort criteria are available:
  *   <ul>
  *    <li>Horde_Imap_Client::SORT_ARRIVAL</li>
  *    <li>Horde_Imap_Client::SORT_CC</li>
  *    <li>Horde_Imap_Client::SORT_DATE</li>
  *    <li>Horde_Imap_Client::SORT_FROM</li>
  *    <li>Horde_Imap_Client::SORT_SEQUENCE</li>
  *    <li>Horde_Imap_Client::SORT_SIZE</li>
  *    <li>Horde_Imap_Client::SORT_SUBJECT</li>
  *    <li>Horde_Imap_Client::SORT_TO</li>
  *    <li>
  *     [On servers that support SORT=DISPLAY, these criteria are also
  *     available:]
  *     <ul>
  *      <li>Horde_Imap_Client::SORT_DISPLAYFROM</li>
  *      <li>Horde_Imap_Client::SORT_DISPLAYTO</li>
  *     </ul>
  *    </li>
  *    <li>
  *     [On servers that support SEARCH=FUZZY, this criteria is also
  *     available:]
  *     <ul>
  *      <li>Horde_Imap_Client::SORT_RELEVANCY</li>
  *     </ul>
  *    </li>
  *   </ul>
  *  </li>
  * </ul>
  *
  * @return array  An array with the following keys:
  *   - count: (integer) The number of messages that match the search
  *            criteria. Always returned.
  *   - match: (Horde_Imap_Client_Ids) The IDs that match $criteria, sorted
  *            if the 'sort' modifier was set. Returned if
  *            Horde_Imap_Client::SEARCH_RESULTS_MATCH is set.
  *   - max: (integer) The UID (default) or message sequence number (if
  *          'sequence' is true) of the highest message that satisifies
  *          $criteria. Returns null if no matches found. Returned if
  *          Horde_Imap_Client::SEARCH_RESULTS_MAX is set.
  *   - min: (integer) The UID (default) or message sequence number (if
  *          'sequence' is true) of the lowest message that satisifies
  *          $criteria. Returns null if no matches found. Returned if
  *          Horde_Imap_Client::SEARCH_RESULTS_MIN is set.
  *   - modseq: (integer) The highest mod-sequence for all messages being
  *            returned. Returned if 'sort' is false, the search query
  *            includes a MODSEQ command, and the server supports the
  *            CONDSTORE IMAP extension.
  *   - relevancy: (array) The list of relevancy scores. Returned if
  *                Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY is set and
  *                the server supports FUZZY search matching.
  *   - save: (boolean) Whether the search results were saved. Returned if
  *           Horde_Imap_Client::SEARCH_RESULTS_SAVE is set.
  *
  * @throws Horde_Imap_Client_Exception
  */
 public function search($mailbox, $query = null, array $options = array())
 {
     $this->login();
     if (empty($options['results'])) {
         $options['results'] = array(Horde_Imap_Client::SEARCH_RESULTS_MATCH, Horde_Imap_Client::SEARCH_RESULTS_COUNT);
     }
     // Default to an ALL search.
     if (is_null($query)) {
         $query = new Horde_Imap_Client_Search_Query();
     }
     // Check for supported charset.
     $options['_query'] = $query->build($this->capability());
     if (!is_null($options['_query']['charset']) && array_key_exists($options['_query']['charset'], $this->_init['s_charset']) && !$this->_init['s_charset'][$options['_query']['charset']]) {
         foreach (array_merge(array_keys(array_filter($this->_init['s_charset'])), array('US-ASCII')) as $val) {
             try {
                 $new_query = clone $query;
                 $new_query->charset($val);
                 break;
             } catch (Horde_Imap_Client_Exception_SearchCharset $e) {
                 unset($new_query);
             }
         }
         if (!isset($new_query)) {
             throw $e;
         }
         $query = $new_query;
         $options['_query'] = $query->build($this->capability());
     }
     /* RFC 6203: MUST NOT request relevancy results if we are not using
      * FUZZY searching. */
     if (in_array(Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY, $options['results']) && !in_array('SEARCH=FUZZY', $options['_query']['exts_used'])) {
         throw new InvalidArgumentException('Cannot specify RELEVANCY results if not doing a FUZZY search.');
     }
     /* Optimization - if query is just for a count of either RECENT or
      * ALL messages, we can send status information instead. Can't
      * optimize with unseen queries because we may cause an infinite loop
      * between here and the status() call. */
     if (count($options['results']) == 1 && reset($options['results']) == Horde_Imap_Client::SEARCH_RESULTS_COUNT) {
         switch ($options['_query']['query']) {
             case 'ALL':
                 $ret = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES);
                 return array('count' => $ret['messages']);
             case 'RECENT':
                 $ret = $this->status($this->_selected, Horde_Imap_Client::STATUS_RECENT);
                 return array('count' => $ret['recent']);
         }
     }
     $this->openMailbox($mailbox, Horde_Imap_Client::OPEN_AUTO);
     /* Take advantage of search result caching.  If CONDSTORE available,
      * we can cache all queries and invalidate the cache when the MODSEQ
      * changes. If CONDSTORE not available, we can only store queries
      * that don't involve flags. We store results by hashing the options
      * array - the generated query is already added to '_query' key
      * above. */
     $cache = null;
     if (empty($options['nocache']) && $this->_initCache(true) && (isset($this->_init['enabled']['CONDSTORE']) || !$query->flagSearch())) {
         $cache = $this->_getSearchCache('search', $this->_selected, $options);
         if (is_array($cache)) {
             if (isset($cache['data']['match'])) {
                 $cache['data']['match'] = $this->getIdsOb($cache['data']['match']);
             }
             return $cache['data'];
         }
     }
     /* Optimization: Catch when there are no messages in a mailbox. */
     $status_res = $this->status($this->_selected, Horde_Imap_Client::STATUS_MESSAGES | Horde_Imap_Client::STATUS_HIGHESTMODSEQ);
     if ($status_res['messages'] || in_array(Horde_Imap_Client::SEARCH_RESULTS_SAVE, $options['results'])) {
         $ret = $this->_search($query, $options);
     } else {
         $ret = array('count' => 0);
         foreach ($options['results'] as $val) {
             switch ($val) {
                 case Horde_Imap_Client::SEARCH_RESULTS_MATCH:
                     $ret['match'] = $this->getIdsOb();
                     break;
                 case Horde_Imap_Client::SEARCH_RESULTS_MAX:
                     $ret['max'] = null;
                     break;
                 case Horde_Imap_Client::SEARCH_RESULTS_MIN:
                     $ret['min'] = null;
                     break;
                 case Horde_Imap_Client::SEARCH_RESULTS_MIN:
                     if (isset($status_res['highestmodseq'])) {
                         $ret['modseq'] = $status_res['highestmodseq'];
                     }
                     break;
                 case Horde_Imap_Client::SEARCH_RESULTS_RELEVANCY:
                     $ret['relevancy'] = array();
                     break;
             }
         }
     }
     if ($cache) {
         $save = $ret;
         if (isset($save['match'])) {
             $save['match'] = strval($ret['match']);
         }
         $this->_setSearchCache($save, $cache);
     }
     return $ret;
 }