The new framework for creating messages is now in place.
authorWerner Koch <wk@gnupg.org>
Fri, 7 Sep 2007 15:19:08 +0000 (15:19 +0000)
committerWerner Koch <wk@gnupg.org>
Fri, 7 Sep 2007 15:19:08 +0000 (15:19 +0000)
33 files changed:
NEWS
configure.ac
po/de.po
po/sv.po
src/ChangeLog
src/Makefile.am
src/common.c
src/common.h
src/display.cpp
src/ext-commands.cpp
src/ext-commands.h
src/gpgmsg.cpp
src/gpgmsg.hh
src/main.c
src/mapihelp.cpp
src/mapihelp.h
src/message-events.cpp
src/message-events.h
src/message.cpp
src/mimemaker.c [new file with mode: 0644]
src/mimemaker.h [new file with mode: 0644]
src/mimeparser.c
src/mimeparser.h
src/mymapi.h
src/ol-ext-callback.cpp
src/olflange.cpp
src/pgpmime.c
src/pgpmime.h
src/property-sheets.cpp
src/rfc822parse.c
src/session-events.cpp
src/util.h
src/watcher.cpp

diff --git a/NEWS b/NEWS
index 7460526..5283a9e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-Noteworthy changes for version 0.9.92
+Noteworthy changes for version 0.10.0 
 ==================================================
 
 CURRENTLY UNDER HEAVY DEVELOPMENT - DO NOT USE. 
index 054cedb..b6652e7 100644 (file)
@@ -1,5 +1,5 @@
-# configure.ac - for GPGol
-# Copyright (C) 2005, 2006 g10 Code GmbH
+# configure.ac - for GpgOL
+# Copyright (C) 2005, 2006, 2007 g10 Code GmbH
 #
 # This file is free software; as a special exception the author gives
 # unlimited permission to copy and/or distribute it, with or without
@@ -16,7 +16,7 @@ min_automake_version="1.9.4"
 # Remember to change the version number immediately *after* a release.
 # Set my_issvn to "yes" for non-released code.  Remember to run an
 # "svn up" and "autogen.sh" right before creating a distribution.
-m4_define([my_version], [0.9.92])
+m4_define([my_version], [0.10.0])
 m4_define([my_issvn], [yes])
 
 m4_define([svn_revision], m4_esyscmd([echo -n $( (svn info 2>/dev/null \
index daece03..f35a58f 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: GPGol 0.9.4\n"
 "Report-Msgid-Bugs-To: bug-gpgol@g10code.com\n"
-"POT-Creation-Date: 2007-08-21 23:43+0200\n"
+"POT-Creation-Date: 2007-09-06 21:27+0200\n"
 "PO-Revision-Date: 2007-04-13 12:55+0200\n"
 "Last-Translator: Werner Koch <wk@gnupg.org>\n"
 "Language-Team: de\n"
@@ -15,7 +15,7 @@ msgstr ""
 "Content-Type: text/plain; charset=utf-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: src/common.c:131
+#: src/common.c:136
 msgid "GPG - Save decrypted attachment"
 msgstr "GPG - Sichern der entschlüsselten Anlage"
 
@@ -287,7 +287,7 @@ msgstr "Unterschrifterstellungsfehler eines Anhangs"
 msgid "The default key may not contain any spaces."
 msgstr "Der Standardschlüssel darf keine Leerzeichen enthalten."
 
-#: src/olflange.cpp:479
+#: src/olflange.cpp:475
 msgid ""
 "This version of Outlook is too old!\n"
 "\n"
@@ -316,7 +316,7 @@ msgstr "Ungültige Passphrase; bitte nochmal versuchen..."
 msgid "Select Signing Key"
 msgstr "Signaturschlüssel auswählen"
 
-#: src/pgpmime.c:356
+#: src/pgpmime.c:358
 msgid ""
 "Error creating file\n"
 "Please select another one"
@@ -324,27 +324,27 @@ msgstr ""
 "Fehler bei der Erstellung der Datei.\n"
 "Bitte wählen Sie eine anderen Namen."
 
-#: src/pgpmime.c:358 src/pgpmime.c:509
+#: src/pgpmime.c:360 src/pgpmime.c:511
 msgid "I/O-Error"
 msgstr "Ein-/Ausgabefehler"
 
-#: src/pgpmime.c:508
+#: src/pgpmime.c:510
 msgid "Error writing file"
 msgstr "Dateischreibfehler"
 
-#: src/pgpmime.c:584
+#: src/pgpmime.c:586
 msgid "[PGP/MIME message]"
 msgstr "[PGP/MIME Nachricht]"
 
-#: src/pgpmime.c:605
+#: src/pgpmime.c:607
 msgid "[PGP/MIME message without plain text body]"
 msgstr "[PGP/MIME Nachricht ohne reinen Textkörper]"
 
-#: src/pgpmime.c:680
+#: src/pgpmime.c:682
 msgid "[PGP/MIME signed message without a plain text body]"
 msgstr "[PGP/MIME signierte Nachricht ohne reinen Textkörper]"
 
-#: src/pgpmime.c:692
+#: src/pgpmime.c:694
 msgid "[PGP/MIME signature]"
 msgstr "[PGP/MIME Signatur]"
 
index 97907cd..1b4a342 100644 (file)
--- a/po/sv.po
+++ b/po/sv.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: GPGol\n"
 "Report-Msgid-Bugs-To: bug-gpgol@g10code.com\n"
-"POT-Creation-Date: 2007-08-21 23:43+0200\n"
+"POT-Creation-Date: 2007-09-06 21:27+0200\n"
 "PO-Revision-Date: 2006-12-12 23:52+0100\n"
 "Last-Translator: Daniel Nylander <po@danielnylander.se>\n"
 "Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
@@ -15,7 +15,7 @@ msgstr ""
 "Content-Type: text/plain; charset=utf-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: src/common.c:131
+#: src/common.c:136
 msgid "GPG - Save decrypted attachment"
 msgstr "GPG - Spara dekrypterad bilaga"
 
@@ -281,7 +281,7 @@ msgstr "Signering av bilaga misslyckades"
 msgid "The default key may not contain any spaces."
 msgstr "Standardnyckeln får inte innehålla några blanksteg."
 
-#: src/olflange.cpp:479
+#: src/olflange.cpp:475
 msgid ""
 "This version of Outlook is too old!\n"
 "\n"
@@ -309,7 +309,7 @@ msgstr "Ogiltig lösenfras; försök igen..."
 msgid "Select Signing Key"
 msgstr "Välj signeringsnyckel"
 
-#: src/pgpmime.c:356
+#: src/pgpmime.c:358
 msgid ""
 "Error creating file\n"
 "Please select another one"
@@ -317,27 +317,27 @@ msgstr ""
 "Fel vid skapande av fil\n"
 "Välj en annan"
 
-#: src/pgpmime.c:358 src/pgpmime.c:509
+#: src/pgpmime.c:360 src/pgpmime.c:511
 msgid "I/O-Error"
 msgstr "In-/Ut-fel"
 
-#: src/pgpmime.c:508
+#: src/pgpmime.c:510
 msgid "Error writing file"
 msgstr "Fel vid skrivning av fil"
 
-#: src/pgpmime.c:584
+#: src/pgpmime.c:586
 msgid "[PGP/MIME message]"
 msgstr "[PGP/MIME-meddelande]"
 
-#: src/pgpmime.c:605
+#: src/pgpmime.c:607
 msgid "[PGP/MIME message without plain text body]"
 msgstr "[PGP/MIME-meddelande utan vanlig meddelandetext]"
 
-#: src/pgpmime.c:680
+#: src/pgpmime.c:682
 msgid "[PGP/MIME signed message without a plain text body]"
 msgstr "[PGP/MIME-signerat meddelande utan vanlig meddelandetext]"
 
-#: src/pgpmime.c:692
+#: src/pgpmime.c:694
 msgid "[PGP/MIME signature]"
 msgstr "[PGP/MIME-signatur]"
 
index badbe01..4b91341 100644 (file)
@@ -1,3 +1,60 @@
+2007-09-07  Werner Koch  <wk@g10code.com>
+
+       * common.c (qp_decode): Handle softe line breaks. 
+
+2007-09-06  Werner Koch  <wk@g10code.com>
+
+       * mapihelp.cpp (mapi_get_body): New.
+
+       * mimeparser.c (protocol_t): Move to .. common.h.
+
+2007-09-05  Werner Koch  <wk@g10code.com>
+
+       * mimemaker.c, mimemaker.h: New.
+
+2007-08-31  Werner Koch  <wk@g10code.com>
+
+       * mapihelp.cpp (mapi_set_header): New.
+       * message.cpp (pgp_mime_from_clearsigned): New.
+       (message_verify): Use it.
+       * main.c: Call srand ().
+       * util.h (trailing_ws_p): New.
+       * common.c (generate_boundary): New.
+
+2007-08-30  Werner Koch  <wk@g10code.com>
+
+       * message-events.h (class GpgolMessageEvents): Rename M_IS_SMIME
+       to M_PROCESSED.
+       * message-events.cpp (OnRead, OnReadComplete): Ditto.
+       (OnReadComplete): Remove preview decryption stuff.
+       (OnWriteComplete): Remove GpgMsg based code.
+       * ext-commands.cpp (DoCommand): Ditto
+       (InstallCommands): Do not init the watcher.
+       * olflange.cpp (GpgolExt): Do not init the watcher.
+       (GpgolExt): Do not clanup the watcher.
+       * main.c (DllMain): Ditto.
+       * gpgmsg.cpp, gpgmsg.hh: Remove.
+       * watcher.cpp: Remove
+       * pgpmime.c, pgpmime.h: Remove.
+
+       * mapihelp.h (MSGTYPE_GPGOL_CLEAR_SIGNED)
+       (MSGTYPE_GPGOL_PGP_MESSAGE): New.
+       * mapihelp.cpp (mapi_get_message_type): Map them.
+       (get_msgcls_from_pgp_lines): New.
+       (mapi_change_message_class): Detect old old style PGP messages.
+       (mapi_get_body_as_stream): New.
+       * message.cpp (message_verify, message_decrypt): Handle them.
+       * message-events.cpp (OnRead): Ditto.
+
+2007-08-29  Werner Koch  <wk@g10code.com>
+
+       * ext-commands.h (class GpgolExtCommands): Add members
+       M_NCMDKEYMANAGER and M_NCMDDECRYPT.
+       * ext-commands.cpp (GpgolExtCommands): Initialize it.
+       (InstallCommands): Use them here instead of reusing another variable.
+       (DoCommand, Help, QueryHelpText, QueryButtonInfo): Restructured
+       for better readibility.
+
 2007-08-21  Werner Koch  <wk@g10code.com>
 
        * w32-gettext.c (SUBLANG_BENGALI_BANGLADESH): Fix to 2 as per MSDN.
index 81fc24e..c30e57f 100644 (file)
@@ -31,9 +31,8 @@ gpgol_SOURCES = \
        myexchext.h                 \
        display.cpp display.h       \
        message.cpp message.h       \
-       gpgmsg.cpp gpgmsg.hh        \
-       pgpmime.c pgpmime.h         \
        mimeparser.c mimeparser.h   \
+       mimemaker.c mimemaker.h     \
        msgcache.c msgcache.h       \
         engine-gpgme.c engine.h    \
        rfc822parse.c rfc822parse.h \
@@ -48,7 +47,6 @@ gpgol_SOURCES = \
        mymapi.h  mymapitags.h      \
        serpent.c serpent.h         \
         vasprintf.c                 \
-       watcher.cpp \
        ext-commands.cpp ext-commands.h       \
        session-events.cpp  session-events.h  \
        message-events.cpp  message-events.h  \
index d3ff477..b12b230 100644 (file)
 HINSTANCE glob_hinst = NULL;
 
 
+/* The base-64 list used for base64 encoding. */
+static unsigned char bintoasc[64+1] = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
+                                       "abcdefghijklmnopqrstuvwxyz" 
+                                       "0123456789+/"); 
+
 /* The reverse base-64 list used for base-64 decoding. */
 static unsigned char const asctobin[256] = {
   0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
@@ -443,12 +448,29 @@ qp_decode (char *buffer, size_t length)
   char *d, *s;
 
   for (s=d=buffer; length; length--)
-    if (*s == '=' && length > 2 && hexdigitp (s+1) && hexdigitp (s+2))
+    if (*s == '=')
       {
-        s++;
-        *(unsigned char*)d++ = xtoi_2 (s);
-        s += 2;
-        length -= 2;
+        if (length > 2 && hexdigitp (s+1) && hexdigitp (s+2))
+          {
+            s++;
+            *(unsigned char*)d++ = xtoi_2 (s);
+            s += 2;
+            length -= 2;
+          }
+        else if (length > 2 && s[1] == '\r' && s[2] == '\n')
+          {
+            /* Soft line break.  */
+            s += 3;
+            length -= 2;
+          }
+        else if (length > 1 && s[1] == '\n')
+          {
+            /* Soft line break with only a Unix line terminator. */
+            s += 2;
+            length -= 1;
+          }
+        else
+          *d++ = *s++;
       }
     else
       *d++ = *s++;
@@ -532,3 +554,30 @@ b64_decode (b64_state_t *state, char *buffer, size_t length)
   return d - buffer;
 }
 
+
+/* Create a boundary.  Note that mimemaker.c knows about the structure
+   of the boundary (i.e. that it starts with "=-=") so that it can
+   protect against accidently used boundaries within the content.  */
+char *
+generate_boundary (char *buffer)
+{
+  char *p = buffer;
+  int i;
+
+#if RAND_MAX < (64*2*BOUNDARYSIZE)
+#error RAND_MAX is way too small
+#endif
+
+  *p++ = '=';
+  *p++ = '-';
+  *p++ = '=';
+  for (i=0; i < BOUNDARYSIZE-6; i++) 
+    *p++ = bintoasc[rand () % 64];
+  *p++ = '=';
+  *p++ = '-';
+  *p++ = '=';
+  *p = 0;
+
+  return buffer;
+}
+
index d93aff2..4edefbe 100644 (file)
@@ -34,6 +34,15 @@ extern "C" {
 #endif
 #endif
 
+/* Identifiers for the protocol.  */
+typedef enum
+  {
+    PROTOCOL_UNKNOWN = 0,
+    PROTOCOL_OPENPGP,
+    PROTOCOL_SMIME
+  }
+protocol_t;
+
 
 /* Possible options for the recipient dialog. */
 enum
@@ -140,11 +149,11 @@ size_t qp_decode (char *buffer, size_t length);
 void b64_init (b64_state_t *state);
 size_t b64_decode (b64_state_t *state, char *buffer, size_t length);
 
+/* The length of the boundary - the buffer needs to be allocated one
+   byte larger. */
+#define BOUNDARYSIZE 20
+char *generate_boundary (char *buffer);
 
-/*-- watcher.cpp --*/
-int watcher_init_hook (void);
-int watcher_free_hook (void);
-void watcher_set_callback_ctx (void *cb);
 
 /*-- recipient-dialog.c --*/
 unsigned int recipient_dialog_box (gpgme_key_t **ret_rset);
index 8cd3c2c..1e5f6b4 100644 (file)
@@ -271,7 +271,7 @@ set_message_body (LPMESSAGE message, const char *string, bool is_html)
   /* Note: we once tried to delete the RTF property here to avoid any
      syncing mess and more important to make sure that no RTF rendered
      plaintext is left over.  The side effect of this was that the
-     entire PR_BODY go deleted too. */
+     entire PR_BODY got deleted too. */
 
   return 0;
 }
index d7c6f31..ad7f6bd 100644 (file)
@@ -30,7 +30,6 @@
 #include "myexchext.h"
 #include "common.h"
 #include "display.h"
-#include "gpgmsg.hh"
 #include "msgcache.h"
 #include "engine.h"
 #include "mapihelp.h"
@@ -81,9 +80,13 @@ GpgolExtCommands::GpgolExtCommands (GpgolExt* pParentInterface)
   m_lRef = 0; 
   m_lContext = 0; 
   m_nCmdEncrypt = 0;  
+  m_nCmdDecrypt = 0;  
   m_nCmdSign = 0; 
   m_nCmdShowInfo = 0;  
   m_nCmdCheckSig = 0;
+  m_nCmdKeyManager = 0;
+  m_nCmdDebug1 = 0;
+  m_nCmdDebug2 = 0;
   m_nToolbarButtonID1 = 0; 
   m_nToolbarButtonID2 = 0; 
   m_nToolbarBitmap1 = 0;
@@ -299,19 +302,30 @@ GpgolExtCommands::InstallCommands (
 
   if (m_lContext == EECONTEXT_READNOTEMESSAGE)
     {
-      if (opt.compat.auto_decrypt)
-        watcher_set_callback_ctx ((void *)pEECB);
 
-      if (m_pExchExt->getMsgtype (pEECB) == MSGTYPE_UNKNOWN)
-        toolbar_add_menu (pEECB, pnCommandIDBase, "", NULL,
-                          _("&Decrypt and verify message"), &m_nCmdEncrypt,
-                          NULL);
-      else
-        toolbar_add_menu (pEECB, pnCommandIDBase, "", NULL,
-                          _("&Display S/MIME information"), &m_nCmdShowInfo,
-                          NULL);
+      switch (m_pExchExt->getMsgtype (pEECB))
+        {
+        case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
+        case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
+        case MSGTYPE_GPGOL_PGP_MESSAGE:
+          toolbar_add_menu (pEECB, pnCommandIDBase, "", NULL,
+                            _("&Decrypt and verify message"), &m_nCmdDecrypt,
+                            NULL);
+          break;
+        default:
+          break;
+        }
+
+      /* We always enable the verify button as it might be useful on
+         an already decryopted message. */
       toolbar_add_menu (pEECB, pnCommandIDBase,
-                        _("Check &signature"), &m_nCmdCheckSig,
+                        _("&Verify signature"), &m_nCmdCheckSig,
+                        _("&Display crypto information"), &m_nCmdShowInfo,
+                        NULL);
+
+      toolbar_add_menu (pEECB, pnCommandIDBase, "", NULL,
+                        _("Debug-1 (open_inspector)"), &m_nCmdDebug1,
+                        _("Debug-2 (n/a)"), &m_nCmdDebug2,
                         NULL);
       
       hwnd_toolbar = toolbar_from_tbe (pTBEArray, nTBECnt, &tb_idx);
@@ -365,7 +379,7 @@ GpgolExtCommands::InstallCommands (
   if (m_lContext == EECONTEXT_VIEWER) 
     {
       toolbar_add_menu (pEECB, pnCommandIDBase, "", NULL,
-                        _("GPG Key &Manager"), &m_nCmdEncrypt,
+                        _("GPG Key &Manager"), &m_nCmdKeyManager,
                         NULL);
 
       hwnd_toolbar = toolbar_from_tbe (pTBEArray, nTBECnt, &tb_idx);
@@ -392,9 +406,16 @@ GpgolExtCommands::DoCommand (
                   UINT nCommandID)         // The command id.
 {
   HRESULT hr;
+  HWND hWnd = NULL;
+  LPMESSAGE message = NULL;
+  LPMDB mdb = NULL;
+      
+  if (FAILED (pEECB->GetWindow (&hWnd)))
+    hWnd = NULL;
+
+  log_debug ("%s:%s: commandID=%u (%#x) hwnd=%p\n",
+             SRCNAME, __func__, nCommandID, nCommandID, hWnd);
 
-  log_debug ("%s:%s: commandID=%u (%#x)\n",
-             SRCNAME, __func__, nCommandID, nCommandID);
   if (nCommandID == SC_CLOSE && m_lContext == EECONTEXT_READNOTEMESSAGE)
     {
       /* This is the system close command. Replace it with our own to
@@ -429,7 +450,7 @@ GpgolExtCommands::DoCommand (
                      SRCNAME, __func__, hr);
         }
 
-      /* We are not interested in the close command - pass it on. */
+      /* Closing on our own failed - pass it on. */
       return S_FALSE; 
     }
   else if (nCommandID == 154)
@@ -438,79 +459,86 @@ GpgolExtCommands::DoCommand (
       /* What we might want to do is to call Reply, then GetInspector
          and then Activate - this allows us to get full control over
          the quoted message and avoids the ugly msgcache. */
+      return S_FALSE; /* Pass it on.  */
     }
   else if (nCommandID == 155)
     {
       log_debug ("%s:%s: command ReplyAll called\n", SRCNAME, __func__);
+      return S_FALSE; /* Pass it on.  */
     }
   else if (nCommandID == 156)
     {
       log_debug ("%s:%s: command Forward called\n", SRCNAME, __func__);
+      return S_FALSE; /* Pass it on.  */
     }
-  
-
-
-  if (nCommandID != m_nCmdEncrypt
-      && nCommandID != m_nCmdSign
-      && nCommandID != m_nCmdShowInfo
-      && nCommandID != m_nCmdCheckSig)
-    return S_FALSE;
-  
-
-  if (m_lContext == EECONTEXT_READNOTEMESSAGE) 
+  else if (nCommandID == m_nCmdEncrypt 
+           && m_lContext == EECONTEXT_READNOTEMESSAGE)
     {
-      HWND hWnd = NULL;
-      LPMESSAGE message = NULL;
-      LPMDB mdb = NULL;
-
-      if (FAILED (pEECB->GetWindow (&hWnd)))
-        hWnd = NULL;
-//       else
-//         log_window_hierarchy (hWnd, "%s:%s:%d: Windows hierarchy:",
-//                               SRCNAME, __func__, __LINE__);
-
       hr = pEECB->GetObject (&mdb, (LPMAPIPROP *)&message);
       if (SUCCEEDED (hr))
         {
-          if (nCommandID == m_nCmdEncrypt)
-            {
-              GpgMsg *m = CreateGpgMsg (message);
-              m->setExchangeCallback ((void*)pEECB);
-              m->decrypt (hWnd, 0);
-              delete m;
-           }
-          else if (nCommandID == m_nCmdShowInfo)
-            {
-              MessageBox (NULL, 
-                          _("Here you should see S/MIME related info"),
-                          "GpgOL", MB_ICONINFORMATION|MB_OK);
-            }
-          else if (nCommandID == m_nCmdCheckSig)
-            {
-              message_verify (message, m_pExchExt->getMsgtype (pEECB), 1);
-            }
-
+//           GpgMsg *m = CreateGpgMsg (message);
+//           m->setExchangeCallback ((void*)pEECB);
+//           m->decrypt (hWnd, 0);
+//           delete m;
+       }
+      ul_release (message);
+      ul_release (mdb);
+    }
+  else if (nCommandID == m_nCmdShowInfo
+           && m_lContext == EECONTEXT_READNOTEMESSAGE)
+    {
+      MessageBox (NULL, 
+                  _("Here you should see crypto related info"),
+                  "GpgOL", MB_ICONINFORMATION|MB_OK);
+    }
+  else if (nCommandID == m_nCmdCheckSig
+           && m_lContext == EECONTEXT_READNOTEMESSAGE)
+    {
+      hr = pEECB->GetObject (&mdb, (LPMAPIPROP *)&message);
+      if (SUCCEEDED (hr))
+        {
+          message_verify (message, m_pExchExt->getMsgtype (pEECB), 1);
        }
       ul_release (message);
       ul_release (mdb);
     }
-  else if (m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
+  else if (nCommandID == m_nCmdEncrypt
+           && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
+    {
+      m_pExchExt->m_gpgEncrypt = !m_pExchExt->m_gpgEncrypt;
+    }
+  else if (nCommandID == m_nCmdSign
+           && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
     {
-      if (nCommandID == m_nCmdEncrypt)
-        m_pExchExt->m_gpgEncrypt = !m_pExchExt->m_gpgEncrypt;
-      if (nCommandID == m_nCmdSign)
-        m_pExchExt->m_gpgSign = !m_pExchExt->m_gpgSign;
+      m_pExchExt->m_gpgSign = !m_pExchExt->m_gpgSign;
     }
-  else if (m_lContext == EECONTEXT_VIEWER)
+  else if (nCommandID == m_nCmdKeyManager
+           && m_lContext == EECONTEXT_VIEWER)
     {
       if (start_key_manager ())
         MessageBox (NULL, _("Could not start Key-Manager"),
                     "GpgOL", MB_ICONERROR|MB_OK);
     }
+  else if (nCommandID == m_nCmdDebug1
+           && m_lContext == EECONTEXT_READNOTEMESSAGE)
+    {
+      hr = pEECB->GetObject (&mdb, (LPMAPIPROP *)&message);
+      if (SUCCEEDED (hr))
+        {
+          open_inspector (pEECB, message);
+       }
+      ul_release (message);
+      ul_release (mdb);
+
+    }
+  else
+    return S_FALSE; /* Pass on unknown command. */
 
   return S_OK; 
 }
 
+
 /* Called by Exchange when it receives a WM_INITMENU message, allowing
    the extension object to enable, disable, or update its menu
    commands before the user sees them. This method is called
@@ -525,238 +553,212 @@ GpgolExtCommands::InitMenu(LPEXCHEXTCALLBACK pEECB)
 }
 
 
-/* Called by Exhange when the user requests help for a menu item.
-   Return value: S_OK when it is a menu item of this plugin and the
-   help was shown; otherwise S_FALSE.  */
+/* Called by Exchange when the user requests help for a menu item.
+   PEECP is the pointer to Exchange Callback Interface.  NCOMMANDID is
+   the command id.  Return value: S_OK when it is a menu item of this
+   plugin and the help was shown; otherwise S_FALSE.  */
 STDMETHODIMP 
-GpgolExtCommands::Help (
-       LPEXCHEXTCALLBACK pEECB, // The pointer to Exchange Callback Interface.
-       UINT nCommandID)         // The command id.
+GpgolExtCommands::Help (LPEXCHEXTCALLBACK pEECB, UINT nCommandID)
 {
-  if (m_lContext == EECONTEXT_READNOTEMESSAGE) 
+  if (nCommandID == m_nCmdDecrypt && 
+      m_lContext == EECONTEXT_READNOTEMESSAGE) 
     {
-      if (nCommandID == m_nCmdEncrypt) 
-        {
-          MessageBox (m_hWnd,
-                      _("Decrypt and verify the message."),
-                      "GpgOL", MB_OK);
-          return S_OK;
-       }
-      else if (nCommandID == m_nCmdShowInfo) 
-        {
-          MessageBox (m_hWnd,
-                      _("Show information about the S/MIME message status"),
-                      "GpgOL", MB_OK);
-          return S_OK;
-       }
-      else if (nCommandID == m_nCmdCheckSig) 
-        {
-          MessageBox (m_hWnd,
-                      _("Check the signature now and display the result"),
-                      "GpgOL", MB_OK);
-          return S_OK;
-       }
+      MessageBox (m_hWnd,
+                  _("Select this option to decrypt and verify the message."),
+                  "GpgOL", MB_OK);
     }
-  else if (m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
+  else if (nCommandID == m_nCmdShowInfo
+           && m_lContext == EECONTEXT_READNOTEMESSAGE) 
     {
-      if (nCommandID == m_nCmdEncrypt)
-        {
-          MessageBox (m_hWnd,
-                      _("Select this option to encrypt the message."),
-                      "GpgOL", MB_OK); 
-          return S_OK;
-       } 
-      else if (nCommandID == m_nCmdSign) 
-        {
-          MessageBox (m_hWnd,
-                      _("Select this option to sign the message."),
-                      "GpgOL", MB_OK); 
-          return S_OK;
-       } 
+      MessageBox (m_hWnd,
+                  _("Select this option to show information"
+                    " on the crypto status"),
+                  "GpgOL", MB_OK);
     }
-  else if (m_lContext == EECONTEXT_VIEWER) 
+  else if (nCommandID == m_nCmdCheckSig
+           && m_lContext == EECONTEXT_READNOTEMESSAGE) 
     {
-      if (nCommandID == m_nCmdEncrypt) 
-        {
-          MessageBox (m_hWnd, 
-                      _("Open GPG Key Manager"),
-                      "GpgOL", MB_OK);
-          return S_OK;
-       } 
+      MessageBox (m_hWnd,
+                  _("Check the signature now and display the result"),
+                  "GpgOL", MB_OK);
     }
-  
-  return S_FALSE;
+  else if (nCommandID == m_nCmdEncrypt 
+           && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
+    {
+      MessageBox (m_hWnd,
+                  _("Select this option to encrypt the message."),
+                  "GpgOL", MB_OK);     
+    } 
+  else if (nCommandID == m_nCmdSign
+           && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
+    {
+      MessageBox (m_hWnd,
+                  _("Select this option to sign the message."),
+                  "GpgOL", MB_OK);     
+    }
+  else if (nCommandID == m_nCmdKeyManager
+           && m_lContext == EECONTEXT_VIEWER) 
+    {
+      MessageBox (m_hWnd, _("Select this option to open GpgOL Key Manager"),
+                  "GpgOL", MB_OK);
+    }
+  else
+    return S_FALSE;
+
+  return S_OK;
 }
 
 
 /* Called by Exchange to get the status bar text or the tooltip of a
-   menu item.  Returns S_OK when it is a menu item of this plugin and
-   the text was set; otherwise S_FALSE. */
+   menu item.  NCOMMANDID is the command id corresponding to the menu
+   item activated.  LFLAGS identifies either EECQHT_STATUS or
+   EECQHT_TOOLTIP.  PSZTEXT is a pointer to buffer to received the
+   text to be displayed.  NCHARCNT is the available space in PSZTEXT.
+
+   Returns S_OK when it is a menu item of this plugin and the text was
+   set; otherwise S_FALSE.  */
 STDMETHODIMP 
-GpgolExtCommands::QueryHelpText(
-          UINT nCommandID,  // The command id corresponding to the
-                            //  menu item activated.
-         ULONG lFlags,     // Identifies either EECQHT_STATUS
-                            //  or EECQHT_TOOLTIP.
-          LPTSTR pszText,   // A pointer to buffer to be populated 
-                            //  with text to display.
-         UINT nCharCnt)    // The count of characters available in psz buffer.
+GpgolExtCommands::QueryHelpText(UINT nCommandID, ULONG lFlags,
+                                LPTSTR pszText,  UINT nCharCnt)    
 {
        
-  if (m_lContext == EECONTEXT_READNOTEMESSAGE) 
+  if (nCommandID == m_nCmdDecrypt
+      && m_lContext == EECONTEXT_READNOTEMESSAGE) 
     {
-      if (nCommandID == m_nCmdEncrypt)
-        {
-          if (lFlags == EECQHT_STATUS)
-            lstrcpyn (pszText, ".", nCharCnt);
-          if (lFlags == EECQHT_TOOLTIP)
-            lstrcpyn (pszText,
-                      _("Decrypt message and verify signature"),
-                      nCharCnt);
-          return S_OK;
-       }
-      else if (nCommandID == m_nCmdShowInfo)
-        {
-          if (lFlags == EECQHT_STATUS)
-            lstrcpyn (pszText, ".", nCharCnt);
-          if (lFlags == EECQHT_TOOLTIP)
-            lstrcpyn (pszText,
-                      _("Show S/MIME status info"),
-                      nCharCnt);
-          return S_OK;
-       }
-      else if (nCommandID == m_nCmdCheckSig)
-        {
-          if (lFlags == EECQHT_STATUS)
-            lstrcpyn (pszText, ".", nCharCnt);
-          if (lFlags == EECQHT_TOOLTIP)
-            lstrcpyn (pszText,
-                      _("Check the signature now and display the result"),
-                      nCharCnt);
-          return S_OK;
-       }
+      if (lFlags == EECQHT_STATUS)
+        lstrcpyn (pszText, ".", nCharCnt);
+      if (lFlags == EECQHT_TOOLTIP)
+        lstrcpyn (pszText, _("Decrypt message and verify signature"), 
+                  nCharCnt);
     }
-  else if (m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
+  else if (nCommandID == m_nCmdShowInfo
+           && m_lContext == EECONTEXT_READNOTEMESSAGE) 
     {
-      if (nCommandID == m_nCmdEncrypt) 
-        {
-          if (lFlags == EECQHT_STATUS)
-            lstrcpyn (pszText, ".", nCharCnt);
-          if (lFlags == EECQHT_TOOLTIP)
-            lstrcpyn (pszText,
-                      _("Encrypt message with GPG"),
-                      nCharCnt);
-          return S_OK;
-       }
-      else if (nCommandID == m_nCmdSign) 
-        {
-          if (lFlags == EECQHT_STATUS)
-            lstrcpyn (pszText, ".", nCharCnt);
-          if (lFlags == EECQHT_TOOLTIP)
-            lstrcpyn (pszText,
-                      _("Sign message with GPG"),
-                      nCharCnt);
-          return S_OK;
-       }
+      if (lFlags == EECQHT_STATUS)
+        lstrcpyn (pszText, ".", nCharCnt);
+      if (lFlags == EECQHT_TOOLTIP)
+        lstrcpyn (pszText, _("Show S/MIME status info"),
+                  nCharCnt);
     }
-  else if (m_lContext == EECONTEXT_VIEWER) 
+  else if (nCommandID == m_nCmdCheckSig
+           && m_lContext == EECONTEXT_READNOTEMESSAGE) 
     {
-      if (nCommandID == m_nCmdEncrypt) 
-        {
-          if (lFlags == EECQHT_STATUS)
-            lstrcpyn (pszText, ".", nCharCnt);
-          if (lFlags == EECQHT_TOOLTIP)
-               lstrcpyn (pszText,
-                          _("Open GPG Key Manager"),
-                          nCharCnt);
-          return S_OK;
-       }       
+      if (lFlags == EECQHT_STATUS)
+        lstrcpyn (pszText, ".", nCharCnt);
+      if (lFlags == EECQHT_TOOLTIP)
+        lstrcpyn (pszText,
+                  _("Check the signature now and display the result"),
+                  nCharCnt);
     }
+  else if (nCommandID == m_nCmdEncrypt
+           && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
+    {
+      if (lFlags == EECQHT_STATUS)
+        lstrcpyn (pszText, ".", nCharCnt);
+      if (lFlags == EECQHT_TOOLTIP)
+        lstrcpyn (pszText,
+                  _("Encrypt message with GPG"),
+                  nCharCnt);
+    }
+  else if (nCommandID == m_nCmdSign
+           && m_lContext == EECONTEXT_SENDNOTEMESSAGE) 
+    {
+      if (lFlags == EECQHT_STATUS)
+        lstrcpyn (pszText, ".", nCharCnt);
+      if (lFlags == EECQHT_TOOLTIP)
+        lstrcpyn (pszText,
+                  _("Sign message with GPG"),
+                  nCharCnt);
+    }
+  else if (nCommandID == m_nCmdKeyManager
+           && m_lContext == EECONTEXT_VIEWER) 
+    {
+      if (lFlags == EECQHT_STATUS)
+        lstrcpyn (pszText, ".", nCharCnt);
+      if (lFlags == EECQHT_TOOLTIP)
+        lstrcpyn (pszText,
+                  _("Open the GpgOL Key Manager"),
+                  nCharCnt);
+    }
+  else 
+    return S_FALSE;
 
-  return S_FALSE;
+  return S_OK;
 }
 
 
-/* Called by Exchange to get toolbar button infos.  Returns S_OK when
-   it is a button of this plugin and the requested info was delivered;
-   otherwise S_FALSE. */
+/* Called by Exchange to get toolbar button infos.  TOOLBARID is the
+   toolbar identifier.  BUTTONID is the toolbar button index.  PTBB is
+   a pointer to toolbar button structure DESCRIPTION is a pointer to
+   buffer receiving the text for the button.  DESCRIPTION_SIZE is the
+   maximum size of DESCRIPTION.  FLAGS are flags which might have the
+   EXCHEXT_UNICODE bit set.
+
+   Returns S_OK when it is a button of this plugin and the requested
+   info was delivered; otherwise S_FALSE.  */
 STDMETHODIMP 
-GpgolExtCommands::QueryButtonInfo (
-       ULONG lToolbarID,       // The toolbar identifier.
-       UINT nToolbarButtonID,  // The toolbar button index.
-        LPTBBUTTON pTBB,        // A pointer to toolbar button structure
-                                //  (see TBBUTTON structure).
-       LPTSTR lpszDescription, // A pointer to string describing button.
-       UINT nCharCnt,          // The maximum size of lpsz buffer.
-        ULONG lFlags)           // EXCHEXT_UNICODE may be specified
+GpgolExtCommands::QueryButtonInfo (ULONG toolbarid, UINT buttonid, 
+                                   LPTBBUTTON pTBB, 
+                                   LPTSTR description, UINT description_size,
+                                   ULONG flags)          
 {
-       if (m_lContext == EECONTEXT_READNOTEMESSAGE)
-       {
-               if (nToolbarButtonID == m_nToolbarButtonID1)
-               {
-                       pTBB->iBitmap = m_nToolbarBitmap1;             
-                       pTBB->idCommand = m_nCmdEncrypt;
-                       pTBB->fsState = TBSTATE_ENABLED;
-                       pTBB->fsStyle = TBSTYLE_BUTTON;
-                       pTBB->dwData = 0;
-                       pTBB->iString = -1;
-                       lstrcpyn (lpszDescription,
-                                  _("Decrypt message and verify signature"),
-                                  nCharCnt);
-                       return S_OK;
-               }
-       }
-       if (m_lContext == EECONTEXT_SENDNOTEMESSAGE)
-       {
-               if (nToolbarButtonID == m_nToolbarButtonID1)
-               {
-                       pTBB->iBitmap = m_nToolbarBitmap1;             
-                       pTBB->idCommand = m_nCmdEncrypt;
-                       pTBB->fsState = TBSTATE_ENABLED;
-                       if (m_pExchExt->m_gpgEncrypt)
-                               pTBB->fsState |= TBSTATE_CHECKED;
-                       pTBB->fsStyle = TBSTYLE_BUTTON | TBSTYLE_CHECK;
-                       pTBB->dwData = 0;
-                       pTBB->iString = -1;
-                       lstrcpyn (lpszDescription,
-                                  _("Encrypt message with GPG"),
-                                  nCharCnt);
-                       return S_OK;
-               }
-               if (nToolbarButtonID == m_nToolbarButtonID2)
-               {
-                       pTBB->iBitmap = m_nToolbarBitmap2;             
-                       pTBB->idCommand = m_nCmdSign;
-                       pTBB->fsState = TBSTATE_ENABLED;
-                       if (m_pExchExt->m_gpgSign)
-                               pTBB->fsState |= TBSTATE_CHECKED;
-                       pTBB->fsStyle = TBSTYLE_BUTTON | TBSTYLE_CHECK;
-                       pTBB->dwData = 0;
-                       pTBB->iString = -1;
-                       lstrcpyn (lpszDescription,
-                                  _("Sign message with GPG"),
-                                  nCharCnt);
-                       return S_OK;
-               }
-       }
-       if (m_lContext == EECONTEXT_VIEWER)
-       {
-               if (nToolbarButtonID == m_nToolbarButtonID1)
-               {
-                       pTBB->iBitmap = m_nToolbarBitmap1;             
-                       pTBB->idCommand = m_nCmdEncrypt;
-                       pTBB->fsState = TBSTATE_ENABLED;
-                       pTBB->fsStyle = TBSTYLE_BUTTON;
-                       pTBB->dwData = 0;
-                       pTBB->iString = -1;
-                       lstrcpyn (lpszDescription,
-                                  _("Open GPG Key Manager"),
-                                  nCharCnt);
-                       return S_OK;
-               }
-       }
+  if (buttonid == m_nToolbarButtonID1
+      && m_lContext == EECONTEXT_READNOTEMESSAGE)
+    {
+      pTBB->iBitmap = m_nToolbarBitmap1;             
+      pTBB->idCommand = m_nCmdEncrypt;
+      pTBB->fsState = TBSTATE_ENABLED;
+      pTBB->fsStyle = TBSTYLE_BUTTON;
+      pTBB->dwData = 0;
+      pTBB->iString = -1;
+      lstrcpyn (description,
+                _("Decrypt message and verify signature"),
+                description_size);
+    }
+  else if (buttonid == m_nToolbarButtonID1
+           && m_lContext == EECONTEXT_SENDNOTEMESSAGE)
+    {
+      pTBB->iBitmap = m_nToolbarBitmap1;             
+      pTBB->idCommand = m_nCmdEncrypt;
+      pTBB->fsState = TBSTATE_ENABLED;
+      if (m_pExchExt->m_gpgEncrypt)
+        pTBB->fsState |= TBSTATE_CHECKED;
+      pTBB->fsStyle = TBSTYLE_BUTTON | TBSTYLE_CHECK;
+      pTBB->dwData = 0;
+      pTBB->iString = -1;
+      lstrcpyn (description, _("Encrypt message with GPG"),
+                description_size);
+    }
+  else if (buttonid == m_nToolbarButtonID2
+           && m_lContext == EECONTEXT_SENDNOTEMESSAGE)
+    {
+      pTBB->iBitmap = m_nToolbarBitmap2;             
+      pTBB->idCommand = m_nCmdSign;
+      pTBB->fsState = TBSTATE_ENABLED;
+      if (m_pExchExt->m_gpgSign)
+        pTBB->fsState |= TBSTATE_CHECKED;
+      pTBB->fsStyle = TBSTYLE_BUTTON | TBSTYLE_CHECK;
+      pTBB->dwData = 0;
+      pTBB->iString = -1;
+      lstrcpyn (description, _("Sign message with GPG"),
+                description_size);
+    }
+  else if (buttonid == m_nToolbarButtonID1
+           && m_lContext == EECONTEXT_VIEWER)
+    {
+      pTBB->iBitmap = m_nToolbarBitmap1;             
+      pTBB->idCommand = m_nCmdEncrypt;
+      pTBB->fsState = TBSTATE_ENABLED;
+      pTBB->fsStyle = TBSTYLE_BUTTON;
+      pTBB->dwData = 0;
+      pTBB->iString = -1;
+      lstrcpyn (description, _("Open GPG Key Manager"),
+                description_size);
+    }
+  else
+    return S_FALSE;
 
-       return S_FALSE;
+  return S_OK;
 }
 
 
index 1a4ec5c..aad237c 100644 (file)
@@ -38,9 +38,13 @@ private:
   ULONG m_lContext;
   
   UINT  m_nCmdEncrypt;
+  UINT  m_nCmdDecrypt;
   UINT  m_nCmdSign;
   UINT  m_nCmdShowInfo;
   UINT  m_nCmdCheckSig;
+  UINT  m_nCmdKeyManager;
+  UINT  m_nCmdDebug1;
+  UINT  m_nCmdDebug2;
 
   UINT  m_nToolbarButtonID1;
   UINT  m_nToolbarButtonID2;     
index e661980..7950dce 100644 (file)
@@ -19,6 +19,7 @@
  * 02110-1301, USA.
  */
 
+#error not anymore used
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
@@ -31,7 +32,6 @@
 #include "mymapitags.h"
 #include "myexchext.h"
 #include "common.h"
-#include "gpgmsg.hh"
 #include "util.h"
 #include "msgcache.h"
 #include "pgpmime.h"
index 2ea715f..4d75ade 100644 (file)
@@ -21,6 +21,8 @@
 #ifndef GPGMSG_HH
 #define GPGMSG_HH
 
+#error not anymore used.
+
 #include "common.h"
 
 /* To manage a message we use our own class to keep track about all
index b3eb2a6..75a33ff 100644 (file)
@@ -108,7 +108,15 @@ get_crypt_random (size_t nbytes)
 static int
 initialize_session_key (void)
 {
-  the_session_key = get_crypt_random (16);
+  the_session_key = get_crypt_random (16+sizeof (unsigned int));
+  if (the_session_key)
+    {
+      /* We use rand() in generate_boundary so we need to seed it. */
+      unsigned int tmp;
+
+      memcpy (&tmp, the_session_key+16, sizeof (unsigned int));
+      srand (tmp);
+    }
   return !the_session_key;
 }
 
@@ -164,7 +172,6 @@ DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
     }
   else if (reason == DLL_PROCESS_DETACH)
     {
-      watcher_free_hook ();
     }
   
   return TRUE;
@@ -190,6 +197,15 @@ create_initialization_vector (size_t nbytes)
 }
 
 
+/* Create a new boundary for use with MIME. */
+void
+create_boundary (char *buffer, size_t buflen)
+{
+
+
+}
+
+
 /* Acquire the mutex for logging.  Returns 0 on success. */
 static int 
 lock_log (void)
index ec59a2f..15823e9 100644 (file)
@@ -178,6 +178,301 @@ get_gpgolprotectiv_tag (LPMESSAGE message, ULONG *r_tag)
 
 
 
+/* Set an arbitary header in the message MSG with NAME to the value
+   VAL. */
+int
+mapi_set_header (LPMESSAGE msg, const char *name, const char *val)
+{  
+  HRESULT hr;
+  LPSPropTagArray pProps = NULL;
+  SPropValue pv;
+  MAPINAMEID mnid, *pmnid;     
+  /* {00020386-0000-0000-C000-000000000046}  ->  GUID For X-Headers */
+  GUID guid = {0x00020386, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00,
+                                            0x00, 0x00, 0x00, 0x46} };
+  
+  if (!msg)
+    return -1;
+
+  memset (&mnid, 0, sizeof mnid);
+  mnid.lpguid = &guid;
+  mnid.ulKind = MNID_STRING;
+  mnid.Kind.lpwstrName = utf8_to_wchar (name);
+  pmnid = &mnid;
+  hr = msg->GetIDsFromNames (1, &pmnid, MAPI_CREATE, &pProps);
+  xfree (mnid.Kind.lpwstrName);
+  if (FAILED (hr)) 
+    {
+      log_error ("%s:%s: can't get mapping for header `%s': hr=%#lx\n",
+                 SRCNAME, __func__, name, hr); 
+      return -1;
+    }
+    
+  pv.ulPropTag = (pProps->aulPropTag[0] & 0xFFFF0000) | PT_STRING8;
+  pv.Value.lpszA = (char *)val;
+  hr = HrSetOneProp(msg, &pv); 
+  if (hr)
+    {
+      log_error ("%s:%s: can't set header `%s': hr=%#lx\n",
+                 SRCNAME, __func__, name, hr); 
+      return -1;
+    }
+  return 0;
+}
+
+
+
+/* Return the body as a new IStream object.  Returns NULL on failure.
+   The stream returns the body as an ASCII stream (Use mapi_get_body
+   for an UTF-8 value).  */
+LPSTREAM
+mapi_get_body_as_stream (LPMESSAGE message)
+{
+  HRESULT hr;
+  LPSTREAM stream;
+
+  if (!message)
+    return NULL;
+
+  /* We try to get it as an ASCII body.  If this fails we would either
+     need to implement some kind of stream filter to translated to
+     utf-8 or read everyting into a memory buffer and [provide an
+     istream from that memory buffer.  */
+  hr = message->OpenProperty (PR_BODY_A, &IID_IStream, 0, 0, 
+                              (LPUNKNOWN*)&stream);
+  if (hr)
+    {
+      log_debug ("%s:%s: OpenProperty failed: hr=%#lx", SRCNAME, __func__, hr);
+      return NULL;
+    }
+
+  return stream;
+}
+
+
+
+/* Return the body of the message in an allocated buffer.  The buffer
+   is guaranteed to be Nul terminated.  The actual length (ie. the
+   strlen()) will be stored at R_NBYTES.  The body will be returned in
+   UTF-8 encoding. Returns NULL if no body is available.  */
+char *
+mapi_get_body (LPMESSAGE message, size_t *r_nbytes)
+{
+  HRESULT hr;
+  LPSPropValue lpspvFEID = NULL;
+  LPSTREAM stream;
+  STATSTG statInfo;
+  ULONG nread;
+  char *body = NULL;
+
+  if (r_nbytes)
+    *r_nbytes = 0;
+  hr = HrGetOneProp ((LPMAPIPROP)message, PR_BODY, &lpspvFEID);
+  if (SUCCEEDED (hr))  /* Message is small enough to be retrieved directly. */
+    { 
+      switch ( PROP_TYPE (lpspvFEID->ulPropTag) )
+        {
+        case PT_UNICODE:
+          body = wchar_to_utf8 (lpspvFEID->Value.lpszW);
+          if (!body)
+            log_debug ("%s: error converting to utf8\n", __func__);
+          break;
+          
+        case PT_STRING8:
+          body = xstrdup (lpspvFEID->Value.lpszA);
+          break;
+          
+        default:
+          log_debug ("%s: proptag=0x%08lx not supported\n",
+                     __func__, lpspvFEID->ulPropTag);
+          break;
+        }
+      MAPIFreeBuffer (lpspvFEID);
+    }
+  else /* Message is large; use an IStream to read it.  */
+    {
+      hr = message->OpenProperty (PR_BODY, &IID_IStream, 0, 0, 
+                                  (LPUNKNOWN*)&stream);
+      if (hr)
+        {
+          log_debug ("%s:%s: OpenProperty failed: hr=%#lx",
+                     SRCNAME, __func__, hr);
+          return NULL;
+        }
+      
+      hr = stream->Stat (&statInfo, STATFLAG_NONAME);
+      if (hr)
+        {
+          log_debug ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
+          stream->Release ();
+          return NULL;
+        }
+      
+      /* Fixme: We might want to read only the first 1k to decide
+         whether this is actually an OpenPGP message and only then
+         continue reading.  */
+      body = (char*)xmalloc ((size_t)statInfo.cbSize.QuadPart + 2);
+      hr = stream->Read (body, (size_t)statInfo.cbSize.QuadPart, &nread);
+      if (hr)
+        {
+          log_debug ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
+          xfree (body);
+          stream->Release ();
+          return NULL;
+        }
+      body[nread] = 0;
+      body[nread+1] = 0;
+      if (nread != statInfo.cbSize.QuadPart)
+        {
+          log_debug ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
+          xfree (body);
+          stream->Release ();
+          return NULL;
+        }
+      stream->Release ();
+      
+      {
+        char *tmp;
+        tmp = wchar_to_utf8 ((wchar_t*)body);
+        if (!tmp)
+          log_debug ("%s: error converting to utf8\n", __func__);
+        else
+          {
+            xfree (body);
+            body = tmp;
+          }
+      }
+    }
+
+  if (r_nbytes)
+    *r_nbytes = strlen (body);
+  return body;
+}
+
+
+
+/* Look at the body of the MESSAGE and try to figure out whether this
+   is a supported PGP message.  Returns the new message class on
+   return or NULL if not.  */
+static char *
+get_msgcls_from_pgp_lines (LPMESSAGE message)
+{
+  HRESULT hr;
+  LPSPropValue lpspvFEID = NULL;
+  LPSTREAM stream;
+  STATSTG statInfo;
+  ULONG nread;
+  char *body = NULL;
+  char *p;
+  char *msgcls = NULL;
+
+  hr = HrGetOneProp ((LPMAPIPROP)message, PR_BODY, &lpspvFEID);
+  if (SUCCEEDED (hr))  /* Message is small enough to be retrieved directly. */
+    { 
+      switch ( PROP_TYPE (lpspvFEID->ulPropTag) )
+        {
+        case PT_UNICODE:
+          body = wchar_to_utf8 (lpspvFEID->Value.lpszW);
+          if (!body)
+            log_debug ("%s: error converting to utf8\n", __func__);
+          break;
+          
+        case PT_STRING8:
+          body = xstrdup (lpspvFEID->Value.lpszA);
+          break;
+          
+        default:
+          log_debug ("%s: proptag=0x%08lx not supported\n",
+                     __func__, lpspvFEID->ulPropTag);
+          break;
+        }
+      MAPIFreeBuffer (lpspvFEID);
+    }
+  else /* Message is large; use an IStream to read it.  */
+    {
+      hr = message->OpenProperty (PR_BODY, &IID_IStream, 0, 0, 
+                                  (LPUNKNOWN*)&stream);
+      if (hr)
+        {
+          log_debug ("%s:%s: OpenProperty failed: hr=%#lx",
+                     SRCNAME, __func__, hr);
+          return NULL;
+        }
+      
+      hr = stream->Stat (&statInfo, STATFLAG_NONAME);
+      if (hr)
+        {
+          log_debug ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
+          stream->Release ();
+          return NULL;
+        }
+      
+      /* Fixme: We might want to read only the first 1k to decide
+         whether this is actually an OpenPGP message and only then
+         continue reading.  */
+      body = (char*)xmalloc ((size_t)statInfo.cbSize.QuadPart + 2);
+      hr = stream->Read (body, (size_t)statInfo.cbSize.QuadPart, &nread);
+      if (hr)
+        {
+          log_debug ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
+          xfree (body);
+          stream->Release ();
+          return NULL;
+        }
+      body[nread] = 0;
+      body[nread+1] = 0;
+      if (nread != statInfo.cbSize.QuadPart)
+        {
+          log_debug ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
+          xfree (body);
+          stream->Release ();
+          return NULL;
+        }
+      stream->Release ();
+      
+      /* FIXME: We might want to avoid this by directly comparing
+         against wchar_t.  */
+      {
+        char *tmp;
+        tmp = wchar_to_utf8 ((wchar_t*)body);
+        if (!tmp)
+          log_debug ("%s: error converting to utf8\n", __func__);
+        else
+          {
+            xfree (body);
+            body = tmp;
+          }
+      }
+    }
+
+  /* The entire body of the message is now availble in the utf-8
+     string BODY.  Walk over it to figure out its type.  */
+  /* FiXME: We should use a state machine to check whether this is
+     really a valid PGP MIME message and possible even check that
+     there there is no other text before or after the signed text.  */
+  for (p=body; p && *p; p = (p=strchr (p+1, '\n')? (p+1):NULL))
+    if (!strncmp (p, "-----BEGIN PGP ", 15))
+      {
+        if (!strncmp (p+15, "SIGNED MESSAGE-----", 19)
+            && trailing_ws_p (p+15+19))
+          {
+            msgcls = xstrdup ("IPM.Note.GpgOL.ClearSigned");
+            break;
+          }
+        else if (!strncmp (p+15, "MESSAGE-----", 12)
+                 && trailing_ws_p (p+15+12))
+          {
+            msgcls = xstrdup ("IPM.Note.GpgOL.PGPMessage");
+            break;
+          }
+      }
+         
+  xfree (body);
+  return msgcls;
+}
+
+
+
 /* This function checks whether MESSAGE requires processing by us and
    adjusts the message class to our own.  Return true if the message
    was changed. */
@@ -231,6 +526,11 @@ mapi_change_message_class (LPMESSAGE message)
                     newvalue = xstrdup ("IPM.Note.GpgOL.MultipartEncrypted");
                   xfree (proto);
                 }
+              else if (!strcmp (ct, "text/plain"))
+                {
+                  newvalue = get_msgcls_from_pgp_lines (message);
+                }
+              
               xfree (ct);
             }
         }
@@ -344,6 +644,10 @@ mapi_get_message_type (LPMESSAGE message)
             msgtype = MSGTYPE_GPGOL_OPAQUE_SIGNED;
           else if (!strcmp (s, ".OpaqueEncrypted"))
             msgtype = MSGTYPE_GPGOL_OPAQUE_ENCRYPTED;
+          else if (!strcmp (s, ".ClearSigned"))
+            msgtype = MSGTYPE_GPGOL_CLEAR_SIGNED;
+          else if (!strcmp (s, ".PGPMessage"))
+            msgtype = MSGTYPE_GPGOL_PGP_MESSAGE;
           else
             log_debug ("%s:%s: message class `%s' not supported",
                        SRCNAME, __func__, s-14);
@@ -406,7 +710,7 @@ mapi_to_mime (LPMESSAGE message, const char *filename)
 }
 
 
-/* Return a binary propery in a malloced buffer with its length stored
+/* Return a binary property in a malloced buffer with its length stored
    at R_NBYTES.  Returns NULL on error.  */
 char *
 mapi_get_binary_prop (LPMESSAGE message, ULONG proptype, size_t *r_nbytes)
@@ -528,8 +832,9 @@ get_attach_mime_tag (LPATTACH obj)
   hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_MIME_TAG_A, &propval);
   if (FAILED (hr))
     {
-      log_error ("%s:%s: error getting attachment's MIME tag: hr=%#lx",
-                 SRCNAME, __func__, hr);
+      if (hr != MAPI_E_NOT_FOUND)
+        log_error ("%s:%s: error getting attachment's MIME tag: hr=%#lx",
+                   SRCNAME, __func__, hr);
       return NULL; 
     }
   switch ( PROP_TYPE (propval->ulPropTag) )
@@ -748,7 +1053,7 @@ mapi_get_attach_as_stream (LPMESSAGE message, mapi_attach_item_t *item)
 
 /* Return a malloced buffer with the content of the attachment. If
    R_NBYTES is not NULL the number of bytes will get stored there.
-   ATT must have an attachment method of ATATCH_BY_VALUE.  Returns
+   ATT must have an attachment method of ATTACH_BY_VALUE.  Returns
    NULL on error.  If UNPROTECT is set and the appropriate crypto
    attribute is available, the function returns the unprotected
    version of the atatchment. */
@@ -1143,7 +1448,7 @@ has_gpgol_body_name (LPATTACH obj)
    If R_NBYTES is not NULL the number of bytes in the returned buffer
    is stored there.  If R_ISHTML is not NULL a flag indicating whether
    the HTML is html formatted is stored there.  If R_PROTECTED is not
-   NULL a flag indicating whethernthe message was protected is store
+   NULL a flag indicating whether the message was protected is stored
    there.  If no body attachment can be found or on any other error
    NULL is returned.  Caller must free the returned string. */
 char *
index a9444c5..de601ae 100644 (file)
@@ -37,16 +37,20 @@ typedef enum
     MSGTYPE_GPGOL_MULTIPART_SIGNED,
     MSGTYPE_GPGOL_MULTIPART_ENCRYPTED,
     MSGTYPE_GPGOL_OPAQUE_SIGNED,
-    MSGTYPE_GPGOL_OPAQUE_ENCRYPTED
+    MSGTYPE_GPGOL_OPAQUE_ENCRYPTED,
+    MSGTYPE_GPGOL_CLEAR_SIGNED,
+    MSGTYPE_GPGOL_PGP_MESSAGE
   }
 msgtype_t;
 
 typedef enum
   {
     ATTACHTYPE_UNKNOWN = 0,
-    ATTACHTYPE_MOSS = 1,     /* The original MOSS message (ie. a
-                                S/MIME or PGP/MIME message. */
-    ATTACHTYPE_FROMMOSS = 2  /* Attachment created from MOSS.  */
+    ATTACHTYPE_MOSS = 1,         /* The original MOSS message (ie. a
+                                    S/MIME or PGP/MIME message. */
+    ATTACHTYPE_FROMMOSS = 2,     /* Attachment created from MOSS.  */
+    ATTACHTYPE_MOSSTEMPL = 3     /* Attachment has been created in the
+                                    course of sendig a message */ 
   }
 attachtype_t;
 
@@ -57,7 +61,7 @@ struct mapi_attach_item_s
                            the table. */
   int mapipos;          /* The position which needs to be passed to
                            MAPI to open the attachment.  -1 means that
-                           there is no valid atatchment.  */
+                           there is no valid attachment.  */
    
   int method;           /* MAPI attachment method. */
   char *filename;       /* Malloced filename of this attachment or NULL. */
@@ -70,7 +74,7 @@ struct mapi_attach_item_s
   /* If not NULL the parameters of the content_type. */
   const char *content_type_parms; 
 
-  /* The attachment type from Property GpgOL Atatch Type.  */
+  /* The attachment type from Property GpgOL Attach Type.  */
   attachtype_t attach_type;
 
 };
@@ -83,12 +87,17 @@ int get_gpgolattachtype_tag (LPMESSAGE message, ULONG *r_tag);
 int get_gpgolsigstatus_tag (LPMESSAGE message, ULONG *r_tag);
 int get_gpgolprotectiv_tag (LPMESSAGE message, ULONG *r_tag);
 
+int mapi_set_header (LPMESSAGE msg, const char *name, const char *val);
+
 int mapi_change_message_class (LPMESSAGE message);
 msgtype_t mapi_get_message_type (LPMESSAGE message);
 int mapi_to_mime (LPMESSAGE message, const char *filename);
 
 char *mapi_get_binary_prop (LPMESSAGE message,ULONG proptype,size_t *r_nbytes);
 
+LPSTREAM mapi_get_body_as_stream (LPMESSAGE message);
+char *mapi_get_body (LPMESSAGE message, size_t *r_nbytes);
+
 mapi_attach_item_t *mapi_create_attach_table (LPMESSAGE message, int fast);
 void mapi_release_attach_table (mapi_attach_item_t *table);
 LPSTREAM mapi_get_attach_as_stream (LPMESSAGE message, 
index ec29bdc..425fbc1 100644 (file)
 #include <config.h>
 #endif
 #include <windows.h>
-#include <time.h>  /* FIXME; just for testing.  */
 
 #include "mymapi.h"
 #include "mymapitags.h"
 #include "myexchext.h"
 #include "display.h"
 #include "common.h"
-#include "gpgmsg.hh"
 #include "msgcache.h"
 #include "mapihelp.h"
 
@@ -39,6 +37,7 @@
 #include "olflange.h"
 #include "ol-ext-callback.h"
 #include "mimeparser.h"
+#include "mimemaker.h"
 #include "message.h"
 #include "message-events.h"
 
@@ -74,7 +73,7 @@ GpgolMessageEvents::GpgolMessageEvents (GpgolExt *pParentInterface)
   m_lRef = 0; 
   m_bOnSubmitActive = FALSE;
   m_want_html = FALSE;
-  m_is_smime = FALSE;
+  m_processed = FALSE;
 }
 
 
@@ -123,13 +122,13 @@ GpgolMessageEvents::OnRead (LPEXCHEXTCALLBACK pEECB)
     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
       log_debug ("%s:%s: processing multipart signed message\n", 
                  SRCNAME, __func__);
-      m_is_smime = TRUE;
+      m_processed = TRUE;
       message_verify (message, m_pExchExt->getMsgtype (pEECB), 0);
       break;
     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
       log_debug ("%s:%s: processing multipart encrypted message\n",
                  SRCNAME, __func__);
-      m_is_smime = TRUE;
+      m_processed = TRUE;
       message_decrypt (message, m_pExchExt->getMsgtype (pEECB), 0);
       /* Hmmm, we might want to abort it and run our own inspector
          instead.  */
@@ -137,13 +136,26 @@ GpgolMessageEvents::OnRead (LPEXCHEXTCALLBACK pEECB)
     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
       log_debug ("%s:%s: processing opaque signed message\n", 
                  SRCNAME, __func__);
-      m_is_smime = TRUE;
+      m_processed = TRUE;
+      message_verify (message, m_pExchExt->getMsgtype (pEECB), 0);
+      break;
+    case MSGTYPE_GPGOL_CLEAR_SIGNED:
+      log_debug ("%s:%s: processing clear signed pgp message\n", 
+                 SRCNAME, __func__);
+      m_processed = TRUE;
       message_verify (message, m_pExchExt->getMsgtype (pEECB), 0);
       break;
     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
       log_debug ("%s:%s: processing opaque encrypted message\n",
                  SRCNAME, __func__);
-      m_is_smime = TRUE;
+      m_processed = TRUE;
+      message_decrypt (message, m_pExchExt->getMsgtype (pEECB), 0);
+      /* Hmmm, we might want to abort it and run our own inspector
+         instead.  */
+      break;
+    case MSGTYPE_GPGOL_PGP_MESSAGE:
+      log_debug ("%s:%s: processing pgp message\n", SRCNAME, __func__);
+      m_processed = TRUE;
       message_decrypt (message, m_pExchExt->getMsgtype (pEECB), 0);
       /* Hmmm, we might want to abort it and run our own inspector
          instead.  */
@@ -165,34 +177,9 @@ GpgolMessageEvents::OnReadComplete (LPEXCHEXTCALLBACK pEECB, ULONG lFlags)
 {
   log_debug ("%s:%s: received\n", SRCNAME, __func__);
 
-
-  /* The preview_info stuff does not work because for some reasons we
-     can't update the window.  Thus disabled for now. */
-  if (!m_is_smime && opt.preview_decrypt /*|| !opt.compat.no_preview_info*/)
-    {
-      HRESULT hr;
-      HWND hWnd = NULL;
-      LPMESSAGE message = NULL;
-      LPMDB mdb = NULL;
-
-      if (FAILED (pEECB->GetWindow (&hWnd)))
-        hWnd = NULL;
-      hr = pEECB->GetObject (&mdb, (LPMAPIPROP *)&message);
-      if (SUCCEEDED (hr))
-        {
-          GpgMsg *m = CreateGpgMsg (message);
-          m->setExchangeCallback ((void*)pEECB);
-          m->setPreview (1);
-          /* If preview decryption has been requested, do so.  If not,
-             pass true as the second arg to let the fucntion display a
-             hint on what kind of message this is. */
-          m->decrypt (hWnd, !opt.preview_decrypt);
-          delete m;
-       }
-      ul_release (message);
-      ul_release (mdb);
-    }
-  else if (m_is_smime)
+  /* If the message has been processed by is (i.e. in OnRead), we now
+     use our own display code.  */
+  if (m_processed)
     {
       HRESULT hr;
       HWND hwnd = NULL;
@@ -213,6 +200,10 @@ GpgolMessageEvents::OnReadComplete (LPEXCHEXTCALLBACK pEECB, ULONG lFlags)
                                                  &ishtml, &wasprotected);
           if (body)
             update_display (hwnd, /*wasprotected?NULL:*/pEECB, ishtml, body);
+          else
+            update_display (hwnd, NULL, 0, 
+                            _("[Crypto operation failed - "
+                              "can't show the body of the message]"));
         }
       ul_release (message);
       ul_release (mdb);
@@ -317,6 +308,8 @@ GpgolMessageEvents::OnWrite (LPEXCHEXTCALLBACK pEECB)
 }
 
 
+
+
 /* Called by Exchange when the data has been written to the message.
    Encrypts and signs the message if the options are set.  PEECB is a
    pointer to the IExchExtCallback interface.  Returns: S_FALSE to
@@ -328,14 +321,15 @@ GpgolMessageEvents::OnWrite (LPEXCHEXTCALLBACK pEECB)
 STDMETHODIMP 
 GpgolMessageEvents::OnWriteComplete (LPEXCHEXTCALLBACK pEECB, ULONG lFlags)
 {
-  log_debug ("%s:%s: received\n", SRCNAME, __func__);
-
   HRESULT hrReturn = S_FALSE;
   LPMESSAGE msg = NULL;
   LPMDB pMDB = NULL;
   HWND hWnd = NULL;
   int rc;
 
+  log_debug ("%s:%s: received\n", SRCNAME, __func__);
+
+
   if (lFlags & (EEME_FAILED|EEME_COMPLETE_FAILED))
     return S_FALSE; /* We don't need to rollback anything in case
                        other extensions flagged a failure. */
@@ -345,77 +339,38 @@ GpgolMessageEvents::OnWriteComplete (LPEXCHEXTCALLBACK pEECB, ULONG lFlags)
   
   if (m_bWriteFailed)     /* Operation failed already. */
     return S_FALSE;
-  
+
+  /* Try to get the current window. */
   if (FAILED(pEECB->GetWindow (&hWnd)))
     hWnd = NULL;
 
+  /* Get the object and call the encryption or signing fucntion.  */
   HRESULT hr = pEECB->GetObject (&pMDB, (LPMAPIPROP *)&msg);
   if (SUCCEEDED (hr))
     {
-//      SPropTagArray proparray;
-
-      GpgMsg *m = CreateGpgMsg (msg);
-      m->setExchangeCallback ((void*)pEECB);
       if (m_pExchExt->m_gpgEncrypt && m_pExchExt->m_gpgSign)
-        rc = m->signEncrypt (hWnd, m_want_html);
+        rc = mime_sign_encrypt (msg, PROTOCOL_OPENPGP);
       else if (m_pExchExt->m_gpgEncrypt && !m_pExchExt->m_gpgSign)
-        rc = m->encrypt (hWnd, m_want_html);
+        rc = mime_encrypt (msg, PROTOCOL_OPENPGP);
       else if (!m_pExchExt->m_gpgEncrypt && m_pExchExt->m_gpgSign)
-        rc = m->sign (hWnd, m_want_html);
+        rc = mime_sign (msg, PROTOCOL_OPENPGP);
       else
         rc = 0;
-      delete m;
-
-      /* If we are encrypting we need to make sure that the other
-         format gets deleted and is not actually sent in the clear.
-         Note that this other format is always HTML because we have
-         moved that into an attachment and kept PR_BODY.  It seems
-         that OL always creates text and HTML if HTML has been
-         selected. */
-      /* ARGHH: This seems to delete also the PR_BODY for some reasonh
-         - need to disable this safe net. */
-//       if (m_pExchExt->m_gpgEncrypt)
-//         {
-//           log_debug ("%s:%s: deleting possible extra property PR_BODY_HTML\n",
-//                      SRCNAME, __func__);
-//           proparray.cValues = 1;
-//           proparray.aulPropTag[0] = PR_BODY_HTML;
-//           msg->DeleteProps (&proparray, NULL);
-//         }
-     
+      
       if (rc)
         {
           hrReturn = E_FAIL;
           m_bWriteFailed = TRUE;       
-
-         /* Outlook should now correctly react and do not deliver
-            the message in case of an error.
-          */
-         #if 0
-          if (m_pExchExt->m_gpgEncrypt)
-            {
-              log_debug ("%s:%s: deleting property PR_BODY due to error\n",
-                         SRCNAME, __func__);
-              proparray.cValues = 1;
-              proparray.aulPropTag[0] = PR_BODY;
-              hr = msg->DeleteProps (&proparray, NULL);
-              if (hr != S_OK)
-                log_debug ("%s:%s: DeleteProps failed: hr=%#lx\n",
-                           SRCNAME, __func__, hr);
-              /* FIXME: We should delete the attachments too. 
-                 We really, really should do this!!!          */
-            }
-          #endif
         }
     }
-
+  
   ul_release (msg);
   ul_release (pMDB);
-
+  
   return hrReturn;
 }
 
+
 /* Called by Exchange when the user selects the "check names" command.
    PEECB is a pointer to the IExchExtCallback interface.  Returns
    S_FALSE to signal Exchange to continue calling extensions. */
@@ -452,7 +407,7 @@ GpgolMessageEvents::OnSubmit (LPEXCHEXTCALLBACK pEECB)
 }
 
 
-/* Called by Echange after the message has been submitted to MAPI.
+/* Called by Exchange after the message has been submitted to MAPI.
    PEECB is a pointer to the IExchExtCallback interface. */
 STDMETHODIMP_ (VOID) 
 GpgolMessageEvents::OnSubmitComplete (LPEXCHEXTCALLBACK pEECB,
index 1402e36..43a4c56 100644 (file)
@@ -42,8 +42,7 @@ class GpgolMessageEvents : public IExchExtMessageEvents
   GpgolExt* m_pExchExt;
   BOOL    m_bWriteFailed;
   BOOL    m_want_html;       /* Encryption of HTML is desired. */
-  BOOL    m_is_smime;        /* The message has an smime message class
-                                we want to process.  */
+  BOOL    m_processed;       /* The message has been porcessed by us.  */
   
  public:
   STDMETHODIMP QueryInterface (REFIID riid, LPVOID *ppvObj);
index 997dedf..cb4a33c 100644 (file)
                         } while (0)
 
 
+/* Convert the clear signed message from INPUT into a PS?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.  */
+static char *
+pgp_mime_from_clearsigned (LPSTREAM input, size_t *outputlen)
+{
+  HRESULT hr;
+  STATSTG statinfo;
+  ULONG nread;
+  char *body = NULL;
+  char *p, *p0, *dest, *mark;
+  char boundary[BOUNDARYSIZE+1];
+  char top_header[200 + 2*BOUNDARYSIZE]; 
+  char sig_header[100 + BOUNDARYSIZE]; 
+  char end_header[10 + BOUNDARYSIZE];
+  size_t reserved_space;
+  int state;
+
+  *outputlen = 0;
+
+  /* Note that our parser does not make use of the micalg parameter.  */
+  generate_boundary (boundary);
+  snprintf (top_header, sizeof top_header,
+            "MIME-Version: 1.0\r\n"
+            "Content-Type: multipart/signed; boundary=\"%s\";\r\n"
+            "              protocol=\"application/pgp-signature\"\r\n"
+            "\r\n"
+            "--%s\r\n", boundary, boundary);
+  snprintf (sig_header, sizeof sig_header,
+            "--%s\r\n"
+            "Content-Type: application/pgp-signature\r\n"
+            "\r\n", boundary);
+  snprintf (end_header, sizeof end_header,
+            "\r\n"
+            "--%s--\r\n", boundary);
+  reserved_space = (strlen (top_header) + strlen (sig_header) 
+                    + strlen (end_header)+ 100);
+
+  hr = input->Stat (&statinfo, STATFLAG_NONAME);
+  if (hr)
+    {
+      log_debug ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
+      return NULL;
+    }
+      
+  body = (char*)xmalloc (reserved_space
+                         + (size_t)statinfo.cbSize.QuadPart + 2);
+  hr = input->Read (body+reserved_space,
+                    (size_t)statinfo.cbSize.QuadPart, &nread);
+  if (hr)
+    {
+      log_debug ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
+      xfree (body);
+      return NULL;
+    }
+  body[reserved_space + nread] = 0;
+  body[reserved_space + nread+1] = 0;  /* Just in case this is
+                                          accidently an wchar_t. */
+  if (nread != statinfo.cbSize.QuadPart)
+    {
+      log_debug ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
+      xfree (body);
+      return NULL;
+    }
+
+  /* We do in place conversion. */
+  state = 0;
+  dest = NULL;
+  for (p=body+reserved_space; p && *p; p = (p=strchr (p+1, '\n'))? (p+1):NULL)
+    {
+      if (!state && !strncmp (p, "-----BEGIN PGP SIGNED MESSAGE-----", 34)
+          && trailing_ws_p (p+34) )
+        {
+          dest = stpcpy (body, top_header);
+          state = 1;
+        }
+      else if (state == 1)
+        {
+          /* Wait for an empty line.  */
+          if (trailing_ws_p (p))
+            state = 2;
+        }
+      else if (state == 2 && strncmp (p, "-----", 5))
+        {
+          /* Copy signed data. */
+          p0 = p;
+          if (*p == '-' && p[1] == ' ')
+            p +=2;  /* Remove escaping.  */
+          mark = NULL;
+          while (*p && *p != '\n')
+            {
+              if (*p == ' ' || *p == '\t' || *p == '\r')
+                mark = p;
+              else
+                mark = NULL;
+              *dest++ = *p++;
+            }
+          if (mark)
+            *mark =0; /* Remove trailing white space.  */
+          if (*p == '\n')
+            {
+              if (p[-1] == '\r')
+                *dest++ = '\r';
+              *dest++ = '\n';
+            }
+          if (p > p0)
+            p--; /* Adjust so that the strchr (p+1, '\n') can work. */
+        }
+      else if (state == 2)
+        {
+          /* Armor line encountered.  */
+          p0 = p;
+          if (strncmp (p, "-----BEGIN PGP SIGNATURE-----", 29)
+              || !trailing_ws_p (p+29) )
+            fprintf (stderr,"Invalid clear signed message\n");
+          state = 3;
+          dest = stpcpy (dest, sig_header);
+        
+          while (*p && *p != '\n')
+            *dest++ = *p++;
+          if (*p == '\n')
+            {
+              if (p[-1] == '\r')
+                *dest++ = '\r';
+              *dest++ = '\n';
+            }
+          if (p > p0)
+            p--; /* Adjust so that the strchr (p+1, '\n') can work. */
+        }
+      else if (state == 3 && strncmp (p, "-----", 5))
+        {
+          /* Copy signature.  */
+          p0 = p;
+          while (*p && *p != '\n')
+            *dest++ = *p++;
+          if (*p == '\n')
+            {
+              if (p[-1] == '\r')
+                *dest++ = '\r';
+              *dest++ = '\n';
+            }
+          if (p > p0)
+            p--; /* Adjust so that the strchr (p+1, '\n') can work. */
+        }
+      else if (state == 3)
+        {
+          /* Armor line encountered.  */
+          p0 = p;
+          if (strncmp (p, "-----END PGP SIGNATURE-----", 27)
+              || !trailing_ws_p (p+27) )
+            fprintf (stderr,"Invalid clear signed message (no end)\n");
+          while (*p && *p != '\n')
+            *dest++ = *p++;
+          if (*p == '\n')
+            {
+              if (p[-1] == '\r')
+                *dest++ = '\r';
+              *dest++ = '\n';
+            }
+          dest = stpcpy (dest, end_header);
+          if (p > p0)
+            p--; /* Adjust so that the strchr (p+1, '\n') can work. */
+          break; /* Ah well, we can stop here.  */
+        }
+    }
+  if (!dest)
+    {
+      xfree (body);
+      return NULL;
+    }
+  *dest = 0;
+  *outputlen = strlen (body);
+
+  return body;
+}
+
+
 
 /* Verify MESSAGE and update the attachments as required.  MSGTYPE
    should be the type of the message so that the fucntion can decide
 int
 message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
 {
-  mapi_attach_item_t *table;
+  mapi_attach_item_t *table = NULL;
   int moss_idx = -1;
   int i;
   char *inbuf;
@@ -59,8 +237,11 @@ message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
     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:
       return -1; /* Should not be called for such a message.  */
     case MSGTYPE_UNKNOWN:
     case MSGTYPE_GPGOL:
@@ -74,47 +255,71 @@ message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
   else if (mapi_has_sig_status (message))
     return 0; /* Already checked that message.  */
 
-  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].attach_type == ATTACHTYPE_MOSS)
-      {
-        moss_idx = i;
-        break;
-      }
-  if (moss_idx == -1 && !table[0].end_of_table && table[1].end_of_table)
+  if (msgtype == MSGTYPE_GPGOL_CLEAR_SIGNED)
     {
-      /* No MOSS flag found in the table but there is only one
-         attachment.  Due to the message type we know that this is
-         the original MOSS message.  We mark this attachment as
-         hidden, so that it won't get displayed.  We further mark it
-         as our original MOSS attachment so that after parsing we have
-         a mean to find it again (see above).  */ 
-      moss_idx = 0;
-      mapi_mark_moss_attach (message, table+0);
+      /* PGP's clear signed messages are special: All is contained in
+         the body and thus there is no requirement for an
+         attachment.  */
+      LPSTREAM rawstream;
+      
+      rawstream = mapi_get_body_as_stream (message);
+      if (!rawstream)
+        return -1;
+      
+      inbuf = pgp_mime_from_clearsigned (rawstream, &inbuflen);
+      rawstream->Release ();
+      if (!inbuf)
+        return -1;
     }
