Overview

Packages

  • Imap
    • Client

Classes

  • Horde_Imap_Client
  • Horde_Imap_Client_Auth_DigestMD5
  • Horde_Imap_Client_Base
  • Horde_Imap_Client_Cache
  • Horde_Imap_Client_Data_Acl
  • Horde_Imap_Client_Data_AclCommon
  • Horde_Imap_Client_Data_AclNegative
  • Horde_Imap_Client_Data_AclRights
  • Horde_Imap_Client_Data_Envelope
  • Horde_Imap_Client_Data_Fetch
  • Horde_Imap_Client_Data_Fetch_Pop3
  • Horde_Imap_Client_Data_Thread
  • Horde_Imap_Client_DateTime
  • Horde_Imap_Client_Exception
  • Horde_Imap_Client_Exception_NoSupportExtension
  • Horde_Imap_Client_Fetch_Query
  • Horde_Imap_Client_Ids
  • Horde_Imap_Client_Ids_Pop3
  • Horde_Imap_Client_Mailbox
  • Horde_Imap_Client_Search_Query
  • Horde_Imap_Client_Socket
  • Horde_Imap_Client_Socket_Pop3
  • Horde_Imap_Client_Sort
  • Horde_Imap_Client_Translation
  • Horde_Imap_Client_Utf7imap
  • Horde_Imap_Client_Utils
  • Horde_Imap_Client_Utils_Pop3
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Abstraction of the IMAP4rev1 search criteria (see RFC 3501 [6.4.4]).
  4:  * Allows translation between abstracted search criteria and a generated IMAP
  5:  * search criteria string suitable for sending to a remote IMAP server.
  6:  *
  7:  * Copyright 2008-2012 Horde LLC (http://www.horde.org/)
  8:  *
  9:  * See the enclosed file COPYING for license information (LGPL). If you
 10:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 11:  *
 12:  * @author   Michael Slusarz <slusarz@horde.org>
 13:  * @category Horde
 14:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 15:  * @package  Imap_Client
 16:  */
 17: class Horde_Imap_Client_Search_Query implements Serializable
 18: {
 19:     /* Serialized version. */
 20:     const VERSION = 3;
 21: 
 22:     /* Constants for dateSearch() */
 23:     const DATE_BEFORE = 'BEFORE';
 24:     const DATE_ON = 'ON';
 25:     const DATE_SINCE = 'SINCE';
 26: 
 27:     /* Constants for intervalSearch() */
 28:     const INTERVAL_OLDER = 'OLDER';
 29:     const INTERVAL_YOUNGER = 'YOUNGER';
 30: 
 31:     /**
 32:      * The charset of the search strings.  All text strings must be in
 33:      * this charset. By default, this is 'US-ASCII' (see RFC 3501 [6.4.4]).
 34:      *
 35:      * @var string
 36:      */
 37:     protected $_charset = null;
 38: 
 39:     /**
 40:      * The list of search params.
 41:      *
 42:      * @var array
 43:      */
 44:     protected $_search = array();
 45: 
 46:     /**
 47:      * Temp array used when building search string.
 48:      *
 49:      * @var array
 50:      */
 51:     protected $_temp = array();
 52: 
 53:     /**
 54:      * String representation: The IMAP search string.
 55:      */
 56:     public function __toString()
 57:     {
 58:         $utils = new Horde_Imap_Client_Utils();
 59:         $res = $this->build(null);
 60:         return trim($utils->parseCommandArray($res['query']));
 61:     }
 62: 
 63:     /**
 64:      * Sets the charset of the search text.
 65:      *
 66:      * @param string $charset     The charset to use for the search.
 67:      * @param callback $callback  A callback function to run on all text
 68:      *                            values when the charset changes.  It must
 69:      *                            accept three parameters: the text, the old
 70:      *                            charset (will be null if no charset was
 71:      *                            previously given), and the new charset. It
 72:      *                            should return the converted text value.
 73:      */
 74:     public function charset($charset, $callback = null)
 75:     {
 76:         $oldcharset = $this->_charset;
 77:         $this->_charset = strtoupper($charset);
 78:         if (is_null($callback) || ($oldcharset == $this->_charset)) {
 79:             return;
 80:         }
 81: 
 82:         foreach (array('header', 'text') as $item) {
 83:             if (isset($this->_search[$item])) {
 84:                 foreach (array_keys($this->_search[$item]) as $key) {
 85:                     $this->_search[$item][$key]['text'] = call_user_func_array($callback, array($this->_search[$item][$key]['text'], $oldcharset, $this->_charset));
 86:                 }
 87:             }
 88:         }
 89:     }
 90: 
 91:     /**
 92:      * Builds an IMAP4rev1 compliant search string.
 93:      *
 94:      * @param array $exts  The list of extensions supported by the server.
 95:      *                     This determines whether certain criteria can be
 96:      *                     used, and determines whether workarounds are used
 97:      *                     for other criteria. In the format returned by
 98:      *                     Horde_Imap_Client_Base::capability(). If this value
 99:      *                     is null, all extensions are assumed to be
100:      *                     available.
101:      *
102:      * @return array  An array with these elements:
103:      *   - charset: (string) The charset of the search string. If null, no
104:      *              text strings appear in query.
105:      *   - exts: (array) The list of IMAP extensions used to create the
106:      *           string.
107:      *   - imap4: (boolean) True if the search uses IMAP4 criteria (as opposed
108:      *            to IMAP2 search criteria).
109:      *   - query: (array) The IMAP search string.
110:      *
111:      * @throws Horde_Imap_Client_Exception_NoSupportExtension
112:      */
113:     public function build($exts = array())
114:     {
115:         $this->_temp = array(
116:             'cmds' => array(),
117:             'exts' => $exts,
118:             'exts_used' => array(),
119:             'imap4' => false
120:         );
121:         $cmds = &$this->_temp['cmds'];
122:         $charset = null;
123:         $exts_used = &$this->_temp['exts_used'];
124:         $imap4 = &$this->_temp['imap4'];
125:         $ptr = &$this->_search;
126: 
127:         if (isset($ptr['new'])) {
128:             $this->_addFuzzy(!empty($ptr['newfuzzy']));
129:             if ($ptr['new']) {
130:                 $cmds[] = 'NEW';
131:                 unset($ptr['flag']['UNSEEN']);
132:             } else {
133:                 $cmds[] = 'OLD';
134:             }
135:             unset($ptr['flag']['RECENT']);
136:         }
137: 
138:         if (!empty($ptr['flag'])) {
139:             foreach ($ptr['flag'] as $key => $val) {
140:                 if ($key == 'draft') {
141:                     // DRAFT flag was not in IMAP2
142:                     $imap4 = true;
143:                 }
144: 
145:                 $this->_addFuzzy(!empty($val['fuzzy']));
146: 
147:                 $tmp = '';
148:                 if (empty($val['set'])) {
149:                     // This is a 'NOT' search.  All system flags but \Recent
150:                     // have 'UN' equivalents.
151:                     if ($key == 'RECENT') {
152:                         $cmds[] = 'NOT';
153:                         // NOT searches were not in IMAP2
154:                         $imap4 = true;
155:                     } else {
156:                         $tmp = 'UN';
157:                     }
158:                 }
159: 
160:                 if ($val['type'] == 'keyword') {
161:                     $cmds[] = $tmp . 'KEYWORD';
162:                     $cmds[] = array('t' => Horde_Imap_Client::DATA_ATOM, 'v' => $key);
163:                 } else {
164:                     $cmds[] = $tmp . $key;
165:                 }
166:             }
167:         }
168: 
169:         if (!empty($ptr['header'])) {
170:             /* The list of 'system' headers that have a specific search
171:              * query. */
172:             $systemheaders = array(
173:                 'BCC', 'CC', 'FROM', 'SUBJECT', 'TO'
174:             );
175: 
176:             foreach ($ptr['header'] as $val) {
177:                 $this->_addFuzzy(!empty($val['fuzzy']));
178: 
179:                 if (!empty($val['not'])) {
180:                     $cmds[] = 'NOT';
181:                     // NOT searches were not in IMAP2
182:                     $imap4 = true;
183:                 }
184: 
185:                 if (in_array($val['header'], $systemheaders)) {
186:                     $cmds[] = $val['header'];
187:                 } else {
188:                     // HEADER searches were not in IMAP2
189:                     $cmds[] = 'HEADER';
190:                     $cmds[] = array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $val['header']);
191:                     $imap4 = true;
192:                 }
193:                 $cmds[] = array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => isset($val['text']) ? $val['text'] : '');
194:                 $charset = is_null($this->_charset)
195:                     ? 'US-ASCII'
196:                     : $this->_charset;
197:             }
198:         }
199: 
200:         if (!empty($ptr['text'])) {
201:             foreach ($ptr['text'] as $val) {
202:                 $this->_addFuzzy(!empty($val['fuzzy']));
203: 
204:                 if (!empty($val['not'])) {
205:                     $cmds[] = 'NOT';
206:                     // NOT searches were not in IMAP2
207:                     $imap4 = true;
208:                 }
209:                 $cmds[] = $val['type'];
210:                 $cmds[] = array('t' => Horde_Imap_Client::DATA_ASTRING, 'v' => $val['text']);
211:                 if (is_null($charset)) {
212:                     $charset = is_null($this->_charset)
213:                         ? 'US-ASCII'
214:                         : $this->_charset;
215:                 }
216:             }
217:         }
218: 
219:         if (!empty($ptr['size'])) {
220:             foreach ($ptr['size'] as $key => $val) {
221:                 $this->_addFuzzy(!empty($val['fuzzy']));
222:                 if (!empty($val['not'])) {
223:                     $cmds[] = 'NOT';
224:                 }
225:                 $cmds[] = $key;
226:                 $cmds[] = array('t' => Horde_Imap_Client::DATA_NUMBER, 'v' => $val['size']);
227:                 // LARGER/SMALLER searches were not in IMAP2
228:                 $imap4 = true;
229:             }
230:         }
231: 
232:         if (isset($ptr['ids']) &&
233:             (count($ptr['ids']['ids']) || $ptr['ids']['ids']->all)) {
234:             $this->_addFuzzy(!empty($val['fuzzy']));
235:             if (!empty($ptr['ids']['not'])) {
236:                 $cmds[] = 'NOT';
237:             }
238:             if (!$ptr['ids']['ids']->sequence) {
239:                 $cmds[] = 'UID';
240:             }
241:             $cmds[] = $ptr['ids']['ids']->all
242:                 ? '1:*'
243:                 : strval($ptr['ids']['ids']);
244: 
245:             // ID searches were not in IMAP2
246:             $imap4 = true;
247:         }
248: 
249:         if (!empty($ptr['date'])) {
250:             foreach ($ptr['date'] as $val) {
251:                 $this->_addFuzzy(!empty($val['fuzzy']));
252: 
253:                 if (!empty($val['not'])) {
254:                     $cmds[] = 'NOT';
255:                     // NOT searches were not in IMAP2
256:                     $imap4 = true;
257:                 }
258: 
259:                 if (empty($val['header'])) {
260:                     $cmds[] = $val['range'];
261:                 } else {
262:                     $cmds[] = 'SENT' . $val['range'];
263:                     // 'SENT*' searches were not in IMAP2
264:                     $imap4 = true;
265:                 }
266:                 $cmds[] = $val['date'];
267:             }
268:         }
269: 
270:         if (!empty($ptr['within'])) {
271:             if (is_null($exts) || isset($exts['WITHIN'])) {
272:                 $exts_used[] = 'WITHIN';
273:                 $imap4 = true;
274:             }
275: 
276:             foreach ($ptr['within'] as $key => $val) {
277:                 $this->_addFuzzy(!empty($val['fuzzy']));
278:                 if (!empty($val['not'])) {
279:                     $cmds[] = 'NOT';
280:                     // NOT searches were not in IMAP2
281:                     $imap4 = true;
282:                 }
283: 
284:                 if (is_null($exts) || isset($exts['WITHIN'])) {
285:                     $cmds[] = $key;
286:                     $cmds[] = array('t' => Horde_Imap_Client::DATA_NUMBER, 'v' => $val['interval']);
287:                 } else {
288:                     // This workaround is only accurate to within 1 day, due
289:                     // to limitations with the IMAP4rev1 search commands.
290:                     $date = new DateTime('now -' . $val['interval'] . ' seconds');
291:                     $cmds[] = ($key == self::INTERVAL_OLDER)
292:                         ? self::DATE_BEFORE
293:                         : self::DATE_SINCE;
294:                     $cmds[] = $date->format('d-M-Y');
295:                 }
296:             }
297:         }
298: 
299:         if (!empty($ptr['modseq'])) {
300:             if (!is_null($exts) && !isset($exts['CONDSTORE'])) {
301:                 throw new Horde_Imap_Client_Exception_NoSupportExtension('IMAP Server does not support CONDSTORE.');
302:             }
303: 
304:             $exts_used[] = 'CONDSTORE';
305:             $imap4 = true;
306: 
307:             $this->_addFuzzy(!empty($ptr['modseq']['fuzzy']));
308: 
309:             if (!empty($ptr['modseq']['not'])) {
310:                 $cmds[] = 'NOT';
311:             }
312:             $cmds[] = 'MODSEQ';
313:             if (!is_null($ptr['modseq']['name'])) {
314:                 $cmds[] = array('t' => Horde_Imap_Client::DATA_STRING, 'v' => $ptr['modseq']['name']);
315:                 $cmds[] = $ptr['modseq']['type'];
316:             }
317:             $cmds[] = array('t' => Horde_Imap_Client::DATA_NUMBER, 'v' => $ptr['modseq']['value']);
318:         }
319: 
320:         if (isset($ptr['prevsearch'])) {
321:             if (!is_null($exts) && !isset($exts['SEARCHRES'])) {
322:                 throw new Horde_Imap_Client_Exception_NoSupportExtension('IMAP Server does not support SEARCHRES.');
323:             }
324: 
325:             $exts_used[] = 'SEARCHRES';
326:             $imap4 = true;
327: 
328:             $this->_addFuzzy(!empty($ptr['prevsearchfuzzy']));
329: 
330:             if (!$ptr['prevsearch']) {
331:                 $cmds[] = 'NOT';
332:             }
333:             $cmds[] = '$';
334:         }
335: 
336:         // Add AND'ed queries
337:         if (!empty($ptr['and'])) {
338:             foreach ($ptr['and'] as $val) {
339:                 $ret = $val->build();
340:                 $cmds = array_merge($cmds, $ret['query']);
341:             }
342:         }
343: 
344:         // Add OR'ed queries
345:         if (!empty($ptr['or'])) {
346:             foreach ($ptr['or'] as $val) {
347:                 // OR queries were not in IMAP 2
348:                 $imap4 = true;
349: 
350:                 $ret = $val->build();
351: 
352:                 // First OR'd query
353:                 if (empty($cmds)) {
354:                     $cmds = array($ret['query']);
355:                 } else {
356:                     $cmds = array_merge(array(
357:                         'OR',
358:                         $ret['query']
359:                     ), $cmds);
360:                 }
361:             }
362:         }
363: 
364:         // Default search is 'ALL'
365:         if (empty($cmds)) {
366:             $cmds[] = 'ALL';
367:         }
368: 
369:         return array(
370:             'charset' => $charset,
371:             'exts' => array_keys(array_flip($exts_used)),
372:             'imap4' => $imap4,
373:             'query' => $cmds
374:         );
375:     }
376: 
377:     /**
378:      * Adds fuzzy modifier to search keys.
379:      *
380:      * @param boolean $add  Add the fuzzy modifier?
381:      *
382:      * @throws Horde_Imap_Client_Exception_NoSupport_Extension
383:      */
384:     protected function _addFuzzy($add)
385:     {
386:         if ($add) {
387:             if (!isset($this->_temp['exts']['SEARCH']) ||
388:                 !in_array('FUZZY', $this->_temp['exts']['SEARCH'])) {
389:                 throw new Horde_Imap_Client_Exception_NoSupportExtension('IMAP Server does not support SEARCH=FUZZY.');
390:             }
391:             $this->_temp['cmds'][] = 'FUZZY';
392:             $this->_temp['exts_used'][] = 'SEARCH=FUZZY';
393:             $this->_temp['imap4'] = true;
394:         }
395:     }
396: 
397:     /**
398:      * Search for a flag/keywords.
399:      *
400:      * @param string $name  The flag or keyword name.
401:      * @param boolean $set  If true, search for messages that have the flag
402:      *                      set.  If false, search for messages that do not
403:      *                      have the flag set.
404:      * @param array $opts   Additional options:
405:      *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
406:      *            MUST support RFC 6203.
407:      */
408:     public function flag($name, $set = true, array $opts = array())
409:     {
410:         $name = strtoupper(ltrim($name, '\\'));
411:         if (!isset($this->_search['flag'])) {
412:             $this->_search['flag'] = array();
413:         }
414: 
415:         /* The list of defined system flags (see RFC 3501 [2.3.2]). */
416:         $systemflags = array(
417:             'ANSWERED', 'DELETED', 'DRAFT', 'FLAGGED', 'RECENT', 'SEEN'
418:         );
419: 
420:         $this->_search['flag'][$name] = array_filter(array(
421:             'fuzzy' => !empty($opts['fuzzy']),
422:             'set' => $set,
423:             'type' => in_array($name, $systemflags) ? 'flag' : 'keyword'
424:         ));
425:     }
426: 
427:     /**
428:      * Determines if flags are a part of the search.
429:      *
430:      * @return boolean  True if search query involves flags.
431:      */
432:     public function flagSearch()
433:     {
434:         return !empty($this->_search['flag']);
435:     }
436: 
437:     /**
438:      * Search for either new messages (messages that have the '\Recent' flag
439:      * but not the '\Seen' flag) or old messages (messages that do not have
440:      * the '\Recent' flag).  If new messages are searched, this will clear
441:      * any '\Recent' or '\Unseen' flag searches.  If old messages are searched,
442:      * this will clear any '\Recent' flag search.
443:      *
444:      * @param boolean $newmsgs  If true, searches for new messages.  Else,
445:      *                          search for old messages.
446:      * @param array $opts       Additional options:
447:      *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
448:      *            MUST support RFC 6203.
449:      */
450:     public function newMsgs($newmsgs = true, array $opts = array())
451:     {
452:         $this->_search['new'] = $newmsgs;
453:         if (!empty($opts['fuzzy'])) {
454:             $this->_search['newfuzzy'] = true;
455:         }
456:     }
457: 
458:     /**
459:      * Search for text in the header of a message.
460:      *
461:      * @param string $header  The header field.
462:      * @param string $text    The search text.
463:      * @param boolean $not    If true, do a 'NOT' search of $text.
464:      * @param array $opts     Additional options:
465:      *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
466:      *            MUST support RFC 6203.
467:      */
468:     public function headerText($header, $text, $not = false,
469:                                 array $opts = array())
470:     {
471:         if (!isset($this->_search['header'])) {
472:             $this->_search['header'] = array();
473:         }
474:         $this->_search['header'][] = array_filter(array(
475:             'fuzzy' => !empty($opts['fuzzy']),
476:             'header' => strtoupper($header),
477:             'text' => $text,
478:             'not' => $not
479:         ));
480:     }
481: 
482:     /**
483:      * Search for text in either the entire message, or just the body.
484:      *
485:      * @param string $text      The search text.
486:      * @param string $bodyonly  If true, only search in the body of the
487:      *                          message. If false, also search in the headers.
488:      * @param boolean $not      If true, do a 'NOT' search of $text.
489:      * @param array $opts       Additional options:
490:      *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
491:      *            MUST support RFC 6203.
492:      */
493:     public function text($text, $bodyonly = true, $not = false,
494:                          array $opts = array())
495:     {
496:         if (!isset($this->_search['text'])) {
497:             $this->_search['text'] = array();
498:         }
499: 
500:         $this->_search['text'][] = array_filter(array(
501:             'fuzzy' => !empty($opts['fuzzy']),
502:             'not' => $not,
503:             'text' => $text,
504:             'type' => $bodyonly ? 'BODY' : 'TEXT'
505:         ));
506:     }
507: 
508:     /**
509:      * Search for messages smaller/larger than a certain size.
510:      *
511:      * @param integer $size    The size (in bytes).
512:      * @param boolean $larger  Search for messages larger than $size?
513:      * @param boolean $not     If true, do a 'NOT' search of $text.
514:      * @param array $opts      Additional options:
515:      *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
516:      *            MUST support RFC 6203.
517:      */
518:     public function size($size, $larger = false, $not = false,
519:                          array $opts = array())
520:     {
521:         if (!isset($this->_search['size'])) {
522:             $this->_search['size'] = array();
523:         }
524:         $this->_search['size'][$larger ? 'LARGER' : 'SMALLER'] = array_filter(array(
525:             'fuzzy' => !empty($opts['fuzzy']),
526:             'not' => $not,
527:             'size' => (float)$size
528:         ));
529:     }
530: 
531:     /**
532:      * Search for messages within a given ID sequence range. Only one message
533:      * range can be specified per query.
534:      *
535:      * @param Horde_Imap_Client_Ids $ids  The list of IDs to search.
536:      * @param boolean $not                If true, do a 'NOT' search of the
537:      *                                    IDs.
538:      * @param array $opts                 Additional options:
539:      *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
540:      *            MUST support RFC 6203.
541:      */
542:     public function ids(Horde_Imap_Client_Ids $ids, $not = false,
543:                         array $opts = array())
544:     {
545:         $this->_search['ids'] = array_filter(array(
546:             'fuzzy' => !empty($opts['fuzzy']),
547:             'ids' => $ids,
548:             'not' => $not
549:         ));
550:     }
551: 
552:     /**
553:      * Search for messages within a date range.
554:      *
555:      * @param mixed $date    DateTime or Horde_Date object.
556:      * @param string $range  Either:
557:      *   - Horde_Imap_Client_Search_Query::DATE_BEFORE
558:      *   - Horde_Imap_Client_Search_Query::DATE_ON
559:      *   - Horde_Imap_Client_Search_Query::DATE_SINCE
560:      * @param boolean $header  If true, search using the date in the message
561:      *                         headers. If false, search using the internal
562:      *                         IMAP date (usually arrival time).
563:      * @param boolean $not     If true, do a 'NOT' search of the range.
564:      * @param array $opts      Additional options:
565:      *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
566:      *            MUST support RFC 6203.
567:      */
568:     public function dateSearch($date, $range, $header = true, $not = false,
569:                                array $opts = array())
570:     {
571:         if (!isset($this->_search['date'])) {
572:             $this->_search['date'] = array();
573:         }
574:         $this->_search['date'][] = array_filter(array(
575:             'date' => $date->format('d-M-Y'),
576:             'fuzzy' => !empty($opts['fuzzy']),
577:             'header' => $header,
578:             'range' => $range,
579:             'not' => $not
580:         ));
581:     }
582: 
583:     /**
584:      * Search for messages within a given interval. Only one interval of each
585:      * type can be specified per search query. If the IMAP server supports
586:      * the WITHIN extension (RFC 5032), it will be used.  Otherwise, the
587:      * search query will be dynamically created using IMAP4rev1 search
588:      * terms.
589:      *
590:      * @param integer $interval  Seconds from the present.
591:      * @param string $range      Either:
592:      *   - Horde_Imap_Client_Search_Query::INTERVAL_OLDER
593:      *   - Horde_Imap_Client_Search_Query::INTERVAL_YOUNGER
594:      * @param boolean $not       If true, do a 'NOT' search.
595:      * @param array $opts        Additional options:
596:      *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
597:      *            MUST support RFC 6203.
598:      */
599:     public function intervalSearch($interval, $range, $not = false,
600:                                    array $opts = array())
601:     {
602:         if (!isset($this->_search['within'])) {
603:             $this->_search['within'] = array();
604:         }
605:         $this->_search['within'][$range] = array(
606:             'fuzzy' => !empty($opts['fuzzy']),
607:             'interval' => $interval,
608:             'not' => $not
609:         );
610:     }
611: 
612:     /**
613:      * AND queries - the contents of this query will be AND'ed (in its
614:      * entirety) with the contents of each of the queries passed in.  All
615:      * AND'd queries must share the same charset as this query.
616:      *
617:      * @param array $queries  An array of queries to AND with this one.  Each
618:      *                        query is a Horde_Imap_Client_Search_Query
619:      *                        object.
620:      */
621:     public function andSearch($queries)
622:     {
623:         if (!isset($this->_search['and'])) {
624:             $this->_search['and'] = array();
625:         }
626:         $this->_search['and'] = array_merge($this->_search['and'], $queries);
627:     }
628: 
629:     /**
630:      * OR a query - the contents of this query will be OR'ed (in its entirety)
631:      * with the contents of each of the queries passed in.  All OR'd queries
632:      * must share the same charset as this query.  All contents of any single
633:      * query will be AND'ed together.
634:      *
635:      * @param array $queries  An array of queries to OR with this one.  Each
636:      *                        query is a Horde_Imap_Client_Search_Query
637:      *                        object.
638:      */
639:     public function orSearch($queries)
640:     {
641:         if (!isset($this->_search['or'])) {
642:             $this->_search['or'] = array();
643:         }
644:         $this->_search['or'] = array_merge($this->_search['or'], $queries);
645:     }
646: 
647:     /**
648:      * Search for messages modified since a specific moment. The IMAP server
649:      * must support the CONDSTORE extension (RFC 4551) for this query to be
650:      * used.
651:      *
652:      * @param integer $value  The mod-sequence value.
653:      * @param string $name    The entry-name string.
654:      * @param string $type    Either 'shared', 'priv', or 'all'. Defaults to
655:      *                        'all'
656:      * @param boolean $not    If true, do a 'NOT' search.
657:      * @param array $opts     Additional options:
658:      *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
659:      *            MUST support RFC 6203.
660:      */
661:     public function modseq($value, $name = null, $type = null, $not = false,
662:                            array $opts = array())
663:     {
664:         if (!is_null($type)) {
665:             $type = strtolower($type);
666:             if (!in_array($type, array('shared', 'priv', 'all'))) {
667:                 $type = 'all';
668:             }
669:         }
670: 
671:         $this->_search['modseq'] = array_filter(array(
672:             'fuzzy' => !empty($opts['fuzzy']),
673:             'name' => $name,
674:             'not' => $not,
675:             'type' => (!is_null($name) && is_null($type)) ? 'all' : $type,
676:             'value' => $value
677:         ));
678:     }
679: 
680:     /**
681:      * Use the results from the previous SEARCH command. The IMAP server must
682:      * support the SEARCHRES extension (RFC 5182) for this query to be used.
683:      *
684:      * @param boolean $not  If true, don't match the previous query.
685:      * @param array $opts   Additional options:
686:      *   - fuzzy: (boolean) If true, perform a fuzzy search. The IMAP server
687:      *            MUST support RFC 6203.
688:      */
689:     public function previousSearch($not = false, array $opts = array())
690:     {
691:         $this->_search['prevsearch'] = $not;
692:         if (!empty($opts['fuzzy'])) {
693:             $this->_search['prevsearchfuzzy'] = true;
694:         }
695:     }
696: 
697:     /* Serializable methods. */
698: 
699:     /**
700:      * Serialization.
701:      *
702:      * @return string  Serialized data.
703:      */
704:     public function serialize()
705:     {
706:         $data = array(
707:             // Serialized data ID.
708:             self::VERSION,
709:             $this->_search
710:         );
711: 
712:         if (!is_null($this->_charset)) {
713:             $data[] = $this->_charset;
714:         }
715: 
716:         return serialize($data);
717:     }
718: 
719:     /**
720:      * Unserialization.
721:      *
722:      * @param string $data  Serialized data.
723:      *
724:      * @throws Exception
725:      */
726:     public function unserialize($data)
727:     {
728:         $data = @unserialize($data);
729:         if (!is_array($data) ||
730:             !isset($data[0]) ||
731:             ($data[0] != self::VERSION)) {
732:             throw new Exception('Cache version change');
733:         }
734: 
735:         $this->_search = $data[1];
736:         if (isset($data[2])) {
737:             $this->_charset = $data[2];
738:         }
739:     }
740: 
741: }
742: 
API documentation generated by ApiGen