Tweak for some opaque S/MIME messages.
[gpgol.git] / src / message.cpp
index a6eae5d..00d6727 100644 (file)
@@ -1,5 +1,5 @@
 /* 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.
  * 
@@ -30,6 +30,7 @@
 #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", \
@@ -45,18 +46,24 @@ ul_release (LPVOID punk, const char *func)
   if (!punk)
     return;
   res = UlRelease (punk);
-  log_debug ("%s:%s: UlRelease(%p) had %lu references\n", 
-             SRCNAME, func, punk, res);
+  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, msgtype_t msgtype)
+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: 
@@ -64,21 +71,25 @@ message_incoming_handler (LPMESSAGE message, msgtype_t msgtype)
          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 viewd a second time all has
-         been set.  */
-      if (!mapi_has_sig_status (message))
+         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__);
-          mapi_change_message_class (message);
+          if (mapi_change_message_class (message, 0))
+            goto retry;
         }
       break;
     case MSGTYPE_SMIME:
-      if (opt.enable_smime)
+      if (pass == 1 && opt.enable_smime)
         {
           log_debug ("%s:%s: message class not checked with smime enabled "
                      "- doing now\n", SRCNAME, __func__);
-          mapi_change_message_class (message);
+          if (mapi_change_message_class (message, 0))
+            goto retry;
         }
       break;
     case MSGTYPE_GPGOL:
@@ -89,36 +100,36 @@ message_incoming_handler (LPMESSAGE message, msgtype_t msgtype)
       log_debug ("%s:%s: processing multipart signed message\n", 
                  SRCNAME, __func__);
       retval = true;
-      message_verify (message, msgtype, 0);
+      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);
+      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);
+      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);
+      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);
+      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);
+      message_decrypt (message, msgtype, 0, hwnd);
       break;
     }
 
@@ -131,6 +142,7 @@ message_incoming_handler (LPMESSAGE message, msgtype_t msgtype)
 bool
 message_display_handler (LPEXCHEXTCALLBACK eecb, HWND hwnd)
 {
+  int err;
   HRESULT hr;
   LPMESSAGE message = NULL;
   LPMDB mdb = NULL;
@@ -142,18 +154,25 @@ message_display_handler (LPEXCHEXTCALLBACK eecb, HWND hwnd)
     {
       /* (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 winodw we now use the OOM display always.  */
-      body = mapi_get_gpgol_body_attachment (message, NULL, 
-                                             &ishtml, &wasprotected);
-      if (body)
-        update_display (hwnd, /*wasprotected? NULL:*/ eecb, ishtml, body);
+         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
-        update_display (hwnd, NULL, 0, 
-                        _("[Crypto operation failed - "
-                          "can't show the body of the message]"));
+        {
+          put_outlook_property (eecb, "GpgOLStatus", "?");
+          update_display (hwnd, NULL, 0, 
+                          _("[Crypto operation failed - "
+                            "can't show the body of the message]"));
+        }
       xfree (body);
   
-      /*  put_outlook_property (eecb, "EncryptedStatus", "MyStatus"); */
     }
   else
     log_debug_w32 (hr, "%s:%s: error getting message", SRCNAME, __func__);
@@ -165,6 +184,43 @@ message_display_handler (LPEXCHEXTCALLBACK eecb, HWND hwnd)
 }
 
 
+/* 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.  */
@@ -180,44 +236,53 @@ message_wipe_body_cruft (LPEXCHEXTCALLBACK eecb)
   hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
   if (SUCCEEDED (hr))
     {
-      if (mapi_has_last_decrypted (message))
+      switch (mapi_get_message_type (message))
         {
-          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++;
+        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;
 
-          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__); 
-            }
-        }  
-      else
-        log_debug_w32 (hr, "%s:%s: error getting message", 
-                       SRCNAME, __func__);
-     
+        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__);
     }
@@ -238,12 +303,12 @@ message_show_info (LPMESSAGE message, HWND hwnd)
   buflen = strlen (msgcls) + strlen (sigstat) + strlen (mimeinfo) + 200;
   buffer = (char*)xmalloc (buflen+1);
   snprintf (buffer, buflen, 
-            _("Message class: %s\n"
-              "Sig Status   : %s\n"
-              "Structure of the message:\n"
+            _("Signature status: %s\n"
+              "Message class ..: %s\n"
+              "MIME structure .:\n"
               "%s"), 
-            msgcls,
             sigstat,
+            msgcls,
             mimeinfo);
   
   MessageBox (hwnd, buffer, _("GpgOL - Message Information"),
@@ -255,9 +320,15 @@ message_show_info (LPMESSAGE message, HWND hwnd)
 }
 
 
+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 PS?MIME signed
+/* 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.  */
@@ -435,46 +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_SMIME:
     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_test_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)
@@ -494,6 +586,38 @@ message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
         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
     {
       /* PGP/MIME or S/MIME stuff.  */
@@ -533,8 +657,11 @@ message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
         }
     }
 
-  err = mime_verify (protocol, inbuf, inbuflen, message, 0, 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];
@@ -542,6 +669,8 @@ 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)
@@ -553,25 +682,167 @@ message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
   else
     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 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;
   protocol_t protocol;
+  LPATTACH saved_attach = NULL;
+  int need_saved_attach = 0;
+  int need_rfc822_parser = 0;
 
   switch (msgtype)
     {
@@ -581,6 +852,8 @@ message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
     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;
@@ -597,16 +870,61 @@ message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
 
   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)
@@ -615,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,
@@ -656,13 +985,11 @@ message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
         {
           /* 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)
@@ -696,7 +1023,31 @@ 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__);
@@ -705,24 +1056,44 @@ message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
           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 (protocol, cipherstream, message, 0, 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;
 }
@@ -842,9 +1213,9 @@ sign_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd, int signflag)
   else
     {
       if (signflag)
-        err = mime_sign_encrypt (message, protocol, recipients);
+        err = mime_sign_encrypt (message, hwnd, protocol, recipients);
       else
-        err = mime_encrypt (message, protocol, recipients);
+        err = mime_encrypt (message, hwnd, protocol, recipients);
       if (err)
         {
           char buf[200];
@@ -865,7 +1236,7 @@ message_sign (LPMESSAGE message, protocol_t protocol, HWND hwnd)
 {
   gpg_error_t err;
 
-  err = mime_sign (message, protocol);
+  err = mime_sign (message, hwnd, protocol);
   if (err)
     {
       char buf[200];