-
-  if (moss_idx == -1)
+  else
     {
-      mapi_release_attach_table (table);
-      return -1; /* No original attachment - this should not happen.  */
-    }
+      /* PGP/MIME or S/MIME stuff.  */
+      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].attach_type == ATTACHTYPE_MOSS)
+          {
+            moss_idx = i;
+            break;
+          }
+      if (moss_idx == -1 && !table[0].end_of_table && table[1].end_of_table)
+        {
+          /* No MOSS flag found in the table but there is only one
+             attachment.  Due to the message type we know that this is
+             the original MOSS message.  We mark this attachment as
+             hidden, so that it won't get displayed.  We further mark
+             it as our original MOSS attachment so that after parsing
+             we have a mean to find it again (see above).  */ 
+          moss_idx = 0;
+          mapi_mark_moss_attach (message, table+0);
+        }
+      
+      if (moss_idx == -1)
+        {
+          mapi_release_attach_table (table);
+          return -1; /* No original attachment - this should not happen.  */
+        }
 
-  inbuf = mapi_get_attach (message, table+0, &inbuflen);
-  if (!inbuf)
-    {
-      mapi_release_attach_table (table);
-      return -1; /* Problem getting the attachment.  */
+      inbuf = mapi_get_attach (message, table+0, &inbuflen);
+      if (!inbuf)
+        {
+          mapi_release_attach_table (table);
+          return -1; /* Problem getting the attachment.  */
+        }
     }
 
