Overview

Packages

  • IMP

Classes

  • IMP
  • IMP_Ajax_Addresses
  • IMP_Ajax_Application
  • IMP_Ajax_Application_Compose
  • IMP_Ajax_Application_Handler_Common
  • IMP_Ajax_Application_Handler_ComposeAttach
  • IMP_Ajax_Application_Handler_Draft
  • IMP_Ajax_Application_Handler_Dynamic
  • IMP_Ajax_Application_Handler_ImageUnblock
  • IMP_Ajax_Application_Handler_Mboxtoggle
  • IMP_Ajax_Application_Handler_Passphrase
  • IMP_Ajax_Application_Handler_Remote
  • IMP_Ajax_Application_Handler_RemotePrefs
  • IMP_Ajax_Application_Handler_Search
  • IMP_Ajax_Application_Handler_Smartmobile
  • IMP_Ajax_Application_ListMessages
  • IMP_Ajax_Application_ShowMessage
  • IMP_Ajax_Application_Viewport
  • IMP_Ajax_Application_Viewport_Error
  • IMP_Ajax_Imple_ImportEncryptKey
  • IMP_Ajax_Imple_ItipRequest
  • IMP_Ajax_Imple_PassphraseDialog
  • IMP_Ajax_Imple_VcardImport
  • IMP_Ajax_Queue
  • IMP_Api
  • IMP_Application
  • IMP_Auth
  • IMP_Basic_Base
  • IMP_Basic_Compose
  • IMP_Basic_Contacts
  • IMP_Basic_Error
  • IMP_Basic_Folders
  • IMP_Basic_Listinfo
  • IMP_Basic_Mailbox
  • IMP_Basic_Message
  • IMP_Basic_Pgp
  • IMP_Basic_Saveimage
  • IMP_Basic_Search
  • IMP_Basic_Searchbasic
  • IMP_Basic_Smime
  • IMP_Basic_Thread
  • IMP_Block_Newmail
  • IMP_Block_Summary
  • IMP_Compose
  • IMP_Compose_Attachment
  • IMP_Compose_Attachment_Linked_Metadata
  • IMP_Compose_Attachment_Metadata
  • IMP_Compose_Attachment_Storage
  • IMP_Compose_Attachment_Storage_AutoDetermine
  • IMP_Compose_Attachment_Storage_Temp
  • IMP_Compose_Attachment_Storage_VfsLinked
  • IMP_Compose_Exception
  • IMP_Compose_Exception_Address
  • IMP_Compose_HtmlSignature
  • IMP_Compose_Link
  • IMP_Compose_LinkedAttachment
  • IMP_Compose_Ui
  • IMP_Compose_View
  • IMP_Contacts
  • IMP_Contacts_Avatar_Addressbook
  • IMP_Contacts_Avatar_Gravatar
  • IMP_Contacts_Avatar_Unknown
  • IMP_Contacts_Flag_Host
  • IMP_Contacts_Image
  • IMP_Contents
  • IMP_Contents_InlineOutput
  • IMP_Contents_View
  • IMP_Crypt_Pgp
  • IMP_Crypt_Smime
  • IMP_Dynamic_AddressList
  • IMP_Dynamic_Base
  • IMP_Dynamic_Compose
  • IMP_Dynamic_Compose_Common
  • IMP_Dynamic_Helper_Base
  • IMP_Dynamic_Mailbox
  • IMP_Dynamic_Message
  • IMP_Exception
  • IMP_Factory_AuthImap
  • IMP_Factory_Compose
  • IMP_Factory_ComposeAtc
  • IMP_Factory_Contacts
  • IMP_Factory_Contents
  • IMP_Factory_Flags
  • IMP_Factory_Ftree
  • IMP_Factory_Identity
  • IMP_Factory_Imap
  • IMP_Factory_Mail
  • IMP_Factory_MailAutoconfig
  • IMP_Factory_Mailbox
  • IMP_Factory_MailboxCache
  • IMP_Factory_MailboxList
  • IMP_Factory_Maillog
  • IMP_Factory_MimeViewer
  • IMP_Factory_Pgp
  • IMP_Factory_PrefsSort
  • IMP_Factory_Quota
  • IMP_Factory_Search
  • IMP_Factory_Sentmail
  • IMP_Factory_Smime
  • IMP_Factory_Spam
  • 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_Ftree
  • IMP_Ftree_Account
  • IMP_Ftree_Account_Imap
  • IMP_Ftree_Account_Inboxonly
  • IMP_Ftree_Account_Remote
  • IMP_Ftree_Account_Vfolder
  • IMP_Ftree_Element
  • IMP_Ftree_Eltdiff
  • IMP_Ftree_Iterator
  • IMP_Ftree_Iterator_Ancestors
  • IMP_Ftree_IteratorFilter
  • IMP_Ftree_IteratorFilter_Children
  • IMP_Ftree_IteratorFilter_Containers
  • IMP_Ftree_IteratorFilter_Expanded
  • IMP_Ftree_IteratorFilter_Invisible
  • IMP_Ftree_IteratorFilter_Mailboxes
  • IMP_Ftree_IteratorFilter_Nonimap
  • IMP_Ftree_IteratorFilter_Polled
  • IMP_Ftree_IteratorFilter_Remote
  • IMP_Ftree_IteratorFilter_Special
  • IMP_Ftree_IteratorFilter_Subscribed
  • IMP_Ftree_IteratorFilter_Vfolder
  • IMP_Ftree_Prefs
  • IMP_Ftree_Prefs_Expanded
  • IMP_Ftree_Prefs_Poll
  • IMP_Ftree_Select
  • IMP_Images
  • IMP_Imap
  • IMP_Imap_Acl
  • IMP_Imap_Cache_Wrapper
  • IMP_Imap_Config
  • IMP_Imap_Exception
  • IMP_Imap_Password
  • IMP_Imap_PermanentFlags
  • IMP_Imap_Remote
  • IMP_Indices
  • IMP_Indices_Mailbox
  • 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_Pop3
  • IMP_Mailbox_List_Thread
  • IMP_Mailbox_List_Virtual
  • IMP_Mailbox_SessionCache
  • IMP_Mailbox_Ui
  • IMP_Maillog
  • IMP_Maillog_Log_Base
  • IMP_Maillog_Log_Forward
  • IMP_Maillog_Log_Mdn
  • IMP_Maillog_Log_Redirect
  • IMP_Maillog_Log_Reply
  • IMP_Maillog_Log_Replyall
  • IMP_Maillog_Log_Replylist
  • IMP_Maillog_Message
  • IMP_Maillog_Storage_Base
  • IMP_Maillog_Storage_Composite
  • IMP_Maillog_Storage_History
  • IMP_Maillog_Storage_Mdnsent
  • IMP_Maillog_Storage_Null
  • IMP_Mbox_Generate
  • IMP_Mbox_Import
  • IMP_Mbox_Size
  • IMP_Message
  • IMP_Message_Date
  • IMP_Message_Ui
  • IMP_Mime_Headers
  • IMP_Mime_Status
  • IMP_Mime_Status_RenderIssue
  • IMP_Mime_Status_RenderIssue_Display
  • 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_Minimal_Base
  • IMP_Minimal_Compose
  • IMP_Minimal_Error
  • IMP_Minimal_Folders
  • IMP_Minimal_Mailbox
  • IMP_Minimal_Message
  • IMP_Minimal_Messagepart
  • IMP_Minimal_Search
  • IMP_Notification_Event_Status
  • IMP_Notification_Handler_Decorator_ImapAlerts
  • IMP_Notification_Handler_Decorator_NewmailNotify
  • IMP_Perms
  • IMP_Prefs_AttribText
  • IMP_Prefs_Identity
  • IMP_Prefs_Sort
  • IMP_Prefs_Sort_FixedDate
  • IMP_Prefs_Sort_None
  • IMP_Prefs_Sort_Sortpref
  • IMP_Prefs_Sort_Sortpref_Locked
  • IMP_Prefs_Special_Acl
  • IMP_Prefs_Special_ComposeTemplates
  • IMP_Prefs_Special_Drafts
  • IMP_Prefs_Special_Encrypt
  • IMP_Prefs_Special_Flag
  • IMP_Prefs_Special_HtmlSignature
  • IMP_Prefs_Special_ImageReplacement
  • IMP_Prefs_Special_InitialPage
  • IMP_Prefs_Special_Mailto
  • IMP_Prefs_Special_NewmailSound
  • IMP_Prefs_Special_PgpPrivateKey
  • IMP_Prefs_Special_PgpPublicKey
  • IMP_Prefs_Special_Remote
  • IMP_Prefs_Special_Searches
  • IMP_Prefs_Special_Sentmail
  • IMP_Prefs_Special_SmimePrivateKey
  • IMP_Prefs_Special_SmimePublicKey
  • IMP_Prefs_Special_Sourceselect
  • IMP_Prefs_Special_Spam
  • IMP_Prefs_Special_SpecialMboxes
  • IMP_Prefs_Special_Trash
  • IMP_Quota
  • IMP_Quota_Hook
  • IMP_Quota_Imap
  • IMP_Quota_Null
  • IMP_Quota_Ui
  • IMP_Remote
  • IMP_Remote_Account
  • IMP_Script_Package_Autocomplete
  • IMP_Script_Package_ComposeBase
  • IMP_Script_Package_DynamicBase
  • IMP_Script_Package_Editor
  • IMP_Script_Package_Imp
  • IMP_Search
  • IMP_Search_Element
  • IMP_Search_Element_Attachment
  • IMP_Search_Element_Autogenerated
  • IMP_Search_Element_Bulk
  • IMP_Search_Element_Contacts
  • IMP_Search_Element_Daterange
  • 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_IteratorFilter
  • IMP_Search_Query
  • IMP_Search_Ui
  • IMP_Search_Vfolder
  • IMP_Search_Vfolder_Builtin
  • IMP_Search_Vfolder_Vinbox
  • IMP_Search_Vfolder_Vtrash
  • IMP_Sentmail
  • IMP_Sentmail_Mongo
  • IMP_Sentmail_Null
  • IMP_Sentmail_Sql
  • IMP_Smartmobile
  • IMP_Spam
  • IMP_Spam_Email
  • IMP_Spam_Null
  • IMP_Spam_Program
  • IMP_Test
  • IMP_Tree_Flist
  • IMP_Tree_Jquerymobile
  • IMP_Tree_Simplehtml
  • IMP_View_Subinfo

Interfaces

  • IMP_Compose_Attachment_Linked
  • IMP_Contacts_Avatar_Backend
  • IMP_Contacts_Flag_Backend
  • IMP_Spam_Base
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Copyright 2000-2001 Chris Hyde <chris@jeks.net>
  4:  * Copyright 2000-2014 Horde LLC (http://www.horde.org/)
  5:  *
  6:  * See the enclosed file COPYING for license information (GPL). If you
  7:  * did not receive this file, see http://www.horde.org/licenses/gpl.
  8:  *
  9:  * @category  Horde
 10:  * @copyright 2000-2001 Chris Hyde
 11:  * @copyright 2000-2014 Horde LLC
 12:  * @license   http://www.horde.org/licenses/gpl GPL
 13:  * @package   IMP
 14:  */
 15: 
 16: /**
 17:  * This class contains all functions related to handling messages within IMP.
 18:  * Actions such as moving, copying, and deleting messages are handled in here
 19:  * so that code need not be repeated between mailbox, message, and other
 20:  * pages.
 21:  *
 22:  * @author    Chris Hyde <chris@jeks.net>
 23:  * @author    Chuck Hagenbuch <chuck@horde.org>
 24:  * @author    Michael Slusarz <slusarz@horde.org>
 25:  * @category  Horde
 26:  * @copyright 2000-2001 Chris Hyde
 27:  * @copyright 2000-2014 Horde LLC
 28:  * @license   http://www.horde.org/licenses/gpl GPL
 29:  * @package   IMP
 30:  */
 31: class IMP_Message
 32: {
 33:     /**
 34:      * Copies or moves a list of messages to a new mailbox.
 35:      * Handles search and Trash mailboxes.
 36:      * Also handles moves to the tasklist and/or notepad applications.
 37:      *
 38:      * @param string $targetMbox    The mailbox to move/copy messages to
 39:      *                              (UTF-8).
 40:      * @param string $action        Either 'copy' or 'move'.
 41:      * @param IMP_Indices $indices  An indices object.
 42:      * @param array $opts           Additional options:
 43:      *   - create: (boolean) Should the target mailbox be created?
 44:      *             DEFAULT: false
 45:      *   - mailboxob: (IMP_Mailbox_List) Update this mailbox object.
 46:      *                DEFAULT: No update.
 47:      *
 48:      * @return boolean  True if successful, false if not.
 49:      */
 50:     public function copy($targetMbox, $action, IMP_Indices $indices,
 51:                          array $opts = array())
 52:     {
 53:         global $conf, $notification;
 54: 
 55:         if (!count($indices)) {
 56:             return false;
 57:         }
 58: 
 59:         $targetMbox = IMP_Mailbox::get($targetMbox);
 60: 
 61:         /* If the target is a tasklist, handle the move/copy specially. */
 62:         if ($conf['tasklist']['use_tasklist'] &&
 63:             (strpos($targetMbox, IMP::TASKLIST_EDIT) === 0)) {
 64:             $this->_createTasksOrNotes(str_replace(IMP::TASKLIST_EDIT, '', $targetMbox), $action, $indices, 'task');
 65:             return true;
 66:         }
 67: 
 68:         /* If the target is a notepad, handle the move/copy specially. */
 69:         if ($conf['notepad']['use_notepad'] &&
 70:             (strpos($targetMbox, IMP::NOTEPAD_EDIT) === 0)) {
 71:             $this->_createTasksOrNotes(str_replace(IMP::NOTEPAD_EDIT, '', $targetMbox), $action, $indices, 'note');
 72:             return true;
 73:         }
 74: 
 75:         if (!empty($opts['create']) && !$targetMbox->create()) {
 76:             return false;
 77:         }
 78: 
 79:         $imap_move = false;
 80:         $return_value = true;
 81: 
 82:         switch ($action) {
 83:         case 'move':
 84:             $imap_move = true;
 85:             $message = _("There was an error moving messages from \"%s\" to \"%s\". This is what the server said");
 86:             break;
 87: 
 88:         case 'copy':
 89:             $message = _("There was an error copying messages from \"%s\" to \"%s\". This is what the server said");
 90:             break;
 91:         }
 92: 
 93:         foreach ($indices as $ob) {
 94:             try {
 95:                 if ($targetMbox->readonly) {
 96:                     throw new IMP_Exception(_("The target directory is read-only."));
 97:                 }
 98: 
 99:                 if (($action == 'move') && $ob->mbox->readonly) {
100:                     throw new IMP_Exception(_("The source directory is read-only."));
101:                 }
102: 
103:                 $ob->mbox->uidvalid;
104: 
105:                 /* Attempt to copy/move messages to new mailbox. */
106:                 $imp_imap = $ob->mbox->imp_imap;
107:                 $imp_imap->copy($ob->mbox, $targetMbox, array(
108:                     'ids' => $imp_imap->getIdsOb($ob->uids),
109:                     'move' => $imap_move
110:                 ));
111: 
112:                 if (($action == 'move') &&
113:                     !empty($opts['mailboxob']) &&
114:                     $opts['mailboxob']->isBuilt()) {
115:                     $opts['mailboxob']->removeMsgs($ob->mbox->getIndicesOb($ob->uids));
116:                 }
117:             } catch (Exception $e) {
118:                 $error_msg = sprintf($message, $ob->mbox->display, $targetMbox->display) . ': ' . $e->getMessage();
119:                 if ($e instanceof IMP_Imap_Exception) {
120:                     $e->notify($error_msg);
121:                 } else {
122:                     $notification->push($error_msg, 'horde.error');
123:                 }
124:                 $return_value = false;
125:             }
126:         }
127: 
128:         return $return_value;
129:     }
130: 
131:     /**
132:      * Deletes a list of messages.
133:      * Handles search and Trash mailboxes.
134:      *
135:      * @param IMP_Indices $indices  An indices object.
136:      * @param array $opts           Additional options:
137:      *   - keeplog: (boolean) Should any history information of the message be
138:      *              kept?
139:      *   - mailboxob: (IMP_Mailbox_List) Update this mailbox object.
140:      *                DEFAULT: No update.
141:      *   - nuke: (boolean) Override user preferences and nuke (i.e.
142:      *           permanently delete) the messages instead?
143:      *
144:      * @return integer|boolean  The number of messages deleted if successful,
145:      *                          false if not.
146:      */
147:     public function delete(IMP_Indices $indices, array $opts = array())
148:     {
149:         global $injector, $notification, $prefs;
150: 
151:         if (!count($indices)) {
152:             return false;
153:         }
154: 
155:         $trash = IMP_Mailbox::getPref(IMP_Mailbox::MBOX_TRASH);
156:         $use_trash = $prefs->getValue('use_trash');
157:         if ($use_trash && !$trash) {
158:             $notification->push(_("Cannot move messages to Trash - no Trash mailbox set in preferences."), 'horde.error');
159:             return false;
160:         }
161: 
162:         $ajax_queue = $injector->getInstance('IMP_Ajax_Queue');
163:         $maillog = empty($opts['keeplog'])
164:             ? $injector->getInstance('IMP_Maillog')
165:             : null;
166:         $return_value = 0;
167: 
168:         /* Check for Trash mailbox. */
169:         $no_expunge = $use_trash_mbox = $use_vtrash = false;
170:         if ($use_trash &&
171:             empty($opts['nuke']) &&
172:             $injector->getInstance('IMP_Factory_Imap')->create()->access(IMP_Imap::ACCESS_TRASH)) {
173:             $use_vtrash = $trash->vtrash;
174:             $use_trash_mbox = !$use_vtrash;
175:         }
176: 
177:         /* Check whether we are marking messages as seen.
178:          * If using virtual trash, we must mark the message as seen or else it
179:          * will appear as an 'unseen' message for purposes of new message
180:          * counts. */
181:         $mark_seen = empty($opts['nuke']) &&
182:                      ($use_vtrash || $prefs->getValue('delete_mark_seen'));
183: 
184:         if ($use_trash_mbox && !$trash->create()) {
185:             /* If trash mailbox could not be created, just mark message as
186:              * deleted. */
187:             $no_expunge = true;
188:             $return_value = $use_trash_mbox = false;
189:         }
190: 
191:         foreach ($indices as $ob) {
192:             try {
193:                 if (!$ob->mbox->access_deletemsgs) {
194:                     throw new IMP_Exception(_("This mailbox is read-only."));
195:                 }
196: 
197:                 $ob->mbox->uidvalid;
198:             } catch (IMP_Exception $e) {
199:                 $notification->push(sprintf(_("There was an error deleting messages from the mailbox \"%s\"."), $ob->mbox->display) . ' ' . $e->getMessage(), 'horde.error');
200:                 $return_value = false;
201:                 continue;
202:             }
203: 
204:             $imp_indices = $ob->mbox->getIndicesOb($ob->uids);
205:             if ($return_value !== false) {
206:                 $return_value += count($ob->uids);
207:             }
208: 
209:             $imp_imap = $ob->mbox->imp_imap;
210:             $ids_ob = $imp_imap->getIdsOb($ob->uids);
211: 
212:             /* Trash is only valid for IMAP mailboxes. */
213:             if ($use_trash_mbox &&
214:                 ($ob->mbox != $trash) &&
215:                 /* TODO(?): Don't use Trash mailbox for remote accounts. */
216:                 !$ob->mbox->remote_mbox) {
217:                 if ($ob->mbox->access_expunge) {
218:                     try {
219:                         if ($mark_seen) {
220:                             $imp_imap->store($ob->mbox, array(
221:                                 'add' => array(
222:                                     Horde_Imap_Client::FLAG_SEEN
223:                                 ),
224:                                 'ids' => $ids_ob
225:                             ));
226:                         }
227: 
228:                         $imp_imap->copy($ob->mbox, $trash, array(
229:                             'ids' => $ids_ob,
230:                             'move' => true
231:                         ));
232: 
233:                         if (!empty($opts['mailboxob']) &&
234:                             $opts['mailboxob']->isBuilt()) {
235:                             $opts['mailboxob']->removeMsgs($imp_indices);
236:                         }
237:                     } catch (IMP_Imap_Exception $e) {
238:                         if ($e->getCode() == $e::OVERQUOTA) {
239:                             $notification->push(_("You are over your quota, so your messages will be permanently deleted instead of moved to the Trash mailbox."), 'horde.warning');
240:                             $opts['nuke'] = true;
241:                             return $this->delete(new IMP_Indices($ob->mbox, $ob->uids), $opts);
242:                         }
243: 
244:                         return false;
245:                     }
246:                 }
247:             } else {
248:                 /* Delete message logs now. This may result in loss of message
249:                  * log data for messages that might not be deleted - i.e. if
250:                  * an error occurs. But 1) the user has already indicated they
251:                  * don't care about this data and 2) message IDs (used by some
252:                  * maillog backends) won't be available after deletion. */
253:                 if ($maillog) {
254:                     $delete_ids = array();
255:                     foreach ($ids_ob as $val) {
256:                         $delete_ids[] = new IMP_Maillog_Message(
257:                             new IMP_Indices($ob->mbox, $val)
258:                         );
259:                     }
260:                     $maillog->deleteLog($delete_ids);
261:                 }
262: 
263:                 /* Delete the messages. */
264:                 $expunge_now = false;
265:                 $del_flags = array(Horde_Imap_Client::FLAG_DELETED);
266: 
267:                 if (!$use_vtrash &&
268:                     (!$imp_imap->access(IMP_Imap::ACCESS_TRASH) ||
269:                      !empty($opts['nuke']) ||
270:                      ($use_trash &&
271:                       ($ob->mbox == $trash) || $ob->mbox->remote_mbox))) {
272:                     /* Purge messages immediately. */
273:                     $expunge_now = !$no_expunge;
274:                 } elseif ($mark_seen) {
275:                     $del_flags[] = Horde_Imap_Client::FLAG_SEEN;
276:                 }
277: 
278:                 try {
279:                     $imp_imap->store($ob->mbox, array(
280:                         'add' => $del_flags,
281:                         'ids' => $ids_ob
282:                     ));
283:                     if ($expunge_now) {
284:                         $this->expungeMailbox(
285:                             $imp_indices->indices(),
286:                             array(
287:                                 'mailboxob' => empty($opts['mailboxob']) ? null : $opts['mailboxob']
288:                             )
289:                         );
290:                     } elseif (!empty($opts['mailboxob']) &&
291:                               $opts['mailboxob']->isBuilt() &&
292:                               $ob->mbox->hideDeletedMsgs()) {
293:                         $opts['mailboxob']->removeMsgs($imp_indices);
294:                     } else {
295:                         $ajax_queue->flag($del_flags, true, new IMP_Indices($ob->mbox, $ids_ob));
296:                     }
297:                 } catch (IMP_Imap_Exception $e) {}
298:             }
299:         }
300: 
301:         return $return_value;
302:     }
303: 
304:     /**
305:      * Undeletes a list of messages.
306:      * Handles search mailboxes.
307:      * This function works with IMAP only, not POP3.
308:      *
309:      * @param IMP_Indices $indices  An indices object.
310:      *
311:      * @return boolean  True if successful, false if not.
312:      */
313:     public function undelete(IMP_Indices $indices)
314:     {
315:         return $this->flag(array(
316:             'remove' => array(Horde_Imap_Client::FLAG_DELETED)
317:         ), $indices);
318:     }
319: 
320:     /**
321:      * Copies or moves a list of messages to a tasklist or notepad.
322:      * Handles search and Trash mailboxes.
323:      *
324:      * @param string $list          The list in which the task or note will be
325:      *                              created.
326:      * @param string $action        Either 'copy' or 'move'.
327:      * @param IMP_Indices $indices  An indices object.
328:      * @param string $type          The object type to create ('note' or
329:      *                              'task').
330:      */
331:     protected function _createTasksOrNotes($list, $action,
332:                                            IMP_Indices $indices, $type)
333:     {
334:         global $injector, $registry, $notification;
335: 
336:         foreach ($indices as $ob) {
337:             foreach ($ob->uids as $uid) {
338:                 /* Fetch the message contents. */
339:                 $imp_contents = $injector->getInstance('IMP_Factory_Contents')->create($ob->mbox->getIndicesOb($uid));
340: 
341:                 /* Fetch the message headers. */
342:                 $imp_headers = $imp_contents->getHeader();
343:                 $subject = $imp_headers->getValue('subject');
344: 
345:                 /* Re-flow the message for prettier formatting. */
346:                 $body_part = $imp_contents->getMIMEPart($imp_contents->findBody());
347:                 $flowed = new Horde_Text_Flowed($body_part->getContents());
348:                 if ($body_part->getContentTypeParameter('delsp') == 'yes') {
349:                     $flowed->setDelSp(true);
350:                 }
351:                 $body = $flowed->toFlowed(false);
352: 
353:                 /* Convert to current charset */
354:                 /* TODO: When Horde_Icalendar supports setting of charsets
355:                  * we need to set it there instead of relying on the fact
356:                  * that both Nag and IMP use the same charset. */
357:                 $body = Horde_String::convertCharset($body, $body_part->getCharset(), 'UTF-8');
358: 
359:                 /* Create a new iCalendar. */
360:                 $vCal = new Horde_Icalendar();
361:                 $vCal->setAttribute('PRODID', '-//The Horde Project//IMP ' . $registry->getVersion() . '//EN');
362:                 $vCal->setAttribute('METHOD', 'PUBLISH');
363: 
364:                 switch ($type) {
365:                 case 'task':
366:                     /* Create a new vTodo object using this message's
367:                      * contents. */
368:                     $vTodo = Horde_Icalendar::newComponent('vtodo', $vCal);
369:                     $vTodo->setAttribute('SUMMARY', $subject);
370:                     $vTodo->setAttribute('DESCRIPTION', $body);
371:                     $vTodo->setAttribute('PRIORITY', '3');
372: 
373:                     /* Get the list of editable tasklists. */
374:                     try {
375:                         $lists = $registry->call('tasks/listTasklists', array(false, Horde_Perms::EDIT));
376:                     } catch (Horde_Exception $e) {
377:                         $lists = null;
378:                         $notification->push($e);
379:                     }
380: 
381:                     /* Attempt to add the new vTodo item to the requested
382:                      * tasklist. */
383:                     try {
384:                         $res = $registry->call('tasks/import', array($vTodo, 'text/calendar', $list));
385:                     } catch (Horde_Exception $e) {
386:                         $res = null;
387:                         $notification->push($e);
388:                     }
389:                     break;
390: 
391:                 case 'note':
392:                     /* Create a new vNote object using this message's
393:                      * contents. */
394:                     $vNote = Horde_Icalendar::newComponent('vnote', $vCal);
395:                     $vNote->setAttribute('BODY', $subject . "\n". $body);
396: 
397:                     /* Get the list of editable notepads. */
398:                     try {
399:                         $lists = $registry->call('notes/listNotepads', array(false, Horde_Perms::EDIT));
400:                     } catch (Horde_Exception $e) {
401:                         $lists = null;
402:                         $notification->push($e);
403:                     }
404: 
405:                     /* Attempt to add the new vNote item to the requested
406:                      * notepad. */
407:                     try {
408:                         $res = $registry->call('notes/import', array($vNote, 'text/x-vnote', $list));
409:                     } catch (Horde_Exception $e) {
410:                         $res = null;
411:                         $notification->push($e);
412:                     }
413:                     break;
414:                 }
415: 
416:                 if (!is_null($res)) {
417:                     if (!$res) {
418:                         switch ($type) {
419:                         case 'task':
420:                             $notification->push(_("An unknown error occured while creating the new task."), 'horde.error');
421:                             break;
422: 
423:                         case 'note':
424:                             $notification->push(_("An unknown error occured while creating the new note."), 'horde.error');
425:                             break;
426:                         }
427:                     } elseif (!is_null($lists)) {
428:                         $name = '"' . htmlspecialchars($subject) . '"';
429: 
430:                         /* Attempt to convert the object name into a
431:                          * hyperlink. */
432:                         try {
433:                             switch ($type) {
434:                             case 'task':
435:                                 $link = $registry->link('tasks/show', array('uid' => $res));
436:                                 break;
437: 
438:                             case 'note':
439:                                 $link = $registry->hasMethod('notes/show')
440:                                     ? $registry->link('notes/show', array('uid' => $res))
441:                                     : false;
442:                                 break;
443:                             }
444: 
445:                             if ($link) {
446:                                 $name = sprintf('<a href="%s">%s</a>', Horde::url($link), $name);
447:                             }
448: 
449:                             $notification->push(sprintf(_("%s was successfully added to \"%s\"."), $name, htmlspecialchars($lists[$list]->get('name'))), 'horde.success', array('content.raw'));
450:                         } catch (Horde_Exception $e) {}
451:                     }
452:                 }
453:             }
454:         }
455: 
456:         /* Delete the original messages if this is a "move" operation. */
457:         if ($action == 'move') {
458:             $this->delete($indices);
459:         }
460:     }
461: 
462:     /**
463:      * Strips one or all MIME parts out of a message.
464:      * Handles search mailboxes.
465:      *
466:      * @param IMP_Indices $indices  An indices object.
467:      * @param string $partid        The MIME ID of the part to strip. All
468:      *                              parts are stripped if null.
469:      * @param array $opts           Additional options:
470:      *   - mailboxob: (IMP_Mailbox_List) Update this mailbox object.
471:      *                DEFAULT: No update.
472:      *
473:      * @return IMP_Indices  Returns the new indices object.
474:      * @throws IMP_Exception
475:      */
476:     public function stripPart(IMP_Indices $indices, $partid = null,
477:                               array $opts = array())
478:     {
479:         global $injector;
480: 
481:         list($mbox, $uid) = $indices->getSingle();
482:         if (!$uid) {
483:             return;
484:         }
485: 
486:         if ($mbox->readonly) {
487:             throw new IMP_Exception(_("Cannot strip the MIME part as the mailbox is read-only."));
488:         }
489: 
490:         $uidvalidity = $mbox->uidvalid;
491: 
492:         $contents = $injector->getInstance('IMP_Factory_Contents')->create($indices);
493:         $message = $contents->getMIMEMessage();
494:         $boundary = trim($message->getContentTypeParameter('boundary'), '"');
495: 
496:         $url = new Horde_Imap_Client_Url();
497:         $url->mailbox = $mbox;
498:         $url->uid = $uid;
499:         $url->uidvalidity = $uidvalidity;
500: 
501:         $imp_imap = $mbox->imp_imap;
502: 
503:         /* Always add the header to output. */
504:         $url->section = 'HEADER';
505:         $parts = array(
506:             array(
507:                 't' => 'url',
508:                 'v' => strval($url)
509:             )
510:         );
511: 
512:         for ($id = 1; ; ++$id) {
513:             $part = $message->getPart($id);
514:             if (!$part) {
515:                 break;
516:             }
517: 
518:             $parts[] = array(
519:                 't' => 'text',
520:                 'v' => "\r\n--" . $boundary . "\r\n"
521:             );
522: 
523:             if (($id != 1) && is_null($partid) || ($id == $partid)) {
524:                 $newPart = new Horde_Mime_Part();
525:                 $newPart->setType('text/plain');
526: 
527:                 /* Need to make sure all text is in the correct charset. */
528:                 $newPart->setCharset('UTF-8');
529:                 $newPart->setContents(sprintf(_("[Attachment stripped: Original attachment type: %s, name: %s]"), $part->getType(), $contents->getPartName($part)));
530:                 $newPart->setDisposition('attachment');
531: 
532:                 $parts[] = array(
533:                     't' => 'text',
534:                     'v' => $newPart->toString(array(
535:                         'canonical' => true,
536:                         'headers' => true,
537:                         'stream' => true
538:                     ))
539:                 );
540:             } else {
541:                 $url->section = $id . '.MIME';
542:                 $parts[] = array(
543:                     't' => 'url',
544:                     'v' => strval($url)
545:                 );
546: 
547:                 $url->section = $id;
548:                 $parts[] = array(
549:                     't' => 'url',
550:                     'v' => strval($url)
551:                 );
552:             }
553:         }
554: 
555:         $parts[] = array(
556:             't' => 'text',
557:             'v' => "\r\n--" . $boundary . "--\r\n"
558:         );
559: 
560:         /* Get the headers for the message. */
561:         $query = new Horde_Imap_Client_Fetch_Query();
562:         $query->imapDate();
563:         $query->flags();
564: 
565:         try {
566:             $res = $imp_imap->fetch($mbox, $query, array(
567:                 'ids' => $imp_imap->getIdsOb($uid)
568:             ))->first();
569:             if (is_null($res)) {
570:                 throw new IMP_Imap_Exception();
571:             }
572:             $flags = $res->getFlags();
573: 
574:             /* If in Virtual Inbox, we need to reset flag to unseen so that it
575:              * appears again in the mailbox list. */
576:             if ($mbox->vinbox) {
577:                 $flags = array_values(array_diff($flags, array(Horde_Imap_Client::FLAG_SEEN)));
578:             }
579: 
580:             $new_uid = $imp_imap->append($mbox, array(
581:                 array(
582:                     'data' => $parts,
583:                     'flags' => $flags,
584:                     'internaldate' => $res->getImapDate()
585:                 )
586:             ))->ids;
587:             $new_uid = reset($new_uid);
588:         } catch (IMP_Imap_Exception $e) {
589:             throw new IMP_Exception(_("An error occured while attempting to strip the attachment."));
590:         }
591: 
592:         $this->delete($indices, array(
593:             'keeplog' => true,
594:             'mailboxob' => empty($opts['mailboxob']) ? null : $opts['mailboxob'],
595:             'nuke' => true
596:         ));
597: 
598:         $indices_ob = $mbox->getIndicesOb($new_uid);
599: 
600:         if (!empty($opts['mailboxob'])) {
601:             $opts['mailboxob']->setIndex($indices_ob);
602:         }
603: 
604:         /* We need to replace the old UID(s) in the URL params. */
605:         $vars = $injector->getInstance('Horde_Variables');
606:         if (isset($vars->buid)) {
607:             list(,$vars->buid) = $mbox->toBuids($indices_ob)->getSingle();
608:         }
609:         if (isset($vars->uid)) {
610:             $vars->uid = $new_uid;
611:         }
612: 
613:         return $indices_ob;
614:     }
615: 
616:     /**
617:      * Sets or clears a given flag for a list of messages.
618:      * Handles search mailboxes.
619:      * This function works with IMAP only, not POP3.
620:      *
621:      * @param array $action         A list of IMAP flag(s). Keys are 'add'
622:      *                              and/or 'remove'.
623:      * @param IMP_Indices $indices  An indices object.
624:      * @param array $opts           Additional options:
625:      *   - silent: (boolean) Don't output notification messages.
626:      *   - unchangedsince: (array) The unchangedsince value to pass to the
627:      *                     IMAP store command. Keys are mailbox names, values
628:      *                     are the unchangedsince values to use for that
629:      *                     mailbox.
630:      *
631:      * @return boolean  True if successful, false if not.
632:      */
633:     public function flag(array $action, IMP_Indices $indices,
634:                          array $opts = array())
635:     {
636:         global $injector, $notification;
637: 
638:         if (!count($indices)) {
639:             return false;
640:         }
641: 
642:         $opts = array_merge(array(
643:             'unchangedsince' => array()
644:         ), $opts);
645: 
646:         $ajax_queue = $injector->getInstance('IMP_Ajax_Queue');
647:         $ret = true;
648: 
649:         foreach ($indices as $ob) {
650:             try {
651:                 if ($ob->mbox->readonly) {
652:                     throw new IMP_Exception(_("This mailbox is read-only."));
653:                 }
654: 
655:                 $ob->mbox->uidvalid;
656: 
657:                 $unchangedsince = isset($opts['unchangedsince'][strval($ob->mbox)])
658:                     ? $opts['unchangedsince'][strval($ob->mbox)]
659:                     : null;
660: 
661:                 /* Flag/unflag the messages now. */
662:                 $imp_imap = $ob->mbox->imp_imap;
663:                 $res = $imp_imap->store($ob->mbox, array_merge($action, array_filter(array(
664:                     'ids' => $imp_imap->getIdsOb($ob->uids),
665:                     'unchangedsince' => $unchangedsince
666:                 ))));
667: 
668:                 $flag_change = $ob->mbox->getIndicesOb($ob->uids);
669: 
670:                 if ($unchangedsince && count($res)) {
671:                     foreach ($res as $val) {
672:                         unset($flag_change[$val]);
673:                     }
674:                     if (empty($opts['silent'])) {
675:                         $notification->push(sprintf(_("Flags were not changed for at least one message in the mailbox \"%s\" because the flags were altered by another connection to the mailbox prior to this request. You may redo the flag action if desired; this warning is precautionary to ensure you don't overwrite flag changes."), $ob->mbox->display), 'horde.warning');
676:                         $ret = false;
677:                     }
678:                 }
679: 
680:                 foreach ($action as $key => $val) {
681:                     $ajax_queue->flag($val, ($key == 'add'), $flag_change);
682:                     if ($indices instanceof IMP_Indices_Mailbox) {
683:                         $ajax_queue->flag($val, ($key == 'add'), $indices->mailbox->toBuids($flag_change));
684:                     }
685:                 }
686:             } catch (Exception $e) {
687:                 if (empty($opts['silent'])) {
688:                     $notification->push(sprintf(_("There was an error flagging messages in the mailbox \"%s\": %s."), $ob->mbox->display, $e->getMessage()), 'horde.error');
689:                 }
690:                 $ret = false;
691:             }
692:         }
693: 
694:         return $ret;
695:     }
696: 
697:     /**
698:      * Adds or removes flag(s) for all messages in a list of mailboxes.
699:      * This function works with IMAP only, not POP3.
700:      *
701:      * @param array $flags     The IMAP flag(s) to add or remove.
702:      * @param array $mboxes    The list of mailboxes to flag.
703:      * @param boolean $action  If true, add the flag(s), otherwise, remove the
704:      *                         flag(s).
705:      *
706:      * @return boolean  True if successful, false if not.
707:      */
708:     public function flagAllInMailbox($flags, $mboxes, $action = true)
709:     {
710:         if (empty($mboxes) || !is_array($mboxes)) {
711:             return false;
712:         }
713: 
714:         $action_array = $action
715:             ? array('add' => $flags)
716:             : array('remove' => $flags);
717:         $ajax_queue = $GLOBALS['injector']->getInstance('IMP_Ajax_Queue');
718: 
719:         $ajax_queue->poll($mboxes);
720: 
721:         foreach (IMP_Mailbox::get($mboxes) as $val) {
722:             try {
723:                 /* Grab list of UIDs before flagging, to make sure we
724:                  * determine the exact subset that has been flagged. */
725:                 $mailbox_list = $val->list_ob->getIndicesOb();
726:                 $val->imp_imap->store($val, $action_array);
727:                 $ajax_queue->flag(reset($action_array), $action, $mailbox_list);
728:             } catch (IMP_Imap_Exception $e) {
729:                 return false;
730:             }
731:         }
732: 
733:         return true;
734:     }
735: 
736:     /**
737:      * Expunges all deleted messages from the list of mailboxes.
738:      *
739:      * @param array $mbox_list  The list of mailboxes to empty as keys; an
740:      *                          optional array of indices to delete as values.
741:      *                          If the value is not an array, all messages
742:      *                          flagged as deleted in the mailbox will be
743:      *                          deleted.
744:      * @param array $opts       Additional options:
745:      *   - list: (boolean) Return a list of messages expunged.
746:      *           DEFAULT: false
747:      *   - mailboxob: (IMP_Mailbox_List) Update this mailbox object.
748:      *                DEFAULT: No update.
749:      *
750:      * @return IMP_Indices  If 'list' option is true, an indices object
751:      *                      containing the messages that have been expunged.
752:      */
753:     public function expungeMailbox($mbox_list, array $opts = array())
754:     {
755:         $msg_list = !empty($opts['list']);
756: 
757:         if (empty($mbox_list)) {
758:             return $msg_list ? new IMP_Indices() : null;
759:         }
760: 
761:         $process_list = $update_list = array();
762: 
763:         foreach ($mbox_list as $key => $val) {
764:             $key = IMP_Mailbox::get($key);
765: 
766:             if ($key->access_expunge) {
767:                 $ids = $key->imp_imap->getIdsOb(is_array($val) ? $val : Horde_Imap_Client_Ids::ALL);
768: 
769:                 if ($key->search) {
770:                     foreach ($key->getSearchOb()->mboxes as $skey) {
771:                         $process_list[] = array($skey, $ids);
772:                     }
773:                 } else {
774:                     $process_list[] = array($key, $ids);
775:                 }
776:             }
777:         }
778: 
779:         // [0] = IMP_Mailbox object, [1] = Horde_Imap_Client_Ids object
780:         foreach ($process_list as $val) {
781:             /* If expunging a particular UID list, need to check
782:              * UIDVALIDITY. */
783:             if (!$val[1]->all) {
784:                 try {
785:                     $val[0]->uidvalid;
786:                 } catch (IMP_Exception $e) {
787:                     continue;
788:                 }
789:             }
790: 
791:             try {
792:                 $update_list[strval($val[0])] = $val[0]->imp_imap->expunge($val[0], array(
793:                     'ids' => $val[1],
794:                     'list' => $msg_list
795:                 ));
796: 
797:                 if (!empty($opts['mailboxob']) &&
798:                     $opts['mailboxob']->isBuilt()) {
799:                     $opts['mailboxob']->removeMsgs($val[1]->all ? true : $val[0]->getIndicesOb($val[1]));
800:                 }
801:             } catch (IMP_Imap_Exception $e) {}
802:         }
803: 
804:         if ($msg_list) {
805:             return new IMP_Indices($update_list);
806:         }
807:     }
808: 
809:     /**
810:      * Empties an entire mailbox.
811:      *
812:      * @param array $mbox_list  The list of mailboxes to empty.
813:      */
814:     public function emptyMailbox($mbox_list)
815:     {
816:         global $notification, $prefs;
817: 
818:         $trash = ($prefs->getValue('use_trash'))
819:             ? IMP_Mailbox::getPref(IMP_Mailbox::MBOX_TRASH)
820:             : null;
821: 
822:         foreach (IMP_Mailbox::get($mbox_list) as $mbox) {
823:             if (!$mbox->access_empty) {
824:                 $notification->push(sprintf(_("Could not delete messages from %s. This mailbox is read-only."), $mbox->display), 'horde.error');
825:                 continue;
826:             }
827: 
828:             if ($mbox->vtrash) {
829:                 $this->expungeMailbox(array_flip($mbox->getSearchOb()->mboxes));
830:                 $notification->push(_("Emptied all messages from Virtual Trash Folder."), 'horde.success');
831:                 continue;
832:             }
833: 
834:             /* Make sure there is at least 1 message before attempting to
835:              * delete. */
836:             try {
837:                 $imp_imap = $mbox->imp_imap;
838:                 $status = $imp_imap->status($mbox, Horde_Imap_Client::STATUS_MESSAGES);
839:                 if (empty($status['messages'])) {
840:                     $notification->push(sprintf(_("The mailbox %s is already empty."), $mbox->display), 'horde.message');
841:                     continue;
842:                 }
843: 
844:                 if (!$trash || ($trash == $mbox)) {
845:                     $imp_imap->store($mbox, array(
846:                         'add' => array(Horde_Imap_Client::FLAG_DELETED)
847:                     ));
848:                     $this->expungeMailbox(array(strval($mbox) => 1));
849:                 } else {
850:                     $ret = $imp_imap->search($mbox);
851:                     $this->delete($mbox->getIndicesOb($ret['match']));
852:                 }
853: 
854:                 $notification->push(sprintf(_("Emptied all messages from %s."), $mbox->display), 'horde.success');
855:             } catch (IMP_Imap_Exception $e) {}
856:         }
857:     }
858: 
859: }
860: 
API documentation generated by ApiGen