Tweak for some opaque S/MIME messages.
[gpgol.git] / src / message.cpp
index c803ac6..00d6727 100644 (file)
@@ -1,22 +1,20 @@
 /* message.cpp - Functions for message handling
- *     Copyright (C) 2006, 2007 g10 Code GmbH
+ *     Copyright (C) 2006, 2007, 2008 g10 Code GmbH
  * 
  * This file is part of GpgOL.
  * 
  * GpgOL is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * version 2.1 of the License, or (at your option) any later version.
  * 
  * GpgOL is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  * GNU Lesser General Public License for more details.
  * 
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #ifdef HAVE_CONFIG_H
@@ -31,6 +29,8 @@
 #include "mapihelp.h"
 #include "mimeparser.h"
 #include "mimemaker.h"
+#include "display.h"
+#include "ol-ext-callback.h"
 #include "message.h"
 
 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
                         } while (0)
 
 
-/* Convert the clear signed message from INPUT into a PS?MIME signed
+static void 
+ul_release (LPVOID punk, const char *func)
+{
+  ULONG res;
+  
+  if (!punk)
+    return;
+  res = UlRelease (punk);
+  if (opt.enable_debug & DBG_MEMORY)
+    log_debug ("%s:%s: UlRelease(%p) had %lu references\n", 
+               SRCNAME, func, punk, res);
+}
+
+
+/* A helper function used by OnRead and OnOpen to dispatch the
+   message.  Returns true if the message has been processed.  */
+bool
+message_incoming_handler (LPMESSAGE message, HWND hwnd)
+{
+  bool retval = false;
+  msgtype_t msgtype;
+  int pass = 0;
+
+ retry:
+  pass++;
+  msgtype = mapi_get_message_type (message);
+  switch (msgtype)
+    {
+    case MSGTYPE_UNKNOWN: 
+      /* If this message has never passed our change message class
+         code it won't have an unknown msgtype _and_ no sig status
+         flag.  Thus we look at the message class now and change it if
+         required.  It won't get displayed correctly right away but a
+         latter decrypt command or when viewed a second time all has
+         been set.  Note that we should have similar code for some
+         message classes in GpgolUserEvents:OnSelectionChange; but
+         tehre are a couiple of problems.  */
+      if (pass == 1 && !mapi_has_sig_status (message))
+        {
+          log_debug ("%s:%s: message class not yet checked - doing now\n",
+                     SRCNAME, __func__);
+          if (mapi_change_message_class (message, 0))
+            goto retry;
+        }
+      break;
+    case MSGTYPE_SMIME:
+      if (pass == 1 && opt.enable_smime)
+        {
+          log_debug ("%s:%s: message class not checked with smime enabled "
+                     "- doing now\n", SRCNAME, __func__);
+          if (mapi_change_message_class (message, 0))
+            goto retry;
+        }
+      break;
+    case MSGTYPE_GPGOL:
+      log_debug ("%s:%s: ignoring unknown message of original SMIME class\n",
+                 SRCNAME, __func__);
+      break;
+    case MSGTYPE_GPGOL_MULTIPART_SIGNED:
+      log_debug ("%s:%s: processing multipart signed message\n", 
+                 SRCNAME, __func__);
+      retval = true;
+      message_verify (message, msgtype, 0, hwnd);
+      break;
+    case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
+      log_debug ("%s:%s: processing multipart encrypted message\n",
+                 SRCNAME, __func__);
+      retval = true;
+      message_decrypt (message, msgtype, 0, hwnd);
+      break;
+    case MSGTYPE_GPGOL_OPAQUE_SIGNED:
+      log_debug ("%s:%s: processing opaque signed message\n", 
+                 SRCNAME, __func__);
+      retval = true;
+      message_verify (message, msgtype, 0, hwnd);
+      break;
+    case MSGTYPE_GPGOL_CLEAR_SIGNED:
+      log_debug ("%s:%s: processing clear signed pgp message\n", 
+                 SRCNAME, __func__);
+      retval = true;
+      message_verify (message, msgtype, 0, hwnd);
+      break;
+    case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
+      log_debug ("%s:%s: processing opaque encrypted message\n",
+                 SRCNAME, __func__);
+      retval = true;
+      message_decrypt (message, msgtype, 0, hwnd);
+      break;
+    case MSGTYPE_GPGOL_PGP_MESSAGE:
+      log_debug ("%s:%s: processing pgp message\n", SRCNAME, __func__);
+      retval = true;
+      message_decrypt (message, msgtype, 0, hwnd);
+      break;
+    }
+
+  return retval;
+}
+
+
+/* Common Code used by OnReadComplete and OnOpenComplete to display a
+   modified message.   Returns true if the message was encrypted.  */
+bool
+message_display_handler (LPEXCHEXTCALLBACK eecb, HWND hwnd)
+{
+  int err;
+  HRESULT hr;
+  LPMESSAGE message = NULL;
+  LPMDB mdb = NULL;
+  int ishtml, wasprotected = false;
+  char *body;
+  
+  hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
+  if (SUCCEEDED (hr))
+    {
+      /* (old: If the message was protected we don't allow a fallback to the
+         OOM display methods.)  Now: As it is difficult to find the
+         actual window we now use the OOM display always.  */
+      err = mapi_get_gpgol_body_attachment (message, &body, NULL, 
+                                            &ishtml, &wasprotected);
+      if (!err && body)
+        {
+          put_outlook_property (eecb, "GpgOLStatus", 
+                                mapi_get_sig_status (message));
+
+          update_display (hwnd, /*wasprotected? NULL:*/ eecb, ishtml, body);
+        }
+      else
+        {
+          put_outlook_property (eecb, "GpgOLStatus", "?");
+          update_display (hwnd, NULL, 0, 
+                          _("[Crypto operation failed - "
+                            "can't show the body of the message]"));
+        }
+      xfree (body);
+  
+    }
+  else
+    log_debug_w32 (hr, "%s:%s: error getting message", SRCNAME, __func__);
+
+  ul_release (message, __func__);
+  ul_release (mdb, __func__);
+
+  return !!wasprotected;
+}
+
+
+/* Helper for message_wipe_body_cruft.  */
+static void
+do_wipe_body (LPMESSAGE message)
+{
+  HRESULT hr;
+  SPropTagArray proparray;
+  int anyokay = 0;
+  
+  proparray.cValues = 1;
+  proparray.aulPropTag[0] = PR_BODY;
+  hr = message->DeleteProps (&proparray, NULL);
+  if (hr)
+    log_debug_w32 (hr, "%s:%s: deleting PR_BODY failed", SRCNAME, __func__);
+  else
+    anyokay++;
+            
+  proparray.cValues = 1;
+  proparray.aulPropTag[0] = PR_BODY_HTML;
+  message->DeleteProps (&proparray, NULL);
+  if (hr)
+    log_debug_w32 (hr, "%s:%s: deleting PR_BODY_HTML failed", 
+                   SRCNAME, __func__);
+  else
+    anyokay++;
+  
+  if (anyokay)
+    {
+      hr = message->SaveChanges (KEEP_OPEN_READWRITE);
+      if (hr)
+        log_error_w32 (hr, "%s:%s: SaveChanges failed", SRCNAME, __func__); 
+      else
+        log_debug ("%s:%s: SaveChanges succeded; body cruft removed",
+                   SRCNAME, __func__); 
+    }
+}
+
+
+/* If the current message is an encrypted one remove the body
+   properties which might have come up due to OL internal
+   syncronization and a failing olDiscard feature.  */
+void
+message_wipe_body_cruft (LPEXCHEXTCALLBACK eecb)
+{
+  
+  HRESULT hr;
+  LPMESSAGE message = NULL;
+  LPMDB mdb = NULL;
+      
+  log_debug ("%s:%s: enter", SRCNAME, __func__);
+  hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
+  if (SUCCEEDED (hr))
+    {
+      switch (mapi_get_message_type (message))
+        {
+        case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
+        case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
+          {
+            if (mapi_has_last_decrypted (message))
+              do_wipe_body (message);
+            else
+              log_debug_w32 (hr, "%s:%s: "
+                             "error getting message decryption status", 
+                             SRCNAME, __func__);
+          }
+          break;
+
+        case MSGTYPE_GPGOL_PGP_MESSAGE:
+          {
+            /* In general we can't delete the body of a message if it
+               is an inline PGP encrypted message because the body
+               holds the ciphertext.  However, while decrypting, we
+               take a copy of the body and work on that in future; if
+               this has been done we can delete the body.  */
+            mapi_attach_item_t *table;
+            int found = 0;
+            int tblidx;
+
+            table = mapi_create_attach_table (message, 0);
+            if (table)
+              {
+                for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
+                  if (table[tblidx].attach_type == ATTACHTYPE_PGPBODY
+                      && table[tblidx].filename 
+                      && !strcmp (table[tblidx].filename, PGPBODYFILENAME))
+                    {
+                      found = 1;
+                      break;
+                    }
+              }
+            mapi_release_attach_table (table);
+            if (found)
+              do_wipe_body (message);
+          }
+          break;
+
+        default: 
+          break;
+        }
+      
+      ul_release (message, __func__);
+      ul_release (mdb, __func__);
+    }
+}
+
+
+
+/* Display some information about MESSAGE.  */
+void
+message_show_info (LPMESSAGE message, HWND hwnd)
+{
+  char *msgcls = mapi_get_message_class (message);
+  char *sigstat = mapi_get_sig_status (message);
+  char *mimeinfo = mapi_get_mime_info (message);
+  size_t buflen;
+  char *buffer;
+
+  buflen = strlen (msgcls) + strlen (sigstat) + strlen (mimeinfo) + 200;
+  buffer = (char*)xmalloc (buflen+1);
+  snprintf (buffer, buflen, 
+            _("Signature status: %s\n"
+              "Message class ..: %s\n"
+              "MIME structure .:\n"
+              "%s"), 
+            sigstat,
+            msgcls,
+            mimeinfo);
+  
+  MessageBox (hwnd, buffer, _("GpgOL - Message Information"),
+              MB_ICONINFORMATION|MB_OK);
+  xfree (buffer);
+  xfree (mimeinfo);
+  xfree (sigstat);
+  xfree (msgcls);
+}
+
+
+static void
+show_message (HWND hwnd, const char *text)
+{
+  MessageBox (hwnd, text, _("GpgOL"), MB_ICONINFORMATION|MB_OK);
+}
+
+
+\f
+/* Convert the clear signed message from INPUT into a PGP/MIME signed
    message and return it in a new allocated buffer.  OUTPUTLEN
    received the valid length of that buffer; the buffer is guarnateed
    to be Nul terminated.  */