-
   err = mime_verify (inbuf, inbuflen, message, 0, 
                      opt.passwd_ttl, NULL, NULL, 0);
   log_debug ("mime_verify returned %d", err);
+  if (err)
+    {
+      char buf[200];
+      
+      snprintf (buf, sizeof buf, "Verify failed (%s)", gpg_strerror (err));
+      MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
+    }
   xfree (inbuf);
                     
   if (err)
@@ -134,7 +339,7 @@ message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
 int
 message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
 {
-  mapi_attach_item_t *table;
+  mapi_attach_item_t *table = NULL;
   int part2_idx;
   int tblidx;
   int retval = -1;
@@ -149,116 +354,140 @@ message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
     case MSGTYPE_GPGOL:
     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
+    case MSGTYPE_GPGOL_CLEAR_SIGNED:
       return -1; /* Should not have been called for this.  */
     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
       break;
     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
       is_opaque = 1;
       break;
+    case MSGTYPE_GPGOL_PGP_MESSAGE:
+      break;
     }
   
-  table = mapi_create_attach_table (message, 0);
-  if (!table)
-    return -1; /* No attachment - this should not happen.  */
-
-  if (is_opaque)
+  if (msgtype == MSGTYPE_GPGOL_PGP_MESSAGE)
     {
-      /* S/MIME opaque encrypted message: We expect 1 attachment.  As
-         we don't know ether 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 (part2_idx == -1 && table[tblidx].content_type 
-                && (!strcmp (table[tblidx].content_type,
-                            "application/pkcs7-mime")
-                    || !strcmp (table[tblidx].content_type,
-                                "application/x-pkcs7-mime")))
-              part2_idx = tblidx;
-          }
-      if (part2_idx == -1 && tblidx >= 1)
-        {
-          /* We have attachments but none are marked.  Thus we assume
-             that this is the first time we see this message and we
-             will set the mark now if we see appropriate content
-             types. */
-          if (table[0].content_type               
-              && (!strcmp (table[0].content_type, "application/pkcs7-mime")
-                  || !strcmp (table[0].content_type,
-                              "application/x-pkcs7-mime")))
-            part2_idx = 0;
-          if (part2_idx != -1)
-            mapi_mark_moss_attach (message, table+part2_idx);
-        }
-      if (part2_idx == -1)
-        {
-          log_debug ("%s:%s: this is not an S/MIME encrypted message",
-                     SRCNAME, __func__);
-          goto leave;
-        }
-      is_smime = 1;
+      /* 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);
+      if (!cipherstream)
+        goto leave;
     }
-  else 
+  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 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)
-          {
-            if (part1_idx == -1 && table[tblidx].content_type 
-                && !strcmp (table[tblidx].content_type,
-                            "application/pgp-encrypted"))
-              part1_idx = tblidx;
-            else if (part2_idx == -1 && table[tblidx].content_type 
-                     && !strcmp (table[tblidx].content_type,
-                                 "application/octet-stream"))
-              part2_idx = tblidx;
-          }
-      if (part1_idx == -1 && part2_idx == -1 && tblidx >= 2)
+  
+      /* PGP/MIME or S/MIME stuff.  */
+      table = mapi_create_attach_table (message, 0);
+      if (!table)
+        goto leave; /* No attachment - this should not happen.  */
+
+      if (is_opaque)
         {
-          /* At least 2 attachments but none are marked.  Thus we
-             assume that this is the first time we see this message
-             and we will set the mark now if we see appropriate
-             content types. */
-          if (table[0].content_type               
-              && !strcmp (table[0].content_type, "application/pgp-encrypted"))
-            part1_idx = 0;
-          if (table[1].content_type             
-              && !strcmp (table[1].content_type, "application/octet-stream"))
-            part2_idx = 1;
-          if (part1_idx != -1 && part2_idx != -1)
+          /* S/MIME opaque encrypted message: We expect one
+             attachment.  As we don't know ether 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 (part2_idx == -1 && table[tblidx].content_type 
+                    && (!strcmp (table[tblidx].content_type,
+                                 "application/pkcs7-mime")
+                        || !strcmp (table[tblidx].content_type,
+                                    "application/x-pkcs7-mime")))
+                  part2_idx = tblidx;
+              }
+          if (part2_idx == -1 && tblidx >= 1)
+            {
+              /* We have attachments but none are marked.  Thus we
+                 assume that this is the first time we see this
+                 message and we will set the mark now if we see
+                 appropriate content types. */
+              if (table[0].content_type               
+                  && (!strcmp (table[0].content_type, "application/pkcs7-mime")
+                      || !strcmp (table[0].content_type,
+                                  "application/x-pkcs7-mime")))
+                part2_idx = 0;
+              if (part2_idx != -1)
+                mapi_mark_moss_attach (message, table+part2_idx);
+            }
+          if (part2_idx == -1)
             {
-              mapi_mark_moss_attach (message, table+part1_idx);
-              mapi_mark_moss_attach (message, table+part2_idx);
+              log_debug ("%s:%s: this is not an S/MIME encrypted message",
+                         SRCNAME, __func__);
+              goto leave;
             }
+          is_smime = 1;
         }
-      if (part1_idx == -1 || part2_idx == -1)
+      else 
         {
-          log_debug ("%s:%s: this is not a PGP/MIME encrypted message",
-                 SRCNAME, __func__);
-          goto leave;
+          /* 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
+             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)
+              {
+                if (part1_idx == -1 && table[tblidx].content_type 
+                    && !strcmp (table[tblidx].content_type,
+                                "application/pgp-encrypted"))
+                  part1_idx = tblidx;
+                else if (part2_idx == -1 && table[tblidx].content_type 
+                         && !strcmp (table[tblidx].content_type,
+                                     "application/octet-stream"))
+                  part2_idx = tblidx;
+              }
+          if (part1_idx == -1 && part2_idx == -1 && tblidx >= 2)
+            {
+              /* At least 2 attachments but none are marked.  Thus we
+                 assume that this is the first time we see this
+                 message and we will set the mark now if we see
+                 appropriate content types. */
+              if (table[0].content_type               
+                  && !strcmp (table[0].content_type,
+                              "application/pgp-encrypted"))
+                part1_idx = 0;
+              if (table[1].content_type             
+                  && !strcmp (table[1].content_type, 
+                              "application/octet-stream"))
+                part2_idx = 1;
+              if (part1_idx != -1 && part2_idx != -1)
+                {
+                  mapi_mark_moss_attach (message, table+part1_idx);
+                  mapi_mark_moss_attach (message, table+part2_idx);
+                }
+            }
+          if (part1_idx == -1 || part2_idx == -1)
+            {
+              log_debug ("%s:%s: this is not a PGP/MIME encrypted message",
+                         SRCNAME, __func__);
+              goto leave;
+            }
         }
