From: Michael M Slusarz Date: Thu, 29 Apr 2010 16:42:44 +0000 (-0600) Subject: Add IMP_Indices class. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=b17ddd8b5d87709ffaf67728198f8c090a04c31a;p=horde.git Add IMP_Indices class. Provides full OO-interface to indices lists. Makes code cleaner and more consistent. Remove IMP::IDX_SEP constant - use IMAP sequence strings to represent UIDs instead. --- diff --git a/imp/compose-dimp.php b/imp/compose-dimp.php index a24153127..3fd3acd2a 100644 --- a/imp/compose-dimp.php +++ b/imp/compose-dimp.php @@ -71,7 +71,7 @@ if (in_array($vars->type, array('reply', 'reply_all', 'reply_auto', 'reply_list' } try { - $imp_contents = $injector->getInstance('IMP_Contents')->getOb($vars->folder, $vars->uid); + $imp_contents = $injector->getInstance('IMP_Contents')->getOb(new IMP_Indices($vars->folder, $vars->uid)); } catch (Horde_Exception $e) { $notification->push(_("Requested message not found."), 'horde.error'); $vars->uid = $vars->folder = null; @@ -143,7 +143,7 @@ case 'forward_redirect': case 'resume': try { - $result = $imp_compose->resumeDraft($vars->folder, $vars->uid); + $result = $imp_compose->resumeDraft(new IMP_Indices($vars->folder, $vars->uid)); if ($result['mode'] == 'html') { $show_editor = true; diff --git a/imp/compose-mimp.php b/imp/compose-mimp.php index 297eeb0ba..23ca8ba99 100644 --- a/imp/compose-mimp.php +++ b/imp/compose-mimp.php @@ -104,7 +104,7 @@ switch ($vars->a) { // 'd' = draft case 'd': try { - $result = $imp_compose->resumeDraft(IMP::$thismailbox, IMP::$uid); + $result = $imp_compose->resumeDraft(new IMP_Indices(IMP::$thismailbox, IMP::$uid)); $msg = $result['msg']; $header = array_merge($header, $result['header']); @@ -143,7 +143,7 @@ case _("Expand Names"): case 'r': case 'ra': case 'rl': - if (!($imp_contents = $imp_ui->getIMPContents(IMP::$uid, IMP::$thismailbox))) { + if (!($imp_contents = $imp_ui->getIMPContents(new IMP_Indices(IMP::$thismailbox, IMP::$uid)))) { break; } $actions = array('r' => 'reply', 'ra' => 'reply_all', 'rl' => 'reply_list'); @@ -156,7 +156,7 @@ case 'rl': // 'f' = forward case 'f': - if (!($imp_contents = $imp_ui->getIMPContents(IMP::$uid, IMP::$thismailbox))) { + if (!($imp_contents = $imp_ui->getIMPContents(new IMP_Indices(IMP::$thismailbox, IMP::$uid)))) { break; } $fwd_msg = $imp_compose->forwardMessage('forward_attach', $imp_contents, false); @@ -169,7 +169,7 @@ case 'f': // 'rc' = redirect compose case 'rc': $title = _("Redirect"); - if (!($imp_contents = $imp_ui->getIMPContents(IMP::$uid, IMP::$thismailbox))) { + if (!($imp_contents = $imp_ui->getIMPContents(new IMP_Indices(IMP::$thismailbox, IMP::$uid)))) { // TODO: Error message break; } @@ -215,7 +215,7 @@ case _("Send"): $header = array(); if ($ctype = $imp_compose->getMetadata('reply_type')) { - if (!($imp_contents = $imp_ui->getIMPContents($imp_compose->getMetadata('uid'), $imp_compose->getMetadata('mailbox')))) { + if (!($imp_contents = $imp_ui->getIMPContents(new IMP_Indices($imp_compose->getMetadata('mailbox'), $imp_compose->getMetadata('uid'))))) { break; } diff --git a/imp/compose.php b/imp/compose.php index 737a17c23..effcaefeb 100644 --- a/imp/compose.php +++ b/imp/compose.php @@ -75,8 +75,6 @@ if ($vars->actionID) { $save_sent_mail = $vars->save_sent_mail; $sent_mail_folder = $identity->getValue('sent_mail_folder'); -$thismailbox = $vars->thismailbox; -$uid = $vars->uid; /* Check for duplicate submits. */ if ($vars->compose_formToken) { @@ -206,7 +204,7 @@ if ($_SESSION['imp']['file_upload']) { $title = _("New Message"); switch ($vars->actionID) { case 'mailto': - if (!($imp_contents = $imp_ui->getIMPContents($uid, $thismailbox))) { + if (!($imp_contents = $imp_ui->getIMPContents(new IMP_Indices(IMP::$thismailbox, IMP::$uid)))) { break; } $imp_headers = $imp_contents->getHeaderOb(); @@ -234,7 +232,7 @@ case 'mailto_link': case 'draft': try { - $result = $imp_compose->resumeDraft($thismailbox, $uid); + $result = $imp_compose->resumeDraft(new IMP_Indices(IMP::$thismailbox, IMP::$uid)); if (!is_null($rtemode)) { $rtemode = ($result['mode'] == 'html'); @@ -257,7 +255,7 @@ case 'reply': case 'reply_all': case 'reply_auto': case 'reply_list': - if (!($imp_contents = $imp_ui->getIMPContents($uid, $thismailbox))) { + if (!($imp_contents = $imp_ui->getIMPContents(new IMP_Indices(IMP::$thismailbox, IMP::$uid)))) { break; } @@ -293,7 +291,7 @@ case 'forward_attach': case 'forward_auto': case 'forward_body': case 'forward_both': - if (!($imp_contents = $imp_ui->getIMPContents($uid, $thismailbox))) { + if (!($imp_contents = $imp_ui->getIMPContents(new IMP_Indices(IMP::$thismailbox, IMP::$uid)))) { break; } @@ -307,7 +305,7 @@ case 'forward_both': break; case 'redirect_compose': - if (!($imp_contents = $imp_ui->getIMPContents($uid, $thismailbox))) { + if (!($imp_contents = $imp_ui->getIMPContents(new IMP_Indices(IMP::$thismailbox, IMP::$uid)))) { break; } $imp_compose->redirectMessage($imp_contents); @@ -470,12 +468,9 @@ case 'send_message': exit; case 'fwd_digest': - $indices = $vars->fwddigest; - if (!empty($indices)) { - $msglist = unserialize(urldecode($indices)); - if (($subject_header = $imp_compose->attachIMAPMessage($msglist)) !== false) { - $header['subject'] = $subject_header; - } + if (isset($vars->fwddigest) && + (($subject_header = $imp_compose->attachIMAPMessage(new IMP_Indices($vars->fwddigest))) !== false)) { + $header['subject'] = $subject_header; } break; diff --git a/imp/js/DimpBase.js b/imp/js/DimpBase.js index 179af17b7..59f8e64f3 100644 --- a/imp/js/DimpBase.js +++ b/imp/js/DimpBase.js @@ -2236,7 +2236,7 @@ var DimpBase = { if (vs.getBuffer().getMetaData('search')) { $H(r.uids).each(function(pair) { pair.value.each(function(v) { - uids.push(v + DIMP.conf.IDX_SEP + pair.key); + uids.push(pair.key + DIMP.conf.IDX_SEP + v); }); }); diff --git a/imp/js/DimpCore.js b/imp/js/DimpCore.js index e1952b740..9c9050e44 100644 --- a/imp/js/DimpCore.js +++ b/imp/js/DimpCore.js @@ -140,10 +140,10 @@ var DimpCore = { if (b.getMetaData('search')) { s.get('uid').each(function(r) { var parts = r.split(DIMP.conf.IDX_SEP); - if (tmp[parts[1]]) { - tmp[parts[1]].push(parts[0]); + if (tmp[parts[0]]) { + tmp[parts[0]].push(parts[1]); } else { - tmp[parts[1]] = [ parts[0] ]; + tmp[parts[0]] = [ parts[1] ]; } }); } else { diff --git a/imp/lib/Ajax/Application.php b/imp/lib/Ajax/Application.php index 27764dbee..2ea581a65 100644 --- a/imp/lib/Ajax/Application.php +++ b/imp/lib/Ajax/Application.php @@ -526,8 +526,8 @@ class IMP_Ajax_Application extends Horde_Ajax_Application_Base */ public function moveMessages() { - $indices = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->getUtils()->fromSequenceString($this->_vars->uid); - if (!$this->_vars->mboxto || empty($indices)) { + $indices = new IMP_Indices($this->_vars->uid); + if (!$this->_vars->mboxto || !$indices->count()) { return false; } @@ -580,8 +580,8 @@ class IMP_Ajax_Application extends Horde_Ajax_Application_Base */ public function copyMessages() { - $indices = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->getUtils()->fromSequenceString($this->_vars->uid); - if (!$this->_vars->mboxto || empty($indices)) { + $indices = new IMP_Indices($this->_vars->uid); + if (!$this->_vars->mboxto || !$indices->count()) { return false; } @@ -615,8 +615,8 @@ class IMP_Ajax_Application extends Horde_Ajax_Application_Base */ public function flagMessages() { - $indices = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->getUtils()->fromSequenceString($this->_vars->uid); - if (!$this->_vars->flags || empty($indices)) { + $indices = new IMP_Indices($this->_vars->uid); + if (!$this->_vars->flags || !$indices->count()) { return false; } @@ -676,8 +676,8 @@ class IMP_Ajax_Application extends Horde_Ajax_Application_Base */ public function deleteMessages() { - $indices = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->getUtils()->fromSequenceString($this->_vars->uid); - if (empty($indices)) { + $indices = new IMP_Indices($this->_vars->uid); + if (!$indices->count()) { return false; } @@ -738,7 +738,7 @@ class IMP_Ajax_Application extends Horde_Ajax_Application_Base public function reportSpam() { $change = $this->_changed(false); - $indices = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->getUtils()->fromSequenceString($this->_vars->uid); + $indices = new IMP_Indices($this->_vars->uid); $result = false; if (IMP_Spam::reportSpam($indices, $this->_vars->spam ? 'spam' : 'notspam')) { @@ -770,8 +770,8 @@ class IMP_Ajax_Application extends Horde_Ajax_Application_Base */ public function blacklist() { - $indices = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->getUtils()->fromSequenceString($this->_vars->uid); - if (empty($indices)) { + $indices = new IMP_Indices($this->_vars->uid); + if (!$indices->count()) { return false; } @@ -820,30 +820,26 @@ class IMP_Ajax_Application extends Horde_Ajax_Application_Base */ public function showPreview() { - $indices = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->getUtils()->fromSequenceString($this->_vars->uid); - if (count($indices) != 1) { + $indices = new IMP_Indices($this->_vars->uid); + list($mbox, $idx) = $indices->getSingle(); + if (!$idx) { return false; } - $change = $this->_changed($indices); + $change = $this->_changed(false); if (is_null($change)) { return false; } - $ptr = each($indices); $args = array( - 'mailbox' => $ptr['key'], + 'mailbox' => $mbox, 'preview' => true, - 'uid' => intval(reset($ptr['value'])) + 'uid' => intval($idx) ); $result = new stdClass; $result->preview = new stdClass; try { - /* We know we are going to be exclusively dealing with this - * mailbox, so select it on the IMAP server (saves some STATUS - * calls). Open R/W to clear the RECENT flag. */ - $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->openMailbox($ptr['key'], Horde_Imap_Client::OPEN_READWRITE); $show_msg = new IMP_Views_ShowMessage(); $result->preview = (object)$show_msg->showMessage($args); if (isset($result->preview->error)) { @@ -1205,8 +1201,8 @@ class IMP_Ajax_Application extends Horde_Ajax_Application_Base */ public function purgeDeleted() { - $indices = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->getUtils()->fromSequenceString($this->_vars->uid); - $change = $this->_changed($indices); + $indices = new IMP_Indices($this->_vars->uid); + $change = $this->_changed(true); if (is_null($change)) { return false; @@ -1537,9 +1533,7 @@ class IMP_Ajax_Application extends Horde_Ajax_Application_Base { $imp_compose = $GLOBALS['injector']->getInstance('IMP_Compose')->getOb($this->_vars->imp_compose); if (!($imp_contents = $imp_compose->getContentsOb())) { - $indices = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->getUtils()->fromSequenceString($this->_vars->uid); - $i = each($indices); - $imp_contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($i['key'], reset($i['value'])); + $imp_contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb(new IMP_Indices($this->_vars->uid)); } return array($imp_compose, $imp_contents); @@ -1613,9 +1607,9 @@ class IMP_Ajax_Application extends Horde_Ajax_Application_Base * * See the list of variables needed for _viewPortData(). * - * @param array $indices The list of indices that were deleted. - * @param boolean $changed If true, add ViewPort information. - * @param boolean $nothread If true, don't do thread sort check. + * @param IMP_Indices $indices An indices object. + * @param boolean $changed If true, add ViewPort information. + * @param boolean $nothread If true, don't do thread sort check. * * @return object An object with the following entries: *
@@ -1629,11 +1623,12 @@ class IMP_Ajax_Application extends Horde_Ajax_Application_Base
      *          as the values.
      * 
*/ - protected function _generateDeleteResult($indices, $change, $nothread = false) + protected function _generateDeleteResult($indices, $change, + $nothread = false) { $del = new stdClass; $del->mbox = $this->_vars->view; - $del->uids = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->getUtils()->toSequenceString($indices, array('mailbox' => true)); + $del->uids = strval($indices); $del->remove = intval($GLOBALS['prefs']->getValue('hide_deleted') || $GLOBALS['prefs']->getValue('use_trash')); diff --git a/imp/lib/Api.php b/imp/lib/Api.php index 1d05a03dc..20d59678d 100644 --- a/imp/lib/Api.php +++ b/imp/lib/Api.php @@ -113,7 +113,7 @@ class IMP_Api extends Horde_Registry_Api */ public function copyMessages($mailbox, $indices, $target) { - return $GLOBALS['injector']->getInstance('IMP_Message')->copy($target, 'copy', array($mailbox => $indices), true); + return $GLOBALS['injector']->getInstance('IMP_Message')->copy($target, 'copy', new IMP_Indices($mailbox, $indices), true); } /** @@ -127,7 +127,7 @@ class IMP_Api extends Horde_Registry_Api */ public function moveMessages($mailbox, $indices, $target) { - return $GLOBALS['injector']->getInstance('IMP_Message')->copy($target, 'move', array($mailbox => $indices), true); + return $GLOBALS['injector']->getInstance('IMP_Message')->copy($target, 'move', new IMP_Indices($mailbox, $indices), true); } /** @@ -156,7 +156,10 @@ class IMP_Api extends Horde_Registry_Api */ public function searchMailbox($mailbox, $query) { - return $GLOBALS['injector']->getInstance('IMP_Search')->runSearchQuery($query, $mailbox); + $results = $GLOBALS['injector']->getInstance('IMP_Search')->runSearchQuery($query, $mailbox)->indices(); + return isset($results[$mailbox]) + ? $results[$mailbox] + : array(); } /** diff --git a/imp/lib/Block/Newmail.php b/imp/lib/Block/Newmail.php index 0352bd12a..01ad1a1c7 100644 --- a/imp/lib/Block/Newmail.php +++ b/imp/lib/Block/Newmail.php @@ -23,19 +23,22 @@ class IMP_Block_Newmail extends Horde_Block $query = new Horde_Imap_Client_Search_Query(); $query->flag('\\seen', false); $ids = $GLOBALS['injector']->getInstance('IMP_Search')->runSearchQuery($query, 'INBOX', Horde_Imap_Client::SORT_SEQUENCE, 1); + $indices = reset($ids); $html = ''; - if (empty($ids)) { + if (empty($indices)) { $html .= ''; } else { $charset = Horde_Nls::getCharset(); $imp_ui = new IMP_Ui_Mailbox('INBOX'); - $shown = empty($this->_params['msgs_shown']) ? 3 : $this->_params['msgs_shown']; + $shown = empty($this->_params['msgs_shown']) + ? 3 + : $this->_params['msgs_shown']; try { $fetch_ret = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->fetch('INBOX', array( Horde_Imap_Client::FETCH_ENVELOPE => true - ), array('ids' => array_slice($ids, 0, $shown))); + ), array('ids' => array_slice($indices, 0, $shown))); reset($fetch_ret); } catch (Horde_Imap_Client_Exception $e) { $fetch_ret = array(); @@ -52,7 +55,7 @@ class IMP_Block_Newmail extends Horde_Block ''; } - $more_msgs = count($ids) - $shown; + $more_msgs = count($indices) - $shown; $text = ($more_msgs > 0) ? sprintf(ngettext("%d more unseen message...", "%d more unseen messages...", $more_msgs), $more_msgs) : _("Go to your Inbox..."); diff --git a/imp/lib/Compose.php b/imp/lib/Compose.php index ca22d989e..00c335022 100644 --- a/imp/lib/Compose.php +++ b/imp/lib/Compose.php @@ -120,7 +120,7 @@ class IMP_Compose */ public function destroy($action) { - $uids = array(); + $uids = new IMP_Indices(); switch ($action) { case 'save_draft': @@ -129,19 +129,16 @@ class IMP_Compose case 'send': /* Delete the auto-draft and the original resumed draft. */ - $uids[] = $this->getMetadata('draft_uid_resume'); + $uids->add($this->getMetadata('draft_uid_resume')); // Fall-through case 'cancel': /* Delete the auto-draft, but save the original resume draft. */ - $uids[] = $this->getMetadata('draft_uid'); - $uids = array_filter($uids); + $uids->add($this->getMetadata('draft_uid')); break; } - if (!empty($uids)) { - $GLOBALS['injector']->getInstance('IMP_Message')->delete($uids, array('nuke' => true)); - } + $GLOBALS['injector']->getInstance('IMP_Message')->delete($uids, array('nuke' => true)); $this->deleteAllAttachments(); $obs = $GLOBALS['injector']->getInstance('Horde_SessionObjects'); @@ -308,10 +305,10 @@ class IMP_Compose $ids = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->append($drafts_mbox, array(array('data' => $data, 'flags' => $append_flags))); if ($old_uid) { - $GLOBALS['injector']->getInstance('IMP_Message')->delete(array($old_uid), array('nuke' => true)); + $GLOBALS['injector']->getInstance('IMP_Message')->delete($old_uid, array('nuke' => true)); } - $this->_metadata['draft_uid'] = reset($ids) . IMP::IDX_SEP . $drafts_mbox; + $this->_metadata['draft_uid'] = new IMP_Indices($drafts_mbox, reset($ids)); $this->_modified = true; return sprintf(_("The draft has been saved to the \"%s\" folder."), IMP::displayFolder($drafts_mbox)); } catch (Horde_Imap_Client_Exception $e) { @@ -322,8 +319,7 @@ class IMP_Compose /** * Resumes a previously saved draft message. * - * @param string $mailbox Draft mailbox. - * @param integer $uid Message UID. + * @param IMP_Indices $indices An indices object. * * @return mixed An array with the following keys: *
@@ -334,10 +330,10 @@ class IMP_Compose
      * 
* @throws IMP_Compose_Exception */ - public function resumeDraft($mailbox, $uid) + public function resumeDraft($indices) { try { - $contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($mailbox, $uid); + $contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($indices); } catch (IMP_Exception $e) { throw new IMP_Compose_Exception($e); } @@ -403,7 +399,7 @@ class IMP_Compose // even though the server is the same. UIDVALIDITY should // catch any true server/backend changes. ($imp_imap->checkUidvalidity($imap_url['mailbox']) == $imap_url['uidvalidity']) && - $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($imap_url['mailbox'], $imap_url['uid'])) { + $GLOBALS['injector']->getInstance('IMP_Contents')->getOb(new IMP_Indices($imap_url['mailbox'], $imap_url['uid']))) { $this->_metadata['mailbox'] = $imap_url['mailbox']; $this->_metadata['reply_type'] = $reply_type; $this->_metadata['uid'] = $imap_url['uid']; @@ -411,7 +407,7 @@ class IMP_Compose } catch (Exception $e) {} } - $this->_metadata['draft_uid_resume'] = $uid . IMP::IDX_SEP . $mailbox; + $this->_metadata['draft_uid_resume'] = $indices; $this->_modified = true; return array( @@ -589,7 +585,7 @@ class IMP_Compose } $imp_message = $GLOBALS['injector']->getInstance('IMP_Message'); - $reply_uid = array($this->getMetadata('uid') . IMP::IDX_SEP . $this->getMetadata('mailbox')); + $reply_uid = new IMP_Indices($this); switch ($this->getMetadata('reply_type')) { case 'forward': @@ -1560,7 +1556,7 @@ class IMP_Compose if ($attach && in_array($type, array('forward_attach', 'forward_both'))) { - $this->attachIMAPMessage(array($contents->getUid() . IMP::IDX_SEP . $contents->getMailbox())); + $this->attachIMAPMessage(new IMP_Indices($contents)); } return array( @@ -1656,38 +1652,36 @@ class IMP_Compose } /** - * Add mail message(s) from the mail server as a message/rfc822 attachment. + * Add mail message(s) from the mail server as a message/rfc822 + * attachment. * - * @param mixed $indices See IMP::parseIndicesList(). + * @param IMP_Indices $indices An indices object. * * @return mixed String or false. */ public function attachIMAPMessage($indices) { - $msgList = IMP::parseIndicesList($indices); - if (empty($msgList)) { + if (!$indices->count()) { return false; } $attached = 0; - foreach ($msgList as $mbox => $indicesList) { - foreach ($indicesList as $idx) { - ++$attached; - $contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($mbox, $idx); - $headerob = $contents->getHeaderOb(); + foreach ($indices as $mbox => $idx) { + ++$attached; + $contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb(new IMP_Indices($mbox, $idx)); + $headerob = $contents->getHeaderOb(); - $part = new Horde_Mime_Part(); - $part->setCharset(Horde_Nls::getCharset()); - $part->setType('message/rfc822'); - $part->setName(_("Forwarded Message")); - $part->setContents($contents->fullMessageText(array('stream' => true))); + $part = new Horde_Mime_Part(); + $part->setCharset(Horde_Nls::getCharset()); + $part->setType('message/rfc822'); + $part->setName(_("Forwarded Message")); + $part->setContents($contents->fullMessageText(array('stream' => true))); - try { - $this->addMIMEPartAttachment($part); - } catch (IMP_Compose_Exception $e) { - $GLOBALS['notification']->push($e); - return false; - } + try { + $this->addMIMEPartAttachment($part); + } catch (IMP_Compose_Exception $e) { + $GLOBALS['notification']->push($e); + return false; } } @@ -2684,7 +2678,7 @@ class IMP_Compose public function getContentsOb() { return $this->getMetadata('reply_type') - ? $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($this->getMetadata('mailbox'), $this->getMetadata('uid')) + ? $GLOBALS['injector']->getInstance('IMP_Contents')->getOb(new IMP_Indices($this->getMetadata('mailbox'), $this->getMetadata('uid'))) : null; } diff --git a/imp/lib/Contents.php b/imp/lib/Contents.php index f928ae2e4..432215d39 100644 --- a/imp/lib/Contents.php +++ b/imp/lib/Contents.php @@ -81,8 +81,8 @@ class IMP_Contents /** * Constructor. * - * @param mixed $in Either a UID string (UID . IMP::IDX_SEP . Mailbox) or - * a Horde_Mime_Part object. + * @param mixed $in An IMP_Indices or Horde_Mime_Part object. + * * @throws IMP_Exception */ public function __construct($in) @@ -90,7 +90,7 @@ class IMP_Contents if ($in instanceof Horde_Mime_Part) { $this->_message = $in; } else { - list($this->_uid, $this->_mailbox) = explode(IMP::IDX_SEP, $in); + list($this->_mailbox, $this->_uid) = $in->getSingle(); /* Get the Horde_Mime_Part object for the given UID. */ try { diff --git a/imp/lib/Dimp.php b/imp/lib/Dimp.php index 5c8614f28..05dc232f8 100644 --- a/imp/lib/Dimp.php +++ b/imp/lib/Dimp.php @@ -12,6 +12,9 @@ */ class IMP_Dimp { + /* String used to separate indexes. */ + const IDX_SEP = '\0'; + /** * Output a dimp-style action (menubar) link. * diff --git a/imp/lib/Filter.php b/imp/lib/Filter.php index 585decee7..6da336b97 100644 --- a/imp/lib/Filter.php +++ b/imp/lib/Filter.php @@ -49,9 +49,9 @@ class IMP_Filter * Adds the From address from the message(s) to the blacklist and deletes * the message(s). * - * @param array $indices See IMP::parseIndicesList(). - * @param boolean $show_link Show link to the blacklist management in the - * notification message? + * @param IMP_Indices $indices An indices object. + * @param boolean $show_link Show link to the blacklist management in + * the notification message? * * @return boolean True if the messages(s) were deleted. * @throws Horde_Exception @@ -75,9 +75,9 @@ class IMP_Filter /** * Adds the From address from the message(s) to the whitelist. * - * @param array $indices See IMP::parseIndicesList(). - * @param boolean $show_link Show link to the whitelist management in the - * notification message? + * @param IMP_Indices $indices An indices object. + * @param boolean $show_link Show link to the whitelist management in + * the notification message? * * @return boolean True if the messages(s) were whitelisted. * @throws Horde_Exception @@ -90,36 +90,36 @@ class IMP_Filter /** * Internal function to handle adding addresses to [black|white]list. * - * @param array $indices See IMP::parseIndicesList(). - * @param string $descrip The textual description to use. - * @param string $reg1 The name of the mail/ registry call to use for - * adding the addresses. - * @param string $reg2 The name of the mail/ registry call to use for - * linking to the filter management page. - * @param boolean $link Show link to the whitelist management in the - * notification message? + * @param IMP_Indices $indices An indices object. + * @param string $descrip The textual description to use. + * @param string $reg1 The name of the mail/ registry call to use + * for adding the addresses. + * @param string $reg2 The name of the mail/ registry call to use + * for linking to the filter management page. + * @param boolean $link Show link to the whitelist management in + * the notification message? * * @return boolean True on success. * @throws IMP_Exception */ protected function _processBWlist($indices, $descrip, $reg1, $reg2, $link) { - if (!($msgList = IMP::parseIndicesList($indices))) { + if (!$indices->count()) { return false; } $addr = array(); $imp_imap = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb(); - /* Get the list of from addresses. */ - foreach ($msgList as $mbox => $msgIndices) { + foreach (array_keys($indices) as $mbox) { $imp_imap->checkUidvalidity($mbox); + } - foreach ($msgIndices as $idx) { - $contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($mbox, $idx); - $hdr = $contents->getHeaderOb(); - $addr[] = Horde_Mime_Address::bareAddress($hdr->getValue('from')); - } + /* Get the list of from addresses. */ + foreach ($indices as $mbox => $idx) { + $contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb(new IMP_Indices($mbox, $idx)); + $hdr = $contents->getHeaderOb(); + $addr[] = Horde_Mime_Address::bareAddress($hdr->getValue('from')); } $GLOBALS['registry']->call('mail/' . $reg1, array($addr)); @@ -132,4 +132,5 @@ class IMP_Filter return true; } + } diff --git a/imp/lib/IMP.php b/imp/lib/IMP.php index 3d6694735..8032a67dd 100644 --- a/imp/lib/IMP.php +++ b/imp/lib/IMP.php @@ -31,9 +31,6 @@ class IMP const MAILBOX_START_FIRSTPAGE = 3; const MAILBOX_START_LASTPAGE = 4; - /* IMP internal string used to separate indexes. */ - const IDX_SEP = '\0'; - /* Preferences constants. */ const PREF_NO_FOLDER = 'nofolder\0'; const PREF_VTRASH = 'vtrash\0'; @@ -695,60 +692,6 @@ class IMP } /** - * Get message indices list. - * - * @param array $indices The following inputs are allowed: - *
-     * 1. An array of messages indices in the following format:
-     *    msg_id IMP::IDX_SEP msg_mbox
-     *      msg_id      = Message index of the message
-     *      IMP::IDX_SEP = IMP constant used to separate index/mailbox
-     *      msg_folder  = The full mailbox name containing the message index
-     * 2. An array with the full folder name as keys and an array of message
-     *    indices as the values.
-     * 
- * - * @return mixed Returns an array with the folder as key and an array - * of message indices as the value (See #2 above). - * Else, returns false. - */ - static public function parseIndicesList($indices) - { - if (!is_array($indices) || empty($indices)) { - return array(); - } - - $msgList = array(); - - reset($indices); - if (!is_array(current($indices))) { - /* Build the list of indices/mailboxes if input is format #1. */ - while (list(,$msgIndex) = each($indices)) { - if (strpos($msgIndex, self::IDX_SEP) === false) { - return false; - } else { - list($val, $key) = explode(self::IDX_SEP, $msgIndex); - $msgList[$key][] = $val; - } - } - } else { - /* We are dealing with format #2. */ - while (list($key, $val) = each($indices)) { - if ($GLOBALS['injector']->getInstance('IMP_Search')->isSearchMbox($key)) { - $msgList += self::parseIndicesList($val); - } else { - /* Make sure we don't have any duplicate keys. */ - $msgList[$key] = is_array($val) - ? array_keys(array_flip($val)) - : array($val); - } - } - } - - return $msgList; - } - - /** * Convert a preference value to/from the value stored in the preferences. * * To allow folders from the personal namespace to be stored without this diff --git a/imp/lib/Indices.php b/imp/lib/Indices.php new file mode 100644 index 000000000..6768e9c32 --- /dev/null +++ b/imp/lib/Indices.php @@ -0,0 +1,207 @@ + + * @package IMP + */ +class IMP_Indices implements Iterator +{ + /** + * The indices list. + * + * @var array + */ + protected $_indices = array(); + + /** + * Constructor. + * + * Parameters are the same as add(). + * + * @see IMP_Indices::add() + */ + public function __construct() + { + if (func_num_args()) { + call_user_func_array(array($this, 'add'), func_get_args()); + } + } + + /** + * Add indices. + * + * Input format: + *
+     * 1 argument:
+     * -----------
+     * + Array
+     *   Either:
+     *     KEYS: Mailbox names
+     *     VALUES: UIDs
+     *  -or-
+     *     VALUES: IMAP sequence strings
+     * + IMP_Compose object
+     * + IMP_Contents object
+     * + IMP_Indices object
+     * + IMP_Mailbox object
+     * + String
+     *   Format: IMAP sequence string
+     *
+     * 2 arguments:
+     * ------------
+     * 1st argument: Mailbox name
+     * 2nd argument: Either a single UID or an array of UIDs.
+     * 
+ */ + public function add() + { + $data = func_get_arg(0); + $indices = array(); + + switch (func_num_args()) { + case 1: + if (is_array($data)) { + if (is_array(reset($data))) { + foreach ($data as $key => $val) { + $indices[$key] = array_keys(array_flip($val)); + } + } else { + foreach ($data as $val) { + $this->add($val); + } + } + } elseif (is_string($data)) { + $indices = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->getUtils()->fromSequenceString($data); + } elseif ($data instanceof IMP_Compose) { + $indices = array( + $data->getMetadata('mailbox') => array($data->getMetadata('uid')) + ); + } elseif ($data instanceof IMP_Contents) { + $indices = array( + $data->getMailbox() => array($data->getUid()) + ); + } elseif ($data instanceof IMP_Indices) { + $indices = $data->indices(); + } elseif ($data instanceof IMP_Mailbox) { + $idx = $data->getIMAPIndex(); + $indices = array( + $idx['mailbox'] => array($idx['uid']) + ); + } + break; + + case 2: + $secondarg = func_get_arg(1); + $indices = array( + func_get_arg(0) => (is_array($secondarg) ? array_keys(array_flip($secondarg)) : array($secondarg)) + ); + break; + } + + if (!empty($indices)) { + if (empty($this->_indices)) { + $this->_indices = $indices; + } else { + /* Can't use array_merge_recursive() here because keys may + * be numeric mailbox names (e.g. 123), and these keys are + * treated as numeric (not strings) when merging. */ + foreach (array_keys($indices) as $key) { + $this->_indices[$key] = isset($this->_indices[$key]) + ? array_merge($this->_indices[$key], $indices[$key]) + : $indices[$key]; + } + } + } + } + + /** + * Returns mailbox/UID information for the first index. + * + * @return array A 2-element array with the mailbox and the UID. + */ + public function getSingle() + { + $val = reset($this->_indices); + return array(key($this->_indices), reset($val)); + } + + /** + * Index count. + * + * @return integer The number of indices. + */ + public function count() + { + $count = 0; + + foreach (array_keys($this->_indices) as $key) { + $count += count($this->_indices[$key]); + } + + return $count; + } + + /** + * Return a copy of the indices array. + * + * @return array The indices array (keys are mailbox names, values are + * arrays of UIDS). + */ + public function indices() + { + /* This creates a copy of the indices array. Needed because the + * Iterator functions rely on pointers. */ + return $this->_indices; + } + + /* Magic methods. */ + + /** + * String representation of the object. + * + * @return string String representation (IMAP sequence string). + */ + public function __toString() + { + return $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->getUtils()->toSequenceString($this->_indices, array('mailbox' => true)); + } + + /* Iterator methods. */ + + public function current() + { + return current($this->_indices[$this->key()]); + } + + public function key() + { + return key($this->_indices); + } + + public function next() + { + if ((next($this->_indices[$this->key()]) === false) && + (($key = next($this->_indices)) !== false)) { + reset($this->_indices[$key]); + } + } + + public function rewind() + { + reset($this->_indices); + reset(current($this->_indices)); + } + + public function valid() + { + return !is_null(key($this->_indices)); + } + +} diff --git a/imp/lib/Injector/Factory/Contents.php b/imp/lib/Injector/Factory/Contents.php index 4bbaca5e1..63e1f3f07 100644 --- a/imp/lib/Injector/Factory/Contents.php +++ b/imp/lib/Injector/Factory/Contents.php @@ -54,19 +54,20 @@ class IMP_Injector_Factory_Contents /** * Return the IMP_Contents:: instance. * - * @param string $mailbox The mailbox name. - * @param integer $uid The message UID. + * @param IMP_Indices $indices An indices object. * * @return IMP_Contents The singleton contents instance. * @throws IMP_Exception */ - public function getOb($mailbox, $uid) + public function getOb($indices) { - $uid = $uid . IMP::IDX_SEP . $mailbox; - if (!isset($this->_instances[$uid])) { - $this->_instances[$uid] = new IMP_Contents($uid); + $key = strval($indices); + + if (!isset($this->_instances[$key])) { + $this->_instances[$key] = new IMP_Contents($indices); } - return $this->_instances[$uid]; + + return $this->_instances[$key]; } } diff --git a/imp/lib/Injector/Factory/Mailbox.php b/imp/lib/Injector/Factory/Mailbox.php index 1fd3909fc..ff8c6b993 100644 --- a/imp/lib/Injector/Factory/Mailbox.php +++ b/imp/lib/Injector/Factory/Mailbox.php @@ -54,23 +54,18 @@ class IMP_Injector_Factory_Mailbox /** * Return the IMP_Mailbox:: instance. * - * @param string $mailbox The mailbox name. - * @param string $msgmbox The mailbox name of the current index. - * @param integer $uid The message UID of the current index. + * @param string $mailbox The mailbox name. + * @param IMP_Indices $indices An indices object. * * @return IMP_Mailbox The singleton mailbox instance. * @throws IMP_Exception */ - public function getOb($mailbox, $msgmbox = null, $uid = null) + public function getOb($mailbox, $indices = null) { - $uid = (is_null($msgmbox) || is_null($uid)) - ? null - : $uid . IMP::IDX_SEP . $msgmbox; - if (!isset($this->_instances[$mailbox])) { - $this->_instances[$mailbox] = new IMP_Mailbox($mailbox, $uid); - } elseif (!is_null($uid)) { - $this->_instances[$mailbox]->setIndex($uid); + $this->_instances[$mailbox] = new IMP_Mailbox($mailbox, $indices); + } elseif (!is_null($indices)) { + $this->_instances[$mailbox]->setIndex($indices); } return $this->_instances[$mailbox]; diff --git a/imp/lib/LoginTasks/Task/PurgeSentmail.php b/imp/lib/LoginTasks/Task/PurgeSentmail.php index 55cbf2a69..fc6915c22 100644 --- a/imp/lib/LoginTasks/Task/PurgeSentmail.php +++ b/imp/lib/LoginTasks/Task/PurgeSentmail.php @@ -56,13 +56,10 @@ class IMP_LoginTasks_Task_PurgeSentmail extends Horde_LoginTasks_Task $query = new Horde_Imap_Client_Search_Query(); $query->dateSearch($del_time, Horde_Imap_Client_Search_Query::DATE_BEFORE); $msg_ids = $GLOBALS['injector']->getInstance('IMP_Search')->runSearchQuery($query, $mbox); - if (empty($msg_ids)) { - continue; - } /* Go through the message list and delete the messages. */ - if ($imp_message->delete(array($mbox => $msg_ids), array('nuke' => true))) { - $msgcount = count($msg_ids); + if ($imp_message->delete($msg_ids, array('nuke' => true))) { + $msgcount = $msg_ids->count(); if ($msgcount == 1) { $GLOBALS['notification']->push(sprintf(_("Purging 1 message from sent-mail folder %s."), IMP::displayFolder($mbox)), 'horde.message'); } else { diff --git a/imp/lib/LoginTasks/Task/PurgeSpam.php b/imp/lib/LoginTasks/Task/PurgeSpam.php index 9e1098a95..ecfd7f753 100644 --- a/imp/lib/LoginTasks/Task/PurgeSpam.php +++ b/imp/lib/LoginTasks/Task/PurgeSpam.php @@ -54,17 +54,15 @@ class IMP_LoginTasks_Task_PurgeSpam extends Horde_LoginTasks_Task $query = new Horde_Imap_Client_Search_Query(); $query->dateSearch($del_time, Horde_Imap_Client_Search_Query::DATE_BEFORE); $msg_ids = $GLOBALS['injector']->getInstance('IMP_Search')->runSearchQuery($query, $spam_folder); - if (empty($msg_ids)) { - return false; - } /* Go through the message list and delete the messages. */ - if ($GLOBALS['injector']->getInstance('IMP_Message')->delete(array($spam_folder => $msg_ids), array('nuke' => true))) { + if ($GLOBALS['injector']->getInstance('IMP_Message')->delete($msg_ids, array('nuke' => true))) { $msgcount = count($msg_ids); $GLOBALS['notification']->push(sprintf(ngettext("Purging %d message from Spam folder.", "Purging %d messages from Spam folder.", $msgcount), $msgcount), 'horde.message'); + return true; } - return true; + return false; } /** diff --git a/imp/lib/LoginTasks/Task/PurgeTrash.php b/imp/lib/LoginTasks/Task/PurgeTrash.php index 4f6eb6aec..dfe27e5bb 100644 --- a/imp/lib/LoginTasks/Task/PurgeTrash.php +++ b/imp/lib/LoginTasks/Task/PurgeTrash.php @@ -54,17 +54,15 @@ class IMP_LoginTasks_Task_PurgeTrash extends Horde_LoginTasks_Task $query = new Horde_Imap_Client_Search_Query(); $query->dateSearch($del_time, Horde_Imap_Client_Search_Query::DATE_BEFORE); $msg_ids = $GLOBALS['injector']->getInstance('IMP_Search')->runSearchQuery($query, $trash_folder); - if (empty($msg_ids)) { - return false; - } /* Go through the message list and delete the messages. */ - if ($GLOBALS['injector']->getInstance('IMP_Message')->delete(array($trash_folder => $msg_ids), array('nuke' => true))) { - $msgcount = count($msg_ids); + if ($GLOBALS['injector']->getInstance('IMP_Message')->delete($msg_ids, array('nuke' => true))) { + $msgcount = $msg_ids->count(); $GLOBALS['notification']->push(sprintf(ngettext("Purging %d message from Trash folder.", "Purging %d messages from Trash folder.", $msgcount), $msgcount), 'horde.message'); + return true; } - return true; + return false; } /** diff --git a/imp/lib/Mailbox.php b/imp/lib/Mailbox.php index 244acfc64..48ed33729 100644 --- a/imp/lib/Mailbox.php +++ b/imp/lib/Mailbox.php @@ -66,15 +66,15 @@ class IMP_Mailbox /** * Constructor. * - * @param string $mailbox The mailbox to work with. - * @param string $uid UID string (UID . IMP::IDX_SEP . Mailbox). + * @param string $mailbox The mailbox to work with. + * @param IMP_Indices $indices An indices object. */ - public function __construct($mailbox, $uid = null) + public function __construct($mailbox, $indices = null) { $this->_mailbox = $mailbox; $this->_searchmbox = $GLOBALS['injector']->getInstance('IMP_Search')->isSearchMbox($mailbox); - if (is_null($uid)) { + if (is_null($indices)) { unset($_SESSION['imp']['cache']['imp_mailbox'][$mailbox]); } else { /* Try to rebuild sorted information from the session cache. */ @@ -83,7 +83,7 @@ class IMP_Mailbox $this->_sorted = $this->_searchmbox ? $tmp->s : $tmp; $this->_sortedMbox = $this->_searchmbox ? $tmp->m : array(); } - $this->setIndex($uid); + $this->setIndex($indices); } register_shutdown_function(array($this, 'shutdown')); @@ -163,8 +163,7 @@ class IMP_Mailbox * Horde_Imap_Client::fetch() for format. * 'uid' - (string) The unique ID of the message. * - * 'uids' - (array) The array of UIDs. It is in the same format as used - * for IMP::parseIndicesList(). + * 'uids' - (IMP_Indices) An indices object. * */ public function getMailboxArray($msgnum, $options = array()) @@ -238,7 +237,7 @@ class IMP_Mailbox !in_array('\\seen', $v['flags'])))) { if (empty($preview_info[$k])) { try { - $imp_contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($mbox, $k); + $imp_contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb(new IMP_Indices($mbox, $k)); $prev = $imp_contents->generatePreview(); $preview_info[$k] = array('IMPpreview' => $prev['text'], 'IMPpreviewc' => $prev['cut']); if (!is_null($cache)) { @@ -265,7 +264,10 @@ class IMP_Mailbox } catch (Horde_Imap_Client_Exception $e) {} } - return array('overview' => $overview, 'uids' => $uids); + return array( + 'overview' => $overview, + 'uids' => new IMP_Indices($uids) + ); } /** @@ -298,8 +300,7 @@ class IMP_Mailbox } try { - foreach ($GLOBALS['injector']->getInstance('IMP_Search')->runSearch($query, $this->_mailbox) as $val) { - list($idx, $mbox) = explode(IMP::IDX_SEP, $val); + foreach ($GLOBALS['injector']->getInstance('IMP_Search')->runSearch($query, $this->_mailbox) as $mbox => $idx) { $this->_sorted[] = $idx; $this->_sortedMbox[] = $mbox; } @@ -441,7 +442,7 @@ class IMP_Mailbox { if ($rebuild) { $this->_rebuild(); - $this->setIndex(0, 'offset'); + $this->setIndex(0); } return !is_null($this->_arrayIndex); } @@ -611,33 +612,25 @@ class IMP_Mailbox /** * Updates the message array index. * - * @param mixed $data If $type is 'offset', the number of messages to - * increase array index by. If type is 'uid', sets - * array index to the value of the given message - * UID string (UID . IMP::IDX_SEP . Mailbox). - * @param string $type Either 'offset' or 'uid'. + * @param mixed $data If an integer, the number of messages to increase + * array index by. If an indices object, sets array + * index to the index value. */ - public function setIndex($data, $type = 'uid') + public function setIndex($data) { - switch ($type) { - case 'offset': - if (!is_null($this->_arrayIndex)) { - $this->_arrayIndex += $data; - if (empty($this->_sorted[$this->_arrayIndex])) { - $this->_arrayIndex = null; - } - $this->_rebuild(); - } - break; - - case 'uid': - list($uid, $mailbox) = explode(IMP::IDX_SEP, $data); + if ($data instanceof IMP_Indices) { + list($mailbox, $uid) = $data->getSingle(); $this->_arrayIndex = $this->getArrayIndex($uid, $mailbox); if (empty($this->_arrayIndex)) { $this->_rebuild(true); $this->_arrayIndex = $this->getArrayIndex($uid, $mailbox); } - break; + } elseif (!is_null($this->_arrayIndex)) { + $this->_arrayIndex += $data; + if (empty($this->_sorted[$this->_arrayIndex])) { + $this->_arrayIndex = null; + } + $this->_rebuild(); } } @@ -739,18 +732,17 @@ class IMP_Mailbox /** * Returns the current sorted array without the given messages. * - * @param mixed $msgs The list of indices to remove (see - * IMP::parseIndicesList()) or true to remove all - * messages in the mailbox. + * @param mixed $indices An IMP_Indices object or true to remove all + * messages in the mailbox. */ - public function removeMsgs($msgs) + public function removeMsgs($indices) { - if ($msgs === true) { + if ($indices === true) { $this->_rebuild(true); return; } - if (empty($msgs)) { + if (!$indices->count()) { return; } @@ -758,15 +750,13 @@ class IMP_Mailbox $sortcount = count($this->_sorted); /* Remove the current entry and recalculate the range. */ - foreach (IMP::parseIndicesList($msgs) as $key => $val) { - foreach ($val as $uid) { - $val = $this->getArrayIndex($uid, $key); - unset($this->_sorted[$val]); - if ($this->_searchmbox) { - unset($this->_sortedMbox[$val]); - } - ++$msgcount; + foreach ($indices as $mbox => $uid) { + $val = $this->getArrayIndex($uid, $mbox); + unset($this->_sorted[$val]); + if ($this->_searchmbox) { + unset($this->_sortedMbox[$val]); } + ++$msgcount; } $this->_sorted = array_values($this->_sorted); @@ -779,7 +769,7 @@ class IMP_Mailbox /* Update the current array index to its new position in the message * array. */ - $this->setIndex(0, 'offset'); + $this->setIndex(0); } /** diff --git a/imp/lib/Message.php b/imp/lib/Message.php index 919ee0a7f..685089a04 100644 --- a/imp/lib/Message.php +++ b/imp/lib/Message.php @@ -40,11 +40,12 @@ class IMP_Message * Handles search and Trash mailboxes. * Also handles moves to the tasklist and/or notepad applications. * - * @param string $targetMbox The mailbox to move/copy messages to - * (UTF7-IMAP). - * @param string $action Either 'copy' or 'move'. - * @param mixed $indices See IMP::parseIndicesList(). - * @param boolean $new Whether the target mailbox has to be created. + * @param string $targetMbox The mailbox to move/copy messages to + * (UTF7-IMAP). + * @param string $action Either 'copy' or 'move'. + * @param IMP_Indices $indices An indices object. + * @param boolean $new Whether the target mailbox has to be + * created. * * @return boolean True if successful, false if not. */ @@ -52,21 +53,21 @@ class IMP_Message { global $conf, $notification, $prefs; - if (!($msgList = IMP::parseIndicesList($indices))) { + if (!$indices->count()) { return false; } /* If the target is a tasklist, handle the move/copy specially. */ if ($conf['tasklist']['use_tasklist'] && (strpos($targetMbox, '_tasklist_') === 0)) { - $this->_createTasksOrNotes(str_replace('_tasklist_', '', $targetMbox), $action, $msgList, 'task'); + $this->_createTasksOrNotes(str_replace('_tasklist_', '', $targetMbox), $action, $indices, 'task'); return true; } /* If the target is a notepad, handle the move/copy specially. */ if ($conf['notepad']['use_notepad'] && (strpos($targetMbox, '_notepad_') === 0)) { - $this->_createTasksOrNotes(str_replace('_notepad_', '', $targetMbox), $action, $msgList, 'note'); + $this->_createTasksOrNotes(str_replace('_notepad_', '', $targetMbox), $action, $indices, 'note'); return true; } @@ -94,7 +95,7 @@ class IMP_Message $imp_imap = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb(); - foreach ($msgList as $mbox => $msgIndices) { + foreach ($indices->indices() as $mbox => $msgIndices) { $error = null; if ($imp_imap->isReadOnly($targetMbox)) { @@ -122,7 +123,7 @@ class IMP_Message $imp_mailbox = $GLOBALS['injector']->getInstance('IMP_Mailbox')->getOb($mbox); if (($action == 'move') && $imp_mailbox->isBuilt()) { - $imp_mailbox->removeMsgs(array($mbox => $msgIndices)); + $imp_mailbox->removeMsgs(new IMP_Indices($mbox, $msgIndices)); } } catch (Horde_Imap_Client_Exception $e) { $error = $e->getMessage(); @@ -144,8 +145,8 @@ class IMP_Message * Trash folder is being used. * Handles search and Trash mailboxes. * - * @param mixed $indices See IMP::parseIndicesList(). - * @param array $options Additional options: + * @param IMP_Indices $indices An indices object. + * @param array $options Additional options: *
      * 'keeplog' - (boolean) Should any history information of the message be
      *             kept?
@@ -160,7 +161,7 @@ class IMP_Message
     {
         global $conf, $notification, $prefs;
 
-        if (!($msgList = IMP::parseIndicesList($indices))) {
+        if (!$indices->count()) {
             return false;
         }
 
@@ -188,7 +189,7 @@ class IMP_Message
 
         $imp_imap = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb();
 
-        foreach ($msgList as $mbox => $msgIndices) {
+        foreach ($indices->indices() as $mbox => $msgIndices) {
             $error = null;
 
             if ($imp_imap->isReadOnly($mbox)) {
@@ -209,7 +210,7 @@ class IMP_Message
                 continue;
             }
 
-            $indices_array = array($mbox => $msgIndices);
+            $imp_indices = new IMP_Indices($mbox, $msgIndices);
             $return_value += count($msgIndices);
 
             /* Trash is only valid for IMAP mailboxes. */
@@ -219,7 +220,7 @@ class IMP_Message
 
                     $imp_mailbox = $GLOBALS['injector']->getInstance('IMP_Mailbox')->getOb($mbox);
                     if ($imp_mailbox->isBuilt()) {
-                        $imp_mailbox->removeMsgs(array($mbox => $msgIndices));
+                        $imp_mailbox->removeMsgs($imp_indices);
                     }
                 } catch (Horde_Imap_Client_Exception $e) {
                     // @todo Check for overquota error.
@@ -257,7 +258,7 @@ class IMP_Message
                 try {
                     $imp_imap->store($mbox, array('add' => array('\\deleted'), 'ids' => $msgIndices));
                     if ($expunge_now) {
-                        $this->expungeMailbox($indices_array);
+                        $this->expungeMailbox($imp_indices->indices());
                     }
                 } catch (Horde_Imap_Client_Exception $e) {}
 
@@ -285,7 +286,7 @@ class IMP_Message
      * Handles search mailboxes.
      * This function works with IMAP only, not POP3.
      *
-     * @param mixed $indices  See IMP::parseIndicesList().
+     * @param IMP_Indices $indices  An indices object.
      *
      * @return boolean  True if successful, false if not.
      */
@@ -298,137 +299,133 @@ class IMP_Message
      * Copies or moves a list of messages to a tasklist or notepad.
      * Handles search and Trash mailboxes.
      *
-     * @param string $list    The list in which the task or note will be
-     *                        created.
-     * @param string $action  Either 'copy' or 'move'.
-     * @param mixed $msgList  See IMP::parseIndicesList().
-     * @param string $type    The object type to create ('note' or 'task').
+     * @param string $list          The list in which the task or note will be
+     *                              created.
+     * @param string $action        Either 'copy' or 'move'.
+     * @param IMP_Indices $indices  An indices object.
+     * @param string $type          The object type to create ('note' or
+     *                              'task').
      */
-    protected function _createTasksOrNotes($list, $action, $msgList, $type)
+    protected function _createTasksOrNotes($list, $action, $indices, $type)
     {
         global $registry, $notification, $prefs;
 
-        foreach ($msgList as $folder => $msgIndices) {
-            foreach ($msgIndices as $index) {
-                /* Fetch the message contents. */
-                $imp_contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($folder, $index);
-
-                /* Fetch the message headers. */
-                $imp_headers = $imp_contents->getHeaderOb();
-                $subject = $imp_headers->getValue('subject');
-
-                /* Extract the message body. */
-                $mime_message = $imp_contents->getMIMEMessage();
-                $body_id = $imp_contents->findBody();
-                $body_part = $mime_message->getPart($body_id);
-                $body = $body_part->getContents();
-
-                /* Re-flow the message for prettier formatting. */
-                $flowed = new Horde_Text_Flowed($mime_message->replaceEOL($body, "\n"));
-                if ($mime_message->getContentTypeParameter('delsp') == 'yes') {
-                    $flowed->setDelSp(true);
+        foreach ($indices as $folder => $index) {
+            /* Fetch the message contents. */
+            $imp_contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb(new IMP_Indices($folder, $index));
+
+            /* Fetch the message headers. */
+            $imp_headers = $imp_contents->getHeaderOb();
+            $subject = $imp_headers->getValue('subject');
+
+            /* Extract the message body. */
+            $mime_message = $imp_contents->getMIMEMessage();
+            $body_id = $imp_contents->findBody();
+            $body_part = $mime_message->getPart($body_id);
+            $body = $body_part->getContents();
+
+            /* Re-flow the message for prettier formatting. */
+            $flowed = new Horde_Text_Flowed($mime_message->replaceEOL($body, "\n"));
+            if ($mime_message->getContentTypeParameter('delsp') == 'yes') {
+                $flowed->setDelSp(true);
+            }
+            $body = $flowed->toFlowed(false);
+
+            /* Convert to current charset */
+            /* TODO: When Horde_iCalendar supports setting of charsets
+             * we need to set it there instead of relying on the fact
+             * that both Nag and IMP use the same charset. */
+            $body = Horde_String::convertCharset($body, $body_part->getCharset(), Horde_Nls::getCharset());
+
+            /* Create a new iCalendar. */
+            $vCal = new Horde_iCalendar();
+            $vCal->setAttribute('PRODID', '-//The Horde Project//IMP ' . $GLOBALS['registry']->getVersion() . '//EN');
+            $vCal->setAttribute('METHOD', 'PUBLISH');
+
+            switch ($type) {
+            case 'task':
+                /* Create a new vTodo object using this message's contents. */
+                $vTodo = Horde_iCalendar::newComponent('vtodo', $vCal);
+                $vTodo->setAttribute('SUMMARY', $subject);
+                $vTodo->setAttribute('DESCRIPTION', $body);
+                $vTodo->setAttribute('PRIORITY', '3');
+
+                /* Get the list of editable tasklists. */
+                try {
+                    $lists = $registry->call('tasks/listTasklists', array(false, Horde_Perms::EDIT));
+                } catch (Horde_Exception $e) {
+                    $lists = null;
+                    $notification->push($e);
                 }
-                $body = $flowed->toFlowed(false);
-
-                /* Convert to current charset */
-                /* TODO: When Horde_iCalendar supports setting of charsets
-                 * we need to set it there instead of relying on the fact
-                 * that both Nag and IMP use the same charset. */
-                $body = Horde_String::convertCharset($body, $body_part->getCharset(), Horde_Nls::getCharset());
-
-                /* Create a new iCalendar. */
-                $vCal = new Horde_iCalendar();
-                $vCal->setAttribute('PRODID', '-//The Horde Project//IMP ' . $GLOBALS['registry']->getVersion() . '//EN');
-                $vCal->setAttribute('METHOD', 'PUBLISH');
-
-                switch ($type) {
-                case 'task':
-                    /* Create a new vTodo object using this message's
-                     * contents. */
-                    $vTodo = Horde_iCalendar::newComponent('vtodo', $vCal);
-                    $vTodo->setAttribute('SUMMARY', $subject);
-                    $vTodo->setAttribute('DESCRIPTION', $body);
-                    $vTodo->setAttribute('PRIORITY', '3');
-
-                    /* Get the list of editable tasklists. */
-                    try {
-                        $lists = $registry->call('tasks/listTasklists', array(false, Horde_Perms::EDIT));
-                    } catch (Horde_Exception $e) {
-                        $lists = null;
-                        $notification->push($e);
-                    }
 
-                    /* Attempt to add the new vTodo item to the requested
-                     * tasklist. */
-                    try {
-                        $res = $registry->call('tasks/import', array($vTodo, 'text/calendar', $list));
-                    } catch (Horde_Exception $e) {
-                        $res = null;
-                        $notification->push($e);
-                    }
-                    break;
+                /* Attempt to add the new vTodo item to the requested
+                 * tasklist. */
+                try {
+                    $res = $registry->call('tasks/import', array($vTodo, 'text/calendar', $list));
+                } catch (Horde_Exception $e) {
+                    $res = null;
+                    $notification->push($e);
+                }
+                break;
 
-                case 'note':
-                    /* Create a new vNote object using this message's
-                     * contents. */
-                    $vNote = Horde_iCalendar::newComponent('vnote', $vCal);
-                    $vNote->setAttribute('BODY', $subject . "\n". $body);
+            case 'note':
+                /* Create a new vNote object using this message's contents. */
+                $vNote = Horde_iCalendar::newComponent('vnote', $vCal);
+                $vNote->setAttribute('BODY', $subject . "\n". $body);
 
-                    /* Get the list of editable notepads. */
-                    try {
-                        $lists = $registry->call('notes/listNotepads', array(false, Horde_Perms::EDIT));
-                    } catch (Horde_Exception $e) {
-                        $lists = null;
-                        $notification->push($e);
-                    }
+                /* Get the list of editable notepads. */
+                try {
+                    $lists = $registry->call('notes/listNotepads', array(false, Horde_Perms::EDIT));
+                } catch (Horde_Exception $e) {
+                    $lists = null;
+                    $notification->push($e);
+                }
 
-                    /* Attempt to add the new vNote item to the requested
-                     * notepad. */
-                    try {
-                        $res = $registry->call('notes/import', array($vNote, 'text/x-vnote', $list));
-                    } catch (Horde_Exception $e) {
-                        $res = null;
-                        $notification->push($e);
-                    }
-                    break;
+                /* Attempt to add the new vNote item to the requested
+                 * notepad. */
+                try {
+                    $res = $registry->call('notes/import', array($vNote, 'text/x-vnote', $list));
+                } catch (Horde_Exception $e) {
+                    $res = null;
+                    $notification->push($e);
                 }
+                break;
+            }
 
-                if (!is_null($res)) {
-                    if (!$res) {
+            if (!is_null($res)) {
+                if (!$res) {
+                    switch ($type) {
+                    case 'task':
+                        $notification->push(_("An unknown error occured while creating the new task."), 'horde.error');
+                        break;
+
+                    case 'note':
+                        $notification->push(_("An unknown error occured while creating the new note."), 'horde.error');
+                        break;
+                    }
+                } elseif (!is_null($lists)) {
+                    $name = '"' . htmlspecialchars($subject) . '"';
+
+                    /* Attempt to convert the object name into a hyperlink. */
+                    try {
                         switch ($type) {
                         case 'task':
-                            $notification->push(_("An unknown error occured while creating the new task."), 'horde.error');
+                            $link = $registry->link('tasks/show', array('uid' => $res));
                             break;
 
                         case 'note':
-                            $notification->push(_("An unknown error occured while creating the new note."), 'horde.error');
+                            $link = $registry->hasMethod('notes/show')
+                                ? $registry->link('notes/show', array('uid' => $res))
+                                : false;
                             break;
                         }
-                    } elseif (!is_null($lists)) {
-                        $name = '"' . htmlspecialchars($subject) . '"';
-
-                        /* Attempt to convert the object name into a
-                         * hyperlink. */
-                        try {
-                            switch ($type) {
-                            case 'task':
-                                $link = $registry->link('tasks/show', array('uid' => $res));
-                                break;
-
-                            case 'note':
-                                $link = $registry->hasMethod('notes/show')
-                                    ? $registry->link('notes/show', array('uid' => $res))
-                                    : false;
-                                break;
-                            }
-
-                            if ($link) {
-                                $name = sprintf('%s', Horde::url($link), $name);
-                            }
-
-                            $notification->push(sprintf(_("%s was successfully added to \"%s\"."), $name, htmlspecialchars($lists[$list]->get('name'))), 'horde.success', array('content.raw'));
-                        } catch (Horde_Exception $e) {}
-                    }
+
+                        if ($link) {
+                            $name = sprintf('%s', Horde::url($link), $name);
+                        }
+
+                        $notification->push(sprintf(_("%s was successfully added to \"%s\"."), $name, htmlspecialchars($lists[$list]->get('name'))), 'horde.success', array('content.raw'));
+                    } catch (Horde_Exception $e) {}
                 }
             }
         }
@@ -443,26 +440,18 @@ class IMP_Message
      * Strips one or all MIME parts out of a message.
      * Handles search mailboxes.
      *
-     * @param mixed $indices  See IMP::parseIndicesList().
-     * @param string $partid  The MIME ID of the part to strip. All parts are
-     *                        stripped if null.
+     * @param IMP_Indices $indices  An indices object.
+     * @param string $partid        The MIME ID of the part to strip. All
+     *                              parts are stripped if null.
      *
      * @throws IMP_Exception
      */
     public function stripPart($indices, $partid = null)
     {
-        /* Return error if no index was provided. */
-        if (!($msgList = IMP::parseIndicesList($indices))) {
-            throw new IMP_Exception(_("An error occured while attempting to strip the attachment."));
-        }
-
-        /* If more than one UID provided, return error. */
-        reset($msgList);
-        list($mbox, $uid) = each($msgList);
-        if (each($msgList) || (count($uid) > 1)) {
-            throw new IMP_Exception(_("An error occured while attempting to strip the attachment."));
+        list($mbox, $uid) = $indices->getSingle();
+        if (!$uid) {
+            return;
         }
-        $uid = reset($uid);
 
         $imp_imap = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb();
 
@@ -472,7 +461,7 @@ class IMP_Message
 
         $uidvalidity = $imp_imap->checkUidvalidity($mbox);
 
-        $contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($mbox, $uid);
+        $contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($indices);
         $message = $contents->getMIMEMessage();
         $boundary = trim($message->getContentTypeParameter('boundary'), '"');
 
@@ -564,7 +553,7 @@ class IMP_Message
 
         $this->delete($indices, array('nuke' => true, 'keeplog' => true));
 
-        $GLOBALS['injector']->getInstance('IMP_Mailbox')->getOb($mbox)->setIndex($new_uid . IMP::IDX_SEP . $mbox);
+        $GLOBALS['injector']->getInstance('IMP_Mailbox')->getOb($mbox, new IMP_Indices($mbox, $new_uid));
 
         /* We need to replace the old index in the query string with the
          * new index. */
@@ -576,16 +565,16 @@ class IMP_Message
      * Handles search mailboxes.
      * This function works with IMAP only, not POP3.
      *
-     * @param array $flags     The IMAP flag(s) to set or clear.
-     * @param mixed $indices   See IMP::parseIndicesList().
-     * @param boolean $action  If true, set the flag(s), otherwise clear the
-     *                         flag(s).
+     * @param array $flags          The IMAP flag(s) to set or clear.
+     * @param IMP_Indices $indices  An indices object.
+     * @param boolean $action       If true, set the flag(s), otherwise clear
+     *                              the flag(s).
      *
      * @return boolean  True if successful, false if not.
      */
     public function flag($flags, $indices, $action = true)
     {
-        if (!($msgList = IMP::parseIndicesList($indices))) {
+        if (!$indices->count()) {
             return false;
         }
 
@@ -594,7 +583,7 @@ class IMP_Message
             : array('remove' => $flags);
         $imp_imap = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb();
 
-        foreach ($msgList as $mbox => $msgIndices) {
+        foreach ($indices->indices() as $mbox => $msgIndices) {
             $error = null;
 
             if ($imp_imap->isReadOnly($mbox)) {
@@ -717,7 +706,7 @@ class IMP_Message
 
                 $imp_mailbox = $GLOBALS['injector']->getInstance('IMP_Mailbox')->getOb($key);
                 if ($imp_mailbox->isBuilt()) {
-                    $imp_mailbox->removeMsgs(is_array($val) ? array($key => $val) : true);
+                    $imp_mailbox->removeMsgs(is_array($val) ? new IMP_Indices($key, $val) : true);
                 }
             } catch (Horde_Imap_Client_Exception $e) {}
         }
@@ -768,8 +757,7 @@ class IMP_Message
                     $this->expungeMailbox(array($mbox => 1));
                 } else {
                     $ret = $imp_imap->search($mbox);
-                    $indices = array($mbox => $ret['match']);
-                    $this->delete($indices);
+                    $this->delete(new IMP_Indices($mbox, $ret['match']));
                 }
 
                 $notification->push(sprintf(_("Emptied all messages from %s."), $display_mbox), 'horde.success');
diff --git a/imp/lib/Mime/Viewer/Partial.php b/imp/lib/Mime/Viewer/Partial.php
index f7a72e5cf..5e060a650 100644
--- a/imp/lib/Mime/Viewer/Partial.php
+++ b/imp/lib/Mime/Viewer/Partial.php
@@ -82,20 +82,19 @@ class IMP_Horde_Mime_Viewer_Partial extends Horde_Mime_Viewer_Driver
             return null;
         }
 
-        $mbox = $this->_params['contents']->getMailbox();
-
         /* Perform the search to find the other parts of the message. */
         $query = new Horde_Imap_Client_Search_Query();
         $query->headerText('Content-Type', $id);
-        $indices = $GLOBALS['injector']->getInstance('IMP_Search')->runSearchQuery($query, $mbox);
+        $indices = $GLOBALS['injector']->getInstance('IMP_Search')->runSearchQuery($query, $this->_params['contents']->getMailbox());
 
         /* If not able to find the other parts of the message, prepare a
          * status message. */
-        if (count($indices) != $total) {
+        $msg_count = $indices->count();
+        if ($msg_count != $total) {
             self::$_statuscache[$this->_mimepart->getMimeId()] = array(
                 'icon' => Horde::img('alerts/error.png', _("Error")),
                 'text' => array(
-                    sprintf(_("Cannot display message - found only %s of %s parts of this message in the current mailbox."), count($indices), $total)
+                    sprintf(_("Cannot display message - found only %s of %s parts of this message in the current mailbox."), $msg_count, $total)
                 )
             );
             return null;
@@ -103,12 +102,12 @@ class IMP_Horde_Mime_Viewer_Partial extends Horde_Mime_Viewer_Driver
 
         /* Get the contents of each of the parts. */
         $parts = array();
-        foreach ($indices as $val) {
+        foreach ($indices as $mbox => $val) {
             /* No need to fetch the current part again. */
             if ($val == $number) {
                 $parts[$number] = $this->_mimepart->getContents();
             } else {
-                $ic = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($mbox, $val);
+                $ic = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb(new IMP_Indices($mbox, $val));
                 $parts[$ic->getMIMEMessage()->getContentTypeParameter('number')] = $ic->getBody();
             }
         }
diff --git a/imp/lib/Search.php b/imp/lib/Search.php
index e1dcff33e..979721bd8 100644
--- a/imp/lib/Search.php
+++ b/imp/lib/Search.php
@@ -246,14 +246,14 @@ class IMP_Search
      * @param string $id  The search query id to use (by default, will use the
      *                    current ID set in the object).
      *
-     * @return array  The sorted list.
+     * @return IMP_Indices  An indices object.
      * @throws Horde_Imap_Client_Exception
      */
     public function runSearch($ob, $id = null)
     {
         $id = $this->_strip($id);
         $mbox = '';
-        $sorted = array();
+        $sorted = new IMP_Indices();
 
         if (empty($_SESSION['imp']['search'][$id])) {
             return $sorted;
@@ -274,9 +274,7 @@ class IMP_Search
 
         foreach ($search['f'] as $val) {
             $results = $this->imapSearch($val, $query, array('reverse' => $sortpref['dir'], 'sort' => array($sortpref['by'])));
-            foreach ($results['sort'] as $val2) {
-                $sorted[] = $val2 . IMP::IDX_SEP . $val;
-            }
+            $sorted->add($val, $results['sort']);
         }
 
         return $sorted;
@@ -293,16 +291,16 @@ class IMP_Search
      * @param integer $sortby   The sort criteria.
      * @param integer $sortdir  The sort directory.
      *
-     * @return array  The sorted list.
+     * @return IMP_Indices  An indices object.
      */
     public function runSearchQuery($query, $mailbox, $sortby = null,
                                    $sortdir = null)
     {
         try {
             $results = $this->imapSearch($mailbox, $query, array('reverse' => $sortdir, 'sort' => array($sortby)));
-            return $results['sort'];
+            return new IMP_Indices($mailbox, $results['sort']);
         } catch (Horde_Imap_Client_Exception $e) {
-            return array();
+            return new IMP_Indices();
         }
     }
 
diff --git a/imp/lib/Spam.php b/imp/lib/Spam.php
index eb21a44c3..47e8b6511 100644
--- a/imp/lib/Spam.php
+++ b/imp/lib/Spam.php
@@ -17,8 +17,8 @@ class IMP_Spam
      * Reports a list of messages as spam, based on the local configuration
      * parameters.
      *
-     * @param mixed $indices  See IMP::parseIndicesList().
-     * @param string $action  Either 'spam' or 'notspam'.
+     * @param IMP_Indices $indices  An indices object.
+     * @param string $action        Either 'spam' or 'notspam'.
      *
      * @return integer  1 if messages have been deleted, 2 if messages have
      *                  been moved.
@@ -30,15 +30,16 @@ class IMP_Spam
         /* Abort immediately if spam reporting has not been enabled, or if
          * there are no messages. */
         if (empty($GLOBALS['conf'][$action]['reporting']) ||
-            !($msgList = IMP::parseIndicesList($indices))) {
+            !$indices->count()) {
             return 0;
         }
 
+        $imp_imap = $GLOBALS['injector']->getInstance('IMP_Imap')->getOb();
         $report_count = 0;
 
-        foreach ($msgList as $mbox => $msgIndices) {
+        foreach ($indices->indices() as $mbox => $msgIndices) {
             try {
-                $GLOBALS['injector']->getInstance('IMP_Imap')->getOb()->checkUidvalidity($mbox);
+                $imp_imap->checkUidvalidity($mbox);
             } catch (IMP_Exception $e) {
                 continue;
             }
@@ -47,7 +48,7 @@ class IMP_Spam
                 /* Fetch the raw message contents (headers and complete
                  * body). */
                 try {
-                    $imp_contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($mbox, $idx);
+                    $imp_contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb(new IMP_Indices($mbox, $idx));
                 } catch (IMP_Exception $e) {
                     continue;
                 }
diff --git a/imp/lib/Ui/Compose.php b/imp/lib/Ui/Compose.php
index d0ea5df5e..732680425 100644
--- a/imp/lib/Ui/Compose.php
+++ b/imp/lib/Ui/Compose.php
@@ -180,21 +180,18 @@ class IMP_Ui_Compose
     }
 
     /**
-     * Get the IMP_Contents:: object for a Mailbox -> UID combo.
+     * Get the IMP_Contents:: object for a Mailbox/UID.
      *
-     * @param integer $uid     Message UID.
-     * @param string $mailbox  Message mailbox.
+     * @param IMP_Indices $indices  An indices object.
      *
      * @return boolean|IMP_Contents  The contents object, or false on error.
      */
-    public function getIMPContents($uid, $mailbox)
+    public function getIMPContents($indices)
     {
-        if (!empty($uid)) {
-            try {
-                return $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($mailbox, $uid);
-            } catch (IMP_Exception $e) {
-                $GLOBALS['notification']->push(_("Could not retrieve the message from the mail server."), 'horde.error');
-            }
+        try {
+            return $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($indices);
+        } catch (IMP_Exception $e) {
+            $GLOBALS['notification']->push(_("Could not retrieve the message from the mail server."), 'horde.error');
         }
 
         return false;
diff --git a/imp/lib/Ui/Message.php b/imp/lib/Ui/Message.php
index df9163ae8..acb304449 100644
--- a/imp/lib/Ui/Message.php
+++ b/imp/lib/Ui/Message.php
@@ -123,7 +123,7 @@ class IMP_Ui_Message
             $success = true;
 
             if ($mdn_flag) {
-                $GLOBALS['injector']->getInstance('IMP_Message')->flag(array('$MDNSent'), $uid . IMP::IDX_SEP . $mailbox, true);
+                $GLOBALS['injector']->getInstance('IMP_Message')->flag(array('$MDNSent'), new IMP_Indices($mailbox, $uid), true);
             }
         } catch (Exception $e) {
             $success = false;
diff --git a/imp/lib/Views/ListMessages.php b/imp/lib/Views/ListMessages.php
index 1f6965f91..8850f3523 100644
--- a/imp/lib/Views/ListMessages.php
+++ b/imp/lib/Views/ListMessages.php
@@ -331,10 +331,10 @@ class IMP_Views_ListMessages
         $ret = array();
 
         for ($i = $start; $i <= $end; ++$i) {
-            $uid = $sorted_list['s'][$i] .
-                (isset($sorted_list['m'][$i])
-                    ? IMP::IDX_SEP . $sorted_list['m'][$i]
-                    : '');
+            $uid = $sorted_list['s'][$i];
+            if (isset($sorted_list['m'][$i])) {
+                $uid = $sorted_list['m'][$i] . IMP_Dimp::IDX_SEP . $uid;
+            }
             if ($uid) {
                 $ret[$uid] = $i;
             }
@@ -427,10 +427,10 @@ class IMP_Views_ListMessages
                 $msg['listmsg'] = 1;
             }
 
-            /* Need both UID and mailbox to create a unique ID string if
+            /* Need both mailbox and UID to create a unique ID string if
              * using a search mailbox.  Otherwise, use only the UID. */
             if ($search) {
-                $msgs[$ob['uid'] . IMP::IDX_SEP . $ob['mailbox']] = $msg;
+                $msgs[$ob['mailbox'] . IMP_Dimp::IDX_SEP . $ob['uid']] = $msg;
             } else {
                 $msgs[$ob['uid']] = $msg;
             }
diff --git a/imp/lib/Views/ShowMessage.php b/imp/lib/Views/ShowMessage.php
index 9539352d3..39e761f4e 100644
--- a/imp/lib/Views/ShowMessage.php
+++ b/imp/lib/Views/ShowMessage.php
@@ -118,7 +118,7 @@ class IMP_Views_ShowMessage
 
         /* Parse MIME info and create the body of the message. */
         try {
-            $imp_contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($mailbox, $uid);
+            $imp_contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb(new IMP_Indices($mailbox, $uid));
         } catch (IMP_Exception $e) {
             $result['error'] = $error_msg;
             $result['errortype'] = 'horde.error';
diff --git a/imp/mailbox-mimp.php b/imp/mailbox-mimp.php
index b178a1e02..b3c174c18 100644
--- a/imp/mailbox-mimp.php
+++ b/imp/mailbox-mimp.php
@@ -54,12 +54,12 @@ case 'u':
     if ($vars->checkbox == 'd') {
         try {
             Horde::checkRequestToken('imp.message-mimp', $vars->mt);
-            $imp_message->delete($vars->indices);
+            $imp_message->delete(new IMP_Indices($vars->indices));
         } catch (Horde_Exception $e) {
             $notification->push($e);
         }
     } else {
-        $imp_message->undelete($vars->indices);
+        $imp_message->undelete(new IMP_Indices($vars->indices));
     }
     break;
 
@@ -67,7 +67,7 @@ case 'u':
 // 'ri' = report innocent
 case 'rs':
 case 'ri':
-    IMP_Spam::reportSpam($vars->indices, $vars->a == 'rs' ? 'spam' : 'innocent');
+    IMP_Spam::reportSpam(new IMP_Indices($vars->indices), $vars->a == 'rs' ? 'spam' : 'innocent');
     break;
 }
 
@@ -155,7 +155,7 @@ $mbox_info = $imp_mailbox->getMailboxArray(range($pageOb['begin'], $pageOb['end'
 /* Get thread information. */
 if ($sortpref['by'] == Horde_Imap_Client::SORT_THREAD) {
     $imp_thread = new IMP_Imap_Thread($imp_mailbox->getThreadOb());
-    $threadtree = $imp_thread->getThreadTextTree(reset($mbox_info['uids']), $sortpref['dir']);
+    $threadtree = $imp_thread->getThreadTextTree(reset($mbox_info['uids']->indices()), $sortpref['dir']);
 } else {
     $imp_thread = null;
     $threadtree = array();
@@ -166,7 +166,7 @@ while (list(,$ob) = each($mbox_info['overview'])) {
     $msg = array(
         'status' => '',
         'subject' => trim($imp_ui->getSubject($ob['envelope']['subject'])),
-        'uid' => $ob['uid'] . IMP::IDX_SEP . $ob['mailbox']
+        'uid' => strval(new IMP_Indices($ob['mailbox'], $ob['uid']))
     );
 
     /* Format the from header. */
diff --git a/imp/mailbox.php b/imp/mailbox.php
index c94086c80..42dd0ccbb 100644
--- a/imp/mailbox.php
+++ b/imp/mailbox.php
@@ -49,6 +49,7 @@ try {
 /* Is this a search mailbox? */
 $imp_search = $injector->getInstance('IMP_Search');
 $search_mbox = $imp_search->isSearchMbox(IMP::$mailbox);
+$vars = Horde_Variables::getDefaultVariables();
 $vfolder = $imp_search->isVFolder();
 
 /* There is a chance that this page is loaded directly via message.php. If so,
@@ -57,26 +58,20 @@ $vfolder = $imp_search->isVFolder();
 $mailbox_url = Horde::applicationUrl('mailbox.php');
 $mailbox_imp_url = IMP::generateIMPUrl('mailbox.php', IMP::$mailbox);
 if (!Horde_Util::nonInputVar('from_message_page')) {
-    $actionID = Horde_Util::getFormData('actionID');
-    $start = Horde_Util::getFormData('start');
-}
-
-/* Get form data and make sure it's the type that we're expecting. */
-$targetMbox = Horde_Util::getFormData('targetMbox');
-$newMbox = Horde_Util::getFormData('newMbox');
-if (!is_array(($indices = Horde_Util::getFormData('indices')))) {
-    $indices = array($indices);
+    $actionID = $vars->actionID;
+    $start = $vars->start;
 }
 
 $do_filter = false;
 $imp_flags = $injector->getInstance('IMP_Imap_Flags');
 $imp_imap = $injector->getInstance('IMP_Imap')->getOb();
+$indices = new IMP_Indices($vars->indices);
 $open_compose_window = null;
 
 /* Run through the action handlers */
 if ($actionID && ($actionID != 'message_missing')) {
     try {
-        Horde::checkRequestToken('imp.mailbox', Horde_Util::getFormData('mailbox_token'));
+        Horde::checkRequestToken('imp.mailbox', $vars->mailbox_token);
     } catch (Horde_Exception $e) {
         $notification->push($e);
         $actionID = null;
@@ -103,7 +98,7 @@ if ($readonly &&
 
 switch ($actionID) {
 case 'change_sort':
-    IMP::setSort(Horde_Util::getFormData('sortby'), Horde_Util::getFormData('sortdir'));
+    IMP::setSort($vars->sortby, $vars->sortdir);
     break;
 
 case 'blacklist':
@@ -129,31 +124,28 @@ case 'message_missing':
     break;
 
 case 'fwd_digest':
-    if (!empty($indices)) {
-        $options = array('fwddigest' => serialize($indices), 'actionID' => 'fwd_digest');
+    if ($indices->count()) {
+        $options = array('fwddigest' => strval($indices), 'actionID' => 'fwd_digest');
         $open_compose_window = IMP::openComposeWin($options);
     }
     break;
 
 case 'delete_messages':
-    if (!empty($indices)) {
-        $injector->getInstance('IMP_Message')->delete($indices);
-    }
+    $injector->getInstance('IMP_Message')->delete($indices);
     break;
 
 case 'undelete_messages':
-    if (!empty($indices)) {
-        $injector->getInstance('IMP_Message')->undelete($indices);
-    }
+    $injector->getInstance('IMP_Message')->undelete($indices);
     break;
 
 case 'move_messages':
 case 'copy_messages':
-    if (!empty($indices) && !empty($targetMbox)) {
-        if (!empty($newMbox) && ($newMbox == 1)) {
-            $targetMbox = IMP::folderPref($targetMbox, true);
+    if (isset($vars->targetMbox) && $indices->count()) {
+        if (!empty($vars->newMbox) && ($vars->newMbox == 1)) {
+            $targetMbox = IMP::folderPref($vars->targetMbox, true);
             $newMbox = true;
         } else {
+            $targetMbox = $vars->targetMbox;
             $newMbox = false;
         }
         $injector->getInstance('IMP_Message')->copy($targetMbox, ($actionID == 'move_messages') ? 'move' : 'copy', $indices, $newMbox);
@@ -162,7 +154,7 @@ case 'copy_messages':
 
 case 'flag_messages':
     $flag = Horde_Util::getPost('flag');
-    if ($flag && !empty($indices)) {
+    if ($flag && $indices->count()) {
         $flag = $imp_flags->parseFormId($flag);
         $injector->getInstance('IMP_Message')->flag(array($flag['flag']), $indices, $flag['set']);
     }
@@ -186,7 +178,7 @@ case 'empty_mailbox':
     break;
 
 case 'view_messages':
-    $redirect = IMP::generateIMPUrl('thread.php', IMP::$mailbox, null, null, false)->setRaw(true)->add(array('mode' => 'msgview', 'msglist' => $imp_imap->getUtils()->toSequenceString(IMP::parseIndicesList($indices), array('mailbox' => true))));
+    $redirect = IMP::generateIMPUrl('thread.php', IMP::$mailbox, null, null, false)->setRaw(true)->add(array('mode' => 'msgview', 'msglist' => strval($indices)));
     header('Location: ' . $redirect);
     exit;
 }
@@ -219,7 +211,7 @@ if ($conf['user']['allow_folders']) {
 
 /* Build the list of messages in the mailbox. */
 $imp_mailbox = $injector->getInstance('IMP_Mailbox')->getOb(IMP::$mailbox);
-$pageOb = $imp_mailbox->buildMailboxPage(Horde_Util::getFormData('page'), $start);
+$pageOb = $imp_mailbox->buildMailboxPage($vars->page, $start);
 $show_preview = $prefs->getValue('preview_enabled');
 
 $mbox_info = $imp_mailbox->getMailboxArray(range($pageOb['begin'], $pageOb['end']), array('preview' => $show_preview, 'headers' => true, 'structure' => $prefs->getValue('atc_flag')));
@@ -377,7 +369,7 @@ if (!empty($newmsgs)) {
      * the current mailbox. */
     $imp_imap->openMailbox(IMP::$mailbox, Horde_Imap_Client::OPEN_READWRITE);
 
-    if (!Horde_Util::getFormData('no_newmail_popup')) {
+    if (!$vars->no_newmail_popup) {
         /* Newmail alerts. */
         IMP::newmailAlerts($newmsgs);
     }
@@ -581,7 +573,7 @@ $messages = $threadlevel = array();
 /* Get thread object, if necessary. */
 if ($sortpref['by'] == Horde_Imap_Client::SORT_THREAD) {
     $imp_thread = new IMP_Imap_Thread($imp_mailbox->getThreadOb());
-    $threadtree = $imp_thread->getThreadImageTree(reset($mbox_info['uids']), $sortpref['dir']);
+    $threadtree = $imp_thread->getThreadImageTree(reset($mbox_info['uids']->indices()), $sortpref['dir']);
 }
 
 $mh_count = 0;
@@ -712,7 +704,7 @@ while (list(,$ob) = each($mbox_info['overview'])) {
         'preview' => '',
         'status' => '',
         'size' => htmlspecialchars($imp_ui->getSize($ob['size'])),
-        'uid' => $ob['uid'] . IMP::IDX_SEP . htmlspecialchars($ob['mailbox']),
+        'uid' => htmlspecialchars(new IMP_Indices($ob['mailbox'], $ob['uid']))
     );
 
     /* Since this value will be used for an ID element, it cannot contain
diff --git a/imp/message-mimp.php b/imp/message-mimp.php
index b2d331473..e5f4b9645 100644
--- a/imp/message-mimp.php
+++ b/imp/message-mimp.php
@@ -25,7 +25,7 @@ Horde_Nls::setTimeZone();
 $vars = Horde_Variables::getDefaultVariables();
 
 /* Make sure we have a valid index. */
-$imp_mailbox = $GLOBALS['injector']->getInstance('IMP_Mailbox')->getOb(IMP::$mailbox, IMP::$thismailbox, IMP::$uid);
+$imp_mailbox = $GLOBALS['injector']->getInstance('IMP_Mailbox')->getOb(IMP::$mailbox, new IMP_Indices(IMP::$thismailbox, IMP::$uid));
 if (!$imp_mailbox->isValidIndex(false)) {
     header('Location: ' . IMP::generateIMPUrl('mailbox-mimp.php', IMP::$mailbox)->setRaw(true)->add('a', 'm'));
     exit;
@@ -33,7 +33,7 @@ if (!$imp_mailbox->isValidIndex(false)) {
 
 $readonly = $imp_imap->isReadOnly(IMP::$mailbox);
 
-$imp_mimp = $injector->getInstance('IMP_Ui_Mimp');
+$imp_ui_mimp = $injector->getInstance('IMP_Ui_Mimp');
 $imp_hdr_ui = new IMP_Ui_Headers();
 $imp_ui = new IMP_Ui_Message();
 
@@ -49,18 +49,18 @@ case 'u':
     }
     $index_ob = $imp_mailbox->getIMAPIndex();
     $msg_index = $imp_mailbox->getMessageIndex();
-    $index_array = array($index_ob['mailbox'] => array($index_ob['uid']));
+    $imp_indices = new IMP_Indices($index_ob['mailbox'], $index_ob['uid']);
     $imp_message = $injector->getInstance('IMP_Message');
 
     if ($vars->a == 'd') {
         try {
             Horde::checkRequestToken('imp.message-mimp', $vars->mt);
-            $msg_delete = (bool)$imp_message->delete($index_array);
+            $msg_delete = (bool)$imp_message->delete($imp_indices);
         } catch (Horde_Exception $e) {
             $notification->push($e);
         }
     } else {
-        $imp_message->undelete($index_array);
+        $imp_message->undelete($imp_indices);
     }
     break;
 
@@ -71,7 +71,7 @@ case 'ri':
     $index_ob = $imp_mailbox->getIMAPIndex();
     $msg_index = $imp_mailbox->getMessageIndex();
 
-    $msg_delete = (IMP_Spam::reportSpam(array($index_ob['mailbox'] => array($index_ob['uid'])), $vars->a == 'rs' ? 'spam' : 'innocent') === 1);
+    $msg_delete = (IMP_Spam::reportSpam(new IMP_Indices($index_ob['mailbox'], $index_ob['uid']), $vars->a == 'rs' ? 'spam' : 'innocent') === 1);
     break;
 
 // 'pa' = part action
@@ -79,7 +79,7 @@ case 'ri':
 }
 
 if ($imp_ui->moveAfterAction()) {
-    $imp_mailbox->setIndex(1, 'offset');
+    $imp_mailbox->setIndex(1);
 }
 
 /* We may have done processing that has taken us past the end of the
@@ -120,7 +120,7 @@ $use_pop = ($_SESSION['imp']['protocol'] == 'pop');
 
 /* Parse the message. */
 try {
-    $imp_contents = $injector->getInstance('IMP_Contents')->getOb($mailbox_name, $uid);
+    $imp_contents = $injector->getInstance('IMP_Contents')->getOb(new IMP_Indices($imp_mailbox));
 } catch (IMP_Exception $e) {
     header('Location: ' . IMP::generateIMPUrl('mailbox-mimp.php', $mailbox_name)->setRaw(true)->add('a', 'm'));
     exit;
@@ -308,7 +308,7 @@ if ($conf['notspam']['reporting'] &&
     $menu[] = array(_("Report as Innocent"), $self_link->copy()->add(array('a' => 'ri', 'mt' => Horde::getRequestToken('imp.message-mimp'))));
 }
 
-$t->set('menu', $imp_mimp->getMenu('message', $menu));
+$t->set('menu', $imp_ui_mimp->getMenu('message', $menu));
 
 $hdrs = array();
 foreach ($display_headers as $head => $val) {
diff --git a/imp/message.php b/imp/message.php
index 06179e099..076bfd544 100644
--- a/imp/message.php
+++ b/imp/message.php
@@ -32,7 +32,7 @@ if (!($search_mbox = $injector->getInstance('IMP_Search')->isSearchMbox(IMP::$ma
 }
 
 /* Make sure we have a valid index. */
-$imp_mailbox = $GLOBALS['injector']->getInstance('IMP_Mailbox')->getOb(IMP::$mailbox, IMP::$thismailbox, IMP::$uid);
+$imp_mailbox = $GLOBALS['injector']->getInstance('IMP_Mailbox')->getOb(IMP::$mailbox, new IMP_Indices(IMP::$thismailbox, IMP::$uid));
 if (!$imp_mailbox->isValidIndex(false)) {
     _returnToMailbox(null, 'message_missing');
     require IMP_BASE . '/mailbox.php';
@@ -67,7 +67,7 @@ if ($readonly &&
 $index_array = $imp_mailbox->getIMAPIndex();
 $mailbox_name = $index_array['mailbox'];
 $uid = $index_array['uid'];
-$indices_array = array($mailbox_name => array($uid));
+$indices = new IMP_Indices($mailbox_name, $uid);
 
 $imp_flags = $injector->getInstance('IMP_Imap_Flags');
 $imp_hdr_ui = new IMP_Ui_Headers();
@@ -78,25 +78,25 @@ case 'blacklist':
 case 'whitelist':
     $imp_filter = new IMP_Filter();
     if ($vars->actionID == 'blacklist') {
-        $imp_filter->blacklistMessage($indices_array);
+        $imp_filter->blacklistMessage($indices);
     } else {
-        $imp_filter->whitelistMessage($indices_array);
+        $imp_filter->whitelistMessage($indices);
     }
     break;
 
 case 'delete_message':
 case 'undelete_message':
     if ($vars->actionID == 'undelete_message') {
-        $imp_message->undelete($indices_array);
+        $imp_message->undelete($indices);
     } else {
-        $imp_message->delete($indices_array);
+        $imp_message->delete($indices);
         if ($prefs->getValue('mailbox_return')) {
             _returnToMailbox($imp_mailbox->getMessageIndex());
             require IMP_BASE . '/mailbox.php';
             exit;
         }
         if ($imp_ui->moveAfterAction()) {
-            $imp_mailbox->setIndex(1, 'offset');
+            $imp_mailbox->setIndex(1);
         }
     }
     break;
@@ -110,7 +110,7 @@ case 'copy_message':
         } else {
             $newMbox = false;
         }
-        $imp_message->copy($vars->targetMbox, ($vars->actionID == 'move_message') ? 'move' : 'copy', $indices_array, $vars->newMbox);
+        $imp_message->copy($vars->targetMbox, ($vars->actionID == 'move_message') ? 'move' : 'copy', $indices, $vars->newMbox);
         if ($prefs->getValue('mailbox_return')) {
             _returnToMailbox($imp_mailbox->getMessageIndex());
             require IMP_BASE . '/mailbox.php';
@@ -122,10 +122,10 @@ case 'copy_message':
 case 'spam_report':
 case 'notspam_report':
     $action = str_replace('_report', '', $vars->actionID);
-    switch (IMP_Spam::reportSpam($indices_array, $action)) {
+    switch (IMP_Spam::reportSpam($indices, $action)) {
     case 1:
         if ($imp_ui->moveAfterAction()) {
-            $imp_mailbox->setIndex(1, 'offset');
+            $imp_mailbox->setIndex(1);
         }
         break;
     }
@@ -137,10 +137,10 @@ case 'notspam_report':
     break;
 
 case 'flag_message':
-    if (isset($vars->flag) && !empty($indices_array)) {
+    if (isset($vars->flag) && $indices->count()) {
         $peek = true;
         $flag = $imp_flags->parseFormId($vars->flag);
-        $imp_message->flag(array($flag['flag']), $indices_array, $flag['set']);
+        $imp_message->flag(array($flag['flag']), $indices, $flag['set']);
         if ($prefs->getValue('mailbox_return')) {
             _returnToMailbox($imp_mailbox->getMessageIndex());
             require IMP_BASE . '/mailbox.php';
@@ -161,7 +161,7 @@ case 'add_address':
 case 'strip_all':
 case 'strip_attachment':
     try {
-        $imp_message->stripPart($indices_array, ($vars->actionID == 'strip_all') ? null : $vars->imapid);
+        $imp_message->stripPart($indices, ($vars->actionID == 'strip_all') ? null : $vars->imapid);
         $notification->push(_("Attachment successfully stripped."), 'horde.success');
     } catch (Horde_Exception $e) {
         $notification->push($e);
@@ -189,7 +189,7 @@ $uid = $index_array['uid'];
 
 /* Parse the message. */
 try {
-    $imp_contents = $injector->getInstance('IMP_Contents')->getOb($mailbox_name, $uid);
+    $imp_contents = $injector->getInstance('IMP_Contents')->getOb(new IMP_Indices($imp_mailbox));
 } catch (IMP_Exception $e) {
     $imp_mailbox->removeMsgs(true);
     _returnToMailbox(null, 'message_missing');
diff --git a/imp/pgp.php b/imp/pgp.php
index 543237444..b5fe3aee8 100644
--- a/imp/pgp.php
+++ b/imp/pgp.php
@@ -141,7 +141,7 @@ case 'info_personal_private_key':
 
 case 'save_attachment_public_key':
     /* Retrieve the key from the message. */
-    $contents = $injector->getInstance('IMP_Contents')->getOb($vars->mailbox, $vars->uid);
+    $contents = $injector->getInstance('IMP_Contents')->getOb(new IMP_Indices($vars->mailbox, $vars->uid));
     $mime_part = $contents->getMIMEPart($vars->mime_id);
     if (empty($mime_part)) {
         throw new IMP_Exception('Cannot retrieve public key from message.');
diff --git a/imp/rss.php b/imp/rss.php
index 64f1d4f1d..4fb62fdb6 100644
--- a/imp/rss.php
+++ b/imp/rss.php
@@ -57,10 +57,10 @@ if ($new_mail) {
 }
 $ids = $imp_search->runSearchQuery($query, $mailbox, Horde_Imap_Client::SORT_ARRIVAL, 1);
 
-if (!empty($ids)) {
+if ($ids->count()) {
     $imp_ui = new IMP_Ui_Mailbox(IMP::$mailbox);
 
-    $overview = $imp_mailbox->getMailboxArray(array_slice($ids, 0, 20), array('preview' => $prefs->getValue('preview_enabled')));
+    $overview = $imp_mailbox->getMailboxArray(array_slice(reset($ids->indices()), 0, 20), array('preview' => $prefs->getValue('preview_enabled')));
 
     foreach ($overview['overview'] as $ob) {
         $from_addr = $imp_ui->getFrom($ob['envelope'], array('fullfrom' => true));
diff --git a/imp/saveimage.php b/imp/saveimage.php
index 6364dd4f6..6228ebb60 100644
--- a/imp/saveimage.php
+++ b/imp/saveimage.php
@@ -19,7 +19,7 @@ $vars = Horde_Variables::getDefaultVariables();
 /* Run through the action handlers. */
 switch ($vars->actionID) {
 case 'save_image':
-    $contents = $injector->getInstance('IMP_Contents')->getOb($vars->mbox, $vars->uid);
+    $contents = $injector->getInstance('IMP_Contents')->getOb(new IMP_Indices($vars->mbox, $vars->uid));
     $mime_part = $contents->getMIMEPart($vars->id);
     $image_data = array(
         'data' => $mime_part->getContents(),
diff --git a/imp/smime.php b/imp/smime.php
index 167083b61..18cd05292 100644
--- a/imp/smime.php
+++ b/imp/smime.php
@@ -89,7 +89,7 @@ case 'process_import_personal_certs':
 
 case 'save_attachment_public_key':
     /* Retrieve the key from the message. */
-    $contents = $injector->getInstance('IMP_Contents')->getOb($vars->mailbox, $vars->uid);
+    $contents = $injector->getInstance('IMP_Contents')->getOb(new IMP_Indices($vars->mailbox, $vars->uid));
     $mime_part = $contents->getMIMEPart($vars->mime_id);
     if (empty($mime_part)) {
         throw new IMP_Exception('Cannot retrieve public key from message.');
diff --git a/imp/templates/dimp/javascript_defs_dimp.php b/imp/templates/dimp/javascript_defs_dimp.php
index d6baebd72..e13f3920e 100644
--- a/imp/templates/dimp/javascript_defs_dimp.php
+++ b/imp/templates/dimp/javascript_defs_dimp.php
@@ -49,7 +49,7 @@ $code['conf'] = array_filter(array(
     'URI_SEARCH' => (string) Horde::applicationUrl('search.php'),
     'URI_VIEW' => (string) Horde::applicationUrl('view.php'),
 
-    'IDX_SEP' => IMP::IDX_SEP,
+    'IDX_SEP' => IMP_Dimp::IDX_SEP,
     'SESSION_ID' => defined('SID') ? SID : '',
 
     // Other variables
diff --git a/imp/thread.php b/imp/thread.php
index 7fe8551b9..db6812d59 100644
--- a/imp/thread.php
+++ b/imp/thread.php
@@ -21,7 +21,7 @@ Horde_Nls::setTimeZone();
 $mode = Horde_Util::getFormData('mode', 'thread');
 
 $imp_imap = $injector->getInstance('IMP_Imap')->getOb();
-$imp_mailbox = $injector->getInstance('IMP_Mailbox')->getOb(IMP::$mailbox, IMP::$thismailbox, IMP::$uid);
+$imp_mailbox = $injector->getInstance('IMP_Mailbox')->getOb(IMP::$mailbox, new IMP_Indices(IMP::$thismailbox, IMP::$uid));
 
 $error = false;
 if ($mode == 'thread') {
@@ -31,8 +31,8 @@ if ($mode == 'thread') {
     }
 } else {
     /* MSGVIEW MODE: Make sure we have a valid list of messages. */
-    $msglist = $imp_imap->getUtils()->fromSequenceString(Horde_Util::getFormData('msglist'));
-    if (empty($msglist)) {
+    $imp_indices = new IMP_Indices(Horde_Util::getFormData('msglist'));
+    if (!$imp_indices->count()) {
         $error = true;
     }
 }
@@ -71,15 +71,13 @@ if ($mode == 'thread') {
 
     $imp_thread = new IMP_Imap_Thread($threadob);
     $threadtree = $imp_thread->getThreadImageTree($thread, false);
-    $loop_array = array(IMP::$mailbox => $thread);
-} else {
-    $loop_array = IMP::parseIndicesList($msglist);
+    $imp_indices = new IMP_Indices(IMP::$mailbox, $thread);
 }
 
 $charset = Horde_Nls::getCharset();
 $imp_ui = new IMP_Ui_Message();
 
-foreach ($loop_array as $mbox => $idxlist) {
+foreach ($imp_indices->indices() as $mbox => $idxlist) {
     $fetch_res = $imp_imap->fetch($mbox, array(
         Horde_Imap_Client::FETCH_ENVELOPE => true
     ), array('ids' => $idxlist));
@@ -89,7 +87,7 @@ foreach ($loop_array as $mbox => $idxlist) {
 
         /* Get the body of the message. */
         $curr_msg = $curr_tree = array();
-        $contents = $injector->getInstance('IMP_Contents')->getOb($mbox, $idx);
+        $contents = $injector->getInstance('IMP_Contents')->getOb(new IMP_Indices($mbox, $idx));
         $mime_id = $contents->findBody();
         if ($contents->canDisplay($mime_id, IMP_Contents::RENDER_INLINE)) {
             $ret = $contents->renderMIMEPart($mime_id, IMP_Contents::RENDER_INLINE);
@@ -148,7 +146,7 @@ if ($mode == 'thread') {
         'mailbox_token' => Horde::getRequestToken('imp.mailbox')
     ));
     foreach ($thread as $val) {
-        $delete_link->add(array('indices[]' => $val . IMP::IDX_SEP . IMP::$mailbox, 'start' => $imp_mailbox->getArrayIndex($val)));
+        $delete_link->add(array('indices[]' => strval(new IMP_Indices(IMP::$mailbox, $val)), 'start' => $imp_mailbox->getArrayIndex($val)));
     }
     $template->set('delete', Horde::link('#', _("Delete Thread"), null, null, "if (confirm('" . addslashes(_("Are you sure you want to delete all messages in this thread?")) . "')) { window.location = '" . $delete_link . "'; } return false;") . Horde::img('delete.png', _("Delete Thread")) . '');
 }
diff --git a/imp/view.php b/imp/view.php
index a590648c0..3a83ea4da 100644
--- a/imp/view.php
+++ b/imp/view.php
@@ -66,7 +66,7 @@ if ($vars->actionID == 'compose_attach_preview') {
     if (!$vars->uid || !$vars->mailbox) {
         exit;
     }
-    $contents = $injector->getInstance('IMP_Contents')->getOb($vars->mailbox, $vars->uid);
+    $contents = $injector->getInstance('IMP_Contents')->getOb(new IMP_Indices($vars->mailbox, $vars->uid));
 }
 
 /* Run through action handlers */
' . _("No unread messages") . '
' . htmlspecialchars($date, ENT_QUOTES, $charset) . '