1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
16: class IMP_Mailbox_List implements ArrayAccess, Countable, Iterator, Serializable
17: {
18:
19: const VERSION = 2;
20:
21: 22: 23: 24: 25:
26: public $changed = false;
27:
28: 29: 30: 31: 32:
33: protected $_mailbox;
34:
35: 36: 37: 38: 39:
40: protected $_slist = array();
41:
42: 43: 44: 45: 46:
47: protected $_sorted = null;
48:
49: 50: 51: 52: 53: 54:
55: protected $_sortedMbox = array();
56:
57: 58: 59: 60: 61:
62: protected $_threadob = null;
63:
64: 65: 66: 67: 68:
69: public function __construct($mbox)
70: {
71: $this->_mailbox = IMP_Mailbox::get($mbox);
72: }
73:
74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109:
110: public function getMailboxArray($msgnum, $options = array())
111: {
112: $this->_buildMailbox();
113:
114: $headers = $overview = $to_process = $uids = array();
115:
116:
117: foreach ($msgnum as $i) {
118: 119: 120:
121: if (isset($this->_sorted[$i - 1])) {
122: $mboxname = $this->_mailbox->search
123: ? $this->_sortedMbox[$i - 1]
124: : strval($this->_mailbox);
125:
126:
127: $to_process[$mboxname][$this->_sorted[$i - 1]] = $i;
128: }
129: }
130:
131: $fetch_query = new Horde_Imap_Client_Fetch_Query();
132: $fetch_query->envelope();
133: $fetch_query->flags();
134: $fetch_query->size();
135: $fetch_query->uid();
136:
137: if (!empty($options['headers'])) {
138: $headers = array_merge($headers, array(
139: 'importance',
140: 'list-post',
141: 'x-priority'
142: ));
143: }
144:
145: if (!empty($options['type'])) {
146: $headers[] = 'content-type';
147: }
148:
149: if (!empty($headers)) {
150: $fetch_query->headers('imp', $headers, array(
151: 'cache' => true,
152: 'peek' => true
153: ));
154: }
155:
156: $imp_imap = $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create();
157:
158: if (empty($options['preview'])) {
159: $cache = null;
160: $options['preview'] = 0;
161: } else {
162: $cache = $imp_imap->getCache();
163: }
164:
165:
166: foreach ($to_process as $mbox => $ids) {
167: try {
168: $fetch_res = $imp_imap->fetch($mbox, $fetch_query, array(
169: 'ids' => $imp_imap->getIdsOb(array_keys($ids))
170: ));
171:
172: if ($options['preview']) {
173: $preview_info = $tostore = array();
174: if ($cache) {
175: try {
176: $preview_info = $cache->get($mbox, array_keys($ids), array('IMPpreview', 'IMPpreviewc'));
177: } catch (IMP_Imap_Exception $e) {}
178: }
179: }
180:
181: reset($fetch_res);
182: while (list($k, $f) = each($fetch_res)) {
183: $v = array(
184: 'envelope' => $f->getEnvelope(),
185: 'flags' => $f->getFlags(),
186: 'headers' => $f->getHeaders('imp', Horde_Imap_Client_Data_Fetch::HEADER_PARSE),
187: 'mailbox' => $mbox,
188: 'size' => $f->getSize(),
189: 'uid' => $f->getUid()
190: );
191:
192: if (($options['preview'] === 2) ||
193: (($options['preview'] === 1) &&
194: (!$GLOBALS['prefs']->getValue('preview_show_unread') ||
195: !in_array(Horde_Imap_Client::FLAG_SEEN, $v['flags'])))) {
196: if (empty($preview_info[$k])) {
197: try {
198: $imp_contents = $GLOBALS['injector']->getInstance('IMP_Factory_Contents')->create(new IMP_Indices($mbox, $k));
199: $prev = $imp_contents->generatePreview();
200: $preview_info[$k] = array('IMPpreview' => $prev['text'], 'IMPpreviewc' => $prev['cut']);
201: if (!is_null($cache)) {
202: $tostore[$k] = $preview_info[$k];
203: }
204: } catch (Exception $e) {
205: $preview_info[$k] = array('IMPpreview' => '', 'IMPpreviewc' => false);
206: }
207: }
208:
209: $v['preview'] = $preview_info[$k]['IMPpreview'];
210: $v['previewcut'] = $preview_info[$k]['IMPpreviewc'];
211: }
212:
213: $overview[] = $v;
214: }
215:
216: $uids[$mbox] = array_keys($fetch_res);
217:
218: if (!is_null($cache) && !empty($tostore)) {
219: $status = $imp_imap->status($mbox, Horde_Imap_Client::STATUS_UIDVALIDITY);
220: $cache->set($mbox, $tostore, $status['uidvalidity']);
221: }
222: } catch (IMP_Imap_Exception $e) {}
223: }
224:
225: return array(
226: 'overview' => $overview,
227: 'uids' => new IMP_Indices($uids)
228: );
229: }
230:
231: 232: 233: 234: 235:
236: public function isBuilt()
237: {
238: return !is_null($this->_sorted);
239: }
240:
241: 242: 243:
244: protected function _buildMailbox()
245: {
246: if ($this->isBuilt()) {
247: return;
248: }
249:
250: $this->changed = true;
251: $this->_sorted = $this->_sortedMbox = array();
252: $query = null;
253:
254: if ($this->_mailbox->search) {
255: if ($this->_mailbox->hideDeletedMsgs()) {
256: $query = new Horde_Imap_Client_Search_Query();
257: $query->flag(Horde_Imap_Client::FLAG_DELETED, false);
258: }
259:
260: try {
261: foreach ($GLOBALS['injector']->getInstance('IMP_Search')->runSearch($query, $this->_mailbox) as $ob) {
262: $this->_sorted = array_merge($this->_sorted, $ob->uids);
263: $this->_sortedMbox = array_merge($this->_sortedMbox, array_fill(0, count($ob->uids), strval($ob->mbox)));
264: }
265: } catch (IMP_Imap_Exception $e) {
266: $e->notify(_("Mailbox listing failed") . ': ' . $e->getMessage());
267: }
268: } else {
269: $sortpref = $this->_mailbox->getSort(true);
270: if ($sortpref['by'] == Horde_Imap_Client::SORT_THREAD) {
271: $this->_threadob = null;
272: $threadob = $this->getThreadOb();
273: $this->_sorted = $threadob->messageList();
274: if ($sortpref['dir']) {
275: $this->_sorted = array_reverse($this->_sorted);
276: }
277: } else {
278: if ($this->_mailbox->hideDeletedMsgs()) {
279: $query = new Horde_Imap_Client_Search_Query();
280: $query->flag(Horde_Imap_Client::FLAG_DELETED, false);
281: }
282: try {
283: $res = $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create()->search($this->_mailbox, $query, array(
284: 'sort' => array($sortpref['by'])
285: ));
286: if ($sortpref['dir']) {
287: $res['match']->reverse();
288: }
289: $this->_sorted = $res['match']->ids;
290: } catch (IMP_Imap_Exception $e) {
291: $e->notify(_("Mailbox listing failed") . ': ' . $e->getMessage());
292: }
293: }
294: }
295: }
296:
297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307:
308: public function newMessages($results, $uid = false)
309: {
310: return $this->_msgFlagSearch('recent', $results, $uid);
311: }
312:
313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323:
324: public function unseenMessages($results, $uid = false)
325: {
326: return $this->_msgFlagSearch('unseen', $results, $uid);
327: }
328:
329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339:
340: protected function _msgFlagSearch($type, $results, $uid)
341: {
342: $count = ($results == Horde_Imap_Client::SORT_RESULTS_COUNT);
343:
344: if ($this->_mailbox->search || empty($this->_sorted)) {
345: if ($count &&
346: ($type == 'unseen') &&
347: $this->_mailbox->vinbox) {
348: return count($this);
349: }
350:
351: return $count ? 0 : array();
352: }
353:
354: $criteria = new Horde_Imap_Client_Search_Query();
355: $imp_imap = $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create();
356:
357: if ($this->_mailbox->hideDeletedMsgs()) {
358: $criteria->flag(Horde_Imap_Client::FLAG_DELETED, false);
359: } elseif ($count) {
360: try {
361: $status_res = $imp_imap->status($this->_mailbox, $type == 'recent' ? Horde_Imap_Client::STATUS_RECENT : Horde_Imap_Client::STATUS_UNSEEN);
362: return $status_res[$type];
363: } catch (IMP_Imap_Exception $e) {
364: return 0;
365: }
366: }
367:
368: if ($type == 'recent') {
369: $criteria->flag(Horde_Imap_Client::FLAG_RECENT, true);
370: } else {
371: $criteria->flag(Horde_Imap_Client::FLAG_SEEN, false);
372: }
373:
374: try {
375: $res = $imp_imap->search($this->_mailbox, $criteria, array(
376: 'results' => array($results),
377: 'sequence' => !$uid
378: ));
379: return $count ? $res['count'] : $res;
380: } catch (IMP_Imap_Exception $e) {
381: return $count ? 0 : array();
382: }
383: }
384:
385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402:
403: public function buildMailboxPage($page = 0, $start = 0, $opts = array())
404: {
405: $this->_buildMailbox();
406:
407: $ret = array('msgcount' => count($this->_sorted));
408:
409: $page_size = max($GLOBALS['prefs']->getValue('max_msgs'), 1);
410:
411: if ($ret['msgcount'] > $page_size) {
412: $ret['pagecount'] = ceil($ret['msgcount'] / $page_size);
413:
414:
415: if (empty($page) || strcspn($page, '0123456789')) {
416: if (!empty($start)) {
417:
418: $page = ceil($start / $page_size);
419: } else {
420:
421: if ($GLOBALS['session']->exists('imp', 'mbox_page/' . $this->_mailbox)) {
422: $page = $GLOBALS['session']->get('imp', 'mbox_page/' . $this->_mailbox);
423: } elseif ($this->_mailbox->search) {
424: $page = 1;
425: } else {
426: $page = ceil($this->mailboxStart($ret['msgcount']) / $page_size);
427: }
428: }
429: }
430:
431: 432:
433: $ret['page'] = intval($page);
434: if ($ret['page'] > $ret['pagecount']) {
435: $ret['page'] = $ret['pagecount'];
436: } elseif ($ret['page'] < 1) {
437: $ret['page'] = 1;
438: }
439:
440: $ret['begin'] = (($ret['page'] - 1) * $page_size) + 1;
441: $ret['end'] = $ret['begin'] + $page_size - 1;
442: if ($ret['end'] > $ret['msgcount']) {
443: $ret['end'] = $ret['msgcount'];
444: }
445: } else {
446: $ret['begin'] = 1;
447: $ret['end'] = $ret['msgcount'];
448: $ret['page'] = 1;
449: $ret['pagecount'] = 1;
450: }
451:
452: $ret['index'] = $ret['begin'] - 1;
453:
454: 455:
456: $ret['anymsg'] = true;
457: if (!$ret['msgcount'] && !$this->_mailbox->search) {
458: try {
459: $status = $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create()->status($this->_mailbox, Horde_Imap_Client::STATUS_MESSAGES);
460: $ret['anymsg'] = (bool)$status['messages'];
461: } catch (IMP_Imap_Exception $e) {
462: $ret['anymsg'] = false;
463: }
464: }
465:
466:
467: $GLOBALS['session']->set('imp', 'mbox_page/' . $this->_mailbox, $ret['page']);
468:
469: return $ret;
470: }
471:
472: 473: 474: 475: 476: 477: 478: 479:
480: public function mailboxStart($total)
481: {
482: if ($this->_mailbox->search) {
483: return 1;
484: }
485:
486: switch ($GLOBALS['prefs']->getValue('mailbox_start')) {
487: case IMP::MAILBOX_START_FIRSTPAGE:
488: return 1;
489:
490: case IMP::MAILBOX_START_LASTPAGE:
491: return $total;
492:
493: case IMP::MAILBOX_START_FIRSTUNSEEN:
494: if (!$this->_mailbox->access_sort) {
495: return 1;
496: }
497:
498: $sortpref = $this->_mailbox->getSort();
499:
500: 501:
502: if ($sortpref['by'] == Horde_Imap_Client::SORT_SEQUENCE) {
503: try {
504: $res = $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create()->status($this->_mailbox, Horde_Imap_Client::STATUS_FIRSTUNSEEN | Horde_Imap_Client::STATUS_MESSAGES);
505: if (!is_null($res['firstunseen'])) {
506: return $sortpref['dir']
507: ? ($res['messages'] - $res['firstunseen'] + 1)
508: : $res['firstunseen'];
509: }
510: } catch (IMP_Imap_Exception $e) {}
511:
512: return 1;
513: }
514:
515: $unseen_msgs = $this->unseenMessages(Horde_Imap_Client::SORT_RESULTS_MIN, true);
516: return empty($unseen_msgs['min'])
517: ? 1
518: : ($this->getArrayIndex($unseen_msgs['min']) + 1);
519:
520: case IMP::MAILBOX_START_LASTUNSEEN:
521: if (!$this->_mailbox->access_sort) {
522: return 1;
523: }
524:
525: $unseen_msgs = $this->unseenMessages(Horde_Imap_Client::SORT_RESULTS_MAX, true);
526: return empty($unseen_msgs['max'])
527: ? 1
528: : ($this->getArrayIndex($unseen_msgs['max']) + 1);
529: }
530: }
531:
532: 533: 534: 535: 536: 537:
538: public function getThreadOb()
539: {
540: if (is_null($this->_threadob)) {
541: try {
542: $this->_threadob = $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create()->thread($this->_mailbox, array('criteria' => $GLOBALS['session']->get('imp', 'imap_thread')));
543: } catch (IMP_Imap_Exception $e) {
544: $e->notify();
545: return new Horde_Imap_Client_Data_Thread(array(), 'uid');
546: }
547: }
548:
549: return $this->_threadob;
550: }
551:
552: 553: 554:
555: public function rebuild()
556: {
557: $this->_sorted = null;
558: $this->_buildMailbox();
559: }
560:
561: 562: 563: 564: 565: 566: 567: 568: 569: 570:
571: public function getArrayIndex($uid, $mbox = null)
572: {
573: $aindex = null;
574:
575: $this->_buildMailbox();
576:
577: if ($this->_mailbox->search) {
578: if (is_null($mbox)) {
579: $mbox = IMP::$thismailbox;
580: }
581:
582: 583:
584: foreach (array_keys($this->_sorted, $uid) as $key) {
585: if ($this->_sortedMbox[$key] == $mbox) {
586: return $key;
587: }
588: }
589: } else {
590: 591:
592: if (($aindex = array_search($uid, $this->_sorted)) === false) {
593: $aindex = null;
594: }
595: }
596:
597: return $aindex;
598: }
599:
600: 601: 602: 603: 604:
605: public function getIndicesOb()
606: {
607: $this->_buildMailbox();
608: $ob = new IMP_Indices();
609:
610: if ($this->_mailbox->search) {
611: reset($this->_sorted);
612: while (list($k, $v) = each($this->_sorted)) {
613: $ob->add($this->_sortedMbox[$k], $v);
614: }
615: } else {
616: $ob->add($this->_mailbox, $this->_sorted);
617: }
618:
619: return $ob;
620: }
621:
622: 623: 624: 625: 626: 627: 628: 629:
630: public function removeMsgs($indices)
631: {
632: if ($indices === true) {
633: $this->rebuild();
634: return false;
635: }
636:
637: if (!count($indices)) {
638: return false;
639: }
640:
641:
642: foreach ($indices as $ob) {
643: foreach ($ob->uids as $uid) {
644: $val = $this->getArrayIndex($uid, $ob->mbox);
645: unset($this->_sorted[$val]);
646: if ($this->_mailbox->search) {
647: unset($this->_sortedMbox[$val]);
648: }
649: }
650: }
651:
652: $this->changed = true;
653: $this->_sorted = array_values($this->_sorted);
654: if ($this->_mailbox->search) {
655: $this->_sortedMbox = array_values($this->_sortedMbox);
656: }
657: $this->_threadob = null;
658:
659: return true;
660: }
661:
662:
663:
664: 665: 666:
667: public function offsetExists($offset)
668: {
669: return isset($this->_sorted[$offset - 1]);
670: }
671:
672: 673: 674: 675: 676: 677: 678:
679: public function offsetGet($offset)
680: {
681: return isset($this->_sorted[$offset - 1])
682: ? array(
683: 'm' => (empty($this->_sortedMbox) ? $this->_mailbox : IMP_Mailbox::get($this->_sortedMbox[$offset - 1])),
684: 'u' => $this->_sorted[$offset - 1]
685: )
686: : null;
687: }
688:
689: 690: 691:
692: public function offsetSet($offset, $value)
693: {
694: throw new BadMethodCallException('Not supported');
695: }
696:
697: 698: 699:
700: public function offsetUnset($offset)
701: {
702: throw new BadMethodCallException('Not supported');
703: }
704:
705:
706:
707: 708: 709: 710: 711:
712: public function count()
713: {
714: $this->_buildMailbox();
715: return count($this->_sorted);
716: }
717:
718:
719:
720: 721: 722: 723: 724:
725: public function current()
726: {
727: $key = key($this->_sorted);
728: return array(
729: 'm' => (empty($this->_sortedMbox) ? $this->_mailbox : IMP_Mailbox::get($this->_sortedMbox[$key])),
730: 'u' => $this->_sorted[$key]
731: );
732: }
733:
734: 735: 736:
737: public function key()
738: {
739: return (key($this->_sorted) + 1);
740: }
741:
742: 743:
744: public function next()
745: {
746: next($this->_sorted);
747: }
748:
749: 750:
751: public function rewind()
752: {
753: reset($this->_sorted);
754: }
755:
756: 757:
758: public function valid()
759: {
760: return (key($this->_sorted) !== null);
761: }
762:
763:
764:
765: 766: 767: 768: 769:
770: public function serialize()
771: {
772: $data = array(
773: 'm' => $this->_mailbox,
774: 'v' => self::VERSION
775: );
776:
777: if (!is_null($this->_sorted)) {
778: $data['so'] = $this->_sorted;
779: if (!empty($this->_sortedMbox)) {
780: $data['som'] = $this->_sortedMbox;
781: }
782: }
783:
784: foreach ($this->_slist as $val) {
785: $data[$val] = $this->$val;
786: }
787:
788: return serialize($data);
789: }
790:
791: 792: 793: 794: 795: 796: 797:
798: public function unserialize($data)
799: {
800: $data = @unserialize($data);
801: if (!is_array($data) ||
802: !isset($data['v']) ||
803: ($data['v'] != self::VERSION)) {
804: throw new Exception('Cache version change');
805: }
806:
807: $this->_mailbox = $data['m'];
808:
809: if (isset($data['so'])) {
810: $this->_sorted = $data['so'];
811: if (isset($data['som'])) {
812: $this->_sortedMbox = $data['som'];
813: }
814: }
815:
816: foreach ($this->_slist as $val) {
817: $this->$val = $data[$val];
818: }
819: }
820:
821: }
822: