Overview

Packages

  • IMP
  • None

Classes

  • IMP
  • IMP_Ajax_Application
  • IMP_Ajax_Imple_ContactAutoCompleter
  • IMP_Ajax_Imple_PassphraseDialog
  • IMP_Ajax_Queue
  • IMP_Api
  • IMP_Auth
  • IMP_Block_Newmail
  • IMP_Block_Summary
  • IMP_Compose
  • IMP_Compose_Exception
  • IMP_Compose_Stationery
  • IMP_Contents
  • IMP_Crypt_Pgp
  • IMP_Crypt_Smime
  • IMP_Dimp
  • IMP_Exception
  • IMP_Factory_AuthImap
  • IMP_Factory_Compose
  • IMP_Factory_Contents
  • IMP_Factory_Flags
  • IMP_Factory_Identity
  • IMP_Factory_Imap
  • IMP_Factory_Imaptree
  • IMP_Factory_Mail
  • IMP_Factory_Mailbox
  • IMP_Factory_MailboxList
  • IMP_Factory_MimeViewer
  • IMP_Factory_Pgp
  • IMP_Factory_Quota
  • IMP_Factory_Search
  • IMP_Factory_Sentmail
  • IMP_Factory_Smime
  • IMP_Filter
  • IMP_Flag_Base
  • IMP_Flag_Imap
  • IMP_Flag_Imap_Answered
  • IMP_Flag_Imap_Deleted
  • IMP_Flag_Imap_Draft
  • IMP_Flag_Imap_Flagged
  • IMP_Flag_Imap_Forwarded
  • IMP_Flag_Imap_Junk
  • IMP_Flag_Imap_NotJunk
  • IMP_Flag_Imap_Seen
  • IMP_Flag_System_Attachment
  • IMP_Flag_System_Encrypted
  • IMP_Flag_System_HighPriority
  • IMP_Flag_System_List
  • IMP_Flag_System_LowPriority
  • IMP_Flag_System_Match_Address
  • IMP_Flag_System_Match_Flag
  • IMP_Flag_System_Match_Header
  • IMP_Flag_System_Personal
  • IMP_Flag_System_Signed
  • IMP_Flag_System_Unseen
  • IMP_Flag_User
  • IMP_Flags
  • IMP_Imap
  • IMP_Imap_Acl
  • IMP_Imap_Exception
  • IMP_Imap_PermanentFlags
  • IMP_Imap_Thread
  • IMP_Imap_Tree
  • IMP_Indices
  • IMP_Indices_Form
  • IMP_LoginTasks_SystemTask_GarbageCollection
  • IMP_LoginTasks_SystemTask_Upgrade
  • IMP_LoginTasks_SystemTask_UpgradeAuth
  • IMP_LoginTasks_Task_Autocreate
  • IMP_LoginTasks_Task_DeleteAttachmentsMonthly
  • IMP_LoginTasks_Task_DeleteSentmailMonthly
  • IMP_LoginTasks_Task_FilterOnLogin
  • IMP_LoginTasks_Task_PurgeSentmail
  • IMP_LoginTasks_Task_PurgeSpam
  • IMP_LoginTasks_Task_PurgeTrash
  • IMP_LoginTasks_Task_RecoverDraft
  • IMP_LoginTasks_Task_RenameSentmailMonthly
  • IMP_Mailbox
  • IMP_Mailbox_List
  • IMP_Mailbox_List_Track
  • IMP_Maillog
  • IMP_Menu_Dimp
  • IMP_Message
  • IMP_Mime_Status
  • IMP_Mime_Viewer_Alternative
  • IMP_Mime_Viewer_Appledouble
  • IMP_Mime_Viewer_Audio
  • IMP_Mime_Viewer_Enriched
  • IMP_Mime_Viewer_Externalbody
  • IMP_Mime_Viewer_Html
  • IMP_Mime_Viewer_Images
  • IMP_Mime_Viewer_Itip
  • IMP_Mime_Viewer_Mdn
  • IMP_Mime_Viewer_Partial
  • IMP_Mime_Viewer_Pdf
  • IMP_Mime_Viewer_Pgp
  • IMP_Mime_Viewer_Plain
  • IMP_Mime_Viewer_Related
  • IMP_Mime_Viewer_Rfc822
  • IMP_Mime_Viewer_Smil
  • IMP_Mime_Viewer_Smime
  • IMP_Mime_Viewer_Status
  • IMP_Mime_Viewer_Vcard
  • IMP_Mime_Viewer_Video
  • IMP_Mime_Viewer_Zip
  • IMP_Notification_Event_Status
  • IMP_Notification_Handler_Decorator_ImapAlerts
  • IMP_Notification_Handler_Decorator_NewmailNotify
  • IMP_Notification_Listener_AjaxStatus
  • Imp_Prefs_Identity
  • IMP_Prefs_Ui
  • IMP_Quota
  • IMP_Quota_Base
  • IMP_Quota_Command
  • IMP_Quota_Hook
  • IMP_Quota_Imap
  • IMP_Quota_Maildir
  • IMP_Quota_Mdaemon
  • IMP_Quota_Mercury32
  • IMP_Quota_Null
  • IMP_Quota_Sql
  • IMP_Search
  • IMP_Search_Element
  • IMP_Search_Element_Attachment
  • IMP_Search_Element_Autogenerated
  • IMP_Search_Element_Bulk
  • IMP_Search_Element_Contacts
  • IMP_Search_Element_Date
  • IMP_Search_Element_Flag
  • IMP_Search_Element_Header
  • IMP_Search_Element_Mailinglist
  • IMP_Search_Element_Or
  • IMP_Search_Element_Personal
  • IMP_Search_Element_Recipient
  • IMP_Search_Element_Size
  • IMP_Search_Element_Text
  • IMP_Search_Element_Within
  • IMP_Search_Filter
  • IMP_Search_Filter_Attachment
  • IMP_Search_Filter_Autogenerated
  • IMP_Search_Filter_Builtin
  • IMP_Search_Filter_Bulk
  • IMP_Search_Filter_Contacts
  • IMP_Search_Filter_Mailinglist
  • IMP_Search_Filter_Personal
  • IMP_Search_Query
  • IMP_Search_Vfolder
  • IMP_Search_Vfolder_Builtin
  • IMP_Search_Vfolder_Vinbox
  • IMP_Search_Vfolder_Vtrash
  • IMP_Sentmail
  • IMP_Sentmail_Base
  • IMP_Sentmail_Null
  • IMP_Sentmail_Sql
  • IMP_Spam
  • IMP_Test
  • IMP_Tree_Flist
  • IMP_Tree_Jquerymobile
  • IMP_Tree_Simplehtml
  • IMP_Ui_Compose
  • IMP_Ui_Editor
  • IMP_Ui_Folder
  • IMP_Ui_Headers
  • IMP_Ui_Imageview
  • IMP_Ui_Mailbox
  • IMP_Ui_Message
  • IMP_Ui_Mimp
  • IMP_Ui_Search
  • IMP_Views_Compose
  • IMP_Views_ListMessages
  • IMP_Views_ShowMessage
  • Overview
  • Package
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * Defines the AJAX interface for IMP.
   4:  *
   5:  * Copyright 2010-2012 Horde LLC (http://www.horde.org/)
   6:  *
   7:  * See the enclosed file COPYING for license information (GPL). If you
   8:  * did not receive this file, see http://www.horde.org/licenses/gpl.
   9:  *
  10:  * @author   Michael Slusarz <slusarz@horde.org>
  11:  * @category Horde
  12:  * @license  http://www.horde.org/licenses/gpl GPL
  13:  * @package  IMP
  14:  */
  15: class IMP_Ajax_Application extends Horde_Core_Ajax_Application
  16: {
  17:     /**
  18:      * Determines if notification information is sent in response.
  19:      *
  20:      * @var boolean
  21:      */
  22:     public $notify = true;
  23: 
  24:     /**
  25:      * The mailbox (view) we are dealing with on the browser.
  26:      *
  27:      * @var IMP_Mailbox
  28:      */
  29:     protected $_mbox;
  30: 
  31:     /**
  32:      * Queue object.
  33:      *
  34:      * @var IMP_Ajax_Queue
  35:      */
  36:     protected $_queue;
  37: 
  38:     /**
  39:      * Suppress these mailboxes when creating mailbox response.
  40:      *
  41:      * @var array
  42:      */
  43:     protected $_suppress;
  44: 
  45:     /**
  46:      * The list of actions that require readonly access to the session.
  47:      *
  48:      * @var array
  49:      */
  50:     protected $_readOnly = array(
  51:         'html2Text', 'text2Html'
  52:     );
  53: 
  54:     /**
  55:      */
  56:     public function __construct($app, $vars, $action = null)
  57:     {
  58:         parent::__construct($app, $vars, $action);
  59: 
  60:         $this->_queue = $GLOBALS['injector']->getInstance('IMP_Ajax_Queue');
  61: 
  62:         /* Bug #10462: 'view' POST parameter is base64url encoded to
  63:          * workaround suhosin. */
  64:         if (isset($vars->view)) {
  65:             $this->_mbox = IMP_Mailbox::formFrom($vars->view);
  66:         }
  67:     }
  68: 
  69:     /**
  70:      * Determines the HTTP response output type.
  71:      *
  72:      * @see Horde::sendHTTPResponse().
  73:      *
  74:      * @return string  The output type.
  75:      */
  76:     public function responseType()
  77:     {
  78:         switch ($this->_action) {
  79:         case 'addAttachment':
  80:         case 'importMailbox':
  81:             return 'js-json';
  82:         }
  83: 
  84:         return parent::responseType();
  85:     }
  86: 
  87:     /**
  88:      * May add the following entries to the output object:
  89:      *   - flag: (array) See IMP_Ajax_Queue::generate().
  90:      *   - poll: (array) See IMP_Ajax_Queue::generate().
  91:      *   - quota: (array) See IMP_Ajax_Queue::generate().
  92:      */
  93:     public function doAction()
  94:     {
  95:         $res = parent::doAction();
  96: 
  97:         if (is_object($res)) {
  98:             foreach ($this->_queue->generate() as $key => $val) {
  99:                 $res->$key = $val;
 100:             }
 101:         }
 102: 
 103:         return $res;
 104:     }
 105: 
 106:     /**
 107:      * AJAX action: Check access rights for creation of a submailbox.
 108:      *
 109:      * Variables used:
 110:      *   - mbox: (string) The name of the mailbox to check (base64url
 111:      *           encoded).
 112:      *
 113:      * @return boolean  True if submailboxes can be created.
 114:      */
 115:     public function createMailboxPrepare()
 116:     {
 117:         $mbox = IMP_Mailbox::formFrom($this->_vars->mbox);
 118: 
 119:         if (!$mbox->access_creatembox) {
 120:             $GLOBALS['notification']->push(sprintf(_("You may not create child folders in \"%s\"."), $mbox->display), 'horde.error');
 121:             return false;
 122:         }
 123: 
 124:         return true;
 125:     }
 126: 
 127:     /**
 128:      * AJAX action: Create a mailbox.
 129:      *
 130:      * Variables used:
 131:      *   - mbox: (string) The name of the new mailbox.
 132:      *   - noexpand: (integer) Submailbox is not yet expanded.
 133:      *   - parent: (string) The parent mailbox (base64url encoded).
 134:      *
 135:      * @return mixed  False on failure, or an object with the following
 136:      *                entries:
 137:      *   - mailbox: (object) Mailboxes that were altered. Contains the
 138:      *              following properties:
 139:      *       a: (array) Mailboxes that were added (base64url encoded).
 140:      *       c: (array) Mailboxes that were changed (base64url encoded).
 141:      *       d: (array) Mailboxes that were deleted (base64url encoded).
 142:      */
 143:     public function createMailbox()
 144:     {
 145:         if (!isset($this->_vars->mbox)) {
 146:             return false;
 147:         }
 148: 
 149:         $imptree = $GLOBALS['injector']->getInstance('IMP_Imap_Tree');
 150:         $imptree->eltDiffStart();
 151: 
 152:         $result = false;
 153: 
 154:         try {
 155:             $new_mbox = $imptree->createMailboxName(
 156:                 isset($this->_vars->parent) ? IMP_Mailbox::formFrom($this->_vars->parent) : '',
 157:                 Horde_String::convertCharset($this->_vars->mbox, 'UTF-8', 'UTF7-IMAP')
 158:             );
 159: 
 160:             if ($new_mbox->exists) {
 161:                 $GLOBALS['notification']->push(sprintf(_("Mailbox \"%s\" already exists."), $new_mbox->display), 'horde.warning');
 162:             } elseif ($new_mbox->create()) {
 163:                 $result = new stdClass;
 164:                 $result->mailbox = $this->_getMailboxResponse($imptree);
 165:                 if (isset($this->_vars->parent) && $this->_vars->noexpand) {
 166:                     $result->mailbox['noexpand'] = 1;
 167:                 }
 168:             }
 169:         } catch (Horde_Exception $e) {
 170:             $GLOBALS['notification']->push($e);
 171:         }
 172: 
 173:         return $result;
 174:     }
 175: 
 176:     /**
 177:      * AJAX action: Check access rights for deletion/rename of mailbox.
 178:      *
 179:      * Variables used:
 180:      *   - mbox: (string) The name of the mailbox to check (base64url
 181:      *           encoded).
 182:      *   - type: (string) Either 'delete' or 'rename'.
 183:      *
 184:      * @return boolean  True if mailbox can be deleted/renamed.
 185:      */
 186:     public function deleteMailboxPrepare()
 187:     {
 188:         $mbox = IMP_Mailbox::formFrom($this->_vars->mbox);
 189: 
 190:         if (!$mbox->fixed && $mbox->access_deletembox) {
 191:             return true;
 192:         }
 193: 
 194:         switch ($this->_vars->type) {
 195:         case 'delete':
 196:             $GLOBALS['notification']->push(sprintf(_("You may not delete \"%s\"."), $mbox->display), 'horde.error');
 197:             break;
 198: 
 199:         case 'rename':
 200:             $GLOBALS['notification']->push(sprintf(_("You may not rename \"%s\"."), $mbox->display), 'horde.error');
 201:             break;
 202:         }
 203: 
 204:         return false;
 205:     }
 206: 
 207:     /**
 208:      * AJAX action: Delete a mailbox.
 209:      *
 210:      * Variables used:
 211:      *   - mbox: (string) The full mailbox name to delete (base64url encoded).
 212:      *
 213:      * @return mixed  False on failure, or an object with the following
 214:      *                entries:
 215:      *   - mailbox: (object) Mailboxes that were altered. Contains the
 216:      *              following properties:
 217:      *     a: (array) Mailboxes that were added (base64url encoded).
 218:      *     c: (array) Mailboxes that were changed (base64url encoded).
 219:      *     d: (array) Mailboxes that were deleted (base64url encoded).
 220:      */
 221:     public function deleteMailbox()
 222:     {
 223:         if (!$this->_vars->mbox) {
 224:             return false;
 225:         }
 226: 
 227:         $mbox = IMP_Mailbox::formFrom($this->_vars->mbox);
 228: 
 229:         $imptree = $GLOBALS['injector']->getInstance('IMP_Imap_Tree');
 230:         $imptree->eltDiffStart();
 231: 
 232:         if (!$mbox->delete()) {
 233:             return false;
 234:         }
 235: 
 236:         $result = new stdClass;
 237:         $result->mailbox = $this->_getMailboxResponse($imptree);
 238: 
 239:         return $result;
 240:     }
 241: 
 242:     /**
 243:      * AJAX action: Rename a mailbox.
 244:      *
 245:      * Variables used:
 246:      *   - new_name: (string) New mailbox name (child node) (UTF-8).
 247:      *   - new_parent: (string) New parent name (UTF7-IMAP) (base64url
 248:      *                 encoded).
 249:      *   - old_name: (string) Full name of old mailbox (base64url encoded).
 250:      *
 251:      * @return mixed  False on failure, or an object with the following
 252:      *                entries:
 253:      *   - mailbox: (object) Mailboxes that were altered. Contains the
 254:      *              following properties:
 255:      *     a: (array) Mailboxes that were added (base64url encoded).
 256:      *     c: (array) Mailboxes that were changed (base64url encoded).
 257:      *     d: (array) Mailboxes that were deleted (base64url encoded).
 258:      */
 259:     public function renameMailbox()
 260:     {
 261:         if (!$this->_vars->old_name || !$this->_vars->new_name) {
 262:             return false;
 263:         }
 264: 
 265:         $imptree = $GLOBALS['injector']->getInstance('IMP_Imap_Tree');
 266:         $imptree->eltDiffStart();
 267: 
 268:         $result = false;
 269: 
 270:         try {
 271:             $new_name = $imptree->createMailboxName(
 272:                 isset($this->_vars->new_parent) ? IMP_Mailbox::formFrom($this->_vars->new_parent) : '',
 273:                 Horde_String::convertCharset($this->_vars->new_name, 'UTF-8', 'UTF7-IMAP')
 274:             );
 275: 
 276:             $old_name = IMP_Mailbox::formFrom($this->_vars->old_name);
 277: 
 278:             if (($old_name != $new_name) && $old_name->rename($new_name)) {
 279:                 $result = new stdClass;
 280:                 $result->mailbox = $this->_getMailboxResponse($imptree);
 281: 
 282:                 $this->_queue->poll($new_name);
 283:             }
 284:         } catch (Horde_Exception $e) {
 285:             $GLOBALS['notification']->push($e);
 286:         }
 287: 
 288:         return $result;
 289:     }
 290: 
 291:     /**
 292:      * AJAX action: Check access rights for a mailbox, and provide number of
 293:      * messages to be emptied.
 294:      *
 295:      * Variables used:
 296:      *   - mbox: (string) The name of the mailbox to check (base64url
 297:      *           encoded).
 298:      *
 299:      * @return integer  The number of messages to be deleted.
 300:      */
 301:     public function emptyMailboxPrepare()
 302:     {
 303:         $mbox = IMP_Mailbox::formFrom($this->_vars->mbox);
 304: 
 305:         if (!$mbox->access_deletemsgs || !$mbox->access_expunge) {
 306:             $GLOBALS['notification']->push(sprintf(_("The folder \"%s\" may not be emptied."), $mbox->display), 'horde.error');
 307:             return 0;
 308:         }
 309: 
 310:         $poll_info = $mbox->poll_info;
 311:         if (empty($poll_info->msgs)) {
 312:             $GLOBALS['notification']->push(sprintf(_("The folder \"%s\" is already empty."), $mbox->display), 'horde.message');
 313:             return 0;
 314:         }
 315: 
 316:         return $poll_info->msgs;
 317:     }
 318: 
 319:     /**
 320:      * AJAX action: Empty a mailbox.
 321:      *
 322:      * Variables used:
 323:      *   - mbox: (string) The full mailbox name to empty (base64url encoded).
 324:      *
 325:      * @return mixed  False on failure, or an object with the following
 326:      *                entries:
 327:      *   - ViewPort: (object) See _viewPortData().
 328:      */
 329:     public function emptyMailbox()
 330:     {
 331:         if (!$this->_vars->mbox) {
 332:             return false;
 333:         }
 334: 
 335:         $mbox = IMP_Mailbox::formFrom($this->_vars->mbox);
 336: 
 337:         $GLOBALS['injector']->getInstance('IMP_Message')->emptyMailbox(array($mbox));
 338: 
 339:         $this->_queue->poll($mbox);
 340: 
 341:         $result = $this->_viewPortOb($mbox);
 342:         $result->ViewPort->data_reset = 1;
 343:         $result->ViewPort->rowlist_reset = 1;
 344: 
 345:         return $result;
 346:     }
 347: 
 348:     /**
 349:      * AJAX action: Flag all messages in a mailbox.
 350:      *
 351:      * Variables used:
 352:      *   - add: (integer) Add the flags?
 353:      *   - flags: (string) The IMAP flags to add/remove (JSON serialized
 354:      *            array).
 355:      *   - mbox: (string) The full mailbox name (base64url encoded).
 356:      *
 357:      * @return mixed  False on failure, object on success (empty object
 358:      *                ensures queued actions will be run).
 359:      */
 360:     public function flagAll()
 361:     {
 362:         $flags = Horde_Serialize::unserialize($this->_vars->flags, Horde_Serialize::JSON);
 363:         if (!$this->_vars->mbox || empty($flags)) {
 364:             return false;
 365:         }
 366: 
 367:         $mbox = IMP_Mailbox::formFrom($this->_vars->mbox);
 368: 
 369:         if (!$GLOBALS['injector']->getInstance('IMP_Message')->flagAllInMailbox($flags, array($mbox), $this->_vars->add)) {
 370:             return false;
 371:         }
 372: 
 373:         $this->_queue->poll($mbox);
 374: 
 375:         return new stdClass;
 376:     }
 377: 
 378:     /**
 379:      * AJAX action: List mailboxes.
 380:      *
 381:      * Variables used:
 382:      *   - all: (integer) 1 to show all mailboxes.
 383:      *   - initial: (string) 1 to indicate the initial request for mailbox
 384:      *              list.
 385:      *   - mboxes: (string) The list of mailboxes to process (JSON encoded
 386:      *             array; mailboxes are base64url encoded).
 387:      *   - reload: (integer) 1 to force reload of mailboxes.
 388:      *   - unsub: (integer) 1 to show unsubscribed mailboxes.
 389:      *
 390:      * @return mixed  False on failure, or an object with the following
 391:      *                entries:
 392:      *   - expand: (integer) Expand subfolders on load.
 393:      *   - mailbox: (object) Mailboxes that were altered. Contains the
 394:      *              following properties:
 395:      *     a: (array) Mailboxes that were added (base64url encoded).
 396:      *     c: (array) Mailboxes that were changed (base64url encoded).
 397:      *     d: (array) Mailboxes that were deleted (base64url encoded).
 398:      */
 399:     public function listMailboxes()
 400:     {
 401:         /* This might be a long running operation. */
 402:         if ($this->_vars->initial) {
 403:             $GLOBALS['session']->close();
 404:         }
 405: 
 406:         $imptree = $GLOBALS['injector']->getInstance('IMP_Imap_Tree');
 407:         $initreload = ($this->_vars->initial || $this->_vars->reload);
 408:         $result = new stdClass;
 409: 
 410:         $mask = IMP_Imap_Tree::FLIST_VFOLDER;
 411:         if ($this->_vars->unsub) {
 412:             $mask |= IMP_Imap_Tree::FLIST_UNSUB;
 413:         }
 414: 
 415:         if (!$this->_vars->all) {
 416:             if ($initreload) {
 417:                 $mask |= IMP_Imap_Tree::FLIST_ANCESTORS | IMP_Imap_Tree::FLIST_SAMELEVEL;
 418:                 if ($GLOBALS['prefs']->getValue('nav_expanded')) {
 419:                     $result->expand = 1;
 420:                     $mask |= IMP_Imap_Tree::FLIST_EXPANDED;
 421:                 } else {
 422:                     $mask |= IMP_Imap_Tree::FLIST_NOCHILDREN;
 423:                 }
 424:             } else {
 425:                 $result->expand = 1;
 426:                 $mask |= IMP_Imap_Tree::FLIST_EXPANDED | IMP_Imap_Tree::FLIST_NOBASE;
 427:             }
 428:         }
 429: 
 430:         if ($this->_vars->reload) {
 431:             $imptree->init();
 432:         }
 433: 
 434:         $imptree->showUnsubscribed($this->_vars->unsub);
 435: 
 436:         $folder_list = array();
 437:         if (!empty($this->_vars->mboxes)) {
 438:             foreach (IMP_Mailbox::formFrom(Horde_Serialize::unserialize($this->_vars->mboxes, Horde_Serialize::JSON)) as $val) {
 439:                 $imptree->setIteratorFilter($mask, $val);
 440:                 $folder_list += iterator_to_array($imptree);
 441: 
 442:                 if (!$initreload) {
 443:                     $imptree->expand($val);
 444:                 }
 445:             }
 446: 
 447:             if ($initreload && empty($folder_list)) {
 448:                 $imptree->setIteratorFilter($mask, 'INBOX');
 449:                 $folder_list += iterator_to_array($imptree);
 450:             }
 451:         }
 452: 
 453:         /* Add special mailboxes explicitly to the initial folder list, since
 454:          * they are ALWAYS displayed, may appear outside of the folder
 455:          * slice requested, and need to be sorted logically. */
 456:         $this->_suppress = array();
 457:         if ($initreload) {
 458:             foreach (IMP_Mailbox::getSpecialMailboxes() as $val) {
 459:                 if (is_array($val)) {
 460:                     $tmp = array();
 461:                     foreach ($val as $val2) {
 462:                         $tmp[strval($val2)] = $val2->abbrev_label;
 463:                     }
 464:                     asort($tmp, SORT_LOCALE_STRING);
 465:                     $mboxes = IMP_Mailbox::get(array_keys($tmp));
 466:                 } else {
 467:                     $mboxes = array($val);
 468:                 }
 469: 
 470:                 foreach ($mboxes as $val2) {
 471:                     if ($tmp = $imptree[strval($val2)]) {
 472:                         $folder_list[strval($val2)] = $tmp;
 473: 
 474:                         /* Hack: We need to NOT send a container element if
 475:                          * all child elements are special mailboxes. */
 476:                         if ($val2->level &&
 477:                             ($parent = $val2->parent) &&
 478:                             isset($folder_list[strval($parent)])) {
 479:                             $not_special = false;
 480:                             foreach ($parent->subfolders_only as $val3) {
 481:                                 if (!$val3->special) {
 482:                                     $not_special = true;
 483:                                     break;
 484:                                 }
 485:                             }
 486: 
 487:                             if (!$not_special) {
 488:                                 $this->_suppress[] = strval($parent);
 489:                             }
 490:                         }
 491:                     }
 492:                 }
 493:             }
 494:         }
 495: 
 496:         $result->mailbox = $this->_getMailboxResponse($imptree, array(
 497:             'a' => array_values($folder_list),
 498:             'c' => array(),
 499:             'd' => array()
 500:         ));
 501: 
 502:         $this->_queue->quota();
 503:         $this->_suppress = array();
 504: 
 505:         if ($this->_vars->initial) {
 506:             $GLOBALS['session']->start();
 507:         }
 508: 
 509:         return $result;
 510:     }
 511: 
 512:     /**
 513:      * AJAX action: Expand mailboxes (saves expanded state in prefs).
 514:      *
 515:      * Variables used:
 516:      *   - mboxes: (string) The list of mailboxes to process (JSON encoded
 517:      *             array; mailboxes are base64url encoded).
 518:      *
 519:      * @return boolean  True.
 520:      */
 521:     public function expandMailboxes()
 522:     {
 523:         if (!empty($this->_vars->mboxes)) {
 524:             $imptree = $GLOBALS['injector']->getInstance('IMP_Imap_Tree');
 525: 
 526:             foreach (Horde_Serialize::unserialize($this->_vars->mboxes, Horde_Serialize::JSON) as $val) {
 527:                 $imptree->expand(IMP_Mailbox::formFrom($val));
 528:             }
 529:         }
 530: 
 531:         return true;
 532:     }
 533: 
 534:     /**
 535:      * AJAX action: Collapse mailboxes.
 536:      *
 537:      * Variables used:
 538:      *   - all: (integer) 1 to show all mailboxes.
 539:      *   - mboxes: (string) The list of mailboxes to process (JSON encoded
 540:      *             array; mailboxes are base64url encoded) if 'all' is 0.
 541:      *
 542:      * @return boolean  True.
 543:      */
 544:     public function collapseMailboxes()
 545:     {
 546:         $imptree = $GLOBALS['injector']->getInstance('IMP_Imap_Tree');
 547: 
 548:         if ($this->_vars->all) {
 549:             $imptree->collapseAll();
 550:         } elseif (!empty($this->_vars->mboxes)) {
 551:             foreach (Horde_Serialize::unserialize($this->_vars->mboxes, Horde_Serialize::JSON) as $val) {
 552:                 $imptree->collapse(IMP_Mailbox::formFrom($val));
 553:             }
 554:         }
 555: 
 556:         return true;
 557:     }
 558: 
 559:     /**
 560:      * AJAX action: Poll mailboxes.
 561:      *
 562:      * See the list of variables needed for _changed() and _viewPortData().
 563:      * Additional variables used:
 564:      *   - mboxes: (string) The list of mailboxes to process (JSON encoded
 565:      *             array; mailboxes are base64url encoded) if 'all' is 0.
 566:      *
 567:      * @return mixed  False on failure, or an object with the following
 568:      *                entries:
 569:      *   - ViewPort: (object) See _viewPortData().
 570:      */
 571:     public function poll()
 572:     {
 573:         if (empty($this->_vars->mboxes)) {
 574:             $this->_queue->poll($GLOBALS['injector']->getInstance('IMP_Imap_Tree')->getPollList());
 575:         } else {
 576:             $this->_queue->poll(IMP_Mailbox::formFrom(Horde_Serialize::unserialize($this->_vars->mboxes, Horde_Serialize::JSON)));
 577:         }
 578: 
 579:         $this->_queue->quota();
 580: 
 581:         return ($this->_mbox && $this->_changed())
 582:             ? $this->_viewPortData(true)
 583:             : new stdClass;
 584:     }
 585: 
 586:     /**
 587:      * AJAX action: Modify list of polled mailboxes.
 588:      *
 589:      * Variables used:
 590:      *   - add: (integer) 1 to add to the poll list, 0 to remove.
 591:      *   - mbox: (string) The full mailbox name to modify (base64url encoded).
 592:      *
 593:      * @return mixed  False on failure, or an object with the following
 594:      *                entries:
 595:      *   - add: (integer) 1 if added to the poll list, 0 if removed.
 596:      *   - mbox: (string) The full mailbox name modified.
 597:      */
 598:     public function modifyPoll()
 599:     {
 600:         if (!$this->_vars->mbox) {
 601:             return false;
 602:         }
 603: 
 604:         $mbox = IMP_Mailbox::formFrom($this->_vars->mbox);
 605: 
 606:         $imptree = $GLOBALS['injector']->getInstance('IMP_Imap_Tree');
 607: 
 608:         $result = new stdClass;
 609:         $result->add = intval($this->_vars->add);
 610:         $result->mbox = $this->_vars->mbox;
 611: 
 612:         if ($this->_vars->add) {
 613:             $imptree->addPollList($mbox);
 614:             $this->_queue->poll($mbox);
 615:             $GLOBALS['notification']->push(sprintf(_("\"%s\" mailbox now polled for new mail."), $mbox->display), 'horde.success');
 616:         } else {
 617:             $imptree->removePollList($mbox);
 618:             $GLOBALS['notification']->push(sprintf(_("\"%s\" mailbox no longer polled for new mail."), $mbox->display), 'horde.success');
 619:         }
 620: 
 621:         return $result;
 622:     }
 623: 
 624:     /**
 625:      * AJAX action: [un]Subscribe to a mailbox.
 626:      *
 627:      * Variables used:
 628:      *   - mbox: (string) The full mailbox name to [un]subscribe to (base64url
 629:      *           encoded).
 630:      *   - sub: (integer) 1 to subscribe, empty to unsubscribe.
 631:      *
 632:      * @return boolean  True on success, false on failure.
 633:      */
 634:     public function subscribe()
 635:     {
 636:         return $GLOBALS['prefs']->getValue('subscribe')
 637:             ? IMP_Mailbox::formFrom($this->_vars->mbox)->subscribe($this->_vars->sub)
 638:             : false;
 639:     }
 640: 
 641:     /**
 642:      * AJAX action: Import a mailbox.
 643:      *
 644:      * Variables used:
 645:      *   - import_mbox: (string) The mailbox to import into (base64url
 646:      *                  encoded).
 647:      *
 648:      * @return object  False on failure, or an object with the following
 649:      *                 properties:
 650:      *   - action: (string) The action name (importMailbox).
 651:      *   - mbox: (string) The mailbox the messages were imported to (base64url
 652:      *           encoded).
 653:      */
 654:     public function importMailbox()
 655:     {
 656:         global $injector, $notification;
 657: 
 658:         $mbox = IMP_Mailbox::formFrom($this->_vars->import_mbox);
 659: 
 660:         try {
 661:             $notification->push($injector->getInstance('IMP_Ui_Folder')->importMbox($mbox, 'import_file'), 'horde.success');
 662:         } catch (Horde_Exception $e) {
 663:             $notification->push($e);
 664:             return false;
 665:         }
 666: 
 667:         $result = new stdClass;
 668:         $result->action = 'importMailbox';
 669:         $result->mbox = $this->_vars->import_mbox;
 670: 
 671:         $this->_queue->poll($mbox);
 672: 
 673:         return $result;
 674:     }
 675: 
 676:     /**
 677:      * AJAX action: Output ViewPort data.
 678:      *
 679:      * See the list of variables needed for _changed() and _viewPortData().
 680:      * Additional variables used:
 681:      *   - checkcache: (integer) If 1, only send data if cache has been
 682:      *                 invalidated.
 683:      *   - rangeslice: (string) Range slice. See js/viewport.js.
 684:      *   - requestid: (string) Request ID. See js/viewport.js.
 685:      *   - sortby: (integer) The Horde_Imap_Client sort constant.
 686:      *   - sortdir: (integer) 0 for ascending, 1 for descending.
 687:      *
 688:      * @return mixed  False on failure, or an object with the following
 689:      *                entries:
 690:      *   - ViewPort: (object) See _viewPortData().
 691:      */
 692:     public function viewPort()
 693:     {
 694:         if (!$this->_mbox) {
 695:             return false;
 696:         }
 697: 
 698:         /* Change sort preferences if necessary. */
 699:         if (isset($this->_vars->sortby) || isset($this->_vars->sortdir)) {
 700:             $this->_mbox->setSort($this->_vars->sortby, $this->_vars->sortdir);
 701:         }
 702: 
 703:         /* Toggle hide deleted preference if necessary. */
 704:         if (isset($this->_vars->delhide)) {
 705:             $this->_mbox->setHideDeletedMsgs($this->_vars->delhide);
 706:         }
 707: 
 708:         $changed = $this->_changed(false);
 709: 
 710:         if (is_null($changed)) {
 711:             $list_msg = new IMP_Views_ListMessages();
 712:             $result = $list_msg->getBaseOb($this->_mbox);
 713: 
 714:             $req_id = $this->_vars->requestid;
 715:             if (!is_null($req_id)) {
 716:                 $result->ViewPort->requestid = intval($req_id);
 717:             }
 718: 
 719:             return $result;
 720:         }
 721: 
 722:         $this->_queue->poll($this->_mbox);
 723: 
 724:         if ($changed ||
 725:             $this->_vars->rangeslice ||
 726:             !$this->_vars->checkcache) {
 727:             /* Ticket #7422: Listing messages may be a long-running operation
 728:              * so close the session while we are doing it to prevent
 729:              * deadlocks. */
 730:             $GLOBALS['session']->close();
 731: 
 732:             $result = $this->_viewPortData($changed);
 733: 
 734:             /* Reopen the session. */
 735:             $GLOBALS['session']->start();
 736: 
 737:             if (isset($this->_vars->delhide)) {
 738:                 $result->ViewPort->metadata_reset = 1;
 739:             }
 740:         } else {
 741:             $result = false;
 742:         }
 743: 
 744:         return $result;
 745:     }
 746: 
 747:     /**
 748:      * AJAX action: Move messages.
 749:      *
 750:      * See the list of variables needed for _changed(),
 751:      * _generateDeleteResult(), and _checkUidvalidity(). Additional variables
 752:      * used:
 753:      *   - mboxto: (string) Mailbox to move the message to (base64url
 754:      *             encoded).
 755:      *   - uid: (string) Indices of the messages to move (IMAP sequence
 756:      *          string; mailboxes are base64url encoded).
 757:      *
 758:      * @return mixed  False on failure, or an object (see
 759:      *                _generateDeleteResult() for format).
 760:      */
 761:     public function moveMessages()
 762:     {
 763:         $indices = new IMP_Indices_Form($this->_vars->uid);
 764:         if (!$this->_vars->mboxto || !count($indices)) {
 765:             return false;
 766:         }
 767: 
 768:         $change = $this->_changed(true);
 769: 
 770:         if (is_null($change)) {
 771:             return false;
 772:         }
 773: 
 774:         $mbox = IMP_Mailbox::formFrom($this->_vars->mboxto);
 775: 
 776:         $result = $GLOBALS['injector']->getInstance('IMP_Message')->copy($mbox, 'move', $indices);
 777: 
 778:         if ($result) {
 779:             $result = $this->_generateDeleteResult($indices, $change, true);
 780:             $this->_queue->poll($mbox);
 781:         } else {
 782:             $result = $this->_checkUidvalidity();
 783:         }
 784: 
 785:         return $result;
 786:     }
 787: 
 788:     /**
 789:      * AJAX action: Copy messages.
 790:      *
 791:      * See the list of variables needed for _checkUidvalidity(). Additional
 792:      * variables used:
 793:      *   - mboxto: (string) Mailbox to copy the message to (base64url
 794:      *             encoded).
 795:      *   - uid: (string) Indices of the messages to copy (IMAP sequence
 796:      *          string; mailboxes are base64url encoded).
 797:      *
 798:      * @return mixed  False on failure, or an object with the following
 799:      *                entries:
 800:      *   - ViewPort: (object) See _viewPortData().
 801:      */
 802:     public function copyMessages()
 803:     {
 804:         $indices = new IMP_Indices_Form($this->_vars->uid);
 805:         if (!$this->_vars->mboxto || !count($indices)) {
 806:             return false;
 807:         }
 808: 
 809:         $mbox = IMP_Mailbox::formFrom($this->_vars->mboxto);
 810: 
 811:         if ($result = $GLOBALS['injector']->getInstance('IMP_Message')->copy($mbox, 'copy', $indices)) {
 812:             $this->_queue->poll($mbox);
 813:         } else {
 814:             $result = $this->_checkUidvalidity();
 815:         }
 816: 
 817:         return $result;
 818:     }
 819: 
 820:     /**
 821:      * AJAX action: Flag messages.
 822:      *
 823:      * See the list of variables needed for _changed() and
 824:      * _checkUidvalidity().  Additional variables used:
 825:      *   - add: (integer) Set the flag?
 826:      *   - flags: (string) The flags to set (JSON serialized array).
 827:      *   - uid: (string) Indices of the messages to flag (IMAP sequence
 828:      *          string; mailboxes are base64url encoded).
 829:      *
 830:      * @return mixed  False on failure, or an object with the following
 831:      *                entries:
 832:      *   - ViewPort: (object) See _viewPortData().
 833:      */
 834:     public function flagMessages()
 835:     {
 836:         $indices = new IMP_Indices_Form($this->_vars->uid);
 837:         if (!$this->_vars->flags || !count($indices)) {
 838:             return false;
 839:         }
 840: 
 841:         $change = $this->_changed(true);
 842: 
 843:         if (is_null($change)) {
 844:             return false;
 845:         }
 846: 
 847:         $flags = Horde_Serialize::unserialize($this->_vars->flags, Horde_Serialize::JSON);
 848: 
 849:         if (!$GLOBALS['injector']->getInstance('IMP_Message')->flag($flags, $indices, $this->_vars->add)) {
 850:             return $this->_checkUidvalidity();
 851:         }
 852: 
 853:         if (in_array(Horde_Imap_Client::FLAG_SEEN, $flags)) {
 854:             $this->_queue->poll(array_keys($indices->indices()));
 855:         }
 856: 
 857:         return $change
 858:             ? $this->_viewPortData(true)
 859:             : $this->_viewPortOb();
 860:     }
 861: 
 862:     /**
 863:      * AJAX action: Delete messages.
 864:      *
 865:      * See the list of variables needed for _changed(),
 866:      * _generateDeleteResult(), and _checkUidvalidity(). Additional variables
 867:      * used:
 868:      *   - uid: (string) Indices of the messages to delete (IMAP sequence
 869:      *          string; mailboxes are base64url encoded).
 870:      *
 871:      * @return mixed  False on failure, or an object (see
 872:      *                _generateDeleteResult() for format).
 873:      */
 874:     public function deleteMessages()
 875:     {
 876:         $indices = new IMP_Indices_Form($this->_vars->uid);
 877:         if (!count($indices)) {
 878:             return false;
 879:         }
 880: 
 881:         $change = $this->_changed(true);
 882: 
 883:         if ($GLOBALS['injector']->getInstance('IMP_Message')->delete($indices)) {
 884:             return $this->_generateDeleteResult($indices, $change);
 885:         }
 886: 
 887:         return is_null($change)
 888:             ? false
 889:             : $this->_checkUidvalidity();
 890:     }
 891: 
 892:     /**
 893:      * AJAX action: Add contact.
 894:      *
 895:      * Variables used:
 896:      *   - email: (string) The email address to name.
 897:      *   - name: (string) The name associated with the email address.
 898:      *
 899:      * @return boolean  True on success, false on failure.
 900:      */
 901:     public function addContact()
 902:     {
 903:         // Allow name to be empty.
 904:         if (!$this->_vars->email) {
 905:             return false;
 906:         }
 907: 
 908:         try {
 909:             IMP::addAddress($this->_vars->email, $this->_vars->name);
 910:             $GLOBALS['notification']->push(sprintf(_("%s was successfully added to your address book."), $this->_vars->name ? $this->_vars->name : $this->_vars->email), 'horde.success');
 911:             return true;
 912:         } catch (Horde_Exception $e) {
 913:             $GLOBALS['notification']->push($e);
 914:             return false;
 915:         }
 916:     }
 917: 
 918:     /**
 919:      * AJAX action: Report message as [not]spam.
 920:      *
 921:      * See the list of variables needed for _changed(),
 922:      * _generateDeleteResult(), and _checkUidvalidity(). Additional variables
 923:      * used:
 924:      *   - spam: (integer) 1 to mark as spam, 0 to mark as innocent.
 925:      *   - uid: (string) Indices of the messages to report (IMAP sequence
 926:      *          string; mailboxes are base64url encoded).
 927:      *
 928:      * @return mixed  If messages were deleted, data as returned by
 929:      *                _generateDeleteResult(). Else, true.
 930:      */
 931:     public function reportSpam()
 932:     {
 933:         $change = $this->_changed(false);
 934:         $indices = new IMP_Indices_Form($this->_vars->uid);
 935:         $result = true;
 936: 
 937:         if (IMP_Spam::reportSpam($indices, $this->_vars->spam ? 'spam' : 'notspam')) {
 938:             $result = $this->_generateDeleteResult($indices, $change);
 939:         } elseif (!is_null($change)) {
 940:             $result = $this->_checkUidvalidity(true);
 941:         }
 942: 
 943:         return $result;
 944:     }
 945: 
 946:     /**
 947:      * AJAX action: Blacklist/whitelist addresses from messages.
 948:      *
 949:      * See the list of variables needed for _changed(),
 950:      * _generateDeleteResult(), and _checkUidvalidity(). Additional variables
 951:      * used:
 952:      *   - blacklist: (integer) 1 to blacklist, 0 to whitelist.
 953:      *   - uid: (string) Indices of the messages to report (IMAP sequence
 954:      *          string; mailboxes are base64url encoded).
 955:      *
 956:      * @return mixed  False on failure, or an object (see
 957:      *                _generateDeleteResult() for format).
 958:      */
 959:     public function blacklist()
 960:     {
 961:         $indices = new IMP_Indices_Form($this->_vars->uid);
 962:         if (!count($indices)) {
 963:             return false;
 964:         }
 965: 
 966:         $result = false;
 967: 
 968:         if ($this->_vars->blacklist) {
 969:             $change = $this->_changed(false);
 970:             if (!is_null($change)) {
 971:                 try {
 972:                     if ($GLOBALS['injector']->getInstance('IMP_Filter')->blacklistMessage($indices, false)) {
 973:                         $result = $this->_generateDeleteResult($indices, $change);
 974:                     }
 975:                 } catch (Horde_Exception $e) {
 976:                     $result = $this->_checkUidvalidity();
 977:                 }
 978:             }
 979:         } else {
 980:             try {
 981:                 $GLOBALS['injector']->getInstance('IMP_Filter')->whitelistMessage($indices, false);
 982:             } catch (Horde_Exception $e) {
 983:                 $result = $this->_checkUidvalidity();
 984:             }
 985:         }
 986: 
 987:         return $result;
 988:     }
 989: 
 990:     /**
 991:      * AJAX action: Generate data necessary to display a message.
 992:      *
 993:      * See the list of variables needed for _changed() and
 994:      * _checkUidvalidity().  Additional variables used:
 995:      *   - preview: (integer) If set, return preview data. Otherwise, return
 996:      *              full data.
 997:      *   - uid: (string) Index of the messages to display (IMAP sequence
 998:      *          string; mailbox is base64url encoded) - must be single index.
 999:      *
1000:      * @return mixed  If viewing full message, on error will return null.
1001:      *                Otherwise an object with the following entries:
1002:      *   - message: (object) Return from IMP_Views_ShowMessage::showMessage().
1003:      *              If viewing preview, on error this object will contain
1004:      *              error and errortype properties.
1005:      */
1006:     public function showMessage()
1007:     {
1008:         $indices = new IMP_Indices_Form($this->_vars->uid);
1009:         list($mbox, $idx) = $indices->getSingle();
1010: 
1011:         $result = new stdClass;
1012: 
1013:         try {
1014:             if (!$idx) {
1015:                 throw new IMP_Exception(_("Requested message not found."));
1016:             }
1017: 
1018:             $change = $this->_changed(false);
1019:             if (is_null($change)) {
1020:                 throw new IMP_Exception(_("Could not open mailbox."));
1021:             }
1022: 
1023:             $show_msg = new IMP_Views_ShowMessage();
1024:             $msg = (object)$show_msg->showMessage(array(
1025:                 'mailbox' => $mbox,
1026:                 'preview' => $this->_vars->preview,
1027:                 'uid' => $idx
1028:             ));
1029:             $msg->view = $this->_vars->view;
1030:             $msg->save_as = (string)$msg->save_as;
1031: 
1032:             if ($this->_vars->preview) {
1033:                 $result->preview = $msg;
1034:                 if ($change) {
1035:                     $result = $this->_viewPortData(true, $result);
1036:                 } elseif ($this->_mbox->cacheid_date != $this->_vars->cacheid) {
1037:                     /* Cache ID has changed due to viewing this message. So
1038:                      * update the cacheid in the ViewPort. */
1039:                     $result = $this->_viewPortOb(null, $result);
1040:                 }
1041: 
1042:                 $this->_queue->poll($mbox);
1043:             } else {
1044:                 $result->message = $msg;
1045:             }
1046:         } catch (Exception $e) {
1047:             if (!$this->_vars->preview) {
1048:                 throw $e;
1049:             }
1050: 
1051:             $result->preview->error = $e->getMessage();
1052:             $result->preview->errortype = 'horde.error';
1053:             $result->preview->mbox = $mbox->form_to;
1054:             $result->preview->uid = $idx;
1055:             $result->preview->view = $this->_vars->view;
1056:         }
1057: 
1058:         return $result;
1059:     }
1060: 
1061:     /**
1062:      * AJAX action: Convert HTML to text (compose data).
1063:      *
1064:      * Variables used:
1065:      *   - changed: (integer) Has the text changed from the original?
1066:      *   - identity: (integer) The current identity.
1067:      *   - imp_compose: (string) The IMP_Compose cache identifier.
1068:      *   - text: (string) The text to convert.
1069:      *
1070:      * @return object  An object with the following entries:
1071:      *   - text: (string) The converted text.
1072:      */
1073:     public function html2Text()
1074:     {
1075:         $result = new stdClass;
1076: 
1077:         if (!$this->_vars->changed) {
1078:             list($imp_compose, $imp_contents) = $this->_initCompose();
1079: 
1080:             switch ($imp_compose->replyType()) {
1081:             case IMP_Compose::FORWARD_BODY:
1082:             case IMP_Compose::FORWARD_BOTH:
1083:                 $data = $imp_compose->forwardMessageText($imp_contents, array(
1084:                     'format' => 'text'
1085:                 ));
1086:                 $result->text = $data['body'];
1087:                 return $result;
1088: 
1089:             case IMP_Compose::REPLY_ALL:
1090:             case IMP_Compose::REPLY_LIST:
1091:             case IMP_Compose::REPLY_SENDER:
1092:                 $data = $imp_compose->replyMessageText($imp_contents, array(
1093:                     'format' => 'text'
1094:                 ));
1095:                 $result->text = $data['body'];
1096:                 return $result;
1097:             }
1098:         }
1099: 
1100:         $result->text = $GLOBALS['injector']->getInstance('IMP_Ui_Compose')->convertComposeText($this->_vars->text, 'text', intval($this->_vars->identity));
1101: 
1102:         return $result;
1103:     }
1104: 
1105:     /**
1106:      * AJAX action: Convert text to HTML (compose data).
1107:      *
1108:      * Variables used:
1109:      *   - changed: (integer) Has the text changed from the original?
1110:      *   - identity: (integer) The current identity.
1111:      *   - imp_compose: (string) The IMP_Compose cache identifier.
1112:      *   - text: (string) The text to convert.
1113:      *
1114:      * @return object  An object with the following entries:
1115:      *   - text: (string) The converted text.
1116:      */
1117:     public function text2Html()
1118:     {
1119:         $result = new stdClass;
1120: 
1121:         if (!$this->_vars->changed) {
1122:             list($imp_compose, $imp_contents) = $this->_initCompose();
1123: 
1124:             switch ($imp_compose->replyType()) {
1125:             case IMP_Compose::FORWARD_BODY:
1126:             case IMP_Compose::FORWARD_BOTH:
1127:                 $data = $imp_compose->forwardMessageText($imp_contents, array(
1128:                     'format' => 'html'
1129:                 ));
1130:                 $result->text = $data['body'];
1131:                 return $result;
1132: 
1133:             case IMP_Compose::REPLY_ALL:
1134:             case IMP_Compose::REPLY_LIST:
1135:             case IMP_Compose::REPLY_SENDER:
1136:                 $data = $imp_compose->replyMessageText($imp_contents, array(
1137:                     'format' => 'html'
1138:                 ));
1139:                 $result->text = $data['body'];
1140:                 return $result;
1141:             }
1142:         }
1143: 
1144:         $result->text = $GLOBALS['injector']->getInstance('IMP_Ui_Compose')->convertComposeText($this->_vars->text, 'html', intval($this->_vars->identity));
1145: 
1146:         return $result;
1147:     }
1148: 
1149:     /**
1150:      * AJAX action: Get forward compose data.
1151:      *
1152:      * See the list of variables needed for _checkUidvalidity(). Additional
1153:      * variables used:
1154:      *   - dataonly: (boolean) Only return data information (DEFAULT: false).
1155:      *   - imp_compose: (string) The IMP_Compose cache identifier.
1156:      *   - type: (string) Forward type.
1157:      *   - uid: (string) Indices of the messages to forward (IMAP sequence
1158:      *          string; mailboxes are base64url encoded).
1159:      *
1160:      * @return mixed  False on failure, or an object with the following
1161:      *                entries:
1162:      *   - body: (string) The body text of the message.
1163:      *   - format: (string) Either 'text' or 'html'.
1164:      *   - fwd_list: (array) See _getAttachmentInfo().
1165:      *   - header: (array) The headers of the message.
1166:      *   - identity: (integer) The identity ID to use for this message.
1167:      *   - imp_compose: (string) The IMP_Compose cache identifier.
1168:      *   - opts: (array) Additional options needed for DimpCompose.fillForm().
1169:      *   - type: (string) The input 'type' value.
1170:      *   - ViewPort: (object) See _viewPortData().
1171:      */
1172:     public function getForwardData()
1173:     {
1174:         try {
1175:             list($imp_compose, $imp_contents) = $this->_initCompose();
1176: 
1177:             $fwd_map = array(
1178:                 'forward_attach' => IMP_Compose::FORWARD_ATTACH,
1179:                 'forward_auto' => IMP_Compose::FORWARD_AUTO,
1180:                 'forward_body' => IMP_Compose::FORWARD_BODY,
1181:                 'forward_both' => IMP_Compose::FORWARD_BOTH
1182:             );
1183: 
1184:             $fwd_msg = $imp_compose->forwardMessage($fwd_map[$this->_vars->type], $imp_contents);
1185: 
1186:             /* Can't open session read-only since we need to store the message
1187:              * cache id. */
1188:             $result = new stdClass;
1189:             $result->opts = new stdClass;
1190:             $result->opts->fwd_list = $this->_getAttachmentInfo($imp_compose);
1191:             $result->body = $fwd_msg['body'];
1192:             $result->type = $this->_vars->type;
1193:             if (!$this->_vars->dataonly) {
1194:                 $result->format = $fwd_msg['format'];
1195:                 $result->header = $fwd_msg['headers'];
1196:                 $result->identity = $fwd_msg['identity'];
1197:                 $result->imp_compose = $imp_compose->getCacheId();
1198:                 if ($this->_vars->type == 'forward_auto') {
1199:                     $result->opts->auto = array_search($fwd_msg['type'], $fwd_map);
1200:                 }
1201:             }
1202:         } catch (Horde_Exception $e) {
1203:             $GLOBALS['notification']->push($e);
1204:             $result = $this->_checkUidvalidity();
1205:         }
1206: 
1207:         return $result;
1208:     }
1209: 
1210:     /**
1211:      * AJAX action: Get reply data.
1212:      *
1213:      * See the list of variables needed for _checkUidvalidity(). Additional
1214:      * variables used:
1215:      *   - headeronly: (boolean) Only return header information (DEFAULT:
1216:      *                 false).
1217:      *   - imp_compose: (string) The IMP_Compose cache identifier.
1218:      *   - type: (string) See IMP_Compose::replyMessage().
1219:      *   - uid: (string) Indices of the messages to reply to (IMAP sequence
1220:      *          string; mailboxes are base64url encoded).
1221:      *
1222:      * @return mixed  False on failure, or an object with the following
1223:      *                entries:
1224:      *   - body: (string) The body text of the message.
1225:      *   - format: (string) Either 'text' or 'html'.
1226:      *   - header: (array) The headers of the message.
1227:      *   - identity: (integer) The identity ID to use for this message.
1228:      *   - imp_compose: (string) The IMP_Compose cache identifier.
1229:      *   - opts: (array) Additional options needed for DimpCompose.fillForm().
1230:      *   - type: (string) The input 'type' value.
1231:      *   - ViewPort: (object) See _viewPortData().
1232:      */
1233:     public function getReplyData()
1234:     {
1235:         try {
1236:             list($imp_compose, $imp_contents) = $this->_initCompose();
1237: 
1238:             $reply_map = array(
1239:                 'reply' => IMP_Compose::REPLY_SENDER,
1240:                 'reply_all' => IMP_Compose::REPLY_ALL,
1241:                 'reply_auto' => IMP_Compose::REPLY_AUTO,
1242:                 'reply_list' => IMP_Compose::REPLY_LIST
1243:             );
1244: 
1245:             $reply_msg = $imp_compose->replyMessage($reply_map[$this->_vars->type], $imp_contents);
1246: 
1247:             /* Can't open session read-only since we need to store the message
1248:              * cache id. */
1249:             $result = new stdClass;
1250:             $result->header = $reply_msg['headers'];
1251:             $result->type = $this->_vars->type;
1252:             if (!$this->_vars->headeronly) {
1253:                 $result->body = $reply_msg['body'];
1254:                 $result->format = $reply_msg['format'];
1255:                 $result->identity = $reply_msg['identity'];
1256:                 $result->imp_compose = $imp_compose->getCacheId();
1257:                 if ($this->_vars->type == 'reply_auto') {
1258:                     $result->opts = array_filter(array(
1259:                         'auto' => array_search($reply_msg['type'], $reply_map),
1260:                         'reply_list_id' => (isset($reply_msg['reply_list_id']) ? $reply_msg['reply_list_id'] : null),
1261:                         'reply_recip' => (isset($reply_msg['reply_recip']) ? $reply_msg['reply_recip'] : null),
1262:                     ));
1263:                 }
1264:             }
1265:         } catch (Horde_Exception $e) {
1266:             $GLOBALS['notification']->push($e);
1267:             $result = $this->_checkUidvalidity();
1268:         }
1269: 
1270:         return $result;
1271:     }
1272: 
1273:     /**
1274:      * AJAX action: Get compose redirect data.
1275:      *
1276:      * Variables used:
1277:      *   - uid: (string) Index of the message to redirect (IMAP sequence
1278:      *          string; mailbox is base64url encoded).
1279:      *
1280:      * @return mixed  False on failure, or an object with the following
1281:      *                entries:
1282:      *   - imp_compose: (string) The IMP_Compose cache identifier.
1283:      *   - type: (string) The input 'type' value.
1284:      */
1285:     public function getRedirectData()
1286:     {
1287:         list($imp_compose, $imp_contents) = $this->_initCompose();
1288: 
1289:         $imp_compose->redirectMessage(new IMP_Indices($imp_contents->getMailbox(), $imp_contents->getUid()));
1290: 
1291:         $ob = new stdClass;
1292:         $ob->imp_compose = $imp_compose->getCacheId();
1293:         $ob->type = $this->_vars->type;
1294: 
1295:         return $ob;
1296:     }
1297: 
1298:     /**
1299:      * AJAX action: Cancel compose.
1300:      *
1301:      * Variables used:
1302:      *   - imp_compose: (string) The IMP_Compose cache identifier.
1303:      *
1304:      * @return boolean  True.
1305:      */
1306:     public function cancelCompose()
1307:     {
1308:         $imp_compose = $GLOBALS['injector']->getInstance('IMP_Factory_Compose')->create($this->_vars->imp_compose);
1309:         $imp_compose->destroy('cancel');
1310: 
1311:         return true;
1312:     }
1313: 
1314:     /**
1315:      * AJAX action: Delete a draft.
1316:      *
1317:      * Variables used:
1318:      *   - imp_compose: (string) The IMP_Compose cache identifier.
1319:      *
1320:      * @return boolean  True.
1321:      */
1322:     public function deleteDraft()
1323:     {
1324:         $GLOBALS['injector']->getInstance('IMP_Factory_Compose')->create($this->_vars->imp_compose)->destroy('cancel');
1325:         return true;
1326:     }
1327: 
1328:     /**
1329:      * AJAX action: Delete an attachment from compose data.
1330:      *
1331:      * Variables used:
1332:      *   - atc_indices: (string) [JSON array] Attachment IDs to delete.
1333:      *   - imp_compose: (string) The IMP_Compose cache identifier.
1334:      *
1335:      * @return boolean  True.
1336:      */
1337:     public function deleteAttach()
1338:     {
1339:         if (isset($this->_vars->atc_indices)) {
1340:             $imp_compose = $GLOBALS['injector']->getInstance('IMP_Factory_Compose')->create($this->_vars->imp_compose);
1341:             foreach (Horde_Serialize::unserialize($this->_vars->atc_indices, Horde_Serialize::JSON) as $val) {
1342:                 if ($part = $imp_compose[$val]['part']) {
1343:                     $GLOBALS['notification']->push(sprintf(_("Deleted attachment \"%s\"."), Horde_Mime::decode($part->getName(true), 'UTF-8')), 'horde.success');
1344:                 }
1345:                 unset($imp_compose[$val]);
1346:             }
1347:         }
1348: 
1349:         return true;
1350:     }
1351: 
1352:     /**
1353:      * AJAX action: Purge deleted messages.
1354:      *
1355:      * See the list of variables needed for _changed(), and
1356:      * _generateDeleteResult().
1357:      *
1358:      * @return mixed  False on failure, or an object (see
1359:      *                _generateDeleteResult() for format).
1360:      */
1361:     public function purgeDeleted()
1362:     {
1363:         global $injector;
1364: 
1365:         $change = $this->_changed(true);
1366:         if (is_null($change)) {
1367:             return false;
1368:         }
1369: 
1370:         if (!$change) {
1371:             $sort = $this->_mbox->getSort();
1372:             $change = ($sort['by'] == Horde_Imap_Client::SORT_THREAD);
1373:         }
1374: 
1375:         $expunged = $injector->getInstance('IMP_Message')->expungeMailbox(array(strval($this->_mbox) => 1), array('list' => true));
1376: 
1377:         if (!($expunge_count = count($expunged))) {
1378:             return false;
1379:         }
1380: 
1381:         $GLOBALS['notification']->push(sprintf(ngettext("%d message was purged from \"%s\".", "%d messages were purged from \"%s\".", $expunge_count), $expunge_count, $this->_mbox->display), 'horde.success');
1382: 
1383:         return $this->_generateDeleteResult($expunged, $change, true);
1384:     }
1385: 
1386:     /**
1387:      * AJAX action: Send a Message Disposition Notification (MDN).
1388:      *
1389:      * Variables used:
1390:      *   - uid: (string) Index of the messages to send MDN for (IMAP sequence
1391:      *          string; mailbox is base64url encoded) - must be single index.
1392:      *
1393:      * @return mixed  False on failure, or an object with these properties:
1394:      *   - mbox: (string) Mailbox of message (base64url encoded).
1395:      *   - uid: (integer) UID of message.
1396:      */
1397:     public function sendMDN()
1398:     {
1399:         $indices = new IMP_Indices_Form($this->_vars->uid);
1400:         if (count($indices) != 1) {
1401:             return false;
1402:         }
1403: 
1404:         try {
1405:             $contents = $GLOBALS['injector']->getInstance('IMP_Factory_Contents')->create($indices);
1406:         } catch (IMP_Imap_Exception $e) {
1407:             $e->notify(_("The Message Disposition Notification was not sent. This is what the server said") . ': ' . $e->getMessage());
1408:             return false;
1409:         }
1410: 
1411:         list($mbox, $uid) = $indices->getSingle();
1412:         $imp_ui = new IMP_Ui_Message();
1413:         $imp_ui->MDNCheck($mbox, $uid, $contents->getHeaderAndMarkAsSeen(), true);
1414: 
1415:         $GLOBALS['notification']->push(_("The Message Disposition Notification was sent successfully."), 'horde.success');
1416: 
1417:         $result = new stdClass;
1418:         $result->mbox = $mbox->form_to;
1419:         $result->uid = $uid;
1420: 
1421:         return $result;
1422:     }
1423: 
1424:     /**
1425:      * AJAX action: strip attachment.
1426:      *
1427:      * See the list of variables needed for _changed() and
1428:      * _checkUidvalidity().  Additional variables used:
1429:      *   - uid: (string) Index of the messages to preview (IMAP sequence
1430:      *          string; bsae64url encoded) - must be single index.
1431:      *
1432:      * @return mixed  False on failure, the return from showMessage() on
1433:      *                success along with these properties:
1434:      *   - oldmbox: (string) Mailbox of old message (base64url encoded).
1435:      *   - olduid: (integer) UID of old message.
1436:      *   - ViewPort: (object) See _viewPortData().
1437:      */
1438:     public function stripAttachment()
1439:     {
1440:         $indices = new IMP_Indices_Form($this->_vars->uid);
1441:         if (count($indices) != 1) {
1442:             return false;
1443:         }
1444: 
1445:         $change = $this->_changed(false);
1446:         if (is_null($change)) {
1447:             return false;
1448:         }
1449: 
1450:         try {
1451:             $new_indices = $GLOBALS['injector']->getInstance('IMP_Message')->stripPart($indices, $this->_vars->id);
1452:         } catch (IMP_Exception $e) {
1453:             $GLOBALS['notification']->push($e);
1454:             return false;
1455:         }
1456: 
1457:         $GLOBALS['notification']->push(_("Attachment successfully stripped."), 'horde.success');
1458: 
1459:         $this->_vars->preview = 1;
1460:         $this->_vars->uid = $new_indices->formTo();
1461:         $result = $this->showMessage();
1462: 
1463:         $old_indices_list = $indices->getSingle();
1464:         $result->oldmbox = $old_indices_list[0]->form_to;
1465:         $result->olduid = $old_indices_list[1];
1466: 
1467:         $result = $this->_viewPortData(true, $result);
1468: 
1469:         return $result;
1470:     }
1471: 
1472:     /**
1473:      * AJAX action: Add an attachment to a compose message.
1474:      *
1475:      * Variables used:
1476:      *   - composeCache: (string) The IMP_Compose cache identifier.
1477:      *
1478:      * @return object  An object with the following entries:
1479:      *   - atc: (integer) The attachment ID.
1480:      *   - error: (string) An error message.
1481:      *   - imp_compose: (string) The IMP_Compose cache identifier.
1482:      *   - success: (integer) 1 on success, 0 on failure.
1483:      */
1484:     public function addAttachment()
1485:     {
1486:         $result = new stdClass;
1487:         $result->action = 'addAttachment';
1488:         $result->success = 0;
1489: 
1490:         if (!isset($this->_vars->composeCache)) {
1491:             $GLOBALS['notification']->push(_("Your attachment was not uploaded. Most likely, the file exceeded the maximum size allowed by the server configuration."), 'horde.warning');
1492:             return $result;
1493:         }
1494: 
1495:         $imp_compose = $GLOBALS['injector']->getInstance('IMP_Factory_Compose')->create($this->_vars->composeCache);
1496: 
1497:         if ($GLOBALS['session']->get('imp', 'file_upload') &&
1498:             $imp_compose->addFilesFromUpload('file_')) {
1499:             $result->atc = end($this->_getAttachmentInfo($imp_compose));
1500:             $result->success = 1;
1501:             $result->imp_compose = $imp_compose->getCacheId();
1502:         }
1503: 
1504:         return $result;
1505:     }
1506: 
1507:     /**
1508:      * AJAX action: Auto save a draft message.
1509:      *
1510:      * @return object  See self::_dimpDraftAction().
1511:      */
1512:     public function autoSaveDraft()
1513:     {
1514:         return $this->_dimpDraftAction();
1515:     }
1516: 
1517:     /**
1518:      * AJAX action: Save a draft message.
1519:      *
1520:      * @return object  See self::_dimpDraftAction().
1521:      */
1522:     public function saveDraft()
1523:     {
1524:         return $this->_dimpDraftAction();
1525:     }
1526: 
1527:     /**
1528:      * AJAX action: Send a message.
1529:      *
1530:      * See the list of variables needed for _dimpComposeSetup(). Additional
1531:      * variables used:
1532:      *   - encrypt: (integer) The encryption method to use (IMP ENCRYPT
1533:      *              constants).
1534:      *   - html: (integer) In HTML compose mode?
1535:      *   - message: (string) The message text.
1536:      *   - priority: (string) The priority of the message.
1537:      *   - request_read_receipt: (boolean) Add request read receipt header?
1538:      *   - save_attachments_select: (boolean) Whether to save attachments.
1539:      *   - save_sent_mail: (boolean) True if saving sent mail.
1540:      *   - save_sent_mail_folder: (string) base64url encoded version of
1541:      *                            sent mailbox to use.
1542:      *
1543:      * @return object  An object with the following entries:
1544:      *   - action: (string) The AJAX action string
1545:      *   - draft_delete: (integer) If set, remove auto-saved drafts.
1546:      *   - encryptjs: (array) Javascript to run after encryption failure.
1547:      *   - flag: (array) See IMP_Ajax_Queue::generate().
1548:      *   - identity: (integer) If set, this is the identity that is tied to
1549:      *               the current recipient address.
1550:      *   - log: (array) Maillog information
1551:      *   - mailbox: (array) See _getMailboxResponse().
1552:      *   - mbox: (string) Mailbox of original message (base64url encoded).
1553:      *   - success: (integer) 1 on success, 0 on failure.
1554:      *   - uid: (integer) IMAP UID of original message.
1555:      */
1556:     public function sendMessage()
1557:     {
1558:         try {
1559:             list($result, $imp_compose, $headers, $identity) = $this->_dimpComposeSetup();
1560:             if (!IMP::canCompose()) {
1561:                 $result->success = 0;
1562:                 return $result;
1563:             }
1564:         } catch (Horde_Exception $e) {
1565:             $GLOBALS['notification']->push($e);
1566: 
1567:             $result = new stdClass;
1568:             $result->action = $this->_action;
1569:             $result->success = 0;
1570:             return $result;
1571:         }
1572: 
1573:         $headers['replyto'] = $identity->getValue('replyto_addr');
1574: 
1575:         /* Use IMP_Tree to determine whether the sent mail folder was
1576:          * created. */
1577:         $imptree = $GLOBALS['injector']->getInstance('IMP_Imap_Tree');
1578:         $imptree->eltDiffStart();
1579: 
1580:         $sm_displayed = !empty($GLOBALS['conf']['user']['select_sentmail_folder']) && !$GLOBALS['prefs']->isLocked('sent_mail_folder');
1581: 
1582:         $options = array(
1583:             'encrypt' => ($GLOBALS['prefs']->isLocked('default_encrypt') ? $GLOBALS['prefs']->getValue('default_encrypt') : $this->_vars->encrypt),
1584:             'html' => $this->_vars->html,
1585:             'identity' => $identity,
1586:             'priority' => $this->_vars->priority,
1587:             'readreceipt' => $this->_vars->request_read_receipt,
1588:             'save_attachments' => $this->_vars->save_attachments_select,
1589:             'save_sent' => ($sm_displayed
1590:                             ? (bool)$this->_vars->save_sent_mail
1591:                             : $identity->getValue('save_sent_mail')),
1592:             'sent_folder' => ($sm_displayed
1593:                               ? (isset($this->_vars->save_sent_mail_folder) ? IMP_Mailbox::formFrom($this->_vars->save_sent_mail_folder) : $identity->getValue('sent_mail_folder'))
1594:                               : $identity->getValue('sent_mail_folder'))
1595:         );
1596: 
1597:         try {
1598:             $sent = $imp_compose->buildAndSendMessage($this->_vars->message, $headers, $options);
1599:         } catch (IMP_Compose_Exception $e) {
1600:             $result->success = 0;
1601: 
1602:             if (!is_null($e->tied_identity)) {
1603:                 $result->identity = $e->tied_identity;
1604:             }
1605: 
1606:             if ($e->encrypt) {
1607:                 $imp_ui = $GLOBALS['injector']->getInstance('IMP_Ui_Compose');
1608:                 switch ($e->encrypt) {
1609:                 case 'pgp_symmetric_passphrase_dialog':
1610:                     $imp_ui->passphraseDialog('pgp_symm', $imp_compose->getCacheId());
1611:                     break;
1612: 
1613:                 case 'pgp_passphrase_dialog':
1614:                     $imp_ui->passphraseDialog('pgp');
1615:                     break;
1616: 
1617:                 case 'smime_passphrase_dialog':
1618:                     $imp_ui->passphraseDialog('smime');
1619:                     break;
1620:                 }
1621: 
1622:                 Horde::startBuffer();
1623:                 Horde::outputInlineScript(true);
1624:                 if ($js_inline = Horde::endBuffer()) {
1625:                     $result->encryptjs = array($js_inline);
1626:                 }
1627:             } else {
1628:                 /* Don't push notification if showing passphrase dialog -
1629:                  * passphrase dialog contains the necessary information. */
1630:                 $GLOBALS['notification']->push($e);
1631:             }
1632: 
1633:             return $result;
1634:         }
1635: 
1636:         /* Remove any auto-saved drafts. */
1637:         if ($imp_compose->hasDrafts()) {
1638:             $result->draft_delete = 1;
1639:         }
1640: 
1641:         if ($sent) {
1642:             $GLOBALS['notification']->push(empty($headers['subject']) ? _("Message sent successfully.") : sprintf(_("Message \"%s\" sent successfully."), Horde_String::truncate($headers['subject'])), 'horde.success');
1643:         }
1644: 
1645:         /* Update maillog information. */
1646:         if (!empty($GLOBALS['conf']['maillog']['use_maillog'])) {
1647:             $in_reply_to = $imp_compose->getMetadata('in_reply_to');
1648:             if (!empty($in_reply_to) &&
1649:                 ($tmp = IMP_Dimp::getMsgLogInfo($in_reply_to))) {
1650:                 $result->log = $tmp;
1651:             }
1652:         }
1653: 
1654:         if ($reply_mbox = $imp_compose->getMetadata('mailbox')) {
1655:             $result->mbox = $reply_mbox->form_to;
1656:             $result->uid = $imp_compose->getMetadata('uid');
1657:         }
1658: 
1659:         $imp_compose->destroy('send');
1660: 
1661:         $result->mailbox = $this->_getMailboxResponse($imptree);
1662: 
1663:         return $result;
1664:     }
1665: 
1666:     /**
1667:      * Redirect the message.
1668:      *
1669:      * Variables used:
1670:      *   - composeCache: (string) The IMP_Compose cache identifier.
1671:      *   - redirect_to: (string) The address(es) to redirect to.
1672:      *
1673:      * @return object  An object with the following entries:
1674:      *   - action: (string) 'redirectMessage'.
1675:      *   - log: (array) TODO
1676:      *   - success: (integer) 1 on success, 0 on failure.
1677:      */
1678:     public function redirectMessage()
1679:     {
1680:         $result = new stdClass;
1681:         $result->action = $this->_action;
1682:         $result->success = 1;
1683: 
1684:         $log = array();
1685: 
1686:         try {
1687:             $imp_compose = $GLOBALS['injector']->getInstance('IMP_Factory_Compose')->create($this->_vars->composeCache);
1688:             $res = $imp_compose->sendRedirectMessage($this->_vars->redirect_to);
1689: 
1690:             foreach ($res as $val) {
1691:                 $subject = $val->headers->getValue('subject');
1692:                 $GLOBALS['notification']->push(empty($subject) ? _("Message redirected successfully.") : sprintf(_("Message \"%s\" redirected successfully."), Horde_String::truncate($subject)), 'horde.success');
1693: 
1694:                 if (!empty($GLOBALS['conf']['maillog']['use_maillog']) &&
1695:                     ($tmp = IMP_Dimp::getMsgLogInfo($val->headers->getValue('message-id')))) {
1696:                     $log_ob = new stdClass;
1697:                     $log_ob->log = $tmp;
1698:                     $log_ob->mbox = $val->mbox->form_to;
1699:                     $log_ob->uid = $val->uid;
1700:                     $log[] = $log_ob;
1701:                 }
1702:             }
1703:         } catch (Horde_Exception $e) {
1704:             $GLOBALS['notification']->push($e);
1705:             $result->success = 0;
1706:         }
1707: 
1708:         if (!empty($log)) {
1709:             $result->log = $log;
1710:         }
1711: 
1712:         return $result;
1713:     }
1714: 
1715:     /**
1716:      * AJAX action: Create mailbox select list for advanced search page.
1717:      *
1718:      * Variables used:
1719:      *   - unsub: (integer) If set, includes unsubscribed mailboxes.Th
1720:      *
1721:      * @return object  An object with the following entries:
1722:      *   - folder_list: (array)
1723:      *   - tree: (string)
1724:      */
1725:     public function searchMailboxList()
1726:     {
1727:         $ob = $GLOBALS['injector']->getInstance('IMP_Ui_Search')->getSearchMboxList($this->_vars->unsub);
1728: 
1729:         $result = new stdClass;
1730:         $result->folder_list = $ob->folder_list;
1731:         $result->tree = $ob->tree->getTree();
1732: 
1733:         return $result;
1734:     }
1735: 
1736:     /* Protected methods. */
1737: 
1738:     /**
1739:      * Setup environment for dimp compose actions.
1740:      *
1741:      * Variables used:
1742:      *   - composeCache: (string) The IMP_Compose cache identifier.
1743:      *   - from: (string) From address to use.
1744:      *   - identity: (integer) The identity to use
1745:      *
1746:      * @return array  An array with the following values:
1747:      *   - (object) AJAX base return object (with action and success
1748:      *     parameters defined).
1749:      *   - (IMP_Compose) The IMP_Compose object for the message.
1750:      *   - (array) The list of headers for the object.
1751:      *   - (Horde_Prefs_Identity) The identity used for the composition.
1752:      *
1753:      * @throws Horde_Exception
1754:      */
1755:     protected function _dimpComposeSetup()
1756:     {
1757:         global $injector, $prefs;
1758: 
1759:         /* Set up identity. */
1760:         $identity = $injector->getInstance('IMP_Identity');
1761:         if (isset($this->_vars->identity) &&
1762:             !$prefs->isLocked('default_identity')) {
1763:             $identity->setDefault($this->_vars->identity);
1764:         }
1765: 
1766:         /* Set up the From address based on the identity. */
1767:         $headers = array(
1768:             'from' => $identity->getFromLine(null, $this->_vars->from)
1769:         );
1770: 
1771:         $imp_ui = $injector->getInstance('IMP_Ui_Compose');
1772:         $headers['to'] = $imp_ui->getAddressList($this->_vars->to);
1773:         if ($prefs->getValue('compose_cc')) {
1774:             $headers['cc'] = $imp_ui->getAddressList($this->_vars->cc);
1775:         }
1776:         if ($prefs->getValue('compose_bcc')) {
1777:             $headers['bcc'] = $imp_ui->getAddressList($this->_vars->bcc);
1778:         }
1779:         $headers['subject'] = $this->_vars->subject;
1780: 
1781:         $imp_compose = $injector->getInstance('IMP_Factory_Compose')->create($this->_vars->composeCache);
1782: 
1783:         $result = new stdClass;
1784:         $result->action = $this->_action;
1785:         $result->success = 1;
1786: 
1787:         return array($result, $imp_compose, $headers, $identity);
1788:     }
1789: 
1790:     /**
1791:      * Initialize the objects needed to compose.
1792:      *
1793:      * @return array  An IMP_Compose object and an IMP_Contents object.
1794:      */
1795:     protected function _initCompose()
1796:     {
1797:         $imp_compose = $GLOBALS['injector']->getInstance('IMP_Factory_Compose')->create($this->_vars->imp_compose);
1798:         if (!($imp_contents = $imp_compose->getContentsOb())) {
1799:             $imp_contents = $this->_vars->uid
1800:                 ? $GLOBALS['injector']->getInstance('IMP_Factory_Contents')->create(new IMP_Indices_Form($this->_vars->uid))
1801:                 : null;
1802:         }
1803: 
1804:         return array($imp_compose, $imp_contents);
1805:     }
1806: 
1807:     /**
1808:      * Save a draft composed message.
1809:      *
1810:      * See the list of variables needed for _dimpComposeSetup(). Additional
1811:      * variables used:
1812:      *   - html: (integer) In HTML compose mode?
1813:      *   - message: (string) The message text.
1814:      *   - priority: (string) The priority of the message.
1815:      *   - request_read_receipt: (boolean) Add request read receipt header?
1816:      *
1817:      * @return object  An object with the following entries:
1818:      *   - action: (string) The AJAX action string
1819:      *   - success: (integer) 1 on success, 0 on failure.
1820:      */
1821:     protected function _dimpDraftAction()
1822:     {
1823:         try {
1824:             list($result, $imp_compose, $headers, $identity) = $this->_dimpComposeSetup();
1825:         } catch (Horde_Exception $e) {
1826:             $GLOBALS['notification']->push($e);
1827: 
1828:             $result = new stdClass;
1829:             $result->action = $this->_action;
1830:             $result->success = 0;
1831:             return $result;
1832:         }
1833: 
1834:         try {
1835:             $res = $imp_compose->saveDraft($headers, $this->_vars->message, array(
1836:                 'html' => $this->_vars->html,
1837:                 'priority' => $this->_vars->priority,
1838:                 'readreceipt' => $this->_vars->request_read_receipt
1839:             ));
1840:             if ($this->_action == 'autoSaveDraft') {
1841:                 $GLOBALS['notification']->push(_("Draft automatically saved."), 'horde.message');
1842:             } else {
1843:                 $GLOBALS['notification']->push($res);
1844:                 if ($GLOBALS['prefs']->getValue('close_draft')) {
1845:                     $imp_compose->destroy('save_draft');
1846:                 }
1847:             }
1848:         } catch (IMP_Compose_Exception $e) {
1849:             $result->success = 0;
1850:             $GLOBALS['notification']->push($e);
1851:         }
1852: 
1853:         return $result;
1854:     }
1855: 
1856:     /**
1857:      * Check the UID validity of the mailbox.
1858:      *
1859:      * See the list of variables needed for _viewPortData().
1860:      *
1861:      * @return mixed  The JSON result, possibly with ViewPort information
1862:      *                added if UID validity has changed.
1863:      */
1864:     protected function _checkUidvalidity($result = false)
1865:     {
1866:         try {
1867:             $this->_mbox->uidvalid;
1868:         } catch (IMP_Exception $e) {
1869:             $result = $this->_viewPortData(true, $result);
1870:         }
1871: 
1872:         return $result;
1873:     }
1874: 
1875:     /**
1876:      * Generates the delete data needed for dimpbase.js.
1877:      *
1878:      * See the list of variables needed for _viewPortData().
1879:      *
1880:      * @param IMP_Indices $indices  An indices object.
1881:      * @param boolean $changed      If true, add full ViewPort information.
1882:      * @param boolean $force        If true, forces addition of disappear
1883:      *                              information.
1884:      *
1885:      * @return object  An object with the following entries:
1886:      *   - ViewPort: (object) See _viewPortData().
1887:      */
1888:     protected function _generateDeleteResult($indices, $changed,
1889:                                              $force = false)
1890:     {
1891:         /* Check if we need to update thread information. */
1892:         if (!$changed) {
1893:             $sort = $this->_mbox->getSort();
1894:             $changed = ($sort['by'] == Horde_Imap_Client::SORT_THREAD);
1895:         }
1896: 
1897:         if ($changed) {
1898:             $result = $this->_viewPortData(true);
1899:         } else {
1900:             $result = $this->_viewPortOb();
1901: 
1902:             if ($force || $this->_mbox->hideDeletedMsgs(true)) {
1903:                 if ($this->_mbox->search) {
1904:                     $disappear = array();
1905:                     foreach ($indices as $val) {
1906:                         foreach ($val->uids as $val2) {
1907:                             $disappear[] = IMP_Views_ListMessages::searchUid($val->mbox, $val2);
1908:                         }
1909:                     }
1910:                 } else {
1911:                     $disappear = end($indices->getSingle(true));
1912:                 }
1913:                 $result->ViewPort->disappear = $disappear;
1914:             }
1915:         }
1916: 
1917:         $this->_queue->poll(array_keys($indices->indices()));
1918: 
1919:         return $result;
1920:     }
1921: 
1922:     /**
1923:      * Determine if the cache information has changed.
1924:      *
1925:      * Variables used:
1926:      *   - cacheid: (string) The browser (ViewPort) cache identifier.
1927:      *   - forceUpdate: (integer) If 1, forces an update.
1928:      *
1929:      * @param boolean $rw  Open mailbox as READ+WRITE?
1930:      *
1931:      * @return boolean  True if the server state differs from the browser
1932:      *                  state.
1933:      */
1934:     protected function _changed($rw = null)
1935:     {
1936:         /* Only update search mailboxes on forced refreshes. */
1937:         if ($this->_mbox->search) {
1938:             return !empty($this->_vars->forceUpdate);
1939:         }
1940: 
1941:         /* We know we are going to be dealing with this mailbox, so select it
1942:          * on the IMAP server (saves some STATUS calls). */
1943:         if (!is_null($rw)) {
1944:             try {
1945:                 $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create()->openMailbox($this->_mbox, $rw ? Horde_Imap_Client::OPEN_READWRITE : Horde_Imap_Client::OPEN_AUTO);
1946:             } catch (IMP_Imap_Exception $e) {
1947:                 $e->notify();
1948:                 return null;
1949:             }
1950:         }
1951: 
1952:         return ($this->_mbox->cacheid_date != $this->_vars->cacheid);
1953:     }
1954: 
1955:     /**
1956:      * Generate the information necessary for a ViewPort request from/to the
1957:      * browser.
1958:      *
1959:      * @param boolean $change  True if cache information has changed.
1960:      * @param object $base     The object to use as the base.
1961:      *
1962:      * @return array  See IMP_Views_ListMessages::listMessages().
1963:      */
1964:     protected function _viewPortData($change, $base = null)
1965:     {
1966:         $args = array(
1967:             'change' => $change,
1968:             'mbox' => strval($this->_mbox)
1969:         );
1970: 
1971:         $params = array(
1972:             'applyfilter', 'cache', 'cacheid', 'delhide', 'initial', 'qsearch',
1973:             'qsearchfield', 'qsearchfilter', 'qsearchflag', 'qsearchflagnot',
1974:             'qsearchmbox', 'rangeslice', 'requestid', 'sortby', 'sortdir'
1975:         );
1976: 
1977:         foreach ($params as $val) {
1978:             $args[$val] = $this->_vars->$val;
1979:         }
1980: 
1981:         if ($this->_vars->search || $args['initial']) {
1982:             $args += array(
1983:                 'after' => intval($this->_vars->after),
1984:                 'before' => intval($this->_vars->before)
1985:             );
1986:         }
1987: 
1988:         if (!$this->_vars->search) {
1989:             list($slice_start, $slice_end) = explode(':', $this->_vars->slice, 2);
1990:             $args += array(
1991:                 'slice_start' => intval($slice_start),
1992:                 'slice_end' => intval($slice_end)
1993:             );
1994:         } else {
1995:             $search = Horde_Serialize::unserialize($this->_vars->search, Horde_Serialize::JSON);
1996:             $args += array(
1997:                 'search_uid' => isset($search->uid) ? $search->uid : null,
1998:                 'search_unseen' => isset($search->unseen) ? $search->unseen : null
1999:             );
2000:         }
2001: 
2002:         if (is_null($base) || !is_object($base)) {
2003:             $base = new stdClass;
2004:         }
2005: 
2006:         $list_msg = new IMP_Views_ListMessages();
2007:         $base->ViewPort = $list_msg->listMessages($args);
2008: 
2009:         return $base;
2010:     }
2011: 
2012:     /**
2013:      * Formats the response to send to javascript code when dealing with
2014:      * mailbox operations.
2015:      *
2016:      * @param IMP_Imap_Tree $imptree  A Tree object.
2017:      * @param array $changes          An array with three sub arrays - to be
2018:      *                                used instead of the return from
2019:      *                                $imptree->eltDiff():
2020:      *   - a: (array) A list of mailboxes/objects to add.
2021:      *   - c: (array) A list of changed mailboxes.
2022:      *   - d: (array) A list of mailboxes to delete.
2023:      *
2024:      * @return array  The object used by the JS code to update the folder
2025:      *                tree.
2026:      */
2027:     protected function _getMailboxResponse($imptree, $changes = null)
2028:     {
2029:         if (is_null($changes)) {
2030:             $changes = $imptree->eltDiff();
2031:         }
2032:         if (empty($changes)) {
2033:             return false;
2034:         }
2035: 
2036:         $result = array();
2037: 
2038:         if (!empty($changes['a'])) {
2039:             $result['a'] = array();
2040:             foreach ($changes['a'] as $val) {
2041:                 $result['a'][] = $this->_createMailboxElt(is_object($val) ? $val : $imptree[$val]);
2042:             }
2043:         }
2044: 
2045:         if (!empty($changes['c'])) {
2046:             $result['c'] = array();
2047:             foreach ($changes['c'] as $val) {
2048:                 // Skip the base element, since any change there won't ever be
2049:                 // updated on-screen.
2050:                 if ($val != IMP_Imap_Tree::BASE_ELT) {
2051:                     $result['c'][] = $this->_createMailboxElt($imptree[$val]);
2052:                 }
2053:             }
2054:         }
2055: 
2056:         if (!empty($changes['d'])) {
2057:             $result['d'] = array();
2058:             foreach (array_reverse($changes['d']) as $val) {
2059:                 $result['d'][] = IMP_Mailbox::get($val)->form_to;
2060:             }
2061:         }
2062: 
2063:         return $result;
2064:     }
2065: 
2066:     /**
2067:      * Create an object used by DimpCore to generate the folder tree.
2068:      *
2069:      * @param IMP_Mailbox $elt  A mailbox object.
2070:      *
2071:      * @return stdClass  The element object. Contains the following items:
2072:      *   - ch: (boolean) [children] Does the mailbox contain children?
2073:      *         DEFAULT: no
2074:      *   - cl: (string) [class] The CSS class.
2075:      *         DEFAULT: 'base'
2076:      *   - co: (boolean) [container] Is this mailbox a container element?
2077:      *         DEFAULT: no
2078:      *   - i: (string) [icon] A user defined icon to use.
2079:      *        DEFAULT: none
2080:      *   - l: (string) [label] The mailbox display label.
2081:      *        DEFAULT: 'm' val
2082:      *   - m: (string) [mbox] The mailbox value (base64url encoded).
2083:      *   - n: (boolean) [non-imap] A non-IMAP element?
2084:      *        DEFAULT: no
2085:      *   - pa: (string) [parent] The parent element.
2086:      *         DEFAULT: DIMP.conf.base_mbox
2087:      *   - po: (boolean) [polled] Is the element polled?
2088:      *         DEFAULT: no
2089:      *   - s: (boolean) [special] Is this a "special" element?
2090:      *        DEFAULT: no
2091:      *   - sup: (boolean) [suppress] Suppress display of this element?
2092:      *          DEFAULT: no
2093:      *   - t: (string) [title] Mailbox title.
2094:      *        DEFAULT: 'm' val
2095:      *   - un: (boolean) [unsubscribed] Is this mailbox unsubscribed?
2096:      *         DEFAULT: no
2097:      *   - v: (integer) [virtual] Virtual folder? 0 = not vfolder, 1 = system
2098:      *        vfolder, 2 = user vfolder
2099:      *        DEFAULT: 0
2100:      */
2101:     protected function _createMailboxElt(IMP_Mailbox $elt)
2102:     {
2103:         $ob = new stdClass;
2104: 
2105:         if ($elt->children) {
2106:             $ob->ch = 1;
2107:         }
2108:         $ob->m = $elt->form_to;
2109: 
2110:         $label = $elt->label;
2111:         if ($ob->m != $label) {
2112:             $ob->t = $label;
2113:         }
2114: 
2115:         $tmp = htmlspecialchars($elt->abbrev_label);
2116:         if ($ob->m != $tmp) {
2117:             $ob->l = $tmp;
2118:         }
2119: 
2120:         $parent = $elt->parent;
2121:         if ($parent != IMP_Imap_Tree::BASE_ELT) {
2122:             $ob->pa = $parent->form_to;
2123:         }
2124:         if ($elt->vfolder) {
2125:             $ob->v = $elt->editvfolder ? 2 : 1;
2126:         }
2127:         if (!$elt->sub) {
2128:             $ob->un = 1;
2129:         }
2130: 
2131:         if ($elt->container) {
2132:             $ob->cl = 'exp';
2133:             $ob->co = 1;
2134:             if ($elt->nonimap) {
2135:                 $ob->n = 1;
2136:             }
2137:         } else {
2138:             if ($elt->polled) {
2139:                 $ob->po = 1;
2140:                 $this->_queue->poll($elt);
2141:             }
2142: 
2143:             if ($elt->special) {
2144:                 $ob->s = 1;
2145:             } elseif (empty($ob->v) && $elt->children) {
2146:                 $ob->cl = 'exp';
2147:             }
2148:         }
2149: 
2150:         $icon = $elt->icon;
2151:         if ($icon->user_icon) {
2152:             $ob->cl = 'customimg';
2153:             $ob->i = strval($icon->icon);
2154:         } else {
2155:             $ob->cl = $icon->class;
2156:         }
2157: 
2158:         if (!empty($this->_suppress) &&
2159:             in_array($elt->value, $this->_suppress)) {
2160:             $ob->sup = true;
2161:         }
2162: 
2163:         return $ob;
2164:     }
2165: 
2166:     /**
2167:      * Return a basic ViewPort object.
2168:      *
2169:      * @param IMP_Mailbox $mbox  The mailbox view of the ViewPort request.
2170:      *                           Defaults to current view.
2171:      * @param object $base       The base object to add ViewPort data to.
2172:      *                           Creates a new base object if empty.
2173:      *
2174:      * @return object  The return object with ViewPort data added.
2175:      */
2176:     protected function _viewPortOb($mbox = null, $base = null)
2177:     {
2178:         if (is_null($mbox)) {
2179:             $mbox = $this->_mbox;
2180:         }
2181: 
2182:         if (is_null($base)) {
2183:             $base = new stdClass;
2184:         }
2185: 
2186:         $base->ViewPort = new stdClass;
2187:         $base->ViewPort->cacheid = $mbox->cacheid_date;
2188:         $base->ViewPort->view = $mbox->form_to;
2189: 
2190:         return $base;
2191:     }
2192: 
2193:     /**
2194:      * Return information about the current attachments for a message.
2195:      *
2196:      * @param IMP_Compose $imp_compose  An IMP_Compose object.
2197:      *
2198:      * @return array  An array of arrays with the following keys:
2199:      *   - name: (string) The HTML encoded attachment name
2200:      *   - num: (integer) The current attachment number
2201:      *   - size: (string) The size of the attachment in KB
2202:      *   - type: (string) The MIME type of the attachment
2203:      */
2204:     protected function _getAttachmentInfo(IMP_Compose $imp_compose)
2205:     {
2206:         $fwd_list = array();
2207: 
2208:         foreach ($imp_compose as $atc_num => $data) {
2209:             $mime = $data['part'];
2210: 
2211:             $fwd_list[] = array(
2212:                 'name' => htmlspecialchars($mime->getName(true)),
2213:                 'num' => $atc_num,
2214:                 'type' => $mime->getType(),
2215:                 'size' => $mime->getSize()
2216:             );
2217:         }
2218: 
2219:         return $fwd_list;
2220:     }
2221: 
2222: }
2223: 
API documentation generated by ApiGen