+      
+      cipherstream = mapi_get_attach_as_stream (message, table+part2_idx);
+      if (!cipherstream)
+        goto leave; /* Problem getting the attachment.  */
     }
 
-  /* Get the attachment as an allocated buffer and let the mimeparser
-     work on it.  */
-  cipherstream = mapi_get_attach_as_stream (message, table+part2_idx);
-  if (!cipherstream)
-    goto leave; /* Problem getting the attachment.  */
-
   err = mime_decrypt (cipherstream, message, is_smime, opt.passwd_ttl,
                       NULL, NULL, 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);
+    }
   cipherstream->Release ();
   retval = 0;
 
@@ -266,3 +495,152 @@ message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
   mapi_release_attach_table (table);
   return retval;
 }
+
+
+
+\f
+#if 0
+/* Sign the current message. Returns 0 on success. */
+int
+message_sign (LPMESSAGE message, HWND hwnd, int want_html)
+{
+  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;
+    }
+
+  if ( !plaintextlen && !has_attachments (message)) 
+    {
+      log_debug ("%s:%s: leave (empty)", SRCNAME, __func__);
+      plaintext->Release ();
+      return 0; 
+    }
+
+  /* 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);  
+    }
+
+  /* Sign the plaintext */
+  if (plaintextlen)
+    {
+      err = op_sign (plaintext, &signedtext, 
+                     OP_SIG_CLEAR, sign_key, opt.passwd_ttl);
+      if (err)
+        {
+          MessageBox (hwnd, op_strerror (err),
+                      _("Signing Failure"), MB_ICONERROR|MB_OK);
+          plaintext->Release ();
+          return gpg_error (GPG_ERR_GENERAL);
+        }
+    }
+
+  
+  /* 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 ();
+    }
+
+
+  /* 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 ())
+    {
+      unsigned int n;
+      
+      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. */
+    }
+
+  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
diff --git a/src/mimemaker.c b/src/mimemaker.c
new file mode 100644 (file)
index 0000000..3eff240
--- /dev/null
@@ -0,0 +1,1023 @@
+/* mimemaker.c - Construct MIME message out of a MAPI
+ *     Copyright (C) 2007 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.
+ * 
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+
+#define COBJMACROS
+#include <windows.h>
+#include <objidl.h> 
+
+#include "mymapi.h"
+#include "mymapitags.h"
+
+#include "common.h"
+#include "engine.h"
+#include "mapihelp.h"
+#include "mimemaker.h"
+
+#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
+                                     SRCNAME, __func__, __LINE__); \
+                        } while (0)
+
+static const char oid_mimetag[] =
+    {0x2A, 0x86, 0x48, 0x86, 0xf7, 0x14, 0x03, 0x0a, 0x04};
+
+/* The base-64 list used for base64 encoding. */
+static unsigned char bintoasc[64+1] = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
+                                       "abcdefghijklmnopqrstuvwxyz" 
+                                       "0123456789+/"); 
+
+/* The maximum length of a line we are able to process.  RFC822 allows
+   only for 1000 bytes; thus 2000 seems to be a reasonable value. */
+#define LINEBUFSIZE 2000
+
+/* Make sure that PROTOCOL is usable or return a suitable protocol.
+   On error PROTOCOL_UNKNOWN is returned.  */
+static protocol_t
+check_protocol (protocol_t protocol)
+{
+  switch (protocol)
+    {
+    case PROTOCOL_UNKNOWN:
+      log_error ("fixme: automatic protocol selection is not yet supported");
+      return PROTOCOL_UNKNOWN;
+    case PROTOCOL_OPENPGP:
+    case PROTOCOL_SMIME:
+      return protocol;
+    }
+
+  log_error ("%s:%s: BUG", SRCNAME, __func__);
+  return PROTOCOL_UNKNOWN;
+}
+
+
+
+/* Create a new MAPI attchment for MESSAGE which will be used to
+   prepare the MIME message.  On sucess the stream to write the data
+   to is stored at STREAM and the attchment object itself is the
+   retruned.  The caller needs to call SaveChanges.  Returns NULL on
+   failure in which case STREAM will be set to NULL.  */
+static LPATTACH
+create_mapi_attachment (LPMESSAGE message, LPSTREAM *stream)
+{
+  HRESULT hr;
+  ULONG pos;
+  SPropValue prop;
+  LPATTACH att = NULL;
+  LPUNKNOWN punk;
+
+  *stream = NULL;
+  hr = IMessage_CreateAttach (message, NULL, 0, &pos, &att);
+  if (hr)
+    {
+      log_error ("%s:%s: can't create attachment: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      return NULL;
+    }
+
+  prop.ulPropTag = PR_ATTACH_METHOD;
+  prop.Value.ul = ATTACH_BY_VALUE;
+  hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
+  if (hr)
+    {
+      log_error ("%s:%s: can't set attach method: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto failure;
+    }
+
+  /* Mark that attachment so that we know why it has been created.  */
+  if (get_gpgolattachtype_tag (message, &prop.ulPropTag) )
+    goto failure;
+  prop.Value.l = ATTACHTYPE_MOSSTEMPL;
+  hr = HrSetOneProp ((LPMAPIPROP)att, &prop);  
+  if (hr)
+    {
+      log_error ("%s:%s: can't set %s property: hr=%#lx\n",
+                 SRCNAME, __func__, "GpgOL Attach Type", hr); 
+      goto failure;
+    }
+
+
+  /* We better insert a short filename. */
+  prop.ulPropTag = PR_ATTACH_FILENAME_A;
+  prop.Value.lpszA = "gpgolXXX.dat";
+  hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
+  if (hr)
+    {
+      log_error ("%s:%s: can't set attach filename: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto failure;
+    }
+
+
+  /* Even for encrypted messages we need to set the MAPI property to
+     multipart/signed.  This seems to be a part of the trigger which
+     leads OL to process such a message in a special way.  */
+  prop.ulPropTag = PR_ATTACH_TAG;
+  prop.Value.bin.cb  = sizeof oid_mimetag;
+  prop.Value.bin.lpb = (LPBYTE)oid_mimetag;
+  hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
+  if (!hr)
+    {
+      prop.ulPropTag = PR_ATTACH_MIME_TAG_A;
+      prop.Value.lpszA = "multipart/signed";
+      hr = HrSetOneProp ((LPMAPIPROP)att, &prop);
+    }
+  if (hr)
+    {
+      log_error ("%s:%s: can't set attach mime tag: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto failure;
+    }
+  
+  punk = NULL;
+  hr = IAttach_OpenProperty (att, 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 failure;
+    }
+  *stream = (LPSTREAM)punk;
+  return att;
+
+ failure:
+  IAttach_Release (att);
+  return NULL;
+}
+
+
+/* Wrapper around IStream::Write to print an error message.  */
+static int 
+write_buffer (LPSTREAM stream, const void *data, size_t datalen)
+{
+  HRESULT hr;
+
+  hr = IStream_Write (stream, data, datalen, NULL);
+  if (hr)
+    {
+      log_error ("%s:%s: Write failed: hr=%#lx", SRCNAME, __func__, hr);
+      return -1;
+    }
+  return 0;
+}
+
+
+/* Write the string TEXT to the IStream STREAM.  Returns 0 on sucsess,
+   prints an error message and returns -1 on error.  */
+static int 
+write_string (LPSTREAM stream, const char *text)
+{
+  return write_buffer (stream, text, strlen (text));
+}
+
+
+/* Helper to write a boundary to the output stream.  The leading LF
+   will be written as well.  */
+static int
+write_boundary (LPSTREAM stream, const char *boundary, int lastone)
+{
+  int rc = write_string (stream, "\r\n--");
+  if (!rc)
+    rc = write_string (stream, boundary);
+  if (!rc)
+    rc = write_string (stream, lastone? "--\r\n":"\r\n");
+  return rc;
+}
+
+
+/* Write DATALEN bytes of DATA to STREAM in base64 encoding.  This
+   creates a complete Base64 chunk including the trailing fillers.  */
+static int
+write_b64 (LPSTREAM stream, const void *data, size_t datalen)
+{
+  int rc;
+  const unsigned char *p;
+  unsigned char inbuf[4];
+  int idx, quads;
+  char outbuf[4];
+
+  idx = quads = 0;
+  for (p = data; datalen; p++, datalen--)
+    {
+      inbuf[idx++] = *p;
+      if (idx > 2)
+        {
+          outbuf[0] = bintoasc[(*inbuf>>2)&077];
+          outbuf[1] = bintoasc[(((*inbuf<<4)&060)|((inbuf[1] >> 4)&017))&077];
+          outbuf[2] = bintoasc[(((inbuf[1]<<2)&074)|((inbuf[2]>>6)&03))&077];
+          outbuf[3] = bintoasc[inbuf[2]&077];
+          if ((rc = write_buffer (stream, outbuf, 4)))
+            return rc;
+          idx = 0;
+          if (++quads >= (64/4)) 
+            {
+              quads = 0;
+              if ((rc = write_buffer (stream, "\r\n", 2)))
+                return rc;
+            }
+        }
+    }
+
+  if (idx)
+    {
+      outbuf[0] = bintoasc[(*inbuf>>2)&077];
+      if (idx == 1)
+        {
+          outbuf[1] = bintoasc[((*inbuf<<4)&060)&077];
+          outbuf[2] = '=';
+          outbuf[3] = '=';
+        }
+      else 
+        { 
+          outbuf[1] = bintoasc[(((*inbuf<<4)&060)|((inbuf[1]>>4)&017))&077];
+          outbuf[2] = bintoasc[((inbuf[1]<<2)&074)&077];
+          outbuf[3] = '=';
+        }
+      if ((rc = write_buffer (stream, outbuf, 4)))
+        return rc;
+      ++quads;
+    }
+
+  if (quads) 
+    if ((rc = write_buffer (stream, "\r\n", 2)))
+      return rc;
+
+  return 0;
+}
+
+/* Write DATALEN bytes of DATA to STREAM in quoted-prinable encoding. */
+static int
+write_qp (LPSTREAM stream, const void *data, size_t datalen)
+{
+  int rc;
+  const unsigned char *p;
+  char outbuf[80];  /* We only need 76 octect + 2 for the lineend. */
+  int outidx;
+
+  /* Check whether the current character is followed by a line ending.
+     Note that the end of the etxt also counts as a lineending */
+#define nextlf_p() ((datalen > 2 && p[1] == '\r' && p[2] == '\n') \
+                    || (datalen > 1 && p[1] == '\n')              \
+                    || datalen == 1 )
+
+  /* Macro to insert a soft line break if needed.  */
+# define do_softlf(n) \
+          do {                                                        \
+            if (outidx + (n) > 76                                     \
+                || (outidx + (n) == 76 && !nextlf_p()))               \
+              {                                                       \
+                outbuf[outidx++] = '=';                               \
+                outbuf[outidx++] = '\r';                              \
+                outbuf[outidx++] = '\n';                              \
+                if ((rc = write_buffer (stream, outbuf, outidx)))     \
+                  return rc;                                          \
+                outidx = 0;                                           \
+              }                                                       \
+          } while (0)
+              
+  outidx = 0;
+  for (p = data; datalen; p++, datalen--)
+    {
+      if ((datalen > 1 && *p == '\r' && p[1] == '\n') || *p == '\n')
+        {
+          /* Line break.  */
+          outbuf[outidx++] = '\r';
+          outbuf[outidx++] = '\n';
+          if ((rc = write_buffer (stream, outbuf, outidx)))
+            return rc;
+          outidx = 0;
+          if (*p == '\r')
+            {
+              p++;
+              datalen--;
+            }
+        }
+      else if (*p == '\t' || *p == ' ')
+        {
+          /* Check whether tab or space is followed by a line break
+             which forbids verbatim encoding.  If we are already at
+             the end of the buffer we take that as a line end too. */
+          if (nextlf_p())
+            {
+              do_softlf (3);
+              outbuf[outidx++] = '=';
+              outbuf[outidx++] = tohex ((*p>>4)&15);
+              outbuf[outidx++] = tohex (*p&15);
+            }
+          else
+            {
+              do_softlf (1);
+              outbuf[outidx++] = *p;
+            }
+
+        }
+      else if (!outidx && *p == '.' && nextlf_p () )
+        {
+          /* We better protect a line with just a single dot.  */
+          outbuf[outidx++] = '=';
+          outbuf[outidx++] = tohex ((*p>>4)&15);
+          outbuf[outidx++] = tohex (*p&15);
+        }
+      else if (*p >= '!' && *p <= '~' && *p != '=')
+        {
+          do_softlf (1);
+          outbuf[outidx++] = *p;
+        }
+      else
+        {
+          do_softlf (3);
+          outbuf[outidx++] = '=';
+          outbuf[outidx++] = tohex ((*p>>4)&15);
+          outbuf[outidx++] = tohex (*p&15);
+        }
+    }
+  if (outidx)
+    {
+      outbuf[outidx++] = '\r';
+      outbuf[outidx++] = '\n';
+      if ((rc = write_buffer (stream, outbuf, outidx)))
+        return rc;
+    }
+
+# undef do_softlf
+# undef nextlf_p
+  return 0;
+}
+
+
+/* Write DATALEN bytes of DATA to STREAM in plain ascii encoding. */
+static int
+write_plain (LPSTREAM stream, const void *data, size_t datalen)
+{
+  int rc;
+  const unsigned char *p;
+  char outbuf[100];
+  int outidx;
+
+  outidx = 0;
+  for (p = data; datalen; p++, datalen--)
+    {
+      if ((datalen > 1 && *p == '\r' && p[1] == '\n') || *p == '\n')
+        {
+          outbuf[outidx++] = '\r';
+          outbuf[outidx++] = '\n';
+          if ((rc = write_buffer (stream, outbuf, outidx)))
+            return rc;
+          outidx = 0;
+          if (*p == '\r')
+            {
+              p++;
+              datalen--;
+            }
+        }
+      else if (!outidx && *p == '.'
+               && ( (datalen > 2 && p[1] == '\r' && p[2] == '\n') 
+                    || (datalen > 1 && p[1] == '\n') 
+                    || datalen == 1))
+        {
+          /* Better protect a line with just a single dot.  We do
+             this by adding a space.  */
+          outbuf[outidx++] = *p;
+          outbuf[outidx++] = ' ';
+        }
+      else if (outidx > 80)
+        {
+          /* We should never be called for too long lines - QP should
+             have been used.  */
+          log_error ("%s:%s: BUG: line longer than exepcted",
+                     SRCNAME, __func__);
+          return -1; 
+        }
+      else
+        outbuf[outidx++] = *p;
+    }
+
+  if (outidx)
+    {
+      outbuf[outidx++] = '\r';
+      outbuf[outidx++] = '\n';
+      if ((rc = write_buffer (stream, outbuf, outidx)))
+        return rc;
+    }
+
+  return 0;
+}
+
+
+/* Infer the conent type from DATA and FILENAME.  The return value is
+   a static string there won't be an error return.  In case Bae 64
+   encoding is required for the type true will be stored at FORCE_B64;
+   however, this is only a shortcut and if that is not set, the caller
+   should infer the encoding by otehr means. */
+static const char *
+infer_content_type (const char *data, size_t datalen, const char *filename,
+                    int is_mapibody, int *force_b64)
+{
+  static struct {
+    char b64;
+    const char *suffix;
+    const char *ct;
+  } suffix_table[] = 
+    {
+      { 1, "3gp",   "video/3gpp" },
+      { 1, "abw",   "application/x-abiword" },
+      { 1, "ai",    "application/postscript" },
+      { 1, "au",    "audio/basic" },
+      { 1, "bin",   "application/octet-stream" },
+      { 1, "class", "application/java-vm" },
+      { 1, "cpt",   "application/mac-compactpro" },
+      { 0, "css",   "text/css" },
+      { 0, "csv",   "text/comma-separated-values" },
+      { 1, "deb",   "application/x-debian-package" },
+      { 1, "dl",    "video/dl" },
+      { 1, "doc",   "application/msword" },
+      { 1, "dv",    "video/dv" },
+      { 1, "dvi",   "application/x-dvi" },
+      { 1, "eml",   "message/rfc822" },
+      { 1, "eps",   "application/postscript" },
+      { 1, "fig",   "application/x-xfig" },
+      { 1, "flac",  "application/x-flac" },
+      { 1, "fli",   "video/fli" },
+      { 1, "gif",   "image/gif" },
+      { 1, "gl",    "video/gl" },
+      { 1, "gnumeric", "application/x-gnumeric" },
+      { 1, "hqx",   "application/mac-binhex40" },
+      { 1, "hta",   "application/hta" },
+      { 0, "htm",   "text/html" },
+      { 0, "html",  "text/html" },
+      { 0, "ics",   "text/calendar" },
+      { 1, "jar",   "application/java-archive" },
+      { 1, "jpeg",  "image/jpeg" },
+      { 1, "jpg",   "image/jpeg" },
+      { 1, "js",    "application/x-javascript" },
+      { 1, "latex", "application/x-latex" },
+      { 1, "lha",   "application/x-lha" },
+      { 1, "lzh",   "application/x-lzh" },
+      { 1, "lzx",   "application/x-lzx" },
+      { 1, "m3u",   "audio/mpegurl" },
+      { 1, "m4a",   "audio/mpeg" },
+      { 1, "mdb",   "application/msaccess" },
+      { 1, "midi",  "audio/midi" },
+      { 1, "mov",   "video/quicktime" },
+      { 1, "mp2",   "audio/mpeg" },
+      { 1, "mp3",   "audio/mpeg" },
+      { 1, "mp4",   "video/mp4" },
+      { 1, "mpeg",  "video/mpeg" },
+      { 1, "mpega", "audio/mpeg" },
+      { 1, "mpg",   "video/mpeg" },
+      { 1, "mpga",  "audio/mpeg" },
+      { 1, "msi",   "application/x-msi" },
+      { 1, "mxu",   "video/vnd.mpegurl" },
+      { 1, "nb",    "application/mathematica" },
+      { 1, "oda",   "application/oda" },
+      { 1, "odb",   "application/vnd.oasis.opendocument.database" },
+      { 1, "odc",   "application/vnd.oasis.opendocument.chart" },
+      { 1, "odf",   "application/vnd.oasis.opendocument.formula" },
+      { 1, "odg",   "application/vnd.oasis.opendocument.graphics" },
+      { 1, "odi",   "application/vnd.oasis.opendocument.image" },
+      { 1, "odm",   "application/vnd.oasis.opendocument.text-master" },
+      { 1, "odp",   "application/vnd.oasis.opendocument.presentation" },
+      { 1, "ods",   "application/vnd.oasis.opendocument.spreadsheet" },
+      { 1, "odt",   "application/vnd.oasis.opendocument.text" },
+      { 1, "ogg",   "application/ogg" },
+      { 1, "otg",   "application/vnd.oasis.opendocument.graphics-template" },
+      { 1, "oth",   "application/vnd.oasis.opendocument.text-web" },
+      { 1, "otp",  "application/vnd.oasis.opendocument.presentation-template"},
+      { 1, "ots",   "application/vnd.oasis.opendocument.spreadsheet-template"},
+      { 1, "ott",   "application/vnd.oasis.opendocument.text-template" },
+      { 1, "pdf",   "application/pdf" },
+      { 1, "png",   "image/png" },
+      { 1, "pps",   "application/vnd.ms-powerpoint" },
+      { 1, "ppt",   "application/vnd.ms-powerpoint" },
+      { 1, "prf",   "application/pics-rules" },
+      { 1, "ps",    "application/postscript" },
+      { 1, "qt",    "video/quicktime" },
+      { 1, "rar",   "application/rar" },
+      { 1, "rdf",   "application/rdf+xml" },
+      { 1, "rpm",   "application/x-redhat-package-manager" },
+      { 0, "rss",   "application/rss+xml" },
+      { 1, "ser",   "application/java-serialized-object" },
+      { 0, "sh",    "application/x-sh" },
+      { 0, "shtml", "text/html" },
+      { 1, "sid",   "audio/prs.sid" },
+      { 0, "smil",  "application/smil" },
+      { 1, "snd",   "audio/basic" },
+      { 0, "svg",   "image/svg+xml" },
+      { 1, "tar",   "application/x-tar" },
+      { 0, "texi",  "application/x-texinfo" },
+      { 0, "texinfo", "application/x-texinfo" },
+      { 1, "tif",   "image/tiff" },
+      { 1, "tiff",  "image/tiff" },
+      { 1, "torrent", "application/x-bittorrent" },
+      { 1, "tsp",   "application/dsptype" },
+      { 0, "vrml",  "model/vrml" },
+      { 1, "vsd",   "application/vnd.visio" },
+      { 1, "wp5",   "application/wordperfect5.1" },
+      { 1, "wpd",   "application/wordperfect" },
+      { 0, "xhtml", "application/xhtml+xml" },
+      { 1, "xlb",   "application/vnd.ms-excel" },
+      { 1, "xls",   "application/vnd.ms-excel" },
+      { 1, "xlt",   "application/vnd.ms-excel" },
+      { 0, "xml",   "application/xml" },
+      { 0, "xsl",   "application/xml" },
+      { 0, "xul",   "application/vnd.mozilla.xul+xml" },
+      { 1, "zip",   "application/zip" },
+      { 0, NULL, NULL }
+    };
+  int i;
+  char suffix_buffer[12+1];
+  const char *suffix;
+
+  *force_b64 = 0;
+  suffix = filename? strrchr (filename, '.') : NULL;
+  if (suffix && strlen (suffix) < sizeof suffix_buffer -1 )
+    {
+      suffix++;
+      for (i=0; i < sizeof suffix_buffer - 1; i++)
+        suffix_buffer[i] = tolower (*(const unsigned char*)suffix);
+      suffix_buffer[i] = 0;
+      for (i=0; suffix_table[i].suffix; i++)
+        if (!strcmp (suffix_table[i].suffix, suffix_buffer))
+          {
+            if (suffix_table[i].b64)
+              *force_b64 = 1;
+            return suffix_table[i].ct;
+          }
+    }
+
+  /* Not found via filename, look at the content.  */
+
+  if (is_mapibody)
+    {
+      /* Fixme:  This is too simple. */
+      if (datalen > 6  && (!memcmp (data, "<html>", 6)
+                           ||!memcmp (data, "<HTML>", 6)))
+        return "text/html";
+      return "text/plain";
+    }
+
+  return "application/octet-stream";
+}
+
+/* Figure out the best encoding to be used for the part.  Return values are
+     0: Plain ASCII.
+     1: Quoted Printable
+     2: Base64  */
+static const int
+infer_content_encoding (const void *data, size_t datalen)
+{
+  const unsigned char *p;
+  int need_qp;
+  size_t len, maxlen, highbin, lowbin, ntotal;
+
+  ntotal = datalen;
+  len = maxlen = lowbin = highbin = 0;
+  need_qp = 0;
+  for (p = data; datalen; p++, datalen--)
+    {
+      len++;
+      if ((*p & 0x80))
+        highbin++;
+      else if ((datalen > 1 && *p == '\r' && p[1] == '\n') || *p == '\n')
+        {
+          len--;
+          if (len > maxlen)
+            maxlen = len;
+          len = 0;
+        }
+      else if (*p == '\r')
+        {
+          /* CR not followed by a linefeed. */
+          lowbin++;
+        }
+      else if (*p == '\t' || *p == ' ' || *p == '\f')
+        ;
+      else if (*p < ' ' || *p == 127)
+        lowbin++;
+      else if (len == 1 && datalen > 2
+               && *p == '-' && p[1] == '-' && p[2] == ' '
+               && ( (datalen > 4 && p[3] == '\r' && p[4] == '\n') 
+                    || (datalen > 3 && p[3] == '\n') 
+                    || datalen == 3))
+        {
+          /* This is a "-- \r\n" line, thus it indicates the usual
+             signature line delimiter.  We need to protect the
+             trailing space.  */
+          need_qp = 1;
+        }
+      else if (len == 1 && datalen > 5 && !memcmp (p, "--=-=", 5))
+        {
+          /* This look pretty much like a our own boundary.
+             We better protect it by forcing QP encoding.  */
+          need_qp = 1;
+        }
+    }
+  if (len > maxlen)
+    maxlen = len;
+
+  if (maxlen <= 76 && !lowbin && !highbin && !need_qp)
+    return 0; /* Plain ASCII is sufficient.  */
+
+  /* Somewhere in the Outlook documentation 20% is mentioned as
+     discriminating value for Base64.  Though our counting won't be
+     identical we use that value to behave closely to it. */
+  if (ntotal && ((float)(lowbin+highbin))/ntotal < 0.20)
+    return 1; /* Use quoted printable.  */
+  
+  return 2;   /* Use base64.  */
+}
+
+
+
+
+
+
+/* Write a MIME part to STREAM.  The BOUNDARY is written first the
+   DATA is analyzed and appropriate headers are written.  If FILENAME
+   is given it will be added to the part's header. IS_MAPIBODY should
+   be true if teh data has been retrieved from the body property. */
+static int
+write_part (LPSTREAM stream, const char *data, size_t datalen,
+            const char *boundary, const char *filename, int is_mapibody)
+{
+  int rc;
+  const char *ct;
+  int use_b64, use_qp, is_text;
+
+  ct = infer_content_type (data, datalen, filename, is_mapibody, &use_b64);
+  use_qp = 0;
+  if (!use_b64)
+    {
+      switch (infer_content_encoding (data, datalen))
+        {
+        case 0: break;
+        case 1: use_qp = 1; break;
+        default: use_b64 = 1; break;
+        }
+    }
+  is_text = !strncmp (ct, "text/", 5);
+
+  if ((rc = write_boundary (stream, boundary, 0)))
+    return rc;
+  if (!(rc = write_string (stream, "Content-Type: ")))
+    if (!(rc = write_string (stream, ct)))
+      rc = write_string (stream, is_text? ";\r\n":"\r\n");
+  if (rc)
+    return rc;
+
+  /* OL inserts a charset parameter in many cases, so we do it right
+     away for all text parts.  We can assume us-ascii if no special
+     encoding is required.  */
+  if (is_text)
+    if ((rc = write_string (stream, (!use_qp && !use_b64)?
+                            "\tcharset=\"us-ascii\"\r\n":
+                            "\tcharset=\"utf-8\"\r\n")))
+      return rc;
+    
+  /* Note that we need to output even 7bit because OL inserts that
+     anyway.  */
+  if (!(rc = write_string (stream, "Content-Transfer-Encoding: ")))
+    rc = write_string (stream, (use_b64? "base64\r\n":
+                                use_qp? "quoted-printable\r\n":"7bit\r\n"));
+  if (rc)
+    return rc;
+  
+  /* Write delimiter.  */
+  if ((rc = write_string (stream, "\r\n")))
+    return rc;
+  
+  /* Write the content.  */
+  if (use_b64)
+    rc = write_b64 (stream, data, datalen);
+  else if (use_qp)
+    rc = write_qp (stream, data, datalen);
+  else
+    rc = write_plain (stream, data, datalen);
+
+  return rc;
+}
+
+
+/* Return the number of attachments in TABLE to be put into the MIME
+   message.  */
+static int
+count_usable_attachments (mapi_attach_item_t *table)
+{
+  int idx, count = 0;
+  
+  if (table)
+    for (idx=0; !table[idx].end_of_table; idx++)
+      if (table[idx].attach_type == ATTACHTYPE_UNKNOWN
+          && table[idx].method == ATTACH_BY_VALUE)
+        count++;
+  return count;
+}
+
+/* Write old all attachments from TABLE separated by BOUNDARY to
+   STREAM.  This function needs to be syncronized with
+   count_usable_attachments.  */
+static int
+write_attachments (LPSTREAM stream, 
+                   LPMESSAGE message, mapi_attach_item_t *table, 
+                   const char *boundary)
+{
+  int idx, rc;
+  char *buffer;
+  size_t buflen;
+
+  if (table)
+    for (idx=0; !table[idx].end_of_table; idx++)
+      if (table[idx].attach_type == ATTACHTYPE_UNKNOWN
+          && table[idx].method == ATTACH_BY_VALUE)
+        {
+          buffer = mapi_get_attach (message, table+idx, &buflen);
+          if (!buffer)
+            log_debug ("Attachment at index %d not found\n", idx);
+          else
+            log_debug ("Attachment at index %d: length=%d\n", idx, (int)buflen);
+          if (!buffer)
+            return -1;
+          rc = write_part (stream, buffer, buflen, boundary,
+                           table[idx].filename, 0);
+          xfree (buffer);
+        }
+  return 0;
+}
+
+
+/* Delete all attachments from TABLE except for the one we just created */
+static int
+delete_all_attachments (LPMESSAGE message, mapi_attach_item_t *table)
+{
+  HRESULT hr;
+  int idx;
+
+  if (table)
+    for (idx=0; !table[idx].end_of_table; idx++)
+      {
+        if (table[idx].attach_type == ATTACHTYPE_MOSSTEMPL)
+          continue;
+        hr = IMessage_DeleteAttach (message, table[idx].mapipos, 0, NULL, 0);
+        if (hr)
+          {
+            log_error ("%s:%s: DeleteAttach failed: hr=%#lx\n",
+                       SRCNAME, __func__, hr); 
+            return -1;
+          }
+      }
+  return 0;
+}
+
+
+
+/* Sign the MESSAGE using PROTOCOL.  If PROTOCOL is PROTOCOL_UNKNOWN
+   the engine decides what protocol to use.  On return MESSAGE is
+   modified so that sending it will result in a properly MOSS (that is
+   PGP or S/MIME) signed message.  On failure the function tries to
+   keep the original message intact but there is no 100% guarantee for
+   it. */
+int 
+mime_sign (LPMESSAGE message, protocol_t protocol)
+{
+  int rc;
+  HRESULT hr;
+  LPATTACH outattach;
+  LPSTREAM outstream;
+  char boundary[BOUNDARYSIZE+1];
+  char inner_boundary[BOUNDARYSIZE+1];
+  SPropValue prop;
+  mapi_attach_item_t *att_table = NULL;
+  char *body = NULL;
+  int n_att_usable;
+
+  protocol = check_protocol (protocol);
+  if (protocol == PROTOCOL_UNKNOWN)
+    return -1;
+
+  outattach = create_mapi_attachment (message, &outstream);
+  if (!outattach)
+    return -1;
+
+  /* Get the attachment info and the body.  */
+  body = mapi_get_body (message, NULL);
+  if (body && !*body)
+    {
+      xfree (body);
+      body = NULL;
+    }
+  att_table = mapi_create_attach_table (message, 0);
+  n_att_usable = count_usable_attachments (att_table);
+  if (!n_att_usable && !body)
+    {
+      log_debug ("%s:%s: can't sign an empty message\n", SRCNAME, __func__);
+      goto failure;
+    }
+
+  /* Write the top header.  */
+  generate_boundary (boundary);
+  rc = write_string (outstream, ("MIME-Version: 1.0\r\n"
+                                 "Content-Type: multipart/signed;\r\n"
+                                 "\tprotocol=\"application/"));
+  if (!rc)
+    rc = write_string (outstream, 
+                       (protocol == PROTOCOL_OPENPGP
+                        ? "pgp-signature" 
+                        : "pkcs7-signature"));
+  if (!rc)
+    rc = write_string (outstream, ("\";\r\n\tboundary=\""));
+  if (!rc)
+    rc = write_string (outstream, boundary);
+  if (!rc)
+    rc = write_string (outstream, "\"\r\n");
+  if (rc)
+    goto failure;
+
+  if ((body && n_att_usable) || n_att_usable > 1)
+    {
+      /* A body and at least one attachment or more than one attachment  */
+      generate_boundary (inner_boundary);
+      rc = write_boundary (outstream, boundary, 0);
+      if (!rc)
+        rc = write_string (outstream, ("Content-Type: multipart/mixed;\r\n"
+                                       "\tboundary=\""));
+      if (!rc)
+        rc = write_string (outstream, inner_boundary);
+      if (!rc)
+        rc = write_string (outstream, "\"\r\n");
+      if (rc)
+        goto failure;
+    }
+  else
+    {
+      /* Only one part.  */
+      *inner_boundary = 0;
+    }
+
+  if (body)
+    rc = write_part (outstream, body, strlen (body), 
+                     *inner_boundary? inner_boundary : boundary, NULL, 1);
+  if (!rc && n_att_usable)
+    rc = write_attachments (outstream, message, att_table,
+                            *inner_boundary? inner_boundary : boundary);
+  if (rc)
+    goto failure;
+
+  /* Finish the possible multipart/mixed. */
+  if (*inner_boundary)
+    {
+      rc = write_boundary (outstream, inner_boundary, 1);
+      if (rc)
+        goto failure;
+    }
+
+  /* Write signature attachment.  We don't write it directly but a
+     palceholder there.  This spaceholder starts with the prefix of a
+     boundary so that it won't accidently occur in the actual content. */
+  if ((rc = write_boundary (outstream, boundary, 0)))
+    goto failure;
+
+  if ((rc = write_string (outstream, 
+                          (protocol == PROTOCOL_OPENPGP)?
+                          "Content-Type: application/pgp-signature\r\n":
+                          "Content-Type: application/pkcs7-signature\r\n")))
+    goto failure;
+
+  /* If we would add "Content-Transfer-Encoding: 7bit\r\n" to this
+     atatchment, Outlooks does not processed with sending and even
+     does not return any error.  A wild guess is that while OL adds
+     this header itself, it detects that it already exists and somehow
+     gets into a problem.  It is not a problem with the other parts,
+     though.  Hmmm, triggered by the top levels CT protocol parameter?
+     Any way, it is not required that we add it as we won't hash
+     it.  */
+
+
+  if ((rc = write_string (outstream, "\r\n")))
+    goto failure;
+
+  if ((rc = write_string (outstream, "--=-=@SIGNATURE@\r\n\r\n")))
+    goto failure;
+
+  /* Write the final boundary and finish the attachment.  */
+  if ((rc = write_boundary (outstream, boundary, 1)))
+    goto failure;
+
+  hr = IStream_Commit (outstream, 0);
+  if (hr)
+    {
+      log_error ("%s:%s: Commiting output stream failed: hr=%#lx",
+                 SRCNAME, __func__, hr);
+      goto failure;
+    }
+  IStream_Release (outstream);
+  outstream = NULL;
+  hr = IAttach_SaveChanges (outattach, KEEP_OPEN_READWRITE);
+  if (hr)
+    {
+      log_error ("%s:%s: SaveChanges of the attachment failed: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto failure;
+    }
+  IAttach_Release (outattach);
+  outattach = NULL;
+
+  /* Set the message class.  */
+  prop.ulPropTag = PR_MESSAGE_CLASS_A;
+  prop.Value.lpszA = "IPM.Note.SMIME.MultipartSigned"; 
+  hr = IMessage_SetProps (message, 1, &prop, NULL);
+  if (hr)
+    {
+      log_error ("%s:%s: error setting the message class: hr=%#lx\n",
+                 SRCNAME, __func__, hr);
+      goto failure;
+    }
+
+  /* Now delete all parts of the MAPI message except for the one
+     attachment we just created.  */
+  if ((rc = delete_all_attachments (message, att_table)))
+    goto failure;
+  {
+    /* Delete the body parts.  We don't return any error because there
+       might be no body part at all.  To avoid aliasing problems when
+       using static initialized array (SizedSPropTagArray macro) we
+       call it two times in a row.  */
+    SPropTagArray proparray;
+
+    proparray.cValues = 1;
+    proparray.aulPropTag[0] = PR_BODY;
+    IMessage_DeleteProps (message, &proparray, NULL);
+    proparray.cValues = 1;
+    proparray.aulPropTag[0] = PR_BODY_HTML;
+    IMessage_DeleteProps (message, &proparray, NULL);
+  }
+  
+  /* Save the Changes.  */
+  hr = IMessage_SaveChanges (message, KEEP_OPEN_READWRITE|FORCE_SAVE);
+  if (hr)
+    {
+      log_error ("%s:%s: SaveChanges to the message failed: hr=%#lx\n",
+                 SRCNAME, __func__, hr); 
+      goto failure;
+    }
+
+  xfree (body);
+  mapi_release_attach_table (att_table);
+  return 0;
+
+ failure:
+  if (outstream)
+    {
+      IStream_Revert (outstream);
+      IStream_Release (outstream);
+    }
+  if (outattach)
+    {
+      /* Fixme: Should we try to delete it or is tehre a Revert method? */
+      IAttach_Release (outattach);
+    }
+  xfree (body);
+  mapi_release_attach_table (att_table);
+  return -1;
+}
+
+
+int 
+mime_encrypt (LPMESSAGE message, protocol_t protocol)
+{
+  return -1;
+}
+
+
+int 
+mime_sign_encrypt (LPMESSAGE message, protocol_t protocol)
+{
+  return -1;
+}
diff --git a/src/mimemaker.h b/src/mimemaker.h
new file mode 100644 (file)
index 0000000..dbec526
--- /dev/null
@@ -0,0 +1,39 @@
+/* mimemaker.h - Construct MIME from MAPI
+ *     Copyright (C) 2007 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.
+ * 
+ * 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.
+ */
+
+#ifndef MIMEMAKER_H
+#define MIMEMAKER_H
+#ifdef __cplusplus
+extern "C" {
+#if 0
+}
+#endif
+#endif
+
+int mime_sign (LPMESSAGE message, protocol_t protocol);
+int mime_encrypt (LPMESSAGE message, protocol_t protocol);
+int mime_sign_encrypt (LPMESSAGE message, protocol_t protocol);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*MIMEMAKER_H*/
index 4d35337..58bdd87 100644 (file)
@@ -20,7 +20,8 @@
  */
 
 /*
-   EXPLAIN what we are doing here.
+   Fixme: Explain how the this parser works and how it fits into the
+   whole picture.
 */
    
 
@@ -66,14 +67,6 @@ static const char oid_mimetag[] =
 #define LINEBUFSIZE 2000
 
 
-typedef enum
-  {
-    PROTOCOL_UNKNOWN = 0,
-    PROTOCOL_OPENPGP,
-    PROTOCOL_SMIME
-  }
-protocol_t;
-
 
 /* To keep track of the MIME message structures we use a linked list
    with each item corresponding to one part. */
@@ -318,7 +311,7 @@ start_attachment (mime_context_t ctx, int is_body)
 
 
   /* The body attachment is special and should not be show in the list
-     of atatchments.  */
+     of attachments.  */
   if (is_body)
     {
       prop.ulPropTag = PR_ATTACHMENT_HIDDEN;
@@ -560,7 +553,9 @@ t2body (mime_context_t ctx, rfc822parse_t msg)
 
   /* Process the Content-type and all its parameters.  */
   ctmain = ctsub = NULL;
-  field = rfc822parse_parse_field (msg, "Content-Type", -1);
+  field = rfc822parse_parse_field (msg, "GnuPG-Content-Type", -1);
+  if (!field)
+    field = rfc822parse_parse_field (msg, "Content-Type", -1);
   if (field)
     ctmain = rfc822parse_query_media_type (field, &ctsub);
   if (!ctmain)
@@ -935,6 +930,7 @@ mime_verify (const char *message, size_t messagelen,
   while ( (s = memchr (message, '\n', messagelen)) )
     {
       len = s - message + 1;
+      log_debug ("passing '%.*s'\n", (int)len, message);
       plaintext_handler (ctx, message, len);
       if (ctx->parser_error || ctx->line_too_long)
         {
index 44e13a0..6497b60 100644 (file)
@@ -1,4 +1,4 @@
-/* mimeparse.h - Multipart MIME parser.
+/* mimeparser.h - MIME parser.
  *     Copyright (C) 2007 g10 Code GmbH
  *
  * This file is part of GpgOL.
@@ -18,8 +18,8 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  * 02110-1301, USA.
  */
-#ifndef SMIME_H
-#define SMIME_H
+#ifndef MIMEPARSER_H
+#define MIMEPARSER_H
 
 #ifdef __cplusplus
 extern "C" {
@@ -41,4 +41,4 @@ int mime_decrypt (LPSTREAM instream, LPMESSAGE mapi_message, int is_smime,
 #ifdef __cplusplus
 }
 #endif
-#endif /*SMIME_H*/
+#endif /*MIMEPARSER_H*/
index 3a5bb95..1a4f860 100644 (file)
@@ -1021,6 +1021,11 @@ DECLARE_INTERFACE_(ISpoolerHook, IUnknown)
                 (This)->lpVtbl->CreateAttach ((This),(a),(b),(c),(d))
 #define IMessage_DeleteAttach(This,a,b,c,d) \
                 (This)->lpVtbl->DeleteAttach ((This),(a),(b),(c),(d))
+#define IMessage_SetProps(This,a,b,c) \
+                (This)->lpVtbl->SetProps ((This),(a),(b),(c))
+#define IMessage_DeleteProps(This,a,b) \
+                (This)->lpVtbl->DeleteProps ((This),(a),(b))
+#define IMessage_SaveChanges(This,a) (This)->lpVtbl->SaveChanges ((This),(a))
 
 #define IAttach_Release(This)  (This)->lpVtbl->Release ((This))
 #define IAttach_SaveChanges(This,a) (This)->lpVtbl->SaveChanges ((This),(a))
index dd129d2..c77b2c7 100644 (file)
@@ -30,7 +30,6 @@
 #include "myexchext.h"
 #include "display.h"
 #include "common.h"
-#include "gpgmsg.hh"
 #include "msgcache.h"
 #include "engine.h"
 #include "mapihelp.h"
index 8a5a806..042f798 100644 (file)
@@ -1,4 +1,4 @@
-/* olflange.cpp - Flange between Outlook and the GpgMsg class
+/* olflange.cpp - Connect GpgOL to Outlook
  *     Copyright (C) 2001 G Data Software AG, http://www.gdata.de
  *     Copyright (C) 2004, 2005, 2007 g10 Code GmbH
  * 
@@ -37,7 +37,6 @@
 
 #include "common.h"
 #include "display.h"
-#include "gpgmsg.hh"
 #include "msgcache.h"
 #include "engine.h"
 #include "mapihelp.h"
@@ -315,8 +314,6 @@ GpgolExt::GpgolExt (void)
 
   if (!g_initdll)
     {
-      if (opt.compat.auto_decrypt)
-        watcher_init_hook ();
       read_options ();
       op_init ();
       g_initdll = TRUE;
@@ -339,7 +336,6 @@ GpgolExt::~GpgolExt (void)
     {
       if (g_initdll)
         {
-         watcher_free_hook ();
           op_deinit ();
           write_options ();
           g_initdll = FALSE;
index b1dc6f6..59a7f94 100644 (file)
@@ -19,6 +19,8 @@
  * 02110-1301, USA.
  */
 
+#error not used
+
 /*
    EXPLAIN what we are doing here.
 */
index 1996e14..cfea442 100644 (file)
@@ -21,6 +21,8 @@
 #ifndef PGPMIME_H
 #define PGPMIME_H
 
+#error not used
+
 #ifdef __cplusplus
 extern "C" {
 #if 0
index 4d3c2d1..7f8e5b2 100644 (file)
@@ -30,7 +30,6 @@
 #include "myexchext.h"
 #include "common.h"
 #include "display.h"
-#include "gpgmsg.hh"
 #include "msgcache.h"
 #include "engine.h"
 #include "mapihelp.h"
index aa1c0c4..82d0b17 100644 (file)
@@ -342,7 +342,9 @@ transition_to_body (rfc822parse_t msg)
   if (!rc)
     {
       /* Store the boundary if we have multipart type. */
-      ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
+      ctx = rfc822parse_parse_field (msg, "GnuPG-Content-Type", -1);
+      if (!ctx)
+        ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
       if (ctx)
         {
           const char *s;
@@ -764,6 +766,7 @@ parse_field (HDR_LINE hdr)
     const unsigned char *name;
     size_t namelen;
   } tspecial_header[] = {
+    { "GnuPG-Content-Type", 18},
     { "Content-Type", 12},
     { "Content-Transfer-Encoding", 25},
     { "Content-Disposition", 19},
index 5e0ed26..c567203 100644 (file)
@@ -30,7 +30,6 @@
 #include "myexchext.h"
 #include "display.h"
 #include "common.h"
-#include "gpgmsg.hh"
 #include "msgcache.h"
 #include "engine.h"
 #include "mapihelp.h"
index 7a0ebd2..9c6e601 100644 (file)
@@ -107,6 +107,21 @@ int write_options (void);
 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
 #define xtoi_4(p)   ((xtoi_2(p) * 256) + xtoi_2((p)+2))
 
+#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
+
+/***** Inline functions.  ****/
+
+/* Return true if LINE consists only of white space (up to and
+   including the LF). */
+static inline int
+trailing_ws_p (const char *line)
+{
+  for ( ; *line && *line != '\n'; line++)
+    if (*line != ' ' && *line != '\t' && *line != '\r')
+      return 0;
+  return 1;
+}
+
 
 /*****  Missing functions.  ****/
 
index 64a5f99..223cb72 100644 (file)
@@ -19,6 +19,8 @@
  * 02110-1301, USA.
  */
 
+#error not anymore used.
+
 #include <config.h>
 #include <windows.h>
 #include <stdio.h>
@@ -28,7 +30,7 @@
 #include "myexchext.h"
 #include "mymapitags.h"
 #include "common.h"
-#include "gpgmsg.hh"
+
 
 /* Exchange callback context to retrieve the last message. */
 static LPEXCHEXTCALLBACK g_cb = NULL;