1<?php 2/** 3 * Copyright 2011-2017 Horde LLC (http://www.horde.org/) 4 * 5 * See the enclosed file COPYING for license information (GPL). If you 6 * did not receive this file, see http://www.horde.org/licenses/gpl. 7 * 8 * @category Horde 9 * @copyright 2011-2017 Horde LLC 10 * @license http://www.horde.org/licenses/gpl GPL 11 * @package IMP 12 */ 13 14/** 15 * This object is a clearinghouse for actions related to an IMP mailbox. 16 * 17 * @author Michael Slusarz <slusarz@horde.org> 18 * @category Horde 19 * @copyright 2011-2017 Horde LLC 20 * @license http://www.horde.org/licenses/gpl GPL 21 * @package IMP 22 * 23 * @property-read string $abbrev_label Abbreviated version of $label - 24 * displays only the bare mailbox name 25 * (no parents). 26 * @property-read boolean $access_creatembox Can sub mailboxes be created? 27 * @property-read boolean $access_deletembox Can this mailbox be deleted? 28 * @property-read boolean $access_deletembox_acl Can this mailbox be deleted 29 * according to ACL rules? 30 * @property-read boolean $access_deletemsgs Can messages be deleted in this 31 * mailbox? 32 * @property-read boolean $access_empty Can this mailbox be emptied? 33 * @property-read boolean $access_expunge Can messages be expunged in this 34 * mailbox? 35 * @property-read boolean $access_filters Is filtering available? 36 * @property-read boolean $access_flags Are flags available? 37 * @property-read boolean $access_search Is searching available? 38 * @property-read boolean $access_sort Is sorting available? 39 * @property-read boolean $access_sortthread Is thread sort available? 40 * @property-read mixed $acl Either an ACL object for the mailbox, or null if 41 * no ACL found for the mailbox. 42 * @property-read string $basename The basename of the mailbox (UTF-8). 43 * @property-read string $cacheid Cache ID for the mailbox. 44 * @property-read string $cacheid_date Cache ID for the mailbox, with added 45 * data information. 46 * @property-read boolean $children Does the element have children? 47 * @property-read boolean $container Is this a container element? 48 * @property string $display Display version of mailbox. Special mailboxes 49 * are replaced with localized strings and 50 * namespace information is removed. 51 * @property-read string $display_html $display that has been HTML encoded. 52 * @property-read boolean $drafts Is this a Drafts mailbox? 53 * @property-read boolean $editquery Can this search query be edited? 54 * @property-read boolean $editvfolder Can this virtual folder be edited? 55 * @property-read boolean $exists Does this mailbox exist on the IMAP server? 56 * @property-read string $form_to Converts this mailbox to a form 57 * representation. 58 * @property-read object $icon Icon information for the mailbox. Properties: 59 * - alt: (string) The alt text for the icon. 60 * - class: (string) The CSS class name. 61 * - icon: (Horde_Themes_Image) The icon graphic to use. 62 * - iconopen: (Horde_Themes_Image) The openicon to use. 63 * - user_icon: (boolean) Use a user defined icon? 64 * @property-read IMP_Imap $imp_imap The IMP_Imap object for this mailbox. 65 * @property-read string $imap_mbox The actual name of the underlying IMAP 66 * mailbox. 67 * @property-read Horde_Imap_Client_Mailbox $imap_mbox_ob Convert this object 68 * tp an 69 * Imap_Client mailbox 70 * object. 71 * @property-read boolean $inbox Is this the INBOX? 72 * @property-read boolean $innocent_show Show the innocent action in this 73 * mailbox? 74 * @property-read boolean $invisible Is this mailbox invisible? 75 * @property-read boolean $is_imap Is this an IMAP mailbox? 76 * @property-read boolean $is_open Is this level expanded? 77 * @property-read string $label The mailbox label. Essentially is $display 78 * that can be modified by user hook. 79 * @property-read integer $level The child level of this element. 80 * @property-read IMP_Mailbox_List $list_ob Returns the List object for the 81 * mailbox. 82 * @property-read string $namespace Is this a namespace element? 83 * @property-read IMP_Mailbox $namespace_append The mailbox with necessary 84 * namespace information appended. 85 * @property-read string $namespace_delimiter The delimiter for this 86 * namespace. 87 * @property-read Horde_Imap_Client_Data_Namespace $namespace_info Namespace 88 * info. 89 * @property-read boolean $nonimap Is this a non-IMAP element? 90 * @property-read IMP_Mailbox $parent The parent element. Returns null if no 91 * parent. (Base of tree is returned as 92 * a special element). 93 * @property-read string $parent_imap The IMAP parent name. 94 * @property-read IMP_Imap_PermanentFlags $permflags Return the list of 95 * permanent flags 96 * available to set in the 97 * mailbox. 98 * @property-read boolean $polled Show polled information? 99 * @property-read object $poll_info Poll information for the mailbox. 100 * Properties: 101 * - msgs: (integer) The number of total messages in the element, if polled. 102 * - recent: (integer) The number of new messages in the element, if polled. 103 * - unseen: (integer) The number of unseen messages in the element, if 104 * polled. 105 * @property-read string $pref_from Convert mailbox name from preference 106 * storage. 107 * @property-read string $pref_to Convert mailbox name to preference storage. 108 * @property-read boolean $query Is this a search query? 109 * @property-read boolean $readonly Is this mailbox read-only? 110 * @property-read boolean $remote Is this a remote element? 111 * @property-read IMP_Remote_Account $remote_account Return the account 112 * object for this element 113 * (null if not a remote 114 * element). 115 * @property-read boolean $remote_container Is this mailbox a remote special 116 * element? 117 * @property-read boolean $remote_mbox Is this mailbox on a remote server? 118 * @property-read boolean $search Is this a search mailbox? 119 * @property-read string $size Human readable size of the mailbox. 120 * @property-read integer $size_raw Size of mailbox in bytes. @since 6.2.14 121 * @property-read IMP_Prefs_Sort $sortob Sort ob for use with this mailbox. 122 * @property-read boolean $spam Is this a Spam mailbox? 123 * @property-read boolean $spam_show Show the spam action in this mailbox? 124 * @property-read boolean $special Is this is a "special" element? 125 * @property-read boolean $special_outgoing Is this a "special" element 126 * dealing with outgoing messages? 127 * @property-read boolean $specialvfolder Is this a "special" virtual folder? 128 * @property-read boolean $sub Is this mailbox subscribed to? 129 * @property-read array $subfolders Returns the list of subfolders as mailbox 130 * objects (including the current mailbox). 131 * @property-read array $subfolders_only Returns the list of subfolders as 132 * mailbox objects (NOT including the 133 * current mailbox). 134 * @property-read boolean $systemquery Is this a system (built-in) search 135 * query? 136 * @property-read boolean $templates Is this a Templates mailbox? 137 * @property-read boolean $trash Is this a Trash mailbox? 138 * @property-read IMP_Ftree_Element $tree_elt The tree element (null if it 139 * doesn't exist in the tree). 140 * @property-read string $uidvalid Returns the UIDVALIDITY string. Throws an 141 * IMP_Exception on error. 142 * @property-read string $utf7imap The UTF7-IMAP representation of this 143 * object. 144 * @property-read string $value The value of this element (IMAP mailbox name; 145 * UTF-8). 146 * @property-read boolean $vfolder Is this a virtual folder? 147 * @property-read boolean $vfolder_container Is this the virtual folder 148 * container? 149 * @property-read boolean $vinbox Is this the virtual inbox? 150 * @property-read boolean $vtrash Is this the virtual trash? 151 */ 152class IMP_Mailbox 153{ 154 /* Special mailbox prefs. */ 155 const MBOX_DRAFTS = 'drafts_folder'; 156 const MBOX_SENT = 'sent_mail_folder'; 157 const MBOX_SPAM = 'spam_folder'; 158 const MBOX_TEMPLATES = 'composetemplates_mbox'; 159 const MBOX_TRASH = 'trash_folder'; 160 // This is just a placeholder - this pref doesn't exist. 161 const MBOX_USERSPECIAL = 'user_special'; 162 163 /* Special mailbox identifiers. */ 164 const SPECIAL_COMPOSETEMPLATES = 'composetemplates'; 165 const SPECIAL_DRAFTS = 'drafts'; 166 const SPECIAL_SENT = 'sent'; 167 const SPECIAL_SPAM = 'spam'; 168 const SPECIAL_TRASH = 'trash'; 169 const SPECIAL_USER = 'userspecial'; 170 171 /** 172 * The IMAP mailbox name (UTF-8). 173 * 174 * @var string 175 */ 176 protected $_mbox; 177 178 /** 179 * Shortcut to obtaining mailbox object(s). 180 * 181 * @param mixed $mbox The full IMAP mailbox name(s). 182 * 183 * @return mixed The IMP_Mailbox object(s). 184 */ 185 static public function get($mbox) 186 { 187 if (is_array($mbox)) { 188 return array_filter(array_map(array(__CLASS__, 'get'), $mbox)); 189 } 190 191 if ($mbox instanceof IMP_Mailbox) { 192 return $mbox; 193 } 194 195 try { 196 return $GLOBALS['injector'] 197 ->getInstance('IMP_Factory_Mailbox') 198 ->create(strval($mbox)); 199 } catch (IMP_Exception $e) { 200 return null; 201 } 202 } 203 204 /** 205 * Shortcut to obtaining Horde_Imap_Client_Mailbox object(s). 206 * 207 * @param mixed $mbox The full IMAP mailbox name(s). 208 * 209 * @return mixed The Horde_Imap_Client_Mailbox object(s). 210 */ 211 static public function getImapMboxOb($mbox) 212 { 213 if (is_array($mbox)) { 214 return array_filter(array_map(array(__CLASS__, 'getImapMboxOb'), $mbox)); 215 } 216 217 if ($mbox instanceof Horde_Imap_Client_Mailbox) { 218 return $mbox; 219 } 220 221 // Mailbox names are always UTF-8 within IMP. 222 $mbox_ob = new self($mbox); 223 return Horde_Imap_Client_Mailbox::get($mbox_ob->imap_mbox); 224 } 225 226 /** 227 * Shortcut to obtaining a mailbox object from a preference name. 228 * 229 * @var string $pref The preference name. 230 * 231 * @return IMP_Mailbox The IMP_Mailbox object. 232 */ 233 static public function getPref($pref) 234 { 235 return self::get(self::prefFrom($GLOBALS['prefs']->getValue($pref))); 236 } 237 238 /** 239 * Constructor. 240 * 241 * @var string $mbox The full IMAP mailbox name. 242 * 243 * @throws IMP_Exception 244 */ 245 public function __construct($mbox) 246 { 247 if (strlen($mbox) === 0) { 248 throw new IMP_Exception('Mailbox name must not be empty.'); 249 } 250 251 $this->_mbox = $mbox; 252 } 253 254 /** 255 */ 256 public function __toString() 257 { 258 return strval( 259 ($this->_mbox == IMP_Ftree::BASE_ELT) ? '' : $this->_mbox 260 ); 261 } 262 263 /** 264 */ 265 public function __get($key) 266 { 267 global $injector; 268 269 switch ($key) { 270 case 'abbrev_label': 271 $label = $this->label; 272 return ($this->nonimap || ($pos = strrpos($label, $this->namespace_delimiter)) === false) 273 ? $label 274 : substr($label, $pos + 1); 275 276 case 'access_creatembox': 277 return (!($acl = $this->acl) || 278 ($acl[Horde_Imap_Client::ACL_CREATEMBOX])); 279 280 case 'access_deletembox': 281 return ($this->access_deletembox_acl); 282 283 case 'access_deletembox_acl': 284 return (!($acl = $this->acl) || 285 ($acl[Horde_Imap_Client::ACL_DELETEMBOX])); 286 287 case 'access_deletemsgs': 288 return (!($acl = $this->acl) || 289 ($acl[Horde_Imap_Client::ACL_DELETEMSGS])); 290 291 case 'access_empty': 292 if ($this->access_deletemsgs && $this->access_expunge) { 293 $special = $this->getSpecialMailboxes(); 294 return empty($special[self::SPECIAL_TRASH]) || 295 !$special[self::SPECIAL_TRASH]->vtrash || 296 ($special[self::SPECIAL_TRASH] == $this); 297 } 298 return false; 299 300 case 'access_expunge': 301 return (!($acl = $this->acl) || 302 ($acl[Horde_Imap_Client::ACL_EXPUNGE])); 303 304 case 'access_filters': 305 return !$this->search && $this->is_imap; 306 307 case 'access_flags': 308 return $this->is_imap; 309 310 case 'access_search': 311 return $this->is_imap; 312 313 case 'access_sort': 314 /* Although possible to abstract other sorting methods, all other 315 * non-sequence methods require a download of ALL messages, which 316 * is too much overhead.*/ 317 return $this->is_imap; 318 319 case 'access_sortthread': 320 /* Thread sort is always available for IMAP servers, since 321 * Horde_Imap_Client_Socket has a built-in ORDEREDSUBJECT 322 * implementation. We will always prefer REFERENCES, but will 323 * fallback to ORDEREDSUBJECT if the server doesn't support THREAD 324 * sorting. */ 325 return $this->is_imap; 326 327 case 'acl': 328 $cache = $injector->getInstance('IMP_Mailbox_SessionCache'); 329 if (($acl = $cache->getAcl($this->_mbox)) !== false) { 330 return $acl; 331 } 332 333 if ($this->nonimap) { 334 $acl = null; 335 } else { 336 $acl = $injector->getInstance('IMP_Imap_Acl')->getACL($this, true); 337 $hooks = $injector->getInstance('Horde_Core_Hooks'); 338 339 if ($hooks->hookExists('mbox_acl', 'imp')) { 340 $hooks->callHook('mbox_acl', 'imp', array($this, $acl)); 341 } 342 } 343 344 $cache->setAcl($this->_mbox, $acl); 345 346 return $acl; 347 348 case 'basename': 349 if ($this->nonimap) { 350 return $this->label; 351 } 352 353 $mbox = $this->remote_mbox 354 ? $this->label 355 : $this->_mbox; 356 357 return (($pos = strrpos($mbox, $this->namespace_delimiter)) === false) 358 ? strval($mbox) 359 : substr($mbox, $pos + 1); 360 361 case 'cacheid': 362 case 'cacheid_date': 363 return $this->_getCacheID($key == 'cacheid_date'); 364 365 case 'children': 366 return (($elt = $this->tree_elt) && $elt->children); 367 368 case 'container': 369 return (($elt = $this->tree_elt) && $elt->container); 370 371 case 'display': 372 return $this->nonimap 373 ? $this->label 374 : $this->_getDisplay(); 375 376 case 'display_html': 377 return htmlspecialchars($this->display); 378 379 case 'display_notranslate': 380 return $this->nonimap 381 ? $this->label 382 : $this->_getDisplay(true); 383 384 case 'drafts': 385 $special = $this->getSpecialMailboxes(); 386 return ($this->_mbox == $special[self::SPECIAL_DRAFTS]); 387 388 case 'editquery': 389 return $injector->getInstance('IMP_Search')->isQuery($this->_mbox, true); 390 391 case 'editvfolder': 392 return $injector->getInstance('IMP_Search')->isVFolder($this->_mbox, true); 393 394 case 'exists': 395 return $injector->getInstance('IMP_Mailbox_SessionCache')->exists($this); 396 397 case 'form_to': 398 return $this->formTo($this->_mbox); 399 400 case 'icon': 401 return $this->_getIcon(); 402 403 case 'imp_imap': 404 return $injector->getInstance('IMP_Factory_Imap')->create(strval($this)); 405 406 case 'imap_mbox': 407 return strval( 408 $injector->getInstance('IMP_Remote')->getMailboxById($this->_mbox) ?: $this->_mbox 409 ); 410 411 case 'imap_mbox_ob': 412 return self::getImapMboxOb($this->_mbox); 413 414 case 'inbox': 415 return (strcasecmp($this->_mbox, 'INBOX') === 0); 416 417 case 'innocent_show': 418 $p = $this->imp_imap->config->innocent_params; 419 return (!empty($p) && 420 ((isset($p['display']) && empty($p['display'])) || $this->spam)); 421 422 case 'invisible': 423 return (($elt = $this->tree_elt) && $elt->invisible); 424 425 case 'is_imap': 426 return $this->imp_imap->isImap(); 427 428 case 'is_open': 429 return (($elt = $this->tree_elt) && $elt->open); 430 431 case 'label': 432 $cache = $injector->getInstance('IMP_Mailbox_SessionCache'); 433 if (($label = $cache->getLabel($this->_mbox)) !== false) { 434 return $label; 435 } 436 437 /* Returns the plain text label that is displayed for the 438 * current mailbox, replacing virtual search mailboxes with an 439 * appropriate description, removing namespace and mailbox 440 * prefix information from what is shown to the user, and 441 * passing the label through a user-defined hook. */ 442 $imp_search = $injector->getInstance('IMP_Search'); 443 $label = ($ob = $imp_search[$this->_mbox]) 444 ? $ob->label 445 : $this->_getDisplay(); 446 447 $hooks = $injector->getInstance('Horde_Core_Hooks'); 448 if ($hooks->hookExists('mbox_label' ,'imp')) { 449 $label = $hooks->callHook( 450 'mbox_label', 451 'imp', 452 array($this->_mbox, $label) 453 ); 454 } 455 456 $cache->setLabel($this->_mbox, $label); 457 458 return $label; 459 460 case 'level': 461 return ($elt = $this->tree_elt) ? $elt->level : 0; 462 463 case 'list_ob': 464 return $injector->getInstance('IMP_Factory_MailboxList')->create($this); 465 466 case 'namespace': 467 return (($elt = $this->tree_elt) && $elt->namespace); 468 469 case 'namespace_append': 470 $imp_imap = $this->imp_imap; 471 $def_ns = $imp_imap->getNamespace($imp_imap::NS_DEFAULT); 472 if (is_null($def_ns)) { 473 return $this; 474 } 475 $empty_ns = $imp_imap->getNamespace(''); 476 477 /* If default namespace is empty, or there is no empty namespace, 478 * then we can auto-detect namespace from input. 479 * If a non-default namespace is empty, then we must always use 480 * default namespace. */ 481 if (!is_null($empty_ns) && 482 ($def_ns->name == $empty_ns->name)) { 483 return $this; 484 } 485 486 $ns_info = $this->namespace_info; 487 488 if (is_null($ns_info) || !is_null($empty_ns)) { 489 return self::get($def_ns->name . $this->_mbox); 490 } 491 492 return $this; 493 494 case 'namespace_delimiter': 495 $ns_info = $this->namespace_info; 496 return is_null($ns_info) 497 ? '' 498 : $ns_info->delimiter; 499 500 case 'namespace_info': 501 return $this->imp_imap->getNamespace(strlen($this) ? $this->_mbox : IMP_Imap::NS_DEFAULT); 502 503 case 'nonimap': 504 return ($this->search || 505 (($elt = $this->tree_elt) && $elt->nonimap)); 506 507 case 'parent': 508 return ($elt = $this->tree_elt) ? $elt->parent->mbox_ob : null; 509 510 case 'parent_imap': 511 return (is_null($p = $this->parent) || !strlen($p)) 512 ? null 513 : $p; 514 515 case 'permflags': 516 if ($this->access_flags) { 517 $imp_imap = $this->imp_imap; 518 try { 519 /* Make sure we are in R/W mailbox mode (SELECT). No flags 520 * are allowed in EXAMINE mode. */ 521 $imp_imap->openMailbox($this, Horde_Imap_Client::OPEN_READWRITE); 522 $status = $imp_imap->status($this->_mbox, Horde_Imap_Client::STATUS_FLAGS | Horde_Imap_Client::STATUS_PERMFLAGS); 523 return new IMP_Imap_PermanentFlags($status['permflags'], $status['flags']); 524 } catch (Exception $e) {} 525 } 526 527 return new IMP_Imap_PermanentFlags(); 528 529 case 'poll_info': 530 $info = new stdClass; 531 $info->msgs = 0; 532 $info->recent = 0; 533 $info->unseen = 0; 534 535 try { 536 if ($msgs_info = $this->imp_imap->status($this->_mbox, Horde_Imap_Client::STATUS_RECENT_TOTAL | Horde_Imap_Client::STATUS_UNSEEN | Horde_Imap_Client::STATUS_MESSAGES)) { 537 if (!empty($msgs_info['recent_total'])) { 538 $info->recent = intval($msgs_info['recent_total']); 539 } 540 $info->msgs = intval($msgs_info['messages']); 541 $info->unseen = intval($msgs_info['unseen']); 542 } 543 } catch (IMP_Imap_Exception $e) {} 544 545 return $info; 546 547 case 'polled': 548 return (!$this->search && 549 (($elt = $this->tree_elt) && $elt->polled)); 550 551 case 'pref_from': 552 return $this->prefFrom($this->_mbox); 553 554 case 'pref_to': 555 return $this->prefTo($this->_mbox); 556 557 case 'query': 558 return $injector->getInstance('IMP_Search')->isQuery($this->_mbox); 559 560 case 'readonly': 561 return (($acl = $this->acl) && 562 !$acl[Horde_Imap_Client::ACL_DELETEMBOX] && 563 !$acl[Horde_Imap_Client::ACL_DELETEMSGS] && 564 !$acl[Horde_Imap_Client::ACL_EXPUNGE] && 565 !$acl[Horde_Imap_Client::ACL_INSERT] && 566 !$acl[Horde_Imap_Client::ACL_SEEN] && 567 !$acl[Horde_Imap_Client::ACL_WRITE]); 568 569 case 'remote': 570 return $injector->getInstance('IMP_Remote')->isRemoteMbox($this->_mbox); 571 572 case 'remote_account': 573 $remote = $injector->getInstance('IMP_Remote'); 574 $account = ($this->remote_container) 575 ? $remote[$this->_mbox] 576 : $remote->getRemoteById($this->_mbox); 577 return $account ?: null; 578 579 case 'remote_container': 580 return (($elt = $this->tree_elt) && $elt->remote); 581 582 case 'remote_mbox': 583 return (($elt = $this->tree_elt) && $elt->remote_mbox); 584 585 case 'search': 586 return $injector->getInstance('IMP_Search')->isSearchMbox($this->_mbox); 587 588 case 'size': 589 return $injector->getInstance('IMP_Mbox_Size')->getSize($this); 590 591 case 'size_raw': 592 return $injector->getInstance('IMP_Mbox_Size')->getSize($this, false); 593 594 case 'sortob': 595 return $this->imp_imap->access(IMP_Imap::ACCESS_SORT) 596 ? $injector->getInstance('IMP_Prefs_Sort') 597 : $injector->getInstance('IMP_Prefs_Sort_None'); 598 599 case 'spam': 600 $special = $this->getSpecialMailboxes(); 601 return ($this->_mbox == $special[self::SPECIAL_SPAM]); 602 603 case 'spam_show': 604 $p = $this->imp_imap->config->spam_params; 605 return (!empty($p) && (!empty($p['display']) || !$this->spam)); 606 607 case 'special': 608 $special = $this->getSpecialMailboxes(); 609 610 switch ($this->_mbox) { 611 case $special[self::SPECIAL_COMPOSETEMPLATES]: 612 case $special[self::SPECIAL_DRAFTS]: 613 case $special[self::SPECIAL_SPAM]: 614 case $special[self::SPECIAL_TRASH]: 615 return true; 616 } 617 618 return in_array($this->_mbox, array_merge( 619 $special[self::SPECIAL_SENT], 620 $special[self::SPECIAL_USER] 621 )); 622 623 case 'special_outgoing': 624 $special = $this->getSpecialMailboxes(); 625 626 return in_array($this->_mbox, array_merge( 627 array( 628 $special[self::SPECIAL_COMPOSETEMPLATES], 629 $special[self::SPECIAL_DRAFTS] 630 ), 631 $special[self::SPECIAL_SENT] 632 )); 633 634 case 'specialvfolder': 635 return !$this->editvfolder; 636 637 case 'sub': 638 return (($elt = $this->tree_elt) && $elt->subscribed); 639 640 case 'subfolders': 641 return $this->get(array_merge(array($this->_mbox), $this->subfolders_only)); 642 643 case 'subfolders_only': 644 return $this->get($this->imp_imap->listMailboxes($this->imap_mbox_ob->list_escape . $this->namespace_delimiter . '*', null, array('flat' => true))); 645 646 case 'systemquery': 647 return $injector->getInstance('IMP_Search')->isSystemQuery($this->_mbox); 648 649 case 'templates': 650 $special = $this->getSpecialMailboxes(); 651 return ($this->_mbox == $special[self::SPECIAL_COMPOSETEMPLATES]); 652 653 case 'trash': 654 $special = $this->getSpecialMailboxes(); 655 return ($this->_mbox == $special[self::SPECIAL_TRASH]); 656 657 case 'tree_elt': 658 $ftree = $injector->getInstance('IMP_Ftree'); 659 return $ftree[$this->_mbox]; 660 661 case 'uidvalid': 662 $cache = $injector->getInstance('IMP_Mailbox_SessionCache'); 663 $uidvalid = $cache->getUidvalidity($this->_mbox); 664 if ($uidvalid === 0) { 665 return; 666 } 667 668 // POP3 and non-IMAP mailboxes do not support UIDVALIDITY. 669 if (!$this->is_imap || $this->nonimap) { 670 $cache->setUidvalidity($this->_mbox, 0); 671 return false; 672 } 673 674 $status = $this->imp_imap->status($this->_mbox, Horde_Imap_Client::STATUS_UIDVALIDITY); 675 676 if (($first = ($uidvalid === false)) || 677 ($status['uidvalidity'] != $uidvalid)) { 678 $uidvalid = $status['uidvalidity']; 679 $cache->setUidvalidity($this->_mbox, $uidvalid); 680 681 if (!$first) { 682 throw new IMP_Exception(_("Mailbox structure on server has changed.")); 683 } 684 } 685 686 return $uidvalid; 687 688 case 'utf7imap': 689 return Horde_String::convertCharset($this->_mbox, 'UTF-8', 'UTF7-IMAP'); 690 691 case 'value': 692 return $this->_mbox; 693 694 case 'vfolder': 695 return $injector->getInstance('IMP_Search')->isVFolder($this->_mbox); 696 697 case 'vfolder_container': 698 return ($this->_mbox == IMP_Ftree_Account_Vfolder::VFOLDER_KEY); 699 700 case 'vinbox': 701 return $injector->getInstance('IMP_Search')->isVinbox($this->_mbox); 702 703 case 'vtrash': 704 return $injector->getInstance('IMP_Search')->isVTrash($this->_mbox); 705 } 706 707 return false; 708 } 709 710 /** 711 */ 712 public function __set($key, $value) 713 { 714 global $injector; 715 716 switch ($key) { 717 case 'display': 718 $injector->getInstance('IMP_Mailbox_SessionCache')->setDisplay($this->_mbox, $value); 719 break; 720 } 721 } 722 723 /** 724 * Create this mailbox on the server. 725 * 726 * @param array $opts Additional options: 727 * - special_use: (array) An array of special-use attributes to attempt 728 * to add to the mailbox. 729 * DEFAULT: NONE 730 * - subscribe: (boolean) Override preference value of subscribe. 731 * 732 * @return boolean True on success. 733 * @throws Horde_Exception 734 */ 735 public function create(array $opts = array()) 736 { 737 global $injector, $notification, $prefs; 738 739 if ($this->exists) { 740 return true; 741 } 742 743 $imp_imap = $this->imp_imap; 744 745 /* Check permissions. */ 746 if (!$imp_imap->access(IMP_Imap::ACCESS_CREATEMBOX)) { 747 Horde::permissionDeniedError( 748 'imp', 749 'create_mboxes', 750 _("You are not allowed to create mailboxes.") 751 ); 752 return false; 753 } 754 if (!$imp_imap->access(IMP_Imap::ACCESS_CREATEMBOX_MAX)) { 755 Horde::permissionDeniedError( 756 'imp', 757 'max_create_mboxes', 758 sprintf(_("You are not allowed to create more than %d mailboxes."), $imp_imap->max_create_mboxes) 759 ); 760 return false; 761 } 762 763 /* Special use flags. */ 764 $special_use = isset($opts['special_use']) 765 ? $opts['special_use'] 766 : array(); 767 768 /* Attempt to create the mailbox. */ 769 try { 770 $imp_imap->createMailbox($this->_mbox, array('special_use' => $special_use)); 771 } catch (IMP_Imap_Exception $e) { 772 if ($e->getCode() == $e::USEATTR) { 773 unset($opts['special_use']); 774 return $this->create($opts); 775 } 776 777 $e->notify(sprintf(_("The mailbox \"%s\" was not created. This is what the server said"), $this->display) . ': ' . $e->getMessage()); 778 return false; 779 } 780 781 $notification->push(sprintf(_("The mailbox \"%s\" was successfully created."), $this->display), 'horde.success'); 782 783 /* Subscribe, if requested. */ 784 if ((!isset($opts['subscribe']) && $prefs->getValue('subscribe')) || 785 !empty($opts['subscribe'])) { 786 try { 787 $imp_imap->subscribeMailbox($this->_mbox, true); 788 } catch (IMP_Imap_Exception $e) {} 789 } 790 791 /* Update the mailbox tree. */ 792 $injector->getInstance('IMP_Ftree')->insert($this->_mbox); 793 794 return true; 795 } 796 797 /** 798 * Deletes mailbox. 799 * 800 * @param array $opts Addtional options: 801 * - subfolders: (boolean) Delete all subfolders? 802 * DEFAULT: false 803 * - subfolders_only: (boolean) If deleting subfolders, delete only 804 * subfolders (not current mailbox)? 805 * DEFAULT: false 806 * 807 * @return boolean True on success. 808 */ 809 public function delete(array $opts = array()) 810 { 811 global $injector, $notification; 812 813 if ($this->vfolder) { 814 if ($this->editvfolder) { 815 $imp_search = $injector->getInstance('IMP_Search'); 816 $label = $imp_search[$this->_mbox]->label; 817 unset($imp_search[$this->_mbox]); 818 $notification->push(sprintf(_("Deleted Virtual Folder \"%s\"."), $label), 'horde.success'); 819 return true; 820 } 821 822 $notification->push(sprintf(_("Could not delete Virtual Folder \"%s\"."), $this->label), 'horde.error'); 823 return false; 824 } 825 826 $deleted = array(); 827 $imp_imap = $this->imp_imap; 828 if (empty($opts['subfolders'])) { 829 $to_delete = array($this); 830 } else { 831 $to_delete = empty($opts['subfolders_only']) 832 ? $this->subfolders 833 : $this->subfolders_only; 834 } 835 836 foreach ($to_delete as $val) { 837 if (!$val->access_deletembox_acl) { 838 $notification->push(sprintf(_("The mailbox \"%s\" may not be deleted."), $val->display), 'horde.error'); 839 continue; 840 } 841 842 try { 843 $imp_imap->deleteMailbox($val->value); 844 $notification->push(sprintf(_("The mailbox \"%s\" was successfully deleted."), $val->display), 'horde.success'); 845 $deleted[] = $val; 846 } catch (IMP_Imap_Exception $e) { 847 $e->notify(sprintf(_("The mailbox \"%s\" was not deleted. This is what the server said"), $val->display) . ': ' . $e->getMessage()); 848 } 849 } 850 851 if (!empty($deleted)) { 852 $injector->getInstance('IMP_Ftree')->delete($deleted); 853 $this->_onDelete($deleted); 854 } 855 856 return (count($deleted) == count($to_delete)); 857 } 858 859 /** 860 * Rename this mailbox on the server. The subscription status remains the 861 * same. All subfolders will also be renamed. 862 * 863 * @param string $new_name The new mailbox name (UTF-8). 864 * 865 * @return boolean True on success 866 */ 867 public function rename($new_name) 868 { 869 global $injector, $notification; 870 871 /* Don't try to rename to an empty string. */ 872 if (!strlen($new_name)) { 873 return false; 874 } 875 876 if (!$this->access_deletembox_acl) { 877 $notification->push(sprintf(_("The mailbox \"%s\" may not be renamed."), $this->display), 'horde.error'); 878 return false; 879 } 880 881 $new_mbox = $this->get($new_name); 882 $old_list = $this->subfolders; 883 884 try { 885 $this->imp_imap->renameMailbox($this->_mbox, $new_mbox); 886 } catch (IMP_Imap_Exception $e) { 887 $e->notify(sprintf(_("Renaming \"%s\" to \"%s\" failed. This is what the server said"), $this->display, $new_mbox->display) . ': ' . $e->getMessage()); 888 return false; 889 } 890 891 $notification->push(sprintf(_("The mailbox \"%s\" was successfully renamed to \"%s\"."), $this->display, $new_mbox->display), 'horde.success'); 892 893 $injector->getInstance('IMP_Ftree')->rename($this->_mbox, $new_mbox); 894 $this->_onDelete($old_list); 895 896 return true; 897 } 898 899 /** 900 * Subscribe/unsubscribe to an IMAP mailbox. 901 * 902 * @param boolean $sub True to subscribe, false to unsubscribe. 903 * @param array $opts Additional options: 904 * <pre> 905 * - subfolders: (boolean) If true, applies actions to all subfolders. 906 * </pre> 907 * 908 * @return boolean True on success. 909 */ 910 public function subscribe($sub, array $opts = array()) 911 { 912 global $injector, $notification, $prefs; 913 914 /* Skip non-IMAP/container mailboxes. */ 915 if (!$prefs->getValue('subscribe') || 916 $this->nonimap || 917 $this->container) { 918 return false; 919 } 920 921 if (!$sub && $this->inbox) { 922 $notification->push(sprintf(_("You cannot unsubscribe from \"%s\"."), $this->display), 'horde.error'); 923 return false; 924 } 925 926 $imp_imap = $this->imp_imap; 927 928 try { 929 $imp_imap->subscribeMailbox($this->_mbox, $sub); 930 } catch (IMP_Imap_Exception $e) { 931 if ($sub) { 932 $e->notify(sprintf(_("You were not subscribed to \"%s\". Here is what the server said"), $this->display) . ': ' . $e->getMessage()); 933 } else { 934 $e->notify(sprintf(_("You were not unsubscribed from \"%s\". Here is what the server said"), $this->display) . ': ' . $e->getMessage()); 935 } 936 return false; 937 } 938 939 $imap_tree = $injector->getInstance('IMP_Ftree'); 940 if ($sub) { 941 $imap_tree->subscribe($this->_mbox); 942 } else { 943 $imap_tree->unsubscribe($this->_mbox); 944 } 945 946 if (empty($opts['subfolders'])) { 947 $notify = $sub 948 ? sprintf(_("You were successfully subscribed to \"%s\"."), $this->display) 949 : sprintf(_("You were successfully unsubscribed from \"%s\"."), $this->display); 950 } else { 951 $action = false; 952 953 foreach ($this->subfolders_only as $val) { 954 try { 955 $imp_imap->subscribeMailbox($val, $sub); 956 if ($sub) { 957 $imap_tree->subscribe($val); 958 } else { 959 $imap_tree->unsubscribe($val); 960 } 961 962 $action = true; 963 } catch (IMP_Imap_Exception $e) { 964 // Ignore errors for sub-mailboxes. 965 } 966 } 967 968 if ($action) { 969 $notify = $sub 970 ? sprintf(_("You were successfully subscribed to \"%s\" and all subfolders."), $this->display) 971 : sprintf(_("You were successfully unsubscribed from \"%s\" and all subfolders."), $this->display); 972 } 973 } 974 975 $notification->push($notify, 'horde.success'); 976 977 return true; 978 } 979 980 /** 981 * Runs filters on this mailbox. 982 */ 983 public function filter() 984 { 985 if (!$this->search) { 986 $GLOBALS['injector']->getInstance('IMP_Filter')->filter($this); 987 } 988 } 989 990 /** 991 * Filters this mailbox if it is the INBOX and the filter on display pref 992 * is active. 993 * 994 * @return boolean True if filter() was called. 995 */ 996 public function filterOnDisplay() 997 { 998 if ($this->inbox && 999 $GLOBALS['prefs']->getValue('filter_on_display')) { 1000 $this->filter(); 1001 return true; 1002 } 1003 1004 return false; 1005 } 1006 1007 /** 1008 * Return the search query object for this mailbox. 1009 * 1010 * @return IMP_Search_Query The search query object. 1011 */ 1012 public function getSearchOb() 1013 { 1014 $imp_search = $GLOBALS['injector']->getInstance('IMP_Search'); 1015 return $imp_search[$this->_mbox]; 1016 } 1017 1018 /** 1019 * Return an indices object for this mailbox. 1020 * 1021 * @param mixed $in Either a single UID, array of UIDs, or a 1022 * Horde_Imap_Client_Ids object. 1023 * 1024 * @return IMP_Indices An indices object. 1025 */ 1026 public function getIndicesOb($in) 1027 { 1028 return new IMP_Indices($this, $in); 1029 } 1030 1031 /** 1032 * Return the sorting preference for this mailbox. 1033 * 1034 * @param boolean $convert Convert 'by' to a Horde_Imap_Client constant? 1035 * 1036 * @return IMP_Prefs_Sort_Sortpref Sortpref object. 1037 */ 1038 public function getSort($convert = false) 1039 { 1040 global $prefs; 1041 1042 $mbox = $this->search 1043 ? $this 1044 : self::get($this->pref_from); 1045 1046 $ob = $this->sortob[strval($mbox)]; 1047 $ob->convertSortby(); 1048 1049 if ($convert && ($ob->sortby == IMP::IMAP_SORT_DATE)) { 1050 $ob->sortby = $prefs->getValue('sortdate'); 1051 } 1052 1053 return $ob; 1054 } 1055 1056 /** 1057 * Set the sorting preference for this mailbox. 1058 * 1059 * @param integer $by The sort type. 1060 * @param integer $dir The sort direction. 1061 * @param boolean $delete Delete the entry? 1062 */ 1063 public function setSort($by = null, $dir = null, $delete = false) 1064 { 1065 $mbox = $this->search 1066 ? $this 1067 : self::get($this->pref_from); 1068 1069 if ($delete) { 1070 unset($this->sortob[strval($mbox)]); 1071 } else { 1072 $change = array(); 1073 if (!is_null($by)) { 1074 $change['by'] = $by; 1075 } 1076 if (!is_null($dir)) { 1077 $change['dir'] = $dir; 1078 } 1079 $this->sortob[strval($mbox)] = $change; 1080 } 1081 } 1082 1083 /** 1084 * Are deleted messages hidden in this mailbox? 1085 * 1086 * @param boolean $deleted Return value is what should be done with 1087 * deleted messages in general, as opposed to any 1088 * deleted message in the mailbox. 1089 * 1090 * @return boolean True if deleted messages should be hidden. 1091 */ 1092 public function hideDeletedMsgs($deleted = false) 1093 { 1094 global $prefs; 1095 1096 if (!$this->access_flags) { 1097 return true; 1098 } 1099 1100 if ($prefs->getValue('use_trash')) { 1101 /* If using Virtual Trash, only show deleted messages in 1102 * the Virtual Trash mailbox. */ 1103 return $this->get($prefs->getValue(self::MBOX_TRASH))->vtrash 1104 ? !$this->vtrash 1105 : ($prefs->getValue('delhide_trash') ? true : $deleted); 1106 } 1107 1108 return $prefs->getValue('delhide'); 1109 } 1110 1111 /** 1112 * Sets the 'delhide' preference and clears necessary cached data. 1113 * 1114 * @param boolean $value The value to set 'delhide' to. 1115 */ 1116 public function setHideDeletedMsgs($value) 1117 { 1118 $GLOBALS['prefs']->setValue('delhide', $value); 1119 $GLOBALS['injector']->getInstance('IMP_Factory_MailboxList')->expireAll(); 1120 } 1121 1122 /** 1123 * Run a search query on this mailbox that is not stored in the current 1124 * session. Allows custom queries with custom sorts to be used without 1125 * affecting cached mailboxes. 1126 * 1127 * @param Horde_Imap_Client_Search_Query $query The search query object. 1128 * @param integer $sortby The sort criteria. 1129 * @param integer $sortdir The sort directory. 1130 * 1131 * @return IMP_Indices An indices object. 1132 */ 1133 public function runSearchQuery(Horde_Imap_Client_Search_Query $query, 1134 $sortby = null, $sortdir = null) 1135 { 1136 try { 1137 $results = $this->imp_imap->search($this, $query, array( 1138 'sort' => is_null($sortby) ? null : array($sortby) 1139 )); 1140 if ($sortdir) { 1141 $results['match']->reverse(); 1142 } 1143 return $this->getIndicesOb($results['match']); 1144 } catch (IMP_Imap_Exception $e) { 1145 return new IMP_Indices(); 1146 } 1147 } 1148 1149 /** 1150 * Generate a URL using the current mailbox. 1151 * 1152 * @param string|Horde_Url $page Page name to link to. 1153 * @param string $buid The BUID to use on the linked page. 1154 * @param boolean $encode Encode the argument separator? 1155 * 1156 * @return Horde_Url URL to $page with any necessary mailbox information 1157 * added to the parameter list of the URL. 1158 */ 1159 public function url($page, $buid = null, $encode = true) 1160 { 1161 if ($page instanceof Horde_Url) { 1162 return $page->add($this->urlParams($buid))->setRaw(!$encode); 1163 } 1164 1165 switch ($GLOBALS['registry']->getView()) { 1166 case Horde_Registry::VIEW_BASIC: 1167 switch ($page) { 1168 case 'message': 1169 return IMP_Basic_Message::url(array( 1170 'buid' => $buid, 1171 'mailbox' => $this->_mbox 1172 ))->setRaw(!$encode); 1173 1174 case 'mailbox': 1175 return IMP_Basic_Mailbox::url(array( 1176 'mailbox' => $this->_mbox 1177 ))->setRaw(!$encode); 1178 } 1179 break; 1180 1181 case Horde_Registry::VIEW_DYNAMIC: 1182 $anchor = is_null($buid) 1183 ? ('mbox:' . $this->form_to) 1184 : ('msg:' . $this->form_to . ';' . $buid); 1185 return Horde::url('index.php')->setAnchor($anchor); 1186 1187 case Horde_Registry::VIEW_MINIMAL: 1188 switch ($page) { 1189 case 'message': 1190 return IMP_Minimal_Message::url(array( 1191 'buid' => $buid, 1192 'mailbox' => $this->_mbox 1193 ))->setRaw(!$encode); 1194 1195 case 'mailbox': 1196 return IMP_Minimal_Mailbox::url(array( 1197 'mailbox' => $this->_mbox 1198 ))->setRaw(!$encode); 1199 } 1200 break; 1201 1202 case Horde_Registry::VIEW_SMARTMOBILE: 1203 $url = Horde::url('smartmobile.php'); 1204 $anchor = is_null($buid) 1205 ? ('mbox=' . $this->form_to) 1206 : ('msg=' . $this->form_to . ';' . $buid); 1207 $url->setAnchor('mailbox?' . $anchor); 1208 return $url; 1209 } 1210 1211 return Horde::url($page . '.php')->add($this->urlParams($buid))->setRaw(!$encode); 1212 } 1213 1214 /** 1215 * Returns list of URL parameters necessary to indicate current mailbox 1216 * status. 1217 * 1218 * @param string $buid The BUID to use on the linked page. 1219 * 1220 * @return array The list of parameters needed to indicate the current 1221 * mailbox status. 1222 */ 1223 public function urlParams($buid = null) 1224 { 1225 $params = array('mailbox' => $this->form_to); 1226 if (!is_null($buid)) { 1227 $params['buid'] = $buid; 1228 } 1229 return $params; 1230 } 1231 1232 /** 1233 * Determines if this mailbox is equal to the given mailbox. 1234 * Needed because directly comparing two mailbox objects may fail (the 1235 * member variables may be different). 1236 * 1237 * @param mixed $mbox The mailbox to compare to. 1238 * 1239 * @return boolean True if the mailboxes are the same. 1240 */ 1241 public function equals($mbox) 1242 { 1243 return ($mbox == $this->_mbox); 1244 } 1245 1246 /** 1247 * Create an indices object from a list of browser-UIDs. 1248 * 1249 * @param IMP_Indices|array $buids Browser-UIDs. 1250 * 1251 * @return IMP_Indices An indices object. 1252 */ 1253 public function fromBuids($buids) 1254 { 1255 if (is_array($buids)) { 1256 $buids = new IMP_Indices($this->_mbox, $buids); 1257 } 1258 $buid_list = $buids->getSingle(true); 1259 1260 $out = new IMP_Indices(); 1261 1262 if ($buid_list[1]) { 1263 $list_ob = $this->list_ob; 1264 foreach ($buid_list[1] as $buid) { 1265 if ($resolve = $list_ob->resolveBuid($buid)) { 1266 $out->add($resolve['m'], $resolve['u']); 1267 } 1268 } 1269 } 1270 1271 return $out; 1272 } 1273 1274 /** 1275 * Create a BUID indices object from a list of UIDs. 1276 * 1277 * @param IMP_Indices $uids UIDs. 1278 * 1279 * @return IMP_Indices An indices object. 1280 */ 1281 public function toBuids(IMP_Indices $uids) 1282 { 1283 $list_ob = $this->list_ob; 1284 $out = new IMP_Indices(); 1285 1286 foreach ($uids as $val) { 1287 foreach ($val->uids as $val2) { 1288 $out->add($this->_mbox, $list_ob->getBuid($val->mbox, $val2)); 1289 } 1290 } 1291 1292 return $out; 1293 } 1294 1295 /** 1296 * Return the mailbox name to create given a submailbox name. 1297 * 1298 * @param string $new The submailbox name (UTF-8). 1299 * 1300 * @return IMP_Mailbox The mailbox to create. 1301 */ 1302 public function createMailboxName($new) 1303 { 1304 if ($this->remote_container) { 1305 $new = $this->remote_account->mailbox($new); 1306 } else { 1307 $ns_info = $this->namespace_info; 1308 $new = strlen($this) 1309 ? ($this->_mbox . $ns_info->delimiter . $new) 1310 : $ns_info->name . $new; 1311 } 1312 1313 return self::get($new); 1314 } 1315 1316 /* Static methods. */ 1317 1318 /** 1319 * Converts a mailbox string from a form representation. 1320 * Needed because null characters (used for various internal non-IMAP 1321 * mailbox representations) will not work in form elements. 1322 * 1323 * @param mixed $mbox The mailbox name(s). 1324 * 1325 * @return mixed The mailbox object(s). 1326 */ 1327 static public function formFrom($mbox) 1328 { 1329 return is_array($mbox) 1330 ? array_filter(array_map(array(__CLASS__, 'formFrom'), $mbox)) 1331 // Base64url (RFC 4648 [5]) encoding 1332 : self::get(base64_decode(strtr($mbox, '-_', '+/'))); 1333 } 1334 1335 /** 1336 * Converts a mailbox string to a form representation. 1337 * Needed because null characters (used for various internal non-IMAP 1338 * mailbox representations) will not work in form elements. 1339 * 1340 * @param mixed $mbox The mailbox name(s). 1341 * 1342 * @return mixed The converted mailbox string(s). 1343 */ 1344 static public function formTo($mbox) 1345 { 1346 return is_array($mbox) 1347 ? array_filter(array_map(array(__CLASS__, 'formTo'), $mbox)) 1348 // Base64url (RFC 4648 [5]) encoding 1349 : strtr(rtrim(base64_encode($mbox), '='), '+/', '-_'); 1350 } 1351 1352 /** 1353 * Return the list of special mailboxes. 1354 * 1355 * @return array A list of mailboxes, with the self::SPECIAL_* constants 1356 * as keys and values containing the IMP_Mailbox objects or 1357 * null if the mailbox doesn't exist (self::SPECIAL_SENT 1358 * contains an array of objects). 1359 */ 1360 static public function getSpecialMailboxes() 1361 { 1362 global $injector; 1363 1364 return $injector->getInstance('IMP_Mailbox_SessionCache')->getSpecialMailboxes(); 1365 } 1366 1367 /** 1368 * Return the list of sorted special mailboxes. 1369 * 1370 * @return array The list of sorted special mailboxes (IMP_Mailbox 1371 * objects). 1372 */ 1373 static public function getSpecialMailboxesSort() 1374 { 1375 $out = array(); 1376 1377 foreach (array_filter(self::getSpecialMailboxes()) as $val) { 1378 if (is_array($val)) { 1379 $out = array_merge($out, $val); 1380 } else { 1381 $out[] = $val; 1382 } 1383 } 1384 1385 $tmp = array(); 1386 foreach ($out as $val) { 1387 $tmp[strval($val)] = $val->abbrev_label; 1388 } 1389 asort($tmp, SORT_LOCALE_STRING); 1390 1391 return self::get(array_keys($tmp)); 1392 } 1393 1394 /** 1395 * Converts a mailbox name from a value stored in the preferences. 1396 * 1397 * @param string $mbox The mailbox name as stored in a preference. 1398 * 1399 * @return string The full IMAP mailbox name (UTF-8). 1400 */ 1401 static public function prefFrom($mbox) 1402 { 1403 $imp_imap = $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create(); 1404 if ($imp_imap->isImap()) { 1405 $empty_ns = $imp_imap->getNamespace(''); 1406 1407 if (!is_null($empty_ns) && 1408 (strpos($mbox, $empty_ns->delimiter) === 0)) { 1409 /* Prefixed with delimiter => from empty namespace. */ 1410 return substr($mbox, strlen($empty_ns->delimiter)); 1411 } elseif ($imp_imap->getNamespace($mbox, true) === null) { 1412 /* No namespace prefix => from personal namespace. */ 1413 $def_ns = $imp_imap->getNamespace($imp_imap::NS_DEFAULT); 1414 return $def_ns->name . $mbox; 1415 } 1416 } 1417 1418 return $mbox; 1419 } 1420 1421 /** 1422 * Converts a mailbox name to a value to be stored in a preference. 1423 * 1424 * @param string $mbox The full IMAP mailbox name (UTF-8). 1425 * 1426 * @return string The value to store in a preference. 1427 */ 1428 static public function prefTo($mbox) 1429 { 1430 global $injector; 1431 1432 $cache = $injector->getInstance('IMP_Mailbox_SessionCache'); 1433 $mbox_str = $ret = strval($mbox); 1434 1435 if (($pref_to = $cache->getPrefTo($mbox_str)) !== false) { 1436 return $pref_to; 1437 } 1438 1439 if (($ns = self::get($mbox)->namespace_info) !== null) { 1440 $imp_imap = $injector->getInstance('IMP_Factory_Imap')->create(); 1441 $def_ns = $imp_imap->getNamespace($imp_imap::NS_DEFAULT); 1442 1443 if ($ns->name == $def_ns->name) { 1444 /* From personal namespace => strip namespace. */ 1445 $ret = substr($mbox_str, strlen($def_ns->name)); 1446 } else { 1447 $empty_ns = $imp_imap->getNamespace(''); 1448 if ($ns->name == $empty_ns->name) { 1449 /* From empty namespace => prefix with delimiter. */ 1450 $ret = $empty_ns->delimiter . $mbox_str; 1451 } 1452 } 1453 } 1454 1455 $cache->setPrefTo($mbox_str, $ret); 1456 1457 return $ret; 1458 } 1459 1460 /* Internal methods. */ 1461 1462 /** 1463 * Returns a unique identifier for this mailbox's status. 1464 * 1465 * This cache ID is guaranteed to change if messages are added/deleted 1466 * from the mailbox. Additionally, if CONDSTORE is available on the remote 1467 * IMAP server, this ID will change if flag information changes. 1468 * 1469 * For search mailboxes, this value never changes (search mailboxes must 1470 * be forcibly refreshed). 1471 * 1472 * @param boolean $date If true, adds date information to ID. 1473 * 1474 * @return string The cache ID string, which will change when the 1475 * composition of this mailbox changes. 1476 */ 1477 protected function _getCacheID($date = false) 1478 { 1479 global $prefs; 1480 1481 $date = $date 1482 ? 'D' . date('z') 1483 : ''; 1484 1485 if ($this->search) { 1486 return '1' . ($date ? '|' . $date : ''); 1487 } 1488 1489 $sortpref = $this->getSort(true); 1490 $addl = array( 1491 $sortpref->sortby, 1492 $sortpref->sortdir, 1493 intval($prefs->getValue('delhide')) 1494 ); 1495 if ($date) { 1496 $addl[] = $date; 1497 } 1498 1499 try { 1500 return $this->imp_imap->getCacheId($this->_mbox, $addl); 1501 } catch (IMP_Imap_Exception $e) { 1502 /* Assume an error means that a mailbox can not be trusted. */ 1503 return strval(new Horde_Support_Randomid()); 1504 } 1505 } 1506 1507 /** 1508 * If there is information available to tell us about a prefix in front of 1509 * mailbox names that shouldn't be displayed to the user, then use it to 1510 * strip that prefix out. Additionally, translate prefix text if this 1511 * is a special mailbox. 1512 * 1513 * @param boolean $notranslate Don't translate the mailbox prefix? 1514 * 1515 * @return string The mailbox, with any prefix gone/translated. 1516 */ 1517 protected function _getDisplay($notranslate = false) 1518 { 1519 global $injector; 1520 1521 $cache = $injector->getInstance('IMP_Mailbox_SessionCache'); 1522 if (!$notranslate && 1523 (($display = $cache->getDisplay($this->_mbox)) !== false)) { 1524 return $display; 1525 } 1526 1527 /* Handle special container mailboxes. */ 1528 if (($elt = $this->tree_elt) && $elt->nonimap && $elt->container) { 1529 if ($elt->remote) { 1530 return _("Remote Accounts"); 1531 } elseif ($elt->vfolder) { 1532 return _("Virtual Folders"); 1533 } elseif ($elt->namespace_other) { 1534 return _("Other Users"); 1535 } elseif ($elt->namespace_shared) { 1536 return _("Shared"); 1537 } 1538 } 1539 1540 /* Handle remote mailboxes. */ 1541 if ($this->remote) { 1542 return $injector->getInstance('IMP_Remote')->label($this->_mbox); 1543 } 1544 1545 $ns_info = $this->namespace_info; 1546 $out = $this->_mbox; 1547 1548 if (!is_null($ns_info)) { 1549 /* Return translated namespace information. */ 1550 if (strlen($ns_info->translation) && $this->namespace) { 1551 $cache->setDisplay($this->_mbox, $ns_info->translation); 1552 return $ns_info->translation; 1553 } 1554 1555 /* Strip personal namespace information. */ 1556 if ($ns_info->type === $ns_info::NS_PERSONAL) { 1557 $out = $ns_info->stripNamespace($this->_mbox); 1558 } 1559 } 1560 1561 if ($notranslate) { 1562 return $out; 1563 } 1564 1565 /* Bug #9971: Special mailboxes can be empty IMP_Mailbox objects - 1566 * catch this with the strlen check below. */ 1567 foreach ($this->getSpecialMailboxes() as $key => $val) { 1568 switch ($key) { 1569 case self::SPECIAL_COMPOSETEMPLATES: 1570 if (strval($val) == $this->_mbox) { 1571 $out = _("Templates"); 1572 } 1573 break; 1574 1575 case self::SPECIAL_DRAFTS: 1576 if (strval($val) == $this->_mbox) { 1577 $out = _("Drafts"); 1578 } 1579 break; 1580 1581 case self::SPECIAL_SENT: 1582 if (in_array($this->_mbox, $val)) { 1583 $out = _("Sent"); 1584 } 1585 break; 1586 1587 case self::SPECIAL_SPAM: 1588 if (strval($val) == $this->_mbox) { 1589 $out = _("Spam"); 1590 } 1591 break; 1592 1593 case self::SPECIAL_TRASH: 1594 if (strval($val) == $this->_mbox) { 1595 $out = _("Trash"); 1596 } 1597 break; 1598 } 1599 } 1600 1601 if ($this->inbox) { 1602 $out = _("Inbox"); 1603 } elseif (($this->_mbox == $out) && 1604 !is_null($ns_info) && 1605 (strpos($out, 'INBOX' . $ns_info->delimiter) === 0)) { 1606 $out = substr_replace($out, _("Inbox"), 0, 5); 1607 } 1608 1609 $cache->setDisplay($this->_mbox, $out); 1610 1611 return $out; 1612 } 1613 1614 /** 1615 * Return icon information. 1616 * 1617 * @return object Object with the following properties: 1618 * - alt 1619 * - class 1620 * - icon 1621 * - iconopen 1622 * - user_icon 1623 */ 1624 protected function _getIcon() 1625 { 1626 global $injector; 1627 1628 $info = new stdClass; 1629 $info->iconopen = null; 1630 $info->user_icon = false; 1631 1632 if ($this->container) { 1633 /* We are dealing with folders here. */ 1634 if ($this->is_open) { 1635 $info->alt = _("Opened Folder"); 1636 $info->class = 'folderopenImg'; 1637 $info->icon = 'folders/open.png'; 1638 } else { 1639 $info->alt = _("Folder"); 1640 $info->class = 'folderImg'; 1641 $info->icon = 'folders/folder.png'; 1642 $info->iconopen = Horde_Themes::img('folders/open.png'); 1643 } 1644 } elseif ($this->remote_container) { 1645 $info->alt = _("Remote Account"); 1646 $info->class = 'remoteImg'; 1647 $info->icon = 'shared.png'; 1648 } else { 1649 $special = $this->getSpecialMailboxes(); 1650 1651 switch ($this->_mbox) { 1652 case 'INBOX': 1653 $info->alt = _("Inbox"); 1654 $info->class = 'inboxImg'; 1655 $info->icon = 'folders/inbox.png'; 1656 break; 1657 1658 case $special[self::SPECIAL_COMPOSETEMPLATES]: 1659 $info->alt = ("Templates"); 1660 $info->class = 'composetemplatesImg'; 1661 $info->icon = 'folders/drafts.png'; 1662 break; 1663 1664 case $special[self::SPECIAL_DRAFTS]: 1665 $info->alt = _("Drafts"); 1666 $info->class = 'draftsImg'; 1667 $info->icon = 'folders/drafts.png'; 1668 break; 1669 1670 case $special[self::SPECIAL_SPAM]: 1671 $info->alt = _("Spam"); 1672 $info->class = 'spamImg'; 1673 $info->icon = 'folders/spam.png'; 1674 break; 1675 1676 case $special[self::SPECIAL_TRASH]: 1677 $info->alt = _("Trash"); 1678 $info->class = 'trashImg'; 1679 $info->icon = 'folders/trash.png'; 1680 break; 1681 1682 default: 1683 if (in_array($this->_mbox, $special[self::SPECIAL_SENT])) { 1684 $info->alt = _("Sent"); 1685 $info->class = 'sentImg'; 1686 $info->icon = 'folders/sent.png'; 1687 } else { 1688 $info->alt = in_array($this->_mbox, $special[self::SPECIAL_USER]) 1689 ? $this->display 1690 : _("Mailbox"); 1691 if ($this->is_open) { 1692 $info->class = 'folderopenImg'; 1693 $info->icon = 'folders/open.png'; 1694 } else { 1695 $info->class = 'folderImg'; 1696 $info->icon = 'folders/folder.png'; 1697 } 1698 } 1699 break; 1700 } 1701 1702 /* Virtual folders. */ 1703 if ($this->vfolder) { 1704 $imp_search = $injector->getInstance('IMP_Search'); 1705 if ($imp_search->isVTrash($this->_mbox)) { 1706 $info->alt = $imp_search[$this->_mbox]->label; 1707 $info->class = 'trashImg'; 1708 $info->icon = 'folders/trash.png'; 1709 } elseif ($imp_search->isVinbox($this->_mbox)) { 1710 $info->alt = $imp_search[$this->_mbox]->label; 1711 $info->class = 'inboxImg'; 1712 $info->icon = 'folders/inbox.png'; 1713 } 1714 } 1715 } 1716 1717 /* Overwrite the icon information now. */ 1718 $mi = $injector->getInstance('IMP_Mailbox_SessionCache')->getIcons($this->_mbox); 1719 if (!empty($mi)) { 1720 if (isset($mi['alt'])) { 1721 $info->alt = $mi['alt']; 1722 } 1723 $info->icon = strval($mi['icon']); 1724 $info->user_icon = true; 1725 } elseif ($info->icon) { 1726 $info->icon = Horde_Themes::img($info->icon); 1727 } 1728 1729 return $info; 1730 } 1731 1732 /** 1733 * Do the necessary cleanup/cache updates when deleting mailboxes. 1734 * 1735 * @param array $deleted The list of deleted mailboxes. 1736 */ 1737 protected function _onDelete($deleted) 1738 { 1739 /* Clear the mailboxes from the sort prefs. */ 1740 foreach ($this->get($deleted) as $val) { 1741 $val->setSort(null, null, true); 1742 } 1743 } 1744 1745} 1746