1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 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: class IMP_Mailbox implements Serializable
100: {
101:
102: const CHANGED_NO = 0;
103: const CHANGED_YES = 1;
104: const CHANGED_DELETE = 2;
105:
106:
107: const SPECIAL_DRAFTS = 'drafts';
108: const SPECIAL_SENT = 'sent';
109: const SPECIAL_SPAM = 'spam';
110: const SPECIAL_TRASH = 'trash';
111:
112:
113:
114: const CACHE_ACL = 'a';
115:
116: const CACHE_DISPLAY = 'd';
117:
118: const CACHE_ICONS = 'i';
119:
120: const CACHE_NAMESPACE = 'n';
121:
122: const CACHE_READONLY = 'ro';
123:
124: const CACHE_UIDVALIDITY = 'v';
125:
126:
127: const CACHE_HASICONHOOK = 'ih';
128: const CACHE_ICONHOOK = 'ic';
129: const CACHE_HASLABELHOOK = 'lh';
130: const CACHE_HIDEDELETED = 'hd';
131: const CACHE_READONLYHOOK = 'roh';
132: const CACHE_SPECIALMBOXES = 's';
133:
134: 135: 136: 137: 138:
139: public $cache = array();
140:
141: 142: 143: 144: 145:
146: public $changed = self::CHANGED_NO;
147:
148: 149: 150: 151: 152:
153: protected $_import;
154:
155: 156: 157: 158: 159:
160: static protected $_temp = array();
161:
162: 163: 164: 165: 166:
167: protected $_mbox;
168:
169: 170: 171: 172: 173: 174: 175:
176: static public function get($mbox)
177: {
178: if (is_array($mbox)) {
179: return array_filter(array_map(array(__CLASS__, 'get'), $mbox));
180: }
181:
182: try {
183: return $GLOBALS['injector']
184: ->getInstance('IMP_Factory_Mailbox')
185: ->create(strval($mbox));
186: } catch (IMP_Exception $e) {
187: return null;
188: }
189: }
190:
191: 192: 193: 194: 195: 196: 197:
198: static public function getPref($pref)
199: {
200: return self::get(self::prefFrom($GLOBALS['prefs']->getValue($pref)));
201: }
202:
203: 204: 205: 206: 207: 208: 209:
210: public function __construct($mbox)
211: {
212: if (strlen($mbox) == 0) {
213: throw new IMP_Exception('Mailbox name must not be empty.');
214: }
215:
216: $this->_mbox = $mbox;
217:
218: if (!isset(self::$_temp[self::CACHE_HASICONHOOK])) {
219: self::$_temp[self::CACHE_HASICONHOOK] = Horde::hookExists('mbox_icons', 'imp');
220: self::$_temp[self::CACHE_HASLABELHOOK] = Horde::hookExists('mbox_label', 'imp');
221: }
222: }
223:
224: 225:
226: public function __toString()
227: {
228: return strval($this->_mbox);
229: }
230:
231: 232:
233: public function __get($key)
234: {
235: global $injector;
236:
237: switch ($key) {
238: case 'abbrev_label':
239: $label = $this->label;
240: return (($pos = strrpos($label, $this->namespace_delimiter)) === false)
241: ? $label
242: : substr($label, $pos + 1);
243:
244: case 'access_creatembox':
245: return (!($acl = $this->acl) ||
246: ($acl[Horde_Imap_Client::ACL_CREATEMBOX]));
247:
248: case 'access_deletembox':
249: return (!($acl = $this->acl) ||
250: ($acl[Horde_Imap_Client::ACL_DELETEMBOX]));
251:
252: case 'access_deletemsgs':
253: return (!$this->readonly &&
254: (!($acl = $this->acl) ||
255: ($acl[Horde_Imap_Client::ACL_DELETEMSGS])));
256:
257: case 'access_expunge':
258: return (!$this->readonly &&
259: (!($acl = $this->acl) ||
260: ($acl[Horde_Imap_Client::ACL_EXPUNGE])));
261:
262: case 'access_filters':
263: return !$this->search &&
264: !$injector->getInstance('IMP_Factory_Imap')->create()->pop3;
265:
266: case 'access_sort':
267: 268: 269:
270: return !$injector->getInstance('IMP_Factory_Imap')->create()->pop3;
271:
272: case 'access_sortthread':
273: 274: 275: 276: 277:
278: return ($injector->getInstance('IMP_Factory_Imap')->create()->imap &&
279: !$this->search);
280:
281: case 'acl':
282: if (isset($this->cache[self::CACHE_ACL])) {
283: return is_null($this->cache[self::CACHE_ACL])
284: ? null
285: : new Horde_Imap_Client_Data_Acl($this->cache[self::CACHE_ACL]);
286: }
287:
288: $acl = $this->cache[self::CACHE_ACL] = null;
289: $this->changed = self::CHANGED_YES;
290:
291: if (!$this->nonimap) {
292: try {
293: $acl = $injector->getInstance('IMP_Imap_Acl')->getACL($this, true);
294: 295:
296: $this->cache[self::CACHE_ACL] = strval($acl);
297: } catch (IMP_Exception $e) {}
298: }
299:
300: return $acl;
301:
302: case 'basename':
303: if ($this->nonimap) {
304: return $this->label;
305: }
306:
307: $basename = (($pos = strrpos($this->_mbox, $this->namespace_delimiter)) === false)
308: ? $this->_mbox
309: : substr($this->_mbox, $pos + 1);
310:
311: return Horde_String::convertCharset($basename, 'UTF7-IMAP', 'UTF-8');
312:
313: case 'cacheid':
314: case 'cacheid_date':
315: return $this->_getCacheID($key == 'cacheid_date');
316:
317: case 'children':
318: return $injector->getInstance('IMP_Imap_Tree')->hasChildren($this->_mbox);
319:
320: case 'container':
321: return $injector->getInstance('IMP_Imap_Tree')->isContainer($this->_mbox);
322:
323: case 'display':
324: return $this->nonimap
325: ? $this->label
326: : $this->_getDisplay();
327:
328: case 'display_html':
329: return htmlspecialchars($this->display);
330:
331: case 'display_notranslate':
332: return $this->nonimap
333: ? $this->label
334: : $this->_getDisplay(true);
335:
336: case 'drafts':
337: $special = $this->getSpecialMailboxes();
338: return ($this->_mbox == $special[self::SPECIAL_DRAFTS]);
339:
340: case 'editquery':
341: return $injector->getInstance('IMP_Search')->isQuery($this->_mbox, true);
342:
343: case 'editvfolder':
344: return $injector->getInstance('IMP_Search')->isVFolder($this->_mbox, true);
345:
346: case 'exists':
347: if ($ob = $this->getSearchOb()) {
348: return $ob->enabled;
349: }
350:
351: $imaptree = $injector->getInstance('IMP_Imap_Tree');
352: if (isset($imaptree[$this->_mbox])) {
353: return !$imaptree[$this->_mbox]->container;
354: }
355:
356: try {
357: return (bool)$injector->getInstance('IMP_Factory_Imap')->create()->listMailboxes($this->_mbox, array('flat' => true));
358: } catch (IMP_Imap_Exception $e) {
359: return false;
360: }
361:
362: case 'fixed':
363: return (!empty($GLOBALS['conf']['server']['fixed_folders']) &&
364: in_array($this->pref_to, $GLOBALS['conf']['server']['fixed_folders']));
365:
366: case 'form_to':
367: return $this->formTo($this->_mbox);
368:
369: case 'is_open':
370: return $injector->getInstance('IMP_Imap_Tree')->isOpen($this->_mbox);
371:
372: case 'is_trash':
373: return (self::getPref('trash_folder') == $this) || $this->vtrash;
374:
375: case 'icon':
376: return $this->_getIcon();
377:
378: case 'inbox':
379: return (strcasecmp($this->_mbox, 'INBOX') === 0);
380:
381: case 'invisible':
382: return $injector->getInstance('IMP_Imap_Tree')->isInvisible($this->_mbox);
383:
384: case 'label':
385: 386: 387: 388: 389:
390: $imp_search = $injector->getInstance('IMP_Search');
391: $label = ($ob = $imp_search[$this->_mbox])
392: ? $ob->label
393: : $this->_getDisplay();
394:
395: return self::$_temp[self::CACHE_HASLABELHOOK]
396: ? Horde::callHook('mbox_label', array($this->_mbox, $label), 'imp')
397: : $label;
398:
399: case 'level':
400: $elt = $injector->getInstance('IMP_Imap_Tree')->getElement($this->_mbox);
401: return $elt
402: ? $elt['c']
403: : 0;
404:
405: case 'namespace':
406: return $injector->getInstance('IMP_Imap_Tree')->isNamespace($this->_mbox);
407:
408: case 'namespace_append':
409: $imp_imap = $injector->getInstance('IMP_Factory_Imap')->create();
410: $def_ns = $imp_imap->defaultNamespace();
411: if (is_null($def_ns)) {
412: return $this;
413: }
414: $empty_ns = $imp_imap->getNamespace('');
415:
416:
417: 418: 419: 420:
421: if (!is_null($empty_ns) &&
422: ($def_ns['name'] == $empty_ns['name'])) {
423: return $this;
424: }
425:
426: $ns_info = $this->namespace_info;
427:
428: if (is_null($ns_info) || !is_null($empty_ns)) {
429: return self::get($def_ns['name'] . $this->_mbox);
430: }
431:
432: return $this;
433:
434: case 'namespace_delimiter':
435: $ns_info = $this->namespace_info;
436: return is_null($ns_info)
437: ? ''
438: : $ns_info['delimiter'];
439:
440: case 'namespace_info':
441: $keys = array('delimiter', 'hidden', 'name', 'translation', 'type');
442:
443: if (isset($this->cache[self::CACHE_NAMESPACE])) {
444: $ns = $this->cache[self::CACHE_NAMESPACE];
445:
446: if (is_null($ns)) {
447: return null;
448: }
449:
450: $ret = array();
451: foreach ($keys as $key => $val) {
452: $ret[$val] = isset($ns[$key])
453: ? $ns[$key]
454: : '';
455: }
456:
457: return $ret;
458: }
459:
460: $ns_info = $injector->getInstance('IMP_Factory_Imap')->create()->getNamespace($this->_mbox);
461: if (is_null($ns_info)) {
462: $this->cache[self::CACHE_NAMESPACE] = null;
463: } else {
464:
465: $this->cache[self::CACHE_NAMESPACE] = array();
466: foreach ($keys as $id => $key) {
467: if ($ns_info[$key]) {
468: $this->cache[self::CACHE_NAMESPACE][$id] = $ns_info[$key];
469: }
470: }
471: }
472:
473: $this->changed = self::CHANGED_YES;
474:
475: return $ns_info;
476:
477: case 'nonimap':
478: return ($this->search ||
479: $injector->getInstance('IMP_Imap_Tree')->isNonImapElt($this->_mbox));
480:
481: case 'parent':
482: $elt = $injector->getInstance('IMP_Imap_Tree')->getElement($this->_mbox);
483: return $elt
484: ? self::get($elt['p'])
485: : null;
486:
487: case 'permflags':
488: $imp_imap = $injector->getInstance('IMP_Factory_Imap')->create();
489:
490: if ($imp_imap->access(IMP_Imap::ACCESS_FLAGS)) {
491: try {
492: 493:
494: $imp_imap->openMailbox($this->_mbox, Horde_Imap_Client::OPEN_READWRITE);
495: $status = $imp_imap->status($this->_mbox, Horde_Imap_Client::STATUS_FLAGS | Horde_Imap_Client::STATUS_PERMFLAGS);
496: return new IMP_Imap_PermanentFlags($status['permflags'], $status['flags']);
497: } catch (Exception $e) {}
498: }
499:
500: return new IMP_Imap_PermanentFlags();
501:
502: case 'poll_info':
503: $info = new stdClass;
504: $info->msgs = 0;
505: $info->recent = 0;
506: $info->unseen = 0;
507:
508: try {
509: if ($msgs_info = $injector->getInstance('IMP_Factory_Imap')->create()->status($this->_mbox, Horde_Imap_Client::STATUS_RECENT | Horde_Imap_Client::STATUS_UNSEEN | Horde_Imap_Client::STATUS_MESSAGES)) {
510: if (!empty($msgs_info['recent'])) {
511: $info->recent = intval($msgs_info['recent']);
512: }
513: $info->msgs = intval($msgs_info['messages']);
514: $info->unseen = intval($msgs_info['unseen']);
515: }
516: } catch (IMP_Imap_Exception $e) {}
517:
518: return $info;
519:
520: case 'polled':
521: return !$this->search &&
522: $injector->getInstance('IMP_Imap_Tree')->isPolled($this->_mbox);
523:
524: case 'pref_from':
525: return $this->prefFrom($this->_mbox);
526:
527: case 'pref_to':
528: return $this->prefTo($this->_mbox);
529:
530: case 'query':
531: return $injector->getInstance('IMP_Search')->isQuery($this->_mbox);
532:
533: case 'readonly':
534: if (isset($this->cache[self::CACHE_READONLY])) {
535: return $this->cache[self::CACHE_READONLY];
536: }
537:
538: $this->cache[self::CACHE_READONLY] = false;
539: $this->changed = self::CHANGED_YES;
540:
541:
542: if (empty(self::$_temp[self::CACHE_READONLYHOOK])) {
543: try {
544: if (Horde::callHook('mbox_readonly', array($this->_mbox), 'imp')) {
545: $this->cache[self::CACHE_READONLY] = true;
546: }
547: } catch (Horde_Exception_HookNotSet $e) {
548: self::$_temp[self::CACHE_READONLYHOOK] = true;
549: }
550: }
551:
552:
553:
554: return $this->cache[self::CACHE_READONLY];
555:
556: case 'search':
557: return $injector->getInstance('IMP_Search')->isSearchMbox($this->_mbox);
558:
559: case 'special':
560: $special = $this->getSpecialMailboxes();
561:
562: switch ($this->_mbox) {
563: case 'INBOX':
564: case $special[self::SPECIAL_DRAFTS]:
565: case $special[self::SPECIAL_SPAM]:
566: case $special[self::SPECIAL_TRASH]:
567: return true;
568: }
569:
570: return in_array($this->_mbox, $special[self::SPECIAL_SENT]);
571:
572: case 'special_outgoing':
573: $special = $this->getSpecialMailboxes();
574:
575: return in_array($this->_mbox, array_merge(
576: array(
577: $special[self::SPECIAL_DRAFTS]
578: ),
579: $special[self::SPECIAL_SENT]
580: ));
581:
582: case 'specialvfolder':
583: return !$this->editvfolder;
584:
585: case 'sub':
586: return $injector->getInstance('IMP_Imap_Tree')->isSubscribed($this->_mbox);
587:
588: case 'subfolders':
589: return $this->get(array_merge(array($this->_mbox), $this->subfolders_only));
590:
591: case 'subfolders_only':
592: return $this->get($injector->getInstance('IMP_Factory_Imap')->create()->listMailboxes($this->_mbox . $this->namespace_delimiter . '*', null, array('flat' => true)));
593:
594: case 'uidvalid':
595: $imp_imap = $injector->getInstance('IMP_Factory_Imap')->create();
596:
597:
598: if ($imp_imap->pop3 || $this->nonimap) {
599: return;
600: }
601:
602: $status = $imp_imap->status($this->_mbox, Horde_Imap_Client::STATUS_UIDVALIDITY);
603: if (($first = !isset($this->cache[self::CACHE_UIDVALIDITY])) ||
604: ($status['uidvalidity'] != $this->cache[self::CACHE_UIDVALIDITY])) {
605: $this->cache[self::CACHE_UIDVALIDITY] = $status['uidvalidity'];
606: $this->changed = self::CHANGED_YES;
607:
608: if (!$first) {
609: throw new IMP_Exception(_("Mailbox structure on server has changed."));
610: }
611: }
612:
613: return $this->cache[self::CACHE_UIDVALIDITY];
614:
615: case 'value':
616: return $this->_mbox;
617:
618: case 'vfolder':
619: return $injector->getInstance('IMP_Search')->isVFolder($this->_mbox);
620:
621: case 'vfolder_container':
622: return ($this->_mbox == IMP_Imap_Tree::VFOLDER_KEY);
623:
624: case 'vinbox':
625: return $injector->getInstance('IMP_Search')->isVinbox($this->_mbox);
626:
627: case 'vtrash':
628: return $injector->getInstance('IMP_Search')->isVtrash($this->_mbox);
629: }
630:
631: return false;
632: }
633:
634: 635: 636: 637: 638: 639: 640: 641: 642: 643: 644: 645:
646: public function create(array $opts = array())
647: {
648: global $injector, $notification, $prefs, $registry;
649:
650: if ($this->exists) {
651: return true;
652: }
653:
654:
655: if (!IMP::hasPermission('create_folders')) {
656: Horde::permissionDeniedError(
657: 'imp',
658: 'create_folders',
659: _("You are not allowed to create folders.")
660: );
661: return false;
662: }
663: if (!IMP::hasPermission('max_folders')) {
664: Horde::permissionDeniedError(
665: 'imp',
666: 'max_folders',
667: sprintf(_("You are not allowed to create more than %d folders."), $injector->getInstance('Horde_Core_Perms')->hasAppPermission('max_folders'))
668: );
669: return false;
670: }
671:
672:
673: $special_use = isset($opts['special_use'])
674: ? $opts['special_use']
675: : array();
676:
677:
678: try {
679: $injector->getInstance('IMP_Factory_Imap')->create()->createMailbox($this->_mbox, array('special_use' => $special_use));
680: } catch (IMP_Imap_Exception $e) {
681: if ($e->getCode() == Horde_Imap_Client_Exception::USEATTR) {
682: unset($opts['special_use']);
683: return $this->create($opts);
684: }
685:
686: $e->notify(sprintf(_("The mailbox \"%s\" was not created. This is what the server said"), $this->display) . ': ' . $e->getMessage());
687: return false;
688: }
689:
690: $notification->push(sprintf(_("The mailbox \"%s\" was successfully created."), $this->display), 'horde.success');
691:
692:
693: if ((!isset($opts['subscribe']) && $prefs->getValue('subscribe')) ||
694: !empty($opts['subscribe'])) {
695: $this->subscribe(true);
696: }
697:
698:
699: $injector->getInstance('IMP_Imap_Tree')->insert($this->_mbox);
700:
701: return true;
702: }
703:
704: 705: 706: 707: 708: 709: 710:
711: public function delete($force = false)
712: {
713: global $conf, $injector, $notification;
714:
715: if ($this->vfolder) {
716: if ($this->editvfolder) {
717: $imp_search = $injector->getInstance('IMP_Search');
718: $label = $imp_search[$this->_mbox]->label;
719: unset($imp_search[$this->_mbox]);
720: $notification->push(sprintf(_("Deleted Virtual Folder \"%s\"."), $label), 'horde.success');
721: return true;
722: }
723:
724: $notification->push(sprintf(_("Could not delete Virtual Folder \"%s\"."), $this->label), 'horde.error');
725: return false;
726: }
727:
728: if ((!$force && $this->fixed) || !$this->access_deletembox) {
729: $notification->push(sprintf(_("The mailbox \"%s\" may not be deleted."), $this->display), 'horde.error');
730: return false;
731: }
732:
733: try {
734: $injector->getInstance('IMP_Factory_Imap')->create()->deleteMailbox($this->_mbox);
735: $notification->push(sprintf(_("The mailbox \"%s\" was successfully deleted."), $this->display), 'horde.success');
736: } catch (IMP_Imap_Exception $e) {
737: $e->notify(sprintf(_("The mailbox \"%s\" was not deleted. This is what the server said"), $this->display) . ': ' . $e->getMessage());
738: return false;
739: }
740:
741: $injector->getInstance('IMP_Imap_Tree')->delete($this->_mbox);
742: $this->_onDelete(array($this->_mbox));
743:
744: return true;
745: }
746:
747: 748: 749: 750: 751: 752: 753: 754: 755:
756: public function rename($new_name, $force = false)
757: {
758: global $injector, $notification;
759:
760:
761: if (!strlen($new_name)) {
762: return false;
763: }
764:
765: if ((!$force && $this->fixed) || !$this->access_deletembox) {
766: $notification->push(sprintf(_("The mailbox \"%s\" may not be renamed."), $this->display), 'horde.error');
767: return false;
768: }
769:
770: $new_mbox = $this->get($new_name);
771: $old_list = $this->subfolders;
772:
773: try {
774: $injector->getInstance('IMP_Factory_Imap')->create()->renameMailbox($this->_mbox, strval($new_mbox));
775: } catch (IMP_Imap_Exception $e) {
776: $e->notify(sprintf(_("Renaming \"%s\" to \"%s\" failed. This is what the server said"), $this->display, $new_mbox->display) . ': ' . $e->getMessage());
777: return false;
778: }
779:
780: $notification->push(sprintf(_("The mailbox \"%s\" was successfully renamed to \"%s\"."), $this->display, $new_mbox->display), 'horde.success');
781:
782: $injector->getInstance('IMP_Imap_Tree')->rename($this->_mbox, strval($new_mbox));
783: $this->_onDelete($old_list);
784:
785: return true;
786: }
787:
788: 789: 790: 791: 792: 793: 794:
795: public function subscribe($sub)
796: {
797: global $injector, $notification;
798:
799: if (!$sub && $this->inbox) {
800: $notification->push(sprintf(_("You cannot unsubscribe from \"%s\"."), $this->display), 'horde.error');
801: return false;
802: }
803:
804: try {
805: $injector->getInstance('IMP_Factory_Imap')->create()->subscribeMailbox($this->_mbox, $sub);
806: } catch (IMP_Imap_Exception $e) {
807: if ($sub) {
808: $e->notify(sprintf(_("You were not subscribed to \"%s\". Here is what the server said"), $this->display) . ': ' . $e->getMessage());
809: } else {
810: $e->notify(sprintf(_("You were not unsubscribed from \"%s\". Here is what the server said"), $this->display) . ': ' . $e->getMessage());
811: }
812: return false;
813: }
814:
815: if ($sub) {
816: $notification->push(sprintf(_("You were successfully subscribed to \"%s\"."), $this->display), 'horde.success');
817: $injector->getInstance('IMP_Imap_Tree')->subscribe($this->_mbox);
818: } else {
819: $notification->push(sprintf(_("You were successfully unsubscribed from \"%s\"."), $this->display), 'horde.success');
820: $injector->getInstance('IMP_Imap_Tree')->unsubscribe($this->_mbox);
821: }
822:
823: return true;
824: }
825:
826: 827: 828:
829: public function filter()
830: {
831: if (!$this->search) {
832: $GLOBALS['injector']->getInstance('IMP_Filter')->filter($this);
833: }
834: }
835:
836: 837: 838: 839: 840: 841:
842: public function filterOnDisplay()
843: {
844: if ($this->inbox &&
845: $GLOBALS['prefs']->getValue('filter_on_display')) {
846: $this->filter();
847: return true;
848: }
849:
850: return false;
851: }
852:
853: 854: 855: 856: 857: 858: 859:
860: public function getListOb($indices = null)
861: {
862: return $GLOBALS['injector']->getInstance('IMP_Factory_MailboxList')->create($this, $indices);
863: }
864:
865: 866: 867: 868: 869:
870: public function getSearchOb()
871: {
872: $imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
873: return $imp_search[$this->_mbox];
874: }
875:
876: 877: 878: 879: 880: 881: 882: 883:
884: public function getIndicesOb($in)
885: {
886: return new IMP_Indices($this, $in);
887: }
888:
889: 890: 891: 892: 893: 894: 895: 896: 897: 898:
899: public function getSort($convert = false)
900: {
901: global $prefs;
902:
903: $prefmbox = $this->search
904: ? $this->_mbox
905: : $this->pref_from;
906:
907: $sortpref = @unserialize($prefs->getValue('sortpref'));
908: $entry = isset($sortpref[$prefmbox])
909: ? $sortpref[$prefmbox]
910: : array();
911:
912: if (!isset($entry['b'])) {
913: $sortby = $prefs->getValue('sortby');
914: }
915:
916: $ob = array(
917: 'by' => isset($entry['b']) ? $entry['b'] : $sortby,
918: 'dir' => isset($entry['d']) ? $entry['d'] : $prefs->getValue('sortdir'),
919: 'locked' => $prefs->isLocked('sortpref')
920: );
921:
922:
923: if (!$this->access_sort) {
924: $ob['by'] = Horde_Imap_Client::SORT_SEQUENCE;
925: return $ob;
926: }
927:
928: switch ($ob['by']) {
929: case Horde_Imap_Client::SORT_THREAD:
930:
931: if (!$this->access_sortthread) {
932: $ob['by'] = IMP::IMAP_SORT_DATE;
933: }
934: break;
935:
936: case Horde_Imap_Client::SORT_FROM:
937: 938:
939: if ($this->special_outgoing) {
940: $ob['by'] = Horde_Imap_Client::SORT_TO;
941: }
942: break;
943:
944: case Horde_Imap_Client::SORT_TO:
945: if (!$this->special_outgoing) {
946: $ob['by'] = Horde_Imap_Client::SORT_FROM;
947: }
948: break;
949: }
950:
951: if ($convert && ($ob['by'] == IMP::IMAP_SORT_DATE)) {
952: $ob['by'] = $prefs->getValue('sortdate');
953: }
954:
955:
956: if (!$ob['by']) {
957: $ob['by'] = Horde_Imap_Client::SORT_ARRIVAL;
958: }
959:
960: return $ob;
961: }
962:
963: 964: 965: 966: 967: 968: 969:
970: public function setSort($by = null, $dir = null, $delete = false)
971: {
972: global $injector, $prefs;
973:
974: $entry = array();
975: $sortpref = @unserialize($prefs->getValue('sortpref'));
976:
977: $prefmbox = $this->search
978: ? $this->_mbox
979: : $this->pref_from;
980:
981: if ($delete) {
982: unset($sortpref[$prefmbox]);
983: } else {
984: if (!is_null($by)) {
985: $entry['b'] = $by;
986: }
987: if (!is_null($dir)) {
988: $entry['d'] = $dir;
989: }
990:
991: if (!empty($entry)) {
992:
993: $sortpref[$prefmbox] = isset($sortpref[$prefmbox])
994: ? array_merge($sortpref[$prefmbox], $entry)
995: : $entry;
996: }
997: }
998:
999: if ($delete || !empty($entry)) {
1000: $prefs->setValue('sortpref', serialize($sortpref));
1001: }
1002: }
1003:
1004: 1005: 1006: 1007: 1008: 1009: 1010: 1011: 1012:
1013: public function hideDeletedMsgs($deleted = false)
1014: {
1015: global $injector, $prefs;
1016:
1017: $imp_imap = $injector->getInstance('IMP_Factory_Imap')->create();
1018: if (!$imp_imap->access(IMP_Imap::ACCESS_FLAGS)) {
1019: return $imp_imap->imap;
1020: }
1021:
1022: $delhide = isset(self::$_temp[self::CACHE_HIDEDELETED])
1023: ? self::$_temp[self::CACHE_HIDEDELETED]
1024: : null;
1025:
1026: if (is_null($delhide)) {
1027: if ($prefs->getValue('use_trash')) {
1028: 1029:
1030: $delhide = $this->get($prefs->getValue('trash_folder'))->vtrash
1031: ? !$this->vtrash
1032: : $prefs->getValue('delhide_trash') ? true : $deleted;
1033: } else {
1034: $delhide = $prefs->getValue('delhide');
1035: }
1036:
1037: 1038:
1039: if ($delhide) {
1040: $sortpref = $this->getSort();
1041: $delhide = ($sortpref['by'] != Horde_Imap_Client::SORT_THREAD);
1042: }
1043: }
1044:
1045: if (!$deleted) {
1046: self::$_temp[self::CACHE_HIDEDELETED] = $delhide;
1047: }
1048:
1049: return $delhide;
1050: }
1051:
1052: 1053: 1054: 1055: 1056:
1057: public function setHideDeletedMsgs($value)
1058: {
1059: $GLOBALS['prefs']->setValue('delhide', $value);
1060: $this->expire(IMP_Mailbox::CACHE_HIDEDELETED);
1061: $GLOBALS['injector']->getInstance('IMP_Factory_MailboxList')->expireAll();
1062: }
1063:
1064: 1065: 1066: 1067: 1068: 1069: 1070: 1071: 1072: 1073: 1074:
1075: public function runSearchQuery(Horde_Imap_Client_Search_Query $query,
1076: $sortby = null, $sortdir = null)
1077: {
1078: try {
1079: $results = $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create()->search($this, $query, array(
1080: 'sort' => is_null($sortby) ? null : array($sortby)
1081: ));
1082: if ($sortdir) {
1083: $results['match']->reverse();
1084: }
1085: return $this->getIndicesOb($results['match']);
1086: } catch (IMP_Imap_Exception $e) {
1087: return new IMP_Indices();
1088: }
1089: }
1090:
1091: 1092: 1093: 1094: 1095: 1096:
1097: public function expire($entries = null)
1098: {
1099: if (is_null($entries)) {
1100: $changed = true;
1101: $this->cache = array();
1102: } else {
1103: $changed = false;
1104:
1105: if (!is_array($entries)) {
1106: $entries = array($entries);
1107: }
1108:
1109: foreach ($entries as $val) {
1110: $changed = isset($this->cache[$val]);
1111: unset($this->cache[$val], self::$_temp[$val]);
1112: }
1113: }
1114:
1115: if ($changed) {
1116: $this->changed = empty($this->cache)
1117: ? self::CHANGED_DELETE
1118: : self::CHANGED_YES;
1119: }
1120: }
1121:
1122: 1123: 1124: 1125: 1126: 1127: 1128: 1129: 1130: 1131: 1132:
1133: public function url($page, $uid = null, $tmailbox = null, $encode = true)
1134: {
1135: if ($page instanceof Horde_Url) {
1136: $url = clone $page;
1137: } else {
1138: if (($page != 'search.php') && (IMP::getViewMode() == 'dimp')) {
1139: $anchor = is_null($uid)
1140: ? ('mbox:' . $this->form_to)
1141: : ('msg:' . $this->getIndicesOb($uid)->formTo());
1142: return Horde::url('index.php')->setAnchor($anchor);
1143: }
1144:
1145: $url = Horde::url($page);
1146: }
1147:
1148: return $url->add($this->urlParams($uid, $tmailbox))->setRaw(!$encode);
1149: }
1150:
1151: 1152: 1153: 1154: 1155: 1156: 1157: 1158: 1159: 1160: 1161:
1162: public function urlParams($uid = null, $tmailbox = null)
1163: {
1164: $params = array('mailbox' => $this->form_to);
1165: if (!is_null($uid)) {
1166: $params['uid'] = $uid;
1167: if (!is_null($tmailbox) && ($this->_mbox != $tmailbox)) {
1168: $params['thismailbox'] = IMP_Mailbox::get($tmailbox)->form_to;
1169: }
1170: }
1171: return $params;
1172: }
1173:
1174: 1175: 1176: 1177: 1178: 1179: 1180: 1181: 1182:
1183: public function equals($mbox)
1184: {
1185: return ($mbox == $this->_mbox);
1186: }
1187:
1188: 1189: 1190: 1191: 1192: 1193: 1194: 1195: 1196: 1197: 1198:
1199: public function importMbox($fname, $type)
1200: {
1201: $fd = $format = $msg = null;
1202:
1203: if (!file_exists($fname)) {
1204: return false;
1205: }
1206:
1207: switch ($type) {
1208: case 'application/gzip':
1209: case 'application/x-gzip':
1210: case 'application/x-gzip-compressed':
1211:
1212:
1213: if (in_array('compress.zlib', stream_get_wrappers())) {
1214: $fname = 'compress.zlib://' . $fname;
1215: }
1216: break;
1217:
1218: case 'application/x-bzip2':
1219: case 'application/x-bzip':
1220: if (in_array('compress.bzip2', stream_get_wrappers())) {
1221: $fname = 'compress.bzip2://' . $fname;
1222: }
1223: break;
1224:
1225: case 'application/zip':
1226: case 'application/x-compressed':
1227: case 'application/x-zip-compressed':
1228: if (in_array('zip', stream_get_wrappers())) {
1229: $fname = 'zip://' . $fname;
1230: } else {
1231: try {
1232: $zip = Horde_Compress::factory('Zip');
1233: if ($zip->canDecompress) {
1234: $file_data = file_get_contents($fname);
1235:
1236: $zip_info = $zip->decompress($file_data, array(
1237: 'action' => Horde_Compress_Zip::ZIP_LIST
1238: ));
1239:
1240: if (!empty($zip_info)) {
1241: $fd = fopen('php://temp', 'r+');
1242:
1243: foreach (array_keys($zip_info) as $key) {
1244: fwrite($fd, $zip->decompress($file_data, array(
1245: 'action' => Horde_Compress_Zip::ZIP_DATA,
1246: 'info' => $zip_info,
1247: 'key' => $key
1248: )));
1249: }
1250:
1251: rewind($fd);
1252: }
1253: }
1254: } catch (Horde_Compress_Exception $e) {
1255: if ($fd) {
1256: fclose($fd);
1257: $fd = null;
1258: }
1259: }
1260:
1261: $fname = null;
1262: }
1263: break;
1264: }
1265:
1266: if (!is_null($fname)) {
1267: $fd = fopen($fname, 'r');
1268: }
1269:
1270: if (!$fd) {
1271: throw new IMP_Exception(_("The uploaded file cannot be opened"));
1272: }
1273:
1274: $this->_import = array(
1275: 'data' => array(),
1276: 'msgs' => 0,
1277: 'size' => 0
1278: );
1279:
1280: while (!feof($fd)) {
1281: $line = fgets($fd);
1282:
1283:
1284:
1285:
1286: if ((!$format || ($format == 'mbox')) &&
1287: preg_match('/^From (.+@.+|- )/', $line)) {
1288: $format = 'mbox';
1289:
1290: if ($msg) {
1291: 1292:
1293: $this->_importMbox($msg, true);
1294: }
1295:
1296: $msg = fopen('php://temp', 'r+');
1297: } elseif ($msg) {
1298: fwrite($msg, $line);
1299: } elseif (!$format && trim($line)) {
1300: 1301:
1302: $format = 'eml';
1303: $msg = fopen('php://temp', 'r+');
1304: fwrite($msg, $line);
1305: }
1306: }
1307: fclose($fd);
1308:
1309: if ($msg) {
1310: $this->_importMbox($msg);
1311: }
1312:
1313: return $this->_import['msgs']
1314: ? $this->_import['msgs']
1315: : false;
1316: }
1317:
1318: 1319: 1320: 1321: 1322: 1323:
1324: protected function _importMbox($msg, $buffer = false)
1325: {
1326: $this->_import['data'][] = array('data' => $msg);
1327: $this->_import['size'] += intval(ftell($msg));
1328:
1329:
1330: if ($buffer && ($this->_import['size'] < 5242880)) {
1331: return;
1332: }
1333:
1334: try {
1335: $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create()->append($this->_mbox, $this->_import['data']);
1336: $this->_import['msgs'] += count($this->_import['data']);
1337: } catch (IMP_Imap_Exception $e) {}
1338:
1339: foreach ($this->_import['data'] as $val) {
1340: fclose($val['data']);
1341: }
1342:
1343: $this->_import['data'] = array();
1344: $this->_import['size'] = 0;
1345: }
1346:
1347:
1348:
1349: 1350: 1351: 1352: 1353: 1354: 1355: 1356: 1357:
1358: static public function formFrom($mbox)
1359: {
1360: return is_array($mbox)
1361: ? array_filter(array_map(array(__CLASS__, 'formFrom'), $mbox))
1362: : self::get(IMP::base64urlDecode($mbox));
1363: }
1364:
1365: 1366: 1367: 1368: 1369: 1370: 1371: 1372: 1373:
1374: static public function formTo($mbox)
1375: {
1376: return is_array($mbox)
1377: ? array_filter(array_map(array(__CLASS__, 'formTo'), $mbox))
1378: : IMP::base64urlEncode($mbox);
1379: }
1380:
1381: 1382: 1383: 1384: 1385: 1386: 1387: 1388: 1389:
1390: static public function getSpecialMailboxes()
1391: {
1392: if (!isset(self::$_temp[self::CACHE_SPECIALMBOXES])) {
1393: self::$_temp[self::CACHE_SPECIALMBOXES] = array(
1394: self::SPECIAL_DRAFTS => self::getPref('drafts_folder'),
1395: self::SPECIAL_SENT => $GLOBALS['injector']->getInstance('IMP_Identity')->getAllSentmailFolders(),
1396: self::SPECIAL_SPAM => self::getPref('spam_folder'),
1397: self::SPECIAL_TRASH => $GLOBALS['prefs']->getValue('use_trash') ? self::getPref('trash_folder') : null
1398: );
1399: }
1400:
1401: return self::$_temp[self::CACHE_SPECIALMBOXES];
1402: }
1403:
1404: 1405: 1406: 1407: 1408: 1409: 1410:
1411: static public function prefFrom($mbox)
1412: {
1413: $imp_imap = $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create();
1414: $def_ns = $imp_imap->defaultNamespace();
1415: $empty_ns = $imp_imap->getNamespace('');
1416:
1417: if (!is_null($empty_ns) &&
1418: strpos($mbox, $empty_ns['delimiter']) === 0) {
1419:
1420: return substr($mbox, strlen($empty_ns['delimiter']));
1421: } elseif (($ns = $imp_imap->getNamespace($mbox, true)) == null) {
1422:
1423: return $def_ns['name'] . $mbox;
1424: }
1425:
1426: return $mbox;
1427: }
1428:
1429: 1430: 1431: 1432: 1433: 1434: 1435:
1436: static public function prefTo($mbox)
1437: {
1438: $imp_imap = $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create();
1439: $def_ns = $imp_imap->defaultNamespace();
1440: $empty_ns = $imp_imap->getNamespace('');
1441:
1442: if (($ns = self::get($mbox)->namespace_info) !== null) {
1443: if ($ns['name'] == $def_ns['name']) {
1444:
1445: return substr($mbox, strlen($def_ns['name']));
1446: } elseif ($ns['name'] == $empty_ns['name']) {
1447:
1448: return $empty_ns['delimiter'] . $mbox;
1449: }
1450: }
1451:
1452: return strval($mbox);
1453: }
1454:
1455:
1456:
1457: 1458: 1459: 1460: 1461: 1462: 1463: 1464: 1465: 1466: 1467: 1468: 1469: 1470: 1471:
1472: protected function _getCacheID($date = false)
1473: {
1474: $date = $date
1475: ? 'D' . date('mdy')
1476: : '';
1477:
1478: if ($this->search) {
1479: return '1' . ($date ? '|' . $date : '');
1480: }
1481:
1482: $sortpref = $this->getSort(true);
1483: $addl = array($sortpref['by'], $sortpref['dir']);
1484: if ($date) {
1485: $addl[] = $date;
1486: }
1487:
1488: try {
1489: return $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create()->getCacheId($this->_mbox, $addl);
1490: } catch (IMP_Imap_Exception $e) {
1491:
1492: return strval(new Horde_Support_Randomid());
1493: }
1494: }
1495:
1496: 1497: 1498: 1499: 1500: 1501: 1502: 1503: 1504: 1505:
1506: protected function _getDisplay($notranslate = false)
1507: {
1508: if (!$notranslate && isset($this->cache[self::CACHE_DISPLAY])) {
1509: return is_bool($this->cache[self::CACHE_DISPLAY])
1510: ? Horde_String::convertCharset($this->_mbox, 'UTF7-IMAP', 'UTF-8')
1511: : $this->cache[self::CACHE_DISPLAY];
1512: }
1513:
1514:
1515: switch ($this->_mbox) {
1516: case IMP_Imap_Tree::OTHER_KEY:
1517: return _("Other Users");
1518:
1519: case IMP_Imap_Tree::SHARED_KEY:
1520: return _("Shared");
1521:
1522: case IMP_Imap_Tree::VFOLDER_KEY:
1523: return _("Virtual Folders");
1524: }
1525:
1526: $ns_info = $this->namespace_info;
1527: $out = $this->_mbox;
1528:
1529: if (!is_null($ns_info)) {
1530:
1531: if (!empty($ns_info['translate']) && $this->namespace) {
1532: $d = Horde_String::convertCharset($ns_info['translate'], 'UTF7-IMAP', 'UTF-8');
1533: $this->_cache[self::CACHE_DISPLAY] = ($ns_info['translate'] == $this->_mbox)
1534: ? true
1535: : $d;
1536: $this->changed = self::CHANGED_YES;
1537: return $d;
1538: }
1539:
1540:
1541: if (!empty($ns_info['name']) &&
1542: ($ns_info['type'] == Horde_Imap_Client::NS_PERSONAL) &&
1543: (substr($this->_mbox, 0, strlen($ns_info['name'])) == $ns_info['name'])) {
1544: $out = substr($this->_mbox, strlen($ns_info['name']));
1545: }
1546: }
1547:
1548: if ($notranslate) {
1549: return $out;
1550: }
1551:
1552:
1553: $sub = array(
1554: 'INBOX' => _("Inbox")
1555: );
1556:
1557: 1558:
1559: foreach ($this->getSpecialMailboxes() as $key => $val) {
1560: switch ($key) {
1561: case self::SPECIAL_DRAFTS:
1562: $sub[strval($val)] = _("Drafts");
1563: break;
1564:
1565: case self::SPECIAL_SENT:
1566: if (count($val) == 1) {
1567: $sub[strval(reset($val))] = _("Sent");
1568: } else {
1569: $sent = self::getPref('sent_mail_folder');
1570: foreach ($val as $mbox) {
1571: if ($mbox == $sent) {
1572: $sub[strval($mbox)] = _("Sent");
1573: break;
1574: }
1575: }
1576: }
1577: break;
1578:
1579: case self::SPECIAL_SPAM:
1580: $sub[strval($val)] = _("Spam");
1581: break;
1582:
1583: case self::SPECIAL_TRASH:
1584: $sub[strval($val)] = _("Trash");
1585: break;
1586: }
1587: }
1588:
1589: foreach ($sub as $key => $val) {
1590: if (strlen($key) &&
1591: (($key != 'INBOX') || ($this->_mbox == $out)) &&
1592: strpos($this->_mbox, $key) === 0) {
1593: $len = strlen($key);
1594: if ((strlen($this->_mbox) == $len) || ($this->_mbox[$len] == (is_null($ns_info) ? '' : $ns_info['delimiter']))) {
1595: $out = substr_replace($this->_mbox, Horde_String::convertCharset($val, 'UTF-8', 'UTF7-IMAP'), 0, $len);
1596: break;
1597: }
1598: }
1599: }
1600:
1601: $d = Horde_String::convertCharset($out, 'UTF7-IMAP', 'UTF-8');
1602: $this->cache[self::CACHE_DISPLAY] = ($out == $this->_mbox)
1603: ? true
1604: : $d;
1605: $this->changed = self::CHANGED_YES;
1606:
1607: return $d;
1608: }
1609:
1610: 1611: 1612: 1613: 1614: 1615: 1616: 1617: 1618: 1619: 1620: 1621:
1622: protected function _getIcon()
1623: {
1624: $info = new stdClass;
1625: $info->iconopen = null;
1626: $info->user_icon = false;
1627:
1628: if ($this->container) {
1629:
1630: if ($this->is_open) {
1631: $info->alt = _("Opened Folder");
1632: $info->class = 'folderopenImg';
1633: $info->icon = 'folders/open.png';
1634: } else {
1635: $info->alt = _("Folder");
1636: $info->class = 'folderImg';
1637: $info->icon = 'folders/folder.png';
1638: $info->iconopen = Horde_Themes::img('folders/open.png');
1639: }
1640: } else {
1641: $special = $this->getSpecialMailboxes();
1642:
1643: switch ($this->_mbox) {
1644: case 'INBOX':
1645: $info->alt = _("Inbox");
1646: $info->class = 'inboxImg';
1647: $info->icon = 'folders/inbox.png';
1648: break;
1649:
1650: case $special[self::SPECIAL_DRAFTS]:
1651: $info->alt = _("Drafts");
1652: $info->class = 'draftsImg';
1653: $info->icon = 'folders/drafts.png';
1654: break;
1655:
1656: case $special[self::SPECIAL_SPAM]:
1657: $info->alt = _("Spam");
1658: $info->class = 'spamImg';
1659: $info->icon = 'folders/spam.png';
1660: break;
1661:
1662: case $special[self::SPECIAL_TRASH]:
1663: $info->alt = _("Trash");
1664: $info->class = 'trashImg';
1665: $info->icon = 'folders/trash.png';
1666: break;
1667:
1668: default:
1669: if (in_array($this->_mbox, $special[self::SPECIAL_SENT])) {
1670: $info->alt = _("Sent");
1671: $info->class = 'sentImg';
1672: $info->icon = 'folders/sent.png';
1673: } else {
1674: $info->alt = _("Mailbox");
1675: if ($this->is_open) {
1676: $info->class = 'folderopenImg';
1677: $info->icon = 'folders/open.png';
1678: } else {
1679: $info->class = 'folderImg';
1680: $info->icon = 'folders/folder.png';
1681: }
1682: }
1683: break;
1684: }
1685:
1686:
1687: if ($this->vfolder) {
1688: $imp_search = $GLOBALS['injector']->getInstance('IMP_Search');
1689: if ($imp_search->isVTrash($this->_mbox)) {
1690: $info->alt = $imp_search[$this->_mbox]->label;
1691: $info->class = 'trashImg';
1692: $info->icon = 'folders/trash.png';
1693: } elseif ($imp_search->isVinbox($this->_mbox)) {
1694: $info->alt = $imp_search[$this->_mbox]->label;
1695: $info->class = 'inboxImg';
1696: $info->icon = 'folders/inbox.png';
1697: }
1698: }
1699: }
1700:
1701:
1702: if (empty($this->cache[self::CACHE_ICONS]) &&
1703: self::$_temp[self::CACHE_HASICONHOOK]) {
1704: if (!isset(self::$_temp[self::CACHE_ICONHOOK])) {
1705: self::$_temp[self::CACHE_ICONHOOK] = Horde::callHook('mbox_icons', array(), 'imp');
1706: }
1707:
1708: if (isset(self::$_temp[self::CACHE_ICONHOOK][$this->_mbox])) {
1709: $this->cache[self::CACHE_ICONS] = self::$_temp[self::CACHE_ICONHOOK][$this->_mbox];
1710: $this->changed = self::CHANGED_YES;
1711: }
1712: }
1713:
1714: if (!empty($this->cache[self::CACHE_ICONS])) {
1715: $mi = $this->cache[self::CACHE_ICONS];
1716:
1717: if (isset($mi['alt'])) {
1718: $info->alt = $mi['alt'];
1719: }
1720: $info->icon = strval($mi['icon']);
1721: $info->user_icon = true;
1722: } elseif ($info->icon) {
1723: $info->icon = Horde_Themes::img($info->icon);
1724: }
1725:
1726: return $info;
1727: }
1728:
1729: 1730: 1731: 1732: 1733:
1734: protected function _onDelete($deleted)
1735: {
1736:
1737: foreach ($this->get($deleted) as $val) {
1738: $val->setSort(null, null, true);
1739: }
1740: }
1741:
1742:
1743:
1744: 1745:
1746: public function serialize()
1747: {
1748: return json_encode(array(
1749: $this->_mbox,
1750: $this->cache
1751: ));
1752: }
1753:
1754: 1755:
1756: public function unserialize($data)
1757: {
1758: list(
1759: $this->_mbox,
1760: $this->cache
1761: ) = json_decode($data, true);
1762: }
1763:
1764: }
1765: