Request #8092: Use CATENATE (RFC 4469) to strip MIME parts
authorMichael M Slusarz <slusarz@curecanti.org>
Mon, 26 Apr 2010 07:36:28 +0000 (01:36 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Mon, 26 Apr 2010 07:37:22 +0000 (01:37 -0600)
Even if IMAP server doesn't support CATENATE, use the same process to
build the message data as with CATENATE - by defining the parts on the
server using IMAP URLs.

imp/docs/CHANGES
imp/docs/RFCS
imp/lib/Message.php
imp/message.php

index 5915626..0712ae4 100644 (file)
@@ -2,6 +2,8 @@
 v5.0-git
 --------
 
+[mms] Use CATENATE (RFC 4469), if available, to strip MIME parts (Request
+      #8092).
 [mms] Add preference to show flags created by other MUAs (Request #8882).
 [mms] Added HTML signature support (Request #1406).
 [mms] Simplified date sorting display (Ticket #8936).
index 71b51e4..4a6fb6d 100644 (file)
@@ -34,6 +34,7 @@ RFC 3691        UNSELECT
 RFC 4315        UIDPLUS
 RFC 4422        SASL Authentication (for DIGEST-MD5)
 RFC 4466        Collected extensions (updates RFCs 2088, 3501, 3502, 3516)
+RFC 4469/5550   CATENATE
 RFC 4551        CONDSTORE
 RFC 4731        ESEARCH
 RFC 4959        SASL-IR
index 68c302e..7404de9 100644 (file)
@@ -452,63 +452,90 @@ class IMP_Message
             throw new IMP_Exception(_("An error occured while attempting to strip the attachment."));
         }
 
-        /* If more than one index provided, return error. */
+        /* If more than one UID provided, return error. */
         reset($msgList);
-        list($mbox, $index) = each($msgList);
-        if (each($msgList) || (count($index) > 1)) {
+        list($mbox, $uid) = each($msgList);
+        if (each($msgList) || (count($uid) > 1)) {
             throw new IMP_Exception(_("An error occured while attempting to strip the attachment."));
         }
-        $index = implode('', $index);
+        $uid = reset($uid);
 
         if ($GLOBALS['imp_imap']->isReadOnly($mbox)) {
-            throw new IMP_Exception(_("Cannot strip the MIME part as the mailbox is read-only"));
+            throw new IMP_Exception(_("Cannot strip the MIME part as the mailbox is read-only."));
         }
 
-        $GLOBALS['imp_imap']->checkUidvalidity($mbox);
-
-        /* Get a local copy of the message. */
-        $contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($mbox, $index);
-
-        /* Loop through all to-be-stripped mime parts. */
-        if (is_null($partid)) {
-            /* For stripping all parts, it only makes sense to strip base
-             * parts. Stripping subparts may cause issues with display of the
-             * parent multipart type. */
-            for ($i = 2;; ++$i) {
-                $part = $contents->getMIMEPart($i, array('nocontents' => true));
-                if (!$part) {
-                    break;
-                }
-                $partids[] = $i;
+        $uidvalidity = $GLOBALS['imp_imap']->checkUidvalidity($mbox);
+
+        $contents = $GLOBALS['injector']->getInstance('IMP_Contents')->getOb($mbox, $uid);
+        $imap_ob = $GLOBALS['imp_imap']->ob();
+        $message = $contents->getMIMEMessage();
+        $boundary = trim($message->getContentTypeParameter('boundary'), '"');
+
+        $url_array = array(
+            'mailbox' => $mbox,
+            'uid' => $uid ,
+            'uidvalidity' => $uidvalidity
+        );
+
+        /* Always add the header to output. */
+        $parts = array(
+            array(
+                't' => 'url',
+                'v' => $imap_ob->utils->createUrl(array_merge($url_array, array('section' => 'HEADER')))
+            )
+        );
+
+        for ($id = 1; ; ++$id) {
+            $part = $message->getPart($id);
+            if (!$part) {
+                break;
             }
-        } else {
-            $partids = array($partid);
-        }
 
-        $message = $contents->buildMessageContents($partids);
-
-        foreach ($partids as $partid) {
-            $oldPart = $message->getPart($partid);
-            if (!($oldPart instanceof Horde_Mime_Part)) {
-                continue;
+            $parts[] = array(
+                't' => 'text',
+                'v' => "\r\n--" . $boundary . "\r\n"
+            );
+
+            if (($id != 1) && is_null($partid) || ($id == $partid)) {
+                $newPart = new Horde_Mime_Part();
+                $newPart->setType('text/plain');
+
+                /* Need to make sure all text is in the correct charset. */
+                $part_name = $part->getName(true);
+                $newPart->setCharset(Horde_Nls::getCharset());
+                $newPart->setContents(sprintf(_("[Attachment stripped: Original attachment type: %s, name: %s]"), $part->getType(), $part_name ? $part_name : _("unnamed")));
+
+                $parts[] = array(
+                    't' => 'text',
+                    'v' => $newPart->toString(array(
+                        'canonical' => true,
+                        'headers' => true,
+                        'stream' => true
+                    ))
+                );
+            } else {
+                $parts[] = array(
+                    't' => 'url',
+                    'v' => $imap_ob->utils->createUrl(array_merge($url_array, array('section' => $id . '.MIME')))
+                );
+                $parts[] = array(
+                    't' => 'url',
+                    'v' => $imap_ob->utils->createUrl(array_merge($url_array, array('section' => $id)))
+                );
             }
-            $newPart = new Horde_Mime_Part();
-            $newPart->setType('text/plain');
-            $newPart->setDisposition('attachment');
-
-            /* We need to make sure all text is in the correct charset. */
-            $part_name = $oldPart->getName(true);
-            $newPart->setCharset(Horde_Nls::getCharset());
-            $newPart->setContents(sprintf(_("[Attachment stripped: Original attachment type: %s, name: %s]"), $oldPart->getType(), $part_name ? $part_name : _("unnamed")));
-            $message->alterPart($partid, $newPart);
         }
 
+        $parts[] = array(
+            't' => 'text',
+            'v' => "\r\n--" . $boundary . "--\r\n"
+        );
+
         /* Get the headers for the message. */
         try {
-            $res = $GLOBALS['imp_imap']->ob()->fetch($mbox, array(
-                Horde_Imap_Client::FETCH_HEADERTEXT => array(array('peek' => true)),
+            $res = $imap_ob->fetch($mbox, array(
+                Horde_Imap_Client::FETCH_DATE => true,
                 Horde_Imap_Client::FETCH_FLAGS => true
-            ), array('ids' => array($index)));
+            ), array('ids' => array($uid)));
             $res = reset($res);
 
             /* If in Virtual Inbox, we need to reset flag to unseen so that it
@@ -518,19 +545,25 @@ class IMP_Message
                 unset($res['flags'][$pos]);
             }
 
-            $uid = $GLOBALS['imp_imap']->ob()->append($mbox, array(array('data' => $message->toString(array('headers' => $res['headertext'][0], 'stream' => true)), 'flags' => $res['flags'])));
+            $new_uid = $imap_ob->append($mbox, array(
+                array(
+                    'data' => $parts,
+                    'flags' => $res['flags'],
+                    'internaldate' => $res['date']
+                )
+            ));
+            $new_uid = reset($new_uid);
         } catch (Horde_Imap_Client_Exception $e) {
             throw new IMP_Exception(_("An error occured while attempting to strip the attachment."));
         }
 
         $this->delete($indices, array('nuke' => true, 'keeplog' => true));
 
-        $imp_mailbox = $GLOBALS['injector']->getInstance('IMP_Mailbox')->getOb($mbox);
-        $imp_mailbox->setIndex(reset($uid));
+        $GLOBALS['injector']->getInstance('IMP_Mailbox')->getOb($mbox)->setIndex($new_uid . IMP::IDX_SEP . $mbox);
 
         /* We need to replace the old index in the query string with the
          * new index. */
-        $_SERVER['QUERY_STRING'] = preg_replace('/' . $index . '/', reset($uid), $_SERVER['QUERY_STRING']);
+        $_SERVER['QUERY_STRING'] = str_replace($uid, $new_uid, $_SERVER['QUERY_STRING']);
     }
 
     /**
index 52d6353..90088a6 100644 (file)
@@ -162,6 +162,7 @@ case 'strip_all':
 case 'strip_attachment':
     try {
         $imp_message->stripPart($indices_array, ($vars->actionID == 'strip_all') ? null : $vars->imapid);
+        $notification->push(_("Attachment successfully stripped."), 'horde.success');
     } catch (Horde_Exception $e) {
         $notification->push($e);
     }