@@ -216,44 +506,67 @@ pgp_mime_from_clearsigned (LPSTREAM input, size_t *outputlen)
 }
 
 
-
 /* Verify MESSAGE and update the attachments as required.  MSGTYPE
    should be the type of the message so that the fucntion can decide
    what to do.  With FORCE set the verification is done regardlessless
    of a cached signature result. */
 int
-message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
+message_verify (LPMESSAGE message, msgtype_t msgtype, int force, HWND hwnd)
 {
+  HRESULT hr;
   mapi_attach_item_t *table = NULL;
+  LPSTREAM opaquestream = NULL;
   int moss_idx = -1;
   int i;
-  char *inbuf;
-  size_t inbuflen;
+  char *inbuf = NULL;
+  size_t inbuflen = 0;
+  protocol_t protocol = PROTOCOL_UNKNOWN;
   int err;
 
   switch (msgtype)
     {
     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
-      break;
     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
-      log_debug ("Opaque signed message are not yet supported!");
-      return 0;
     case MSGTYPE_GPGOL_CLEAR_SIGNED:
       break;
     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
     case MSGTYPE_GPGOL_PGP_MESSAGE:
+      log_debug ("%s:%s: message of type %d not expected",
+                 SRCNAME, __func__, msgtype);
+      if (force)
+        show_message (hwnd, _("Signature verification of an encrypted message "
+                              "is not possible."));
       return -1; /* Should not be called for such a message.  */
-    case MSGTYPE_UNKNOWN:
     case MSGTYPE_GPGOL:
+    case MSGTYPE_SMIME:
+    case MSGTYPE_UNKNOWN:
+      log_debug ("%s:%s: message of type %d ignored", 
+                 SRCNAME, __func__, msgtype);
+      if (!force)
+        ;
+      else if (msgtype == MSGTYPE_GPGOL)
+        show_message (hwnd, _("Signature verification of this "
+                              "message class is not possible."));
+      else if (msgtype == MSGTYPE_SMIME)
+        show_message (hwnd, _("Signature verification of this "
+                              "S/MIME message is not possible.  Please check "
+                              "that S/MIME processing has been enabled."));
+      else
+        show_message (hwnd, _("This message has no signature."));
       return 0; /* Nothing to do.  */
     }
   
   /* If a verification is forced, we set the cached signature status
-     first to "?" to mark that no verification has yet happened. */
+     first to "?" to mark that no verification has yet happened.  If a
+     verification status has been set and the body attachment is
+     available we don't do a verification again.  The need to check
+     for the body attachment is to avoid problems if that attachment
+     has accidently be deleted. */
   if (force)
     mapi_set_sig_status (message, "?");
-  else if (mapi_has_sig_status (message))
+  else if (mapi_test_sig_status (message) 
+           && !mapi_get_gpgol_body_attachment (message, NULL,NULL,NULL,NULL))
     return 0; /* Already checked that message.  */
 
   if (msgtype == MSGTYPE_GPGOL_CLEAR_SIGNED)
@@ -271,6 +584,39 @@ message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
       rawstream->Release ();
       if (!inbuf)
         return -1;
+      protocol = PROTOCOL_OPENPGP;
+    }
+  else if (msgtype == MSGTYPE_GPGOL_OPAQUE_SIGNED)
+    {
+      /* S/MIME opaque signed message: The data is expected to be in
+         an attachment.  */
+      table = mapi_create_attach_table (message, 0);
+      if (!table)
+        return -1; /* No attachment - this should not happen.  */
+
+      for (i=0; !table[i].end_of_table; i++)
+        if (table[i].content_type               
+            && (!strcmp (table[i].content_type, "application/pkcs7-mime")
+                || !strcmp (table[i].content_type,
+                            "application/x-pkcs7-mime"))
+            && table[i].filename
+            && !strcmp (table[i].filename, "smime.p7m"))
+          break;
+      if (table[i].end_of_table)
+        {
+          log_debug ("%s:%s: attachment for opaque signed S/MIME not found",
+                     SRCNAME, __func__);
+          mapi_release_attach_table (table);
+          return -1;
+        }
+
+      opaquestream = mapi_get_attach_as_stream (message, table+i, NULL);
+      if (!opaquestream)
+        {
+          mapi_release_attach_table (table);
+          return -1; /* Problem getting the attachment.  */
+        }
+      protocol = PROTOCOL_SMIME;
     }
   else
     {
@@ -311,9 +657,11 @@ message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
         }
     }
 
