All embedded information now lives under the original part.
authorMichael M Slusarz <slusarz@curecanti.org>
Sat, 7 Nov 2009 08:04:43 +0000 (01:04 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Tue, 10 Nov 2009 16:56:02 +0000 (09:56 -0700)
Resolves Ticket #8296; Ticket #8629

Don't replace the original part with the embedded information. This
ensures we can always uniquely access the original data if necessary.
Also allows us an easier method to show status messages for related data
and allows us to do fancier things like wrapping entire parts with
colored borders (i.e. multipart/encrypted, multipart/signed).

Fixes problems with S/MIME encrypted/signed messages.

imp/docs/CHANGES
imp/lib/Contents.php
imp/lib/Mime/Viewer/Partial.php
imp/lib/Mime/Viewer/Pgp.php
imp/lib/Mime/Viewer/Plain.php
imp/lib/Mime/Viewer/Smime.php
imp/themes/screen.css

index 7c0b803..f5c1ae9 100644 (file)
@@ -2,6 +2,8 @@
 v5.0-git
 --------
 
+[mms] Wrap content-related MIME parts in a border when viewing inline to
+      indicate their relationship.
 [mms] For drafts, save the original message index for forwards/replies so when
       eventually sent, log information can be updated (Request #8663).
 [mms] Upgrade RTE to CKEditor v3.
index e384271..fef5499 100644 (file)
@@ -872,19 +872,12 @@ class IMP_Contents
                 $mime_part = $this->getMIMEPart($id);
                 $viewer->setMIMEPart($mime_part);
                 $viewer->setParams(array('contents' => $this));
-                $new_parts = $viewer->getEmbeddedMimeParts();
-                if (!is_null($new_parts)) {
-                    foreach (array_keys($new_parts) as $key) {
-                        $this->_embedded[] = $key;
-                        if ($first_id === $key) {
-                            $this->_message = $new_parts[$key];
-                            $this->_build = false;
-                            return $this->_buildMessage();
-                        }
-
-                        $this->_message->alterPart($key, $new_parts[$key]);
-                        $to_process = array_merge($to_process, array_keys($new_parts[$key]->contentTypeMap()));
-                    }
+                $new_part = $viewer->getEmbeddedMimeParts();
+                if (!is_null($new_part)) {
+                    $this->_embedded[] = $id;
+                    $mime_part->addPart($new_part);
+                    $mime_part->buildMimeIds($id);
+                    $to_process = array_merge($to_process, array_keys($new_part->contentTypeMap()));
                     $last_id = $id;
                 }
             }
index 1568798..e027e2f 100644 (file)
@@ -22,20 +22,46 @@ class IMP_Horde_Mime_Viewer_Partial extends Horde_Mime_Viewer_Driver
         'embedded' => true,
         'forceinline' => true,
         'full' => false,
-        'info' => false,
+        'info' => true,
         'inline' => false,
         'raw' => false
     );
 
     /**
-     * If this MIME part can contain embedded MIME parts, and those embedded
-     * MIME parts exist, return a list of MIME parts that contain the embedded
-     * MIME part information.
+     * Cached data.
      *
-     * @return mixed  An array with MIME IDs as the keys and Horde_Mime_Part
-     *                objects as the parts to replace the current value of
-     *                the given MIME ID.
-     *                Returns null if no embedded MIME parts exist.
+     * @var array
+     */
+    static protected $_statuscache = array();
+
+    /**
+     * Return the rendered information about the Horde_Mime_Part object.
+     *
+     * @return array  See Horde_Mime_Viewer_Driver::render().
+     */
+    protected function _renderInfo()
+    {
+        $id = $this->_mimepart->getMimeId();
+
+        if (isset(self::$_statuscache[$id])) {
+            return array(
+                $id => array(
+                    'data' => null,
+                    'status' => array(self::$_statuscache[$id]),
+                    'type' => 'text/plain; charset=' . Horde_Nls::getCharset()
+                )
+            );
+        } else {
+            return array($id => null);
+        }
+    }
+
+    /**
+     * If this MIME part can contain embedded MIME part(s), and those part(s)
+     * exist, return a representation of that data.
+     *
+     * @return mixed  A Horde_Mime_Part object representing the embedded data.
+     *                Returns null if no embedded MIME part(s) exist.
      */
     protected function _getEmbeddedMimeParts()
     {
@@ -54,13 +80,16 @@ class IMP_Horde_Mime_Viewer_Partial extends Horde_Mime_Viewer_Driver
         $query->headerText('Content-Type', $id);
         $indices = $GLOBALS['imp_search']->runSearchQuery($query, $mbox);
 
-        /* If not able to find the other parts of the message, print error. */
+        /* If not able to find the other parts of the message, prepare a
+         * status message. */
         if (count($indices) != $total) {
-            $mime_part = new Horde_Mime_Part();
-            $mime_part->setType('text/plain');
-            $mime_part->setCharset(Horde_Nls::getCharset());
-            $mime_part->setContents(sprintf(_("[Cannot display message - found only %s of %s parts of this message in the current mailbox.]"), count($indices), $total));
-            return array($this->_mimepart->getMimeId() => $mime_part);
+            self::$_statuscache[$this->_mimepart->getMimeId()] = array(
+                'icon' => Horde::img('alerts/error.png', _("Error"), null, $GLOBALS['registry']->getImageDir('horde')),
+                'text' => array(
+                    sprintf(_("Cannot display message - found only %s of %s parts of this message in the current mailbox."), count($indices), $total)
+                )
+            );
+            return null;
         }
 
         /* Get the contents of each of the parts. */
@@ -80,8 +109,10 @@ class IMP_Horde_Mime_Viewer_Partial extends Horde_Mime_Viewer_Driver
 
         /* Combine the parts. */
         $mime_part = Horde_Mime_Part::parseMessage(implode('', $parts), array('forcemime' => true));
+
         return ($mime_part === false)
             ? null
-            : array($this->_mimepart->getMimeId() => $mime_part);
+            : $mime_part;
     }
+
 }