-  err = mime_verify (inbuf, inbuflen, message, 0, 
-                     opt.passwd_ttl, NULL, NULL, 0);
-  log_debug ("mime_verify returned %d", err);
+  if (opaquestream)
+    err = mime_verify_opaque (protocol, opaquestream, message, hwnd, 0);
+  else
+    err = mime_verify (protocol, inbuf, inbuflen, message, hwnd, 0);
+  log_debug ("mime_verify%s returned %d", opaquestream? "_opaque":"", err);
   if (err)
     {
       char buf[200];
@@ -321,41 +669,191 @@ message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
       snprintf (buf, sizeof buf, "Verify failed (%s)", gpg_strerror (err));
       MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
     }
+  if (opaquestream)
+    opaquestream->Release ();
   xfree (inbuf);
                     
   if (err)
-    mapi_set_sig_status (message, gpg_strerror (err));
+    {
+      char buf[200];
+      snprintf (buf, sizeof buf, "- %s", gpg_strerror (err));
+      mapi_set_sig_status (message, gpg_strerror (err));
+    }
   else
-    mapi_set_sig_status (message, "Signature was good");
+    mapi_set_sig_status (message, "! Good signature");
+
+  hr = message->SaveChanges (KEEP_OPEN_READWRITE);
+  if (hr)
+    log_error_w32 (hr, "%s:%s: SaveChanges failed",
+                   SRCNAME, __func__); 
+
 
   mapi_release_attach_table (table);
   return 0;
 }
 
+
+/* Copy the MAPI body to a PGPBODY type attachment. */
+static int
+pgp_body_to_attachment (LPMESSAGE message)
+{
+  HRESULT hr;
+  LPSTREAM instream;
+  ULONG newpos;
+  LPATTACH newatt = NULL;
+  SPropValue prop;
+  LPSTREAM outstream = NULL;
+  LPUNKNOWN punk;
+
+  instream = mapi_get_body_as_stream (message);
+  if (!instream)
+    return -1;
+  
+  hr = message->CreateAttach (NULL, 0, &newpos, &newatt);
+  if (hr)
+    {
+      log_error ("%s:%s: can't create attachment: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto leave;
+    }
+
+  prop.ulPropTag = PR_ATTACH_METHOD;
+  prop.Value.ul = ATTACH_BY_VALUE;
+  hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+  if (hr)
+    {
+      log_error ("%s:%s: can't set attach method: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto leave;
+    }
+
+  /* Mark that attachment so that we know why it has been created.  */
+  if (get_gpgolattachtype_tag (message, &prop.ulPropTag) )
+    goto leave;
+  prop.Value.l = ATTACHTYPE_PGPBODY;
+  hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);       
+  if (hr)
+    {
+      log_error ("%s:%s: can't set %s property: hr=%#lx\n",
+                 SRCNAME, __func__, "GpgOL Attach Type", hr); 
+      goto leave;
+    }
+
+  prop.ulPropTag = PR_ATTACHMENT_HIDDEN;
+  prop.Value.b = TRUE;
+  hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+  if (hr)
+    {
+      log_error ("%s:%s: can't set hidden attach flag: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto leave;
+    }
+
+  prop.ulPropTag = PR_ATTACH_FILENAME_A;
+  prop.Value.lpszA = PGPBODYFILENAME;
+  hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+  if (hr)
+    {
+      log_error ("%s:%s: can't set attach filename: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto leave;
+    }
+
+  punk = (LPUNKNOWN)outstream;
+  hr = newatt->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 0,
+                             MAPI_CREATE|MAPI_MODIFY, &punk);
+  if (FAILED (hr)) 
+    {
+      log_error ("%s:%s: can't create output stream: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto leave;
+    }
+  outstream = (LPSTREAM)punk;
+
+  /* Insert a blank line so that our mime parser skips over the mail
+     headers.  */
+  hr = outstream->Write ("\r\n", 2, NULL);
+  if (hr)
+    {
+      log_error ("%s:%s: Write failed: hr=%#lx", SRCNAME, __func__, hr);
+      goto leave;
+    }
+
+  {
+    ULARGE_INTEGER cb;
+    cb.QuadPart = 0xffffffffffffffffll;
+    hr = instream->CopyTo (outstream, cb, NULL, NULL);
+  }
+  if (hr)
+    {
+      log_error ("%s:%s: can't copy streams: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto leave;
+    }
+  hr = outstream->Commit (0);
+  if (hr)
+    {
+      log_error ("%s:%s: Commiting output stream failed: hr=%#lx",
+                 SRCNAME, __func__, hr);
+      goto leave;
+    }
+  outstream->Release ();
+  outstream = NULL;
+  hr = newatt->SaveChanges (0);
+  if (hr)
+    {
+      log_error ("%s:%s: SaveChanges of the attachment failed: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto leave;
+    }
+  newatt->Release ();
+  newatt = NULL;
+  hr = message->SaveChanges (KEEP_OPEN_READWRITE);
+  if (hr)
+    log_error ("%s:%s: SaveChanges failed: hr=%#lx\n", SRCNAME, __func__, hr); 
+
+ leave:
+  if (outstream)
+    {
+      outstream->Revert ();
+      outstream->Release ();
+    }
+  if (newatt)
+    newatt->Release ();
+  instream->Release ();
+  return hr? -1:0;
+}
+
+
 /* Decrypt MESSAGE, check signature and update the attachments as
    required.  MSGTYPE should be the type of the message so that the
-   function can decide what to do.  With FORCE set the verification is
-   done regardlessless of a cached signature result - hmmm, should we
-   such a thing for an encrypted message? */
+   function can decide what to do.  With FORCE set the decryption is
+   done regardless whether it has already been done.  */
 int
-message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
+message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force, HWND hwnd)
 {
   mapi_attach_item_t *table = NULL;
-  int part2_idx;
+  int part1_idx, part2_idx;
   int tblidx;
   int retval = -1;
   LPSTREAM cipherstream;
   gpg_error_t err;
   int is_opaque = 0;
-  int is_smime = 0;
+  protocol_t protocol;
+  LPATTACH saved_attach = NULL;
+  int need_saved_attach = 0;
+  int need_rfc822_parser = 0;
 
   switch (msgtype)
     {
     case MSGTYPE_UNKNOWN:
+    case MSGTYPE_SMIME:
     case MSGTYPE_GPGOL:
     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
     case MSGTYPE_GPGOL_CLEAR_SIGNED:
+      if (force)
+        show_message (hwnd, _("This message is not encrypted.")); 
       return -1; /* Should not have been called for this.  */
     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
       break;
@@ -366,17 +864,67 @@ message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
       break;
     }
   
+  if (!force && mapi_test_last_decrypted (message))
+    return 0; /* Already decrypted this message once during this
+                 session.  No need to do it again. */
+
   if (msgtype == MSGTYPE_GPGOL_PGP_MESSAGE)
     {
-      /* PGP messages are special:  All is contained in the body and thus
-         there is no requirement for an attachment.  */
-      cipherstream = mapi_get_body_as_stream (message);
+      /* PGP messages are special: All is contained in the body and
+         thus there would be no requirement for an attachment.
+         However, due to problems with Outlook overwriting the body of
+         the message after decryption, we need to save the body away
+         before decrypting it.  We then always look for that original
+         body atatchment and create one if it does not exist.  */
+      part1_idx = -1;
+      table = mapi_create_attach_table (message, 0);
+      if (!table)
+        ;
+      else
+        {
+          for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
+            if (table[tblidx].attach_type == ATTACHTYPE_PGPBODY
+                && table[tblidx].filename 
+                && !strcmp (table[tblidx].filename, PGPBODYFILENAME))
+              {
+                part1_idx = tblidx;
+                break;
+              }
+        }
+      if (part1_idx == -1)
+        {
+          mapi_release_attach_table (table);
+          if (pgp_body_to_attachment (message))
+            table = NULL;
+          else
+            table = mapi_create_attach_table (message, 0);
+          if (table)
+            {
+              for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
+                if (table[tblidx].attach_type == ATTACHTYPE_PGPBODY
+                    && table[tblidx].filename 
+                    && !strcmp (table[tblidx].filename, PGPBODYFILENAME))
+                  {
+                    part1_idx = tblidx;
+                    break;
+                  }
+            }
+        }
+      if (!table || part1_idx == -1)
+        {
+          log_debug ("%s:%s: problem copying the PGP inline encrypted message",
+                     SRCNAME, __func__);
+          goto leave;
+        }
+      cipherstream = mapi_get_attach_as_stream (message, table+part1_idx,
+                                                NULL);
       if (!cipherstream)
-        goto leave;
+        goto leave; /* Problem getting the attachment.  */
+      protocol = PROTOCOL_OPENPGP;
+      need_rfc822_parser = 1;
     }
   else
     {
-  
       /* PGP/MIME or S/MIME stuff.  */
       table = mapi_create_attach_table (message, 0);
       if (!table)
@@ -385,13 +933,24 @@ message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
       if (is_opaque)
         {
           /* S/MIME opaque encrypted message: We expect one
-             attachment.  As we don't know ether we are called the
+             attachment.  As we don't know wether we are called the
              first time, we first try to find this attachment by
              looking at all attachments.  Only if this fails we
              identify it by its order.  */
           part2_idx = -1;
           for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
-            if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
+            if (table[tblidx].attach_type == ATTACHTYPE_MOSSTEMPL)
+              {
+                /* This attachment has been generated by us in the
+                   course of sending a new message.  The content will
+                   be multipart/signed because we used this to trick
+                   out OL.  We stop here and use this part for further
+                   processing.  */
+                part2_idx = tblidx;
+                need_rfc822_parser = 1;
+                break;
+              }
+            else if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
               {
                 if (part2_idx == -1 && table[tblidx].content_type 
                     && (!strcmp (table[tblidx].content_type,
@@ -420,19 +979,17 @@ message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
                          SRCNAME, __func__);
               goto leave;
             }
-          is_smime = 1;
+          protocol = PROTOCOL_SMIME;
         }
       else 
         {
           /* Multipart/encrypted message: We expect 2 attachments.
              The first one with the version number and the second one
-             with the ciphertext.  As we don't know ether we are
+             with the ciphertext.  As we don't know wether we are
              called the first time, we first try to find these
              attachments by looking at all attachments.  Only if this
              fails we identify them by their order (i.e. the first 2
              attachments) and mark them as part1 and part2.  */
-          int part1_idx;
-          
           part1_idx = part2_idx = -1;
           for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
             if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
@@ -466,33 +1023,77 @@ message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
                   mapi_mark_moss_attach (message, table+part2_idx);
                 }
             }
-          if (part1_idx == -1 || part2_idx == -1)
+
+
+          if (part1_idx == -1 || part2_idx == -1 
+              && !table[0].end_of_table && table[1].end_of_table
+              && table[0].attach_type == ATTACHTYPE_MOSS
+              && table[0].filename 
+              && !strcmp (table[0].filename, MIMEATTACHFILENAME))
+            {
+              /* This is likely a PGP/MIME created by us.  Due to the
+                 way we created that message, the MAPI derived content
+                 type is wrong and there is only one attachment
+                 (gpgolXXX.dat).  We simply assume that it is PGP/MIME
+                 encrypted and pass it on to the mime parser.  We also
+                 keep the attachment open so that we can later set it
+                 to hidden if not yet done.  I can't remember whether
+                 it is possible to set the hidden attribute when
+                 creating the message - probably not.  Thus we take
+                 care of it here. */
+              log_debug ("%s:%s: "
+                         "assuming self-created PGP/MIME encrypted message",
+                         SRCNAME, __func__);
+              part2_idx = 0;
+              need_saved_attach = 1;
+            }
+          else if (part1_idx == -1 || part2_idx == -1)
             {
               log_debug ("%s:%s: this is not a PGP/MIME encrypted message",
                          SRCNAME, __func__);
               goto leave;
             }
+          protocol = PROTOCOL_OPENPGP;
         }
       
-      cipherstream = mapi_get_attach_as_stream (message, table+part2_idx);
+      cipherstream = mapi_get_attach_as_stream (message, table+part2_idx,
+                                                need_saved_attach? 
+                                                &saved_attach : NULL );
       if (!cipherstream)
         goto leave; /* Problem getting the attachment.  */
     }
 
-  err = mime_decrypt (cipherstream, message, is_smime, opt.passwd_ttl,
-                      NULL, NULL, 0);
+  err = mime_decrypt (protocol, cipherstream, message, 
+                      need_rfc822_parser, hwnd, 0);
   log_debug ("mime_decrypt returned %d (%s)", err, gpg_strerror (err));
   if (err)
     {
       char buf[200];
       
-      snprintf (buf, sizeof buf, "Decryption failed (%s)", gpg_strerror (err));
-      MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
+      switch (gpg_err_code (err))
+        {
+        case GPG_ERR_NO_DATA:
+          /* The UI server already displayed a message.  */
+          break;
+        default:
+          snprintf (buf, sizeof buf,
+                    _("Decryption failed\n(%s)"), gpg_strerror (err));
+          MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
+          break;
+        }
+    }
+  else
+    {
+      if (saved_attach)
+        mapi_set_attach_hidden (saved_attach);
     }
   cipherstream->Release ();
   retval = 0;
 
+
  leave:
+  if (saved_attach)
+    saved_attach->Release ();
   mapi_release_attach_table (table);
   return retval;
 }
@@ -595,183 +1196,74 @@ release_recipient_array (char **recipients)
 
 
 
-
-
-\f
-#if 0
-/* Sign the current message. Returns 0 on success. */
-int
-message_sign (LPMESSAGE message, HWND hwnd, int want_html)
+static int
+sign_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd, int signflag)
 {
-  HRESULT hr;
-  STATSTG statinfo;
-  LPSTREAM plaintext;
-  size_t plaintextlen;
-  char *signedtext = NULL;
-  int err = 0;
-  gpgme_key_t sign_key = NULL;
-  SPropValue prop;
-  int have_html_attach = 0;
-
-  log_debug ("%s:%s: enter message=%p\n", SRCNAME, __func__, message);
-  
-  /* We don't sign an empty body - a signature on a zero length string
-     is pretty much useless.  We assume that a HTML message always
-     comes with a text/plain alternative. */
-  plaintext = mapi_get_body_as_stream (message);
-  if (!plaintext)
-    plaintextlen = 0;
-  else
-    {
-      hr = input->Stat (&statinfo, STATFLAG_NONAME);
-      if (hr)
-        {
-          log_debug ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
-          plaintext->Release ();
-          return gpg_error (GPG_ERR_GENERAL);
-        }
-      plaintextlen = (size_t)statinfo.cbSize.QuadPart;
-    }
+  gpg_error_t err;
+  char **recipients;
 
-  if ( !plaintextlen && !has_attachments (message)) 
+  recipients = get_recipients (message);
+  if (!recipients || !recipients[0])
     {
-      log_debug ("%s:%s: leave (empty)", SRCNAME, __func__);
-      plaintext->Release ();
-      return 0; 
-    }
+      MessageBox (hwnd, _("No recipients to encrypt to are given"),
+                  "GpgOL", MB_ICONERROR|MB_OK);
 
-  /* Pop up a dialog box to ask for the signer of the message. */
-  if (signer_dialog_box (&sign_key, NULL, 0) == -1)
-    {
-      log_debug ("%s.%s: leave (dialog failed)\n", SRCNAME, __func__);
-      plaintext->Release ();
-      return gpg_error (GPG_ERR_CANCELED);  
+      err = gpg_error (GPG_ERR_GENERAL);
     }
-
-  /* Sign the plaintext */
-  if (plaintextlen)
+  else
     {
-      err = op_sign (plaintext, &signedtext, 
-                     OP_SIG_CLEAR, sign_key, opt.passwd_ttl);
+      if (signflag)
+        err = mime_sign_encrypt (message, hwnd, protocol, recipients);
+      else
+        err = mime_encrypt (message, hwnd, protocol, recipients);
       if (err)
         {
-          MessageBox (hwnd, op_strerror (err),
-                      _("Signing Failure"), MB_ICONERROR|MB_OK);
-          plaintext->Release ();
-          return gpg_error (GPG_ERR_GENERAL);
+          char buf[200];
+          
+          snprintf (buf, sizeof buf,
+                    _("Encryption failed (%s)"), gpg_strerror (err));
+          MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
         }
     }
+  release_recipient_array (recipients);
+  return err;
+}
 
-  
-  /* If those brain dead html mails are requested we now figure out
-     whether a HTML body is actually available and move it to an
-     attachment so that the code below will sign it as a regular
-     attachments.  */
-  if (want_html)
-    {
-      log_debug ("Signing HTML is not yet supported\n");
-//       char *htmltext = loadBody (true);
-      
-//       if (htmltext && *htmltext)
-//         {
-//           if (!createHtmlAttachment (htmltext))
-//             have_html_attach = 1;
-//         }
-//       xfree (htmltext);
-
-      /* If we got a new attachment we need to release the loaded
-         attachment info so that the next getAttachment call will read
-         fresh info. */
-//       if (have_html_attach)
-//         free_attach_info ();
-    }
 
+/* Sign the MESSAGE.  */
+int 
+message_sign (LPMESSAGE message, protocol_t protocol, HWND hwnd)
+{
+  gpg_error_t err;
 
-  /* Note, there is a side-effect when we have HTML mails: The
-     auto-sign-attch option is ignored.  I regard auto-sign-attach as a
-     silly option anyway. */
-  if ((opt.auto_sign_attach || have_html_attach) && has_attachments ())
+  err = mime_sign (message, hwnd, protocol);
+  if (err)
     {
-      unsigned int n;
+      char buf[200];
       
-      n = getAttachments ();
-      log_debug ("%s:%s: message has %u attachments\n", SRCNAME, __func__, n);
-      for (unsigned int i=0; i < n; i++) 
-        signAttachment (hwnd, i, sign_key, opt.passwd_ttl);
-      /* FIXME: we should throw an error if signing of any attachment
-         failed. */
+      snprintf (buf, sizeof buf,
+                _("Signing failed (%s)"), gpg_strerror (err));
+      MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
     }
-
-  set_x_header (message, "GPGOL-VERSION", PACKAGE_VERSION);
-
-  /* Now that we successfully processed the attachments, we can save
-     the changes to the body.  */
-  if (plaintextlen)
-    {
-      err = set_message_body (message, signedtext, 0);
-      if (err)
-        goto leave;
-
-      /* In case we don't have attachments, Outlook will really insert
-         the following content type into the header.  We use this to
-         declare that the encrypted content of the message is utf-8
-         encoded.  If we have atatchments, OUtlook has its own idea of
-         the content type to use.  */
-      prop.ulPropTag=PR_CONTENT_TYPE_A;
-      prop.Value.lpszA="text/plain; charset=utf-8"; 
-      hr = HrSetOneProp (message, &prop);
-      if (hr)
-        log_error ("%s:%s: can't set content type: hr=%#lx\n",
-                   SRCNAME, __func__, hr);
-    }
-  
-  hr = message->SaveChanges (KEEP_OPEN_READWRITE|FORCE_SAVE);
-  if (hr)
-    {
-      log_error ("%s:%s: SaveChanges(message) failed: hr=%#lx\n",
-                 SRCNAME, __func__, hr); 
-      err = gpg_error (GPG_ERR_GENERAL);
-      goto leave;
-    }
-
- leave:
-  xfree (signedtext);
-  gpgme_key_release (sign_key);
-  xfree (plaintext);
-  log_debug ("%s:%s: leave (err=%s)\n", SRCNAME, __func__, op_strerror (err));
   return err;
 }
 
-#endif
 
 
 /* Encrypt the MESSAGE.  */
 int 
-message_encrypt (LPMESSAGE message, HWND hwnd)
+message_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd)
 {
-  gpg_error_t err;
-  char **recipients;
+  return sign_encrypt (message, protocol, hwnd, 0);
+}
 
-  recipients = get_recipients (message);
-  if (!recipients || !recipients[0])
-    {
-      MessageBox (hwnd, _("No recipients for encrypted message given"),
-                  "GpgOL", MB_ICONERROR|MB_OK);
 
-      err = gpg_error (GPG_ERR_GENERAL);
-    }
-  else
-    {
-      err = mime_encrypt (message, PROTOCOL_OPENPGP, recipients);
-      if (err)
-        {
-          char buf[200];
-          
-          snprintf (buf, sizeof buf,
-                    _("Encryption failed (%s)"), gpg_strerror (err));
-          MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
-        }
-    }
-  release_recipient_array (recipients);
-  return err;
+/* Sign+Encrypt the MESSAGE.  */
+int 
+message_sign_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd)
+{
+  return sign_encrypt (message, protocol, hwnd, 1);
 }
+
+
+