index 8c2fd99..82f3406 100644 (file)
@@ -54,11 +54,11 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
     protected $_address = null;
 
     /**
-     * Cache for inline data.
+     * Cached data.
      *
      * @var array
      */
-    static protected $_inlinecache = array();
+    static protected $_cache = array();
 
     /**
      * Return the full rendered version of the Horde_Mime_Part object.
@@ -99,6 +99,8 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
      */
     protected function _renderInline()
     {
+        $id = $this->_mimepart->getMimeId();
+
         if (empty($this->_imppgp) &&
             !empty($GLOBALS['conf']['gnupg']['path'])) {
             $this->_imppgp = Horde_Crypt::singleton(array('IMP', 'Pgp'));
@@ -106,7 +108,7 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
 
         if (Horde_Util::getFormData('rawpgpkey')) {
             return array(
-                $this->_mimepart->getMimeId() => array(
+                $id => array(
                     'data' => $this->_mimepart->getContents(),
                     'status' => array(),
                     'type' => 'text/plain; charset=' . $this->_mimepart->getCharset()
@@ -128,7 +130,17 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
             return $this->_outputPGPSigned();
 
         case 'multipart/encrypted':
-            return $this->_outputPGPEncrypted();
+            if (isset(self::$_cache[$id])) {
+                return array_merge(array(
+                    $id => array(
+                        'data' => null,
+                        'status' => self::$_cache[$id]['status'],
+                        'type' => 'text/plain; charset=' . Horde_Nls::getCharset(),
+                        'wrap' => self::$_cache[$id]['wrap']
+                    )
+                ), self::$_cache[$id]['other']);
+            }
+            // Fall-through
 
         case 'application/pgp-encrypted':
         case 'application/pgp-signature':
@@ -138,14 +150,11 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
     }
 
     /**
-     * If this MIME part can contain embedded MIME parts, and those embedded
-     * MIME parts exist, return an altered version of the Horde_Mime_Part that
-     * contains the embedded MIME part information.
+     * If this MIME part can contain embedded MIME part(s), and those part(s)
+     * exist, return a representation of that data.
      *
-     * @return mixed  An array with MIME IDs as the keys and Horde_Mime_Part
-     *                objects as the parts to replace the current value of
-     *                the given MIME ID.
-     *                Returns null if no embedded MIME parts exist.
+     * @return mixed  A Horde_Mime_Part object representing the embedded data.
+     *                Returns null if no embedded MIME part(s) exist.
      */
     protected function _getEmbeddedMimeParts()
     {
@@ -158,28 +167,25 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
         $version_id = next($partlist);
         $data_id = Horde_Mime::mimeIdArithmetic($version_id, 'next');
 
-        /* Initialize inline data. */
-        $resymmetric = isset(self::$_inlinecache[$base_id]);
-        self::$_inlinecache[$base_id] = array(
-            $base_id => array(
-                'data' => '',
-                'status' => array(
-                    array(
-                        'icon' => Horde::img('mime/encryption.png', 'PGP'),
-                        'text' => $resymmetric ? self::$_inlinecache[$base_id][$base_id]['status'][0]['text'] : array()
-                    )
-                ),
-                'type' => 'text/html; charset=' . Horde_Nls::getCharset()
+        self::$_cache[$base_id] = array(
+            'status' => array(
+                array(
+                    'icon' => Horde::img('mime/encryption.png', 'PGP'),
+                    'text' => array()
+                )
+            ),
+            'other' => array(
+                $version_id => null,
+                $data_id => null
             ),
-            $version_id => null,
-            $data_id => null
+            'wrap' => ''
         );
-        $status = &self::$_inlinecache[$base_id][$base_id]['status'][0]['text'];
+        $status = &self::$_cache[$base_id]['status'][0]['text'];
 
         /* Is PGP active? */
         if (empty($GLOBALS['conf']['gnupg']['path']) ||
             !$GLOBALS['prefs']->getValue('use_pgp')) {
-            $status[] = _("The message below has been encrypted via PGP, however, PGP support is disabled so the message cannot be decrypted.");
+            $status[] = _("The data in this part has been encrypted via PGP, however, PGP support is disabled so the message cannot be decrypted.");
             return null;
         }
 
@@ -204,9 +210,7 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
 
                 if (is_null($symmetric_pass)) {
                     $js_action = '';
-                    if (!$resymmetric) {
-                        $status[] = _("The message has been encrypted via PGP.");
-                    }
+                    $status[] = _("The data in this part has been encrypted via PGP.");
 
                     switch ($_SESSION['imp']['view']) {
                     case 'dimp':
@@ -224,7 +228,6 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
             }
         } catch (Horde_Exception $e) {
             Horde::logMessage($e, __FILE__, __LINE__);
-            unset(self::$_inlinecache[$base_id]);
             return null;
         }
 
@@ -233,27 +236,26 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
             $info = $this->_imppgp->pgpPacketInformation($encrypted_data);
         } catch (Horde_Exception $e) {
             Horde::logMessage($e, __FILE__, __LINE__);
-            unset(self::$_inlinecache[$base_id]);
             return null;
         }
-        $literal = !empty($info['literal']);
 
+        $literal = !empty($info['literal']);
         if ($literal) {
-            $status[] = _("The message below has been compressed via PGP.");
+            $status[] = _("The data in this part has been compressed via PGP.");
         } else {
-            $status[] = _("The message below has been encrypted via PGP.");
+            $status[] = _("The data in this part has been encrypted via PGP.");
             if (!$symmetric) {
                 if (!$this->_imppgp->getPersonalPrivateKey()) {
                     /* Output if there is no personal private key to decrypt
                      * with. */
-                    $status[] = _("The message below has been encrypted via PGP, however, no personal private key exists so the message cannot be decrypted.");
+                    $status[] = _("The data in this part has been encrypted via PGP, however, no personal private key exists so the message cannot be decrypted.");
                     return null;
                 } else {
                     $personal_pass = $this->_imppgp->getPassphrase('personal');
 
                     if (is_null($personal_pass)) {
                         $js_action = '';
-                        $status[] = _("The message has been encrypted via PGP.");
+                        $status[] = _("The data in this part has been encrypted via PGP.");
 
                         switch ($_SESSION['imp']['view']) {
                         case 'dimp':
@@ -281,7 +283,7 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
                 $decrypted_data = $this->_imppgp->decryptMessage($encrypted_data, 'literal');
             }
         } catch (Horde_Exception $e) {
-            $status[] = _("The message below does not appear to be a valid PGP encrypted message. Error: ") . $e->getMessage();
+            $status[] = _("The data in this part does not appear to be a valid PGP encrypted message. Error: ") . $e->getMessage();
             if (!is_null($symmetric_pass)) {
                 $this->_imppgp->unsetPassphrase('symmetric', $this->_getSymmetricID());
                 return $this->_getEmbeddedMimeParts();
@@ -289,12 +291,9 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
             return null;
         }
 
-        unset(self::$_inlinecache[$base_id][$data_id]);
-
-        $msg = Horde_Mime_Part::parseMessage($decrypted_data->message);
-        $msg->buildMimeIds($data_id);
+        self::$_cache[$base_id]['wrap'] = 'mimePartWrapValid';
 
-        return array($data_id => $msg);
+        return Horde_Mime_Part::parseMessage($decrypted_data->message, array('forcemime' => true));
     }
 
     /**
@@ -363,7 +362,8 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
                         'text' => array()
                     )
                 ),
-                'type' => 'text/html; charset=' . Horde_Nls::getCharset()
+                'type' => 'text/html; charset=' . Horde_Nls::getCharset(),
+                'wrap' => 'mimePartWrap'
             ),
             $sig_id => null
         );
@@ -373,11 +373,11 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
             empty($GLOBALS['conf']['gnupg']['path'])) {
             /* If PGP not active, hide signature data and output status
              * information. */
-            $status[] = _("The message below has been digitally signed via PGP, but the signature cannot be verified.");
+            $status[] = _("The data in this part has been digitally signed via PGP, but the signature cannot be verified.");
             return $ret;
         }
 
-        $status[] = _("The message below has been digitally signed via PGP.");
+        $status[] = _("The data in this part has been digitally signed via PGP.");
 
         if ($GLOBALS['prefs']->getValue('pgp_verify') ||
             Horde_Util::getFormData('pgp_verify_msg')) {
@@ -405,6 +405,10 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
                     Horde_Text_Filter::filter($sig_text, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::NOHTML))
                 )
             );
+
+            $ret[$base_id]['wrap'] = $success
+                ? 'mimePartWrapValid'
+                : 'mimePartWrapInvalid';
         } else {
             switch ($_SESSION['imp']['view']) {
             case 'imp':
@@ -421,19 +425,6 @@ class IMP_Horde_Mime_Viewer_Pgp extends Horde_Mime_Viewer_Driver
     }
 
     /**
-     * Generates HTML output for 'multipart/encrypted' MIME parts.
-     *
-     * @return string  The HTML output.
-     */
-    protected function _outputPGPEncrypted()
-    {
-        $id = $this->_mimepart->getMimeId();
-        return isset(self::$_inlinecache[$id])
-            ? self::$_inlinecache[$id]
-            : array();
-    }
-
-    /**
      * Generates the symmetric ID for this message.
      *
      * @return string  Symmetric ID.
index a890c8d..381c53c 100644 (file)
 class IMP_Horde_Mime_Viewer_Plain extends Horde_Mime_Viewer_Plain
 {
     /**
+     * Cached data.
+     *
+     * @var array
+     */
+    static protected $_cache = array();
+
+    /**
      * Return the rendered inline version of the Horde_Mime_Part object.
      *
      * @return array  See Horde_Mime_Viewer_Driver::render().
@@ -24,6 +31,11 @@ class IMP_Horde_Mime_Viewer_Plain extends Horde_Mime_Viewer_Plain
         global $conf, $prefs;
 
         $mime_id = $this->_mimepart->getMimeId();
+
+        if (isset(self::$_cache[$mime_id])) {
+            return null;
+        }
+
         $type = 'text/html; charset=' . Horde_Nls::getCharset();
 
         // Trim extra whitespace in the text.
@@ -133,14 +145,11 @@ class IMP_Horde_Mime_Viewer_Plain extends Horde_Mime_Viewer_Plain
     }
 
     /**
-     * If this MIME part can contain embedded MIME parts, and those embedded
-     * MIME parts exist, return a list of MIME parts that contain the embedded
-     * MIME part information.
+     * If this MIME part can contain embedded MIME part(s), and those part(s)
+     * exist, return a representation of that data.
      *
-     * @return mixed  An array with MIME IDs as the keys and Horde_Mime_Part
-     *                objects as the parts to replace the current value of
-     *                the given MIME ID.
-     *                Returns null if no embedded MIME parts exist.
+     * @return mixed  A Horde_Mime_Part object representing the embedded data.
+     *                Returns null if no embedded MIME part(s) exist.
      */
     protected function _getEmbeddedMimeParts()
     {
@@ -252,9 +261,9 @@ class IMP_Horde_Mime_Viewer_Plain extends Horde_Mime_Viewer_Plain
             }
         }
 
-        $new_part->buildMimeIds($mime_id);
+        self::$_cache[$mime_id] = true;
 
-        return array($mime_id => $new_part);
+        return $new_part;
     }
 
     /**
@@ -291,8 +300,7 @@ class IMP_Horde_Mime_Viewer_Plain extends Horde_Mime_Viewer_Plain
             $new_part->addPart($uupart);
         }
 
-        $new_part->buildMimeIds($mime_id);
-
-        return array($mime_id => $new_part);
+        return $new_part;
     }
+
 }
index fd4fad5..5b57cd9 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * The IMP_Horde_Mime_Viewer_Smime class allows viewing/decrypting of S/MIME
  * messages.
- * This class implements parts of RFC 2630, RFC 2632, and RFC 2633.
+ * This class implements RFC 2630, 2632, & 2633.
  *
  * This class handles the following MIME types:
  *   application/pkcs7-mime
@@ -11,7 +11,7 @@
  *   application/x-pkcs7-signature (in multipart/signed part)
  *
  * This class may add the following parameters to the URL:
- *   'smime_verify_msg' - (boolean) Do verification of S.
+ *   'smime_verify_msg' - (boolean) Do verification of S/MIME message.
  *   'view_smime_key' - (boolean) Display the S/MIME Key.
  *
  * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
@@ -47,11 +47,27 @@ class IMP_Horde_Mime_Viewer_Smime extends Horde_Mime_Viewer_Driver
     protected $_impsmime = null;
 
     /**
-     * Cache for inline data.
+     * Cached data.
      *
      * @var array
      */
-    static protected $_inlinecache = array();
+    static protected $_cache = array();
+
+    /**
+     * Init the S/MIME Horde_Crypt object.
+     */
+    protected function _initSmime()
+    {
+        if (is_null($this->_impsmime) &&
+            $GLOBALS['prefs']->getValue('use_smime')) {
+            try {
+                $this->_impsmime = Horde_Crypt::singleton(array('IMP', 'Smime'));
+                $this->_impsmime->checkForOpenSSL();
+            } catch (Horde_Exception $e) {
+                $this->_impsmime = null;
+            }
+        }
+    }
 
     /**
      * Return the rendered inline version of the Horde_Mime_Part object.
@@ -61,10 +77,10 @@ class IMP_Horde_Mime_Viewer_Smime extends Horde_Mime_Viewer_Driver
     protected function _renderInline()
     {
         /* Check to see if S/MIME support is available. */
-        $this->_initSMIME();
+        $this->_initSmime();
 
         if (Horde_Util::getFormData('view_smime_key')) {
-            return $this->_outputSMIMEKey();
+            return $this->_outputSmime();
         }
 
         if (is_null($this->_impsmime)) {
@@ -75,13 +91,32 @@ class IMP_Horde_Mime_Viewer_Smime extends Horde_Mime_Viewer_Driver
             Horde::addScriptFile('imp.js', 'imp');
         }
 
+        $id = $this->_mimepart->getMimeId();
+
         switch ($this->_mimepart->getType()) {
-        case 'multipart/signed':
-            return $this->_outputSMIMESigned();
+        case 'multipart/signed';
+            if (!in_array($this->_mimepart->getContentTypeParameter('protocol'), array('application/pkcs7-signature', 'application/x-pkcs7-signature'))) {
+                return array();
+            }
+            $this->_parseSignedData(true);
+            // Fall-through
 
         case 'application/pkcs7-mime':
         case 'application/x-pkcs7-mime':
-            return $this->_outputSMIMEEncrypted();
+            if (isset(self::$_cache[$id])) {
+                return array(
+                    $id => array(
+                        'data' => null,
+                        'status' => self::$_cache[$id]['status'],
+                        'type' => 'text/plain; charset=' . Horde_Nls::getCharset(),
+                        'wrap' => self::$_cache[$id]['wrap']
+                    )
+                );
+            }
+            // Fall-through
+
+        default:
+            return array();
         }
     }
 
@@ -99,54 +134,64 @@ class IMP_Horde_Mime_Viewer_Smime extends Horde_Mime_Viewer_Driver
             return null;
         }
 
-        // 'smime-type' must be empty or 'enveloped-data'
-        $smime_type = $this->_mimepart->getContentTypeParameter('smime-type');
-        if ($smime_type == 'signed-data') {
-            // TODO
+        // 'smime-type' must be 'enveloped-data' or 'signed-data'
+        switch ($this->_mimepart->getContentTypeParameter('smime-type')) {
+        case 'enveloped-data':
+            return $this->_parseEnvelopedData();
+
+        case 'signed-data':
+            return $this->_parseSignedData();
+
+        default:
             return null;
         }
+    }
 
+    /**
+     * Parse enveloped (encrypted) data.
+     *
+     * @return mixed  See self::_getEmbeddedMimeParts().
+     */
+    protected function _parseEnvelopedData()
+    {
         $base_id = $this->_mimepart->getMimeId();
 
         /* Initialize inline data. */
-        self::$_inlinecache[$base_id] = array(
-            $base_id => array(
-                'data' => '',
-                'status' => array(
-                    array(
-                        'icon' => Horde::img('mime/encryption.png', 'S/MIME'),
-                        'text' => array(_("This message has been encrypted via S/MIME."))
-                    )
-                ),
-                'type' => 'text/html; charset=' . Horde_Nls::getCharset()
-            )
+        self::$_cache[$base_id] = array(
+            'status' => array(
+                array(
+                    'icon' => Horde::img('mime/encryption.png', 'S/MIME'),
+                    'text' => array(_("The data in this part has been encrypted via S/MIME."))
+                )
+            ),
+            'wrap' => ''
         );
-        $status = &self::$_inlinecache[$base_id][$base_id]['status'][0]['text'];
+        $status = &self::$_cache[$base_id]['status'][0]['text'];
 
         /* Is PGP active? */
-        $this->_initSMIME();
+        $this->_initSmime();
         if (empty($this->_impsmime)) {
-            $status[] = _("S/MIME support is not currently enabled so the message is unable to be decrypted.");
+            $status[] = _("S/MIME support is not currently enabled so the data is unable to be decrypted.");
             return null;
         }
 
         if (!$this->_impsmime->getPersonalPrivateKey()) {
-            $status[] = _("No personal private key exists so the message is unable to be decrypted.");
+            $status[] = _("No personal private key exists so the data is unable to be decrypted.");
             return null;
         }
 
         /* Make sure we have a passphrase. */
         $passphrase = $this->_impsmime->getPassphrase();
-        if ($passphrase === false) {
+        if (is_null($passphrase)) {
             $js_action = '';
 
-            switch ($_SESSION['imp']['view'] == 'imp') {
+            switch ($_SESSION['imp']['view']) {
             case 'dimp':
                 $js_action = 'DimpCore.reloadMessage({});';
                 // Fall through
 
             case 'imp':
-                $status[] = Horde::link('#', '', '', '', IMP::passphraseDialogJS('SMIMEPersonal', $js_action) . ';return false;') . _("You must enter the passphrase for your S/MIME private key to view this message.") . '</a>';
+                $status[] = Horde::link('#', '', '', '', IMP::passphraseDialogJS('SMIMEPersonal', $js_action) . ';return false;') . _("You must enter the passphrase for your S/MIME private key to view this data.") . '</a>';
                 break;
             }
             return null;
@@ -163,175 +208,164 @@ class IMP_Horde_Mime_Viewer_Smime extends Horde_Mime_Viewer_Driver
             return null;
         }
 
-        return array($base_id => Horde_Mime_Part::parseMessage($decrypted_data));
-    }
-
-    /**
-     * Generates HTML output for the S/MIME key.
-     *
-     * @return string  The HTML output.
-     */
-    protected function _outputSMIMEKey()
-    {
-        if (empty($this->_impsmime)) {
-            return array();
-        }
-
-        $raw_text = $this->_mimepart->getMimeId()
-            ? $this->_params['contents']->getBodyPart($this->_mimepart->getMimeId(), array('mimeheaders' => true, 'stream' => true))
-            : $this->_params['contents']->fullMessageText();
+        self::$_cache[$base_id]['wrap'] = 'mimePartWrapValid';
 
-        try {
-            $sig_result = $this->_impsmime->verifySignature($this->_mimepart->replaceEOL($raw_text, Horde_Mime_Part::RFC_EOL));
-        } catch (Horde_Exception $e) {
-            return array();
-        }
+        $new_part = Horde_Mime_Part::parseMessage($decrypted_data, array('forcemime' => true));
 
-        return array(
-            $this->_mimepart->getMimeId() => array(
-                'data' => $this->_impsmime->certToHTML($sig_result->cert),
-                'status' => array(),
-                'type' => 'text/html; charset=' . Horde_Nls::getCharset()
-            )
-        );
-    }
+        $hdrs = $this->_params['contents']->getHeaderOb();
+        $new_part->setMetadata('imp-smime-from', $hdrs->getValue('from'));
 
-    /**
-     * Init the S/MIME Horde_Crypt object.
-     */
-    protected function _initSMIME()
-    {
-        if (is_null($this->_impsmime) &&
-            $GLOBALS['prefs']->getValue('use_smime')) {
-            try {
-                $this->_impsmime = Horde_Crypt::singleton(array('IMP', 'Smime'));
-                $this->_impsmime->checkForOpenSSL();
-            } catch (Horde_Exception $e) {
-                $this->_impsmime = null;
-            }
-        }
+        return $new_part;
     }
 
     /**
-     * Generates HTML output for 'multipart/signed' MIME parts.
+     * Parse signed data.
      *
-     * @return array  TODO
+     * @param boolean $sig_only  Only do signature checking?
+     *
+     * @return mixed  See self::_getEmbeddedMimeParts().
      */
-    protected function _outputSMIMESigned()
+    protected function _parseSignedData($sig_only = false)
     {
         $partlist = array_keys($this->_mimepart->contentTypeMap());
         $base_id = reset($partlist);
         $sig_id = Horde_Mime::mimeIdArithmetic(next($partlist), 'next');
+        $graphicsdir = $GLOBALS['registry']->getImageDir('horde');
 
-        $ret = array(
-            $base_id => array(
-                'data' => '',
-                'status' => array(
-                    array(
-                        'icon' => Horde::img('mime/encryption.png', 'S/MIME'),
-                        'text' => array(_("This message has been digitally signed via S/MIME."))
-                    )
-                ),
-                'type' => 'text/html; charset=' . Horde_Nls::getCharset()
+        /* Initialize inline data. */
+        self::$_cache[$base_id] = array(
+            'status' => array(
+                array(
+                    'icon' => Horde::img('mime/encryption.png', 'S/MIME'),
+                    'text' => array(_("The data in this part has been digitally signed via S/MIME."))
+                )
             ),
-            $sig_id => null
+            'wrap' => 'mimePartWrap'
         );
-        $status = &$ret[$base_id]['status'][0]['text'];
+        $status = &self::$_cache[$base_id]['status'][0]['text'];
 
         if (!$GLOBALS['prefs']->getValue('use_smime')) {
             $status[] = _("S/MIME support is not enabled so the digital signature is unable to be verified.");
-            return $ret;
+            return null;
+        }
+
+        if ($this->_params['contents']->isEmbedded($base_id)) {
+            $hdrs = new Horde_Mime_Headers();
+            $hdrs->addHeader('From', $this->_mimepart->getMetadata('imp-smime-from'));
+            $stream = $this->_mimepart->toString(array('headers' => $hdrs, 'stream' => true));
+        } else {
+            $stream = $base_id
+                ? $this->_params['contents']->getBodyPart($base_id, array('mimeheaders' => true, 'stream' => true))
+                : $this->_params['contents']->fullMessageText(array('stream' => true));
         }
 
-        $stream = $base_id
-            ? $this->_params['contents']->getBodyPart($base_id, array('mimeheaders' => true, 'stream' => true))
-            : $this->_params['contents']->fullMessageText();
         $raw_text = $this->_mimepart->replaceEOL($stream, Horde_Mime_Part::RFC_EOL);
 
+        $this->_initSmime();
         $sig_result = null;
 
         if ($GLOBALS['prefs']->getValue('smime_verify') ||
             Horde_Util::getFormData('smime_verify_msg')) {
             try {
                 $sig_result = $this->_impsmime->verifySignature($raw_text);
+                self::$_cache[$base_id]['status'][0]['icon'] = Horde::img('alerts/success.png', _("Success"), null, $graphicsdir);
+                self::$_cache[$base_id]['wrap'] = 'mimePartWrapValid';
+
+                /* This data has been verified but there was no output from
+                 * the S/MIME program. */
+                if (empty($sig_result->result) ||
+                    ($sig_result->result === true)) {
+                    $email = is_array($sig_result->email)
+                        ? implode(', ', $sig_result->email)
+                        : $sig_result->email;
+                    $status[] = sprintf(_("The data has been verified. Sender: %s."), htmlspecialchars($email));
+                }
+
+                if (!empty($sig_result->cert)) {
+                    $cert = $this->_impsmime->parseCert($sig_result->cert);
+                    if (isset($cert['certificate']['subject']['CommonName'])) {
+                        $subject = $cert['certificate']['subject']['CommonName'];
+                    } elseif (isset($cert['certificate']['subject']['Email'])) {
+                        $subject = $cert['certificate']['subject']['Email'];
+                    } elseif (isset($sig_result->email)) {
+                        $subject = $sig_result->email;
+                    } elseif (isset($smime_from)) {
+                        $subject = $smime_from;
+                    } else {
+                        $subject = null;
+                    }
+
+                    if (!empty($subject) &&
+                        $GLOBALS['registry']->hasMethod('contacts/addField') &&
+                        $GLOBALS['prefs']->getValue('add_source')) {
+                        $status[] = sprintf(_("The S/MIME certificate of %s: "), @htmlspecialchars($subject, ENT_COMPAT, Horde_Nls::getCharset())) . $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', _("View"), array('params' => array('mode' => IMP_Contents::RENDER_INLINE, 'view_smime_key' => 1))) . '/' . Horde::link('#', '', null, null, $this->_impsmime->savePublicKeyURL($sig_result->cert, $this->_params['contents']->getUid(), $sig_id) . ' return false;') . _("Save in your Address Book") . '</a>';
+                    }
+                }
             } catch (Horde_Exception $e) {
-                $ret[$base_id]['status'][0]['icon'] = ($e->getCode() == 'horde.warning')
+                self::$_cache[$base_id]['status'][0]['icon'] = ($e->getCode() == 'horde.warning')
                     ? Horde::img('alerts/warning.png', _("Warning"), null, $graphicsdir)
                     : Horde::img('alerts/error.png', _("Error"), null, $graphicsdir);
+                self::$_cache[$base_id]['wrap'] = 'mimePartWrapInvalid';
                 $status[] = $e->getMessage();
-                return $ret;
             }
         } else {
             switch ($_SESSION['imp']['view']) {
             case 'imp':
-                $status[] = Horde::link(Horde_Util::addParameter(IMP::selfUrl(), 'smime_verify_msg', 1)) . _("Click HERE to verify the message.") . '</a>';
+                $status[] = Horde::link(Horde_Util::addParameter(IMP::selfUrl(), 'smime_verify_msg', 1)) . _("Click HERE to verify the data.") . '</a>';
                 break;
 
             case 'dimp':
-                $status[] = Horde::link('#', '', 'smimeVerifyMsg') . _("Click HERE to verify the message.") . '</a>';
+                $status[] = Horde::link('#', '', 'smimeVerifyMsg') . _("Click HERE to verify the data.") . '</a>';
                 break;
             }
-            return $ret;
+        }
+
+        if ($sig_only) {
+            return;
         }
 
         $subpart = $this->_params['contents']->getMIMEPart($sig_id);
-        if (!isset($subpart)) {
+        if (empty($subpart)) {
             try {
                 $msg_data = $this->_impsmime->extractSignedContents($raw_text);
-                $subpart = Horde_Mime_Part::parseMessage($msg_data);
+                $subpart = Horde_Mime_Part::parseMessage($msg_data, array('forcemime' => true));
             } catch (Horde_Exception $e) {
                 $this->_status[] = $e->getMessage();
-                $subpart = $this->_mimepart;
+                return null;
             }
         }
 
-        $graphicsdir = $GLOBALS['registry']->getImageDir('horde');
-
-        $ret[$base_id]['status'][0]['icon'] = Horde::img('alerts/success.png', _("Success"), null, $graphicsdir);
+        return $subpart;
+    }
 
-        /* This message has been verified but there was no output
-         * from the PGP program. */
-        if (empty($sig_result->result) || ($sig_result->result === true)) {
-            $email = (is_array($sig_result->email))
-                ? implode(', ', $sig_result->email)
-                : $sig_result->email;
-            $status[] = sprintf(_("The message has been verified. Sender: %s."), htmlspecialchars($email));
+    /**
+     * Generates HTML output for the S/MIME key.
+     *
+     * @return string  The HTML output.
+     */
+    protected function _outputSmimeKey()
+    {
+        if (empty($this->_impsmime)) {
+            return array();
         }
 
-        if (!empty($sig_result->cert)) {
-            $cert_details = $this->_impsmime->parseCert($sig_result->cert);
-            if (isset($cert_details['certificate']['subject']['CommonName'])) {
-                $subject = $cert_details['certificate']['subject']['CommonName'];
-            } elseif (isset($cert_details['certificate']['subject']['Email'])) {
-                $subject = $cert_details['certificate']['subject']['Email'];
-            } elseif (isset($sig_result->email)) {
-                $subject = $sig_result->email;
-            } elseif (isset($smime_from)) {
-                $subject = $smime_from;
-            } else {
-                $subject = null;
-            }
+        $raw_text = $this->_mimepart->getMimeId()
+            ? $this->_params['contents']->getBodyPart($this->_mimepart->getMimeId(), array('mimeheaders' => true, 'stream' => true))
+            : $this->_params['contents']->fullMessageText();
 
-            if (!empty($subject) &&
-                $GLOBALS['registry']->hasMethod('contacts/addField') &&
-                $GLOBALS['prefs']->getValue('add_source')) {
-                $status[] = sprintf(_("The S/MIME certificate of %s: "), @htmlspecialchars($subject, ENT_COMPAT, Horde_Nls::getCharset())) . $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', _("View"), array('params' => array('mode' => IMP_Contents::RENDER_INLINE, 'view_smime_key' => 1))) . '/' . Horde::link('#', '', null, null, $this->_impsmime->savePublicKeyURL($sig_result->cert, $this->_params['contents']->getUid(), $sig_id) . ' return false;') . _("Save in your Address Book") . '</a>';
-            }
+        try {
+            $sig_result = $this->_impsmime->verifySignature($this->_mimepart->replaceEOL($raw_text, Horde_Mime_Part::RFC_EOL));
+        } catch (Horde_Exception $e) {
+            return array();
         }
 
-        return $ret;
+        return array(
+            $this->_mimepart->getMimeId() => array(
+                'data' => $this->_impsmime->certToHTML($sig_result->cert),
+                'status' => array(),
+                'type' => 'text/html; charset=' . Horde_Nls::getCharset()
+            )
+        );
     }
 
-    /**
-     * Generates output for encrypted S/MIME parts.
-     *
-     * @return array  TODO
-     */
-    protected function _outputSMIMEEncrypted()
-    {
-        $id = $this->_mimepart->getMimeId();
-        return isset(self::$_inlinecache[$id])
-            ? self::$_inlinecache[$id]
-            : array();
-    }
 }
index ece85ed..7572f28 100644 (file)
@@ -574,10 +574,18 @@ div.mimeStatusMessage, div.mimePartInfo {
     white-space: nowrap;
 }
 
-.mimePartWrap {
+.mimePartWrap, .mimePartWrapValid, .mimePartWrapInvalid {
     border: 2px solid #666;
     margin: 2px 0;
     padding: 4px;
+    -moz-border-radius: 4px;
+    -webkit-border-radius: 4px;
+}
+.mimePartWrapValid {
+    border-color: #cfc;
+}
+.mimePartWrapInvalid {
+    border-color: #ffd0af;
 }
 
 .downloadAtc, .downloadZipAtc, .saveImgAtc, .closeImg, .deleteImg, .foldersImg, .searchuiImg, .reloadImg {