Implement encryption without MAPI data
authorAndre Heinecke <aheinecke@intevation.de>
Thu, 27 Oct 2016 15:48:06 +0000 (17:48 +0200)
committerAndre Heinecke <aheinecke@intevation.de>
Thu, 27 Oct 2016 15:48:06 +0000 (17:48 +0200)
* src/attachment.cpp (Attachment::get_data_string): New helper.
* src/attachment.h: Declare get_data_string.
* src/mail.cpp, src/mail.h (Mail::remove_attachments)
(Mail::add_attachments, get_string, Mail::get_body)
(Mail::get_html_body, Mail::get_recipients): New.
* src/mailitem-events.cpp (request_send): Comment out for now.
(EVENT_SINK_INVOKE): Encrypt in send event.
* src/mimemaker.cpp (sink_data_write): New to work with GpgME::Data.
(encrypt_sign_mail): New works based on Objects.

--
This still uses the engine style encryption. In this first impl
only encryption is supported. Can do both inline and MIME.

src/attachment.cpp
src/attachment.h
src/mail.cpp
src/mail.h
src/mailitem-events.cpp
src/mimemaker.cpp
src/mimemaker.h

index fc528c7..dc9d633 100644 (file)
@@ -56,3 +56,21 @@ Attachment::get_data()
 {
   return m_data;
 }
+
+const std::string &
+Attachment::get_data_string()
+{
+  if (!m_data_string.empty())
+    {
+      return m_data_string;
+    }
+  char buf[4096];
+  m_data.seek (0, SEEK_SET);
+  size_t nread;
+  m_data_string = std::string();
+  while ((nread = m_data.read (buf, 4096)) > 0)
+    {
+      m_data_string += std::string(buf, nread);
+    }
+  return m_data_string;
+}
index 47c536e..af45e75 100644 (file)
@@ -41,10 +41,16 @@ public:
   /* get the underlying data structure */
   GpgME::Data& get_data();
 
+  /* get the data as string. resets the seek pointer
+     of data. The string data is cached after this
+     function is called once. */
+  const std::string & get_data_string();
+
 private:
   GpgME::Data m_data;
   std::string m_utf8DisplayName;
   attachtype_t m_type;
+  std::string m_data_string;
 };
 
 #endif // ATTACHMENT_H
index a3c54e9..b6a3cf8 100644 (file)
@@ -34,6 +34,7 @@
 #include "gpgolstr.h"
 #include "windowmessages.h"
 #include "mlang-charset.h"
+#include "mimemaker.h"
 
 #include <gpgme++/tofuinfo.h>
 #include <gpgme++/verificationresult.h>
@@ -417,11 +418,52 @@ copy_attachment_to_file (std::shared_ptr<Attachment> att, HANDLE hFile)
   return 0;
 }
 
-/** Helper to update the attachments of a mail object in oom.
+int
+Mail::remove_attachments()
+{
+  LPDISPATCH attachments = get_oom_object (m_mailitem, "Attachments");
+  if (!attachments)
+    {
+      log_debug ("%s:%s: Failed to get attachments.",
+                 SRCNAME, __func__);
+      return 0;
+    }
+
+  int count = get_oom_int (attachments, "Count");
+  if (count < 1)
+    {
+      gpgol_release (attachments);
+      return 0;
+    }
+  for (int i = 1; i <= count; i++)
+    {
+      VARIANT aVariant[1];
+      DISPPARAMS dispparams;
+
+      dispparams.rgvarg = aVariant;
+      dispparams.rgvarg[0].vt = VT_INT;
+      dispparams.rgvarg[0].intVal = i;
+      dispparams.cArgs = 1;
+      dispparams.cNamedArgs = 0;
+
+      if (invoke_oom_method_with_parms (attachments, "Remove",
+                                        NULL, &dispparams))
+        {
+          log_error ("%s:%s: Failed to remove attachments.",
+                     SRCNAME, __func__);
+          gpgol_release (attachments);
+          return 1;
+        }
+    }
+  gpgol_release (attachments);
+
+  return 0;
+}
+
+/** Update the attachments of a mail object in oom.
   does not modify the underlying mapi structure. */
-static int
-add_attachments(LPDISPATCH mail,
-                std::vector<std::shared_ptr<Attachment> > attachments)
+int
+Mail::add_attachments(std::vector<std::shared_ptr<Attachment> > attachments)
 {
   int err = 0;
   for (auto att: attachments)
@@ -436,7 +478,7 @@ add_attachments(LPDISPATCH mail,
                      SRCNAME, __func__, att->get_display_name().c_str());
           err = 1;
         }
-      if (add_oom_attachment (mail, wchar_file, wchar_name))
+      if (add_oom_attachment (m_mailitem, wchar_file, wchar_name))
         {
           log_error ("%s:%s: Failed to add attachment: %s",
                      SRCNAME, __func__, att->get_display_name().c_str());
@@ -601,7 +643,7 @@ Mail::parsing_done()
   TRACEPOINT;
 
   /* Update attachments */
-  if (add_attachments (m_mailitem, m_parser->get_attachments()))
+  if (add_attachments (m_parser->get_attachments()))
     {
       log_error ("%s:%s: Failed to update attachments.",
                  SRCNAME, __func__);
@@ -634,28 +676,8 @@ Mail::encrypt_sign ()
       return err;
     }
   flags = get_gpgol_draft_info_flags (message);
-  if (flags == 3)
-    {
-      log_debug ("%s:%s: Sign / Encrypting message",
-                 SRCNAME, __func__);
-      err = message_sign_encrypt (message, proto,
-                                  NULL, get_sender ());
-    }
-  else if (flags == 2)
-    {
-      err = message_sign (message, proto,
-                          NULL, get_sender ());
-    }
-  else if (flags == 1)
-    {
-      err = message_encrypt (message, proto,
-                             NULL, get_sender ());
-    }
-  else
-    {
-      log_debug ("%s:%s: Unknown flags for crypto: %i",
-                 SRCNAME, __func__, flags);
-    }
+  err = encrypt_sign_mail (this, (flags & 1), (flags & 2), proto,
+                           NULL);
   log_debug ("%s:%s: Status: %i",
              SRCNAME, __func__, err);
   gpgol_release (message);
@@ -768,6 +790,7 @@ int
 Mail::revert_all_mails ()
 {
   int err = 0;
+#if 0
   std::map<LPDISPATCH, Mail *>::iterator it;
   for (it = g_mail_map.begin(); it != g_mail_map.end(); ++it)
     {
@@ -785,6 +808,7 @@ Mail::revert_all_mails ()
           continue;
         }
     }
+#endif
   return err;
 }
 
@@ -873,10 +897,45 @@ Mail::is_smime ()
   return m_is_smime;
 }
 
+static std::string
+get_string (LPDISPATCH item, const char *str)
+{
+  char *buf = get_oom_string (item, str);
+  if (!buf)
+    return std::string();
+  std::string ret = buf;
+  xfree (buf);
+  return ret;
+}
+
 std::string
-Mail::get_subject()
+Mail::get_subject() const
 {
-  return std::string(get_oom_string (m_mailitem, "Subject"));
+  return get_string (m_mailitem, "Subject");
+}
+
+std::string
+Mail::get_body() const
+{
+  return get_string (m_mailitem, "Body");
+}
+
+std::string
+Mail::get_html_body() const
+{
+  return get_string (m_mailitem, "HTMLBody");
+}
+
+char **
+Mail::get_recipients() const
+{
+  LPDISPATCH recipients = get_oom_object (m_mailitem, "Recipients");
+  if (!recipients)
+    {
+      TRACEPOINT;
+      return nullptr;
+    }
+  return get_oom_recipients (recipients);
 }
 
 int
index 83c10ab..9b28f05 100644 (file)
@@ -31,6 +31,7 @@
 #include <future>
 
 class ParseController;
+class Attachment;
 
 /** @brief Data wrapper around a mailitem.
  *
@@ -163,7 +164,7 @@ public:
   /** @brief get the subject string (UTF-8 encoded).
     *
     * @returns the subject or an empty string. */
-  std::string get_subject ();
+  std::string get_subject () const;
 
   /** @brief Is this a crypto mail handled by gpgol.
   *
@@ -243,6 +244,23 @@ public:
   /** Remove all categories of this mail */
   void remove_categories ();
 
+  /** Get the body of the mail */
+  std::string get_body () const;
+
+  /** Get the html of the mail */
+  std::string get_html_body () const;
+
+  /** Get the recipients recipients is a null
+      terminated array of strings. Needs to be freed
+      by the caller. */
+  char ** get_recipients () const;
+
+  /** Add attachments to this mail returns 0 on success */
+  int add_attachments (std::vector <std::shared_ptr <Attachment> >attachments);
+
+  /** Remove all attachments of this mail. returns 0 on success. */
+  int remove_attachments ();
+
 private:
   void update_categories ();
   void update_body ();
index e4442cc..446e616 100644 (file)
@@ -95,6 +95,7 @@ MailItemEvents::~MailItemEvents()
     gpgol_release (m_object);
 }
 
+/*
 static DWORD WINAPI
 request_send (LPVOID arg)
 {
@@ -110,6 +111,7 @@ request_send (LPVOID arg)
     }
   return 0;
 }
+*/
 
 static DWORD WINAPI
 request_decrypt (LPVOID arg)
@@ -253,19 +255,18 @@ EVENT_SINK_INVOKE(MailItemEvents)
                         SRCNAME, __func__);
              break;
            }
+          m_mail->update_sender ();
+          m_mail->encrypt_sign ();
           if (m_mail->crypto_successful ())
             {
                log_debug ("%s:%s: Passing send event for message %p.",
                           SRCNAME, __func__, m_object);
-               m_send_seen = false;
                break;
             }
-          m_mail->update_sender ();
-          m_send_seen = true;
-          log_debug ("%s:%s: Message %p cancelling send to let us do crypto.",
+          log_debug ("%s:%s: Message %p cancelling send because crypto failed.",
                      SRCNAME, __func__, m_object);
           *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
-          invoke_oom_method (m_object, "Save", NULL);
+          //invoke_oom_method (m_object, "Save", NULL);
 
           return S_OK;
         }
@@ -312,27 +313,12 @@ EVENT_SINK_INVOKE(MailItemEvents)
         }
       case AfterWrite:
         {
-          if (m_send_seen)
-            {
-              m_send_seen = false;
-              m_mail->encrypt_sign ();
-              if (m_mail->crypto_successful ())
-                {
-                  /* We can't trigger a Send event in the current state.
-                     Appearently Outlook locks some methods in some events.
-                     So we Create a new thread that will sleep a bit before
-                     it requests to send the item again. */
-                  HANDLE thread = CreateThread (NULL, 0, request_send,
-                                                (LPVOID) m_object, 0, NULL);
-                  CloseHandle (thread);
-                }
-              return S_OK;
-            }
-          else if (m_decrypt_after_write)
+          if (m_decrypt_after_write)
             {
               char *uuid = strdup (m_mail->get_uuid ().c_str());
               HANDLE thread = CreateThread (NULL, 0, request_decrypt,
                                             (LPVOID) uuid, 0, NULL);
+              log_debug ("Requesting decrypt again for uuid: %s", uuid);
               CloseHandle (thread);
             }
           break;
index 56c9d9a..58934e6 100644 (file)
@@ -42,6 +42,8 @@
 #include "mimemaker.h"
 #include "oomhelp.h"
 #include "gpgolstr.h"
+#include "mail.h"
+#include "attachment.h"
 
 static const unsigned char oid_mimetag[] =
     {0x2A, 0x86, 0x48, 0x86, 0xf7, 0x14, 0x03, 0x0a, 0x04};
@@ -116,6 +118,27 @@ sink_file_write (sink_t sink, const void *data, size_t datalen)
   return 0;
 }
 
+/* Write method used with a sink_t object that contains a GpgME::Data object.  */
+int
+sink_data_write (sink_t sink, const void *data, size_t datalen)
+{
+  auto d = static_cast<GpgME::Data*>(sink->cb_data);
+
+  if (!d)
+    {
+      log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
+      return -1;
+    }
+  if (!data)
+    return 0;  /* Flush - nothing to do here.  */
+
+  if (d->write(data, datalen) != datalen)
+    {
+      log_error ("%s:%s: Failed to write data", SRCNAME, __func__);
+      return -1;
+    }
+  return 0;
+}
 
 /* Make sure that PROTOCOL is usable or return a suitable protocol.
    On error PROTOCOL_UNKNOWN is returned.  */
@@ -2099,3 +2122,209 @@ done:
   xfree (orig);
   return err;
 }
+
+
+int
+encrypt_sign_mail (Mail *mail, bool encrypt, bool sign, protocol_t protocol,
+                   HWND hwnd)
+{
+  if (!mail || (!encrypt && !sign))
+    {
+      TRACEPOINT;
+      return 1;
+    }
+
+  const auto body = mail->get_body();
+  /* TODO get attachments here, too */
+  if (body.empty())
+    {
+      log_debug ("%s:%s: No body",
+                 SRCNAME, __func__);
+      return 1;
+    }
+
+  /* Crate an attachment as container for the data. */
+  auto attach = std::shared_ptr<Attachment> (new Attachment());
+
+  //struct sink_s tmpsinkmem;
+  //sink_t tmpsink = &tmpsinkmem;
+
+  struct sink_s sinkmem;
+  sink_t sink = &sinkmem;
+  memset (sink, 0, sizeof *sink);
+  sink->cb_data = &(attach->get_data ());
+  sink->writefnc = sink_data_write;
+
+
+  /* Prepare the engine.  We do this early as it is quite common
+     that some recipients are not available and thus the encryption
+     will fail early.  This is also required to allow the UIserver to
+     figure out the protocol to use if we have not forced one.  */
+  engine_filter_t filter = NULL;
+  if (engine_create_filter (&filter, write_buffer_for_cb, sink))
+    {
+      log_debug ("%s:%s: Failed to create filter",
+                 SRCNAME, __func__);
+      return 1;
+    }
+
+  /* Session number */
+  int session_number = engine_new_session_number ();
+  engine_set_session_number (filter, session_number);
+
+  /* Set the subject for the dialog */
+  std::string subject = mail->get_subject();
+  engine_set_session_title (filter, mail->get_subject().c_str());
+
+  const char *sender = mail->get_sender();
+
+  struct sink_s encsinkmem;
+  sink_t encsink = &encsinkmem;
+  memset (encsink, 0, sizeof *encsink);
+  encsink->cb_data = filter;
+  encsink->writefnc = sink_encryption_write;
+
+  if (encrypt)
+    {
+      const auto recipients = mail->get_recipients();
+      int rc = engine_encrypt_prepare (filter, hwnd, protocol,
+                                  sign ? ENGINE_FLAG_SIGN_FOLLOWS : 0,
+                                  sender, recipients, &protocol);
+      for (int i = 0; recipients && recipients[i]; i++)
+        xfree (recipients[i]);
+      xfree (recipients);
+      if (rc)
+        {
+          log_error ("%s:%s: Failed to prepare encrypt",
+                     SRCNAME, __func__);
+          if (recipients)
+            {
+            }
+          return 1;
+        }
+      if (engine_encrypt_start (filter, 0))
+        {
+          log_error ("%s:%s: Failed to start encrypt",
+                     SRCNAME, __func__);
+          return 1;
+        }
+    }
+
+  /* Check the selected protocol */
+  protocol = check_protocol (protocol);
+  if (protocol == PROTOCOL_UNKNOWN)
+    {
+      log_error ("%s:%s: Protocol unkown",
+                 SRCNAME, __func__);
+      return 1;
+    }
+
+  int n_att_usable = 0;
+  bool inline_pgp = opt.inline_pgp && !n_att_usable;
+
+  /* Write the top header.  */
+  char boundary[BOUNDARYSIZE+1];
+  if (!inline_pgp && create_top_encryption_header (sink, protocol, boundary))
+    {
+      log_error ("%s:%s: Failed to write top header",
+                 SRCNAME, __func__);
+      return 1;
+    }
+
+  char inner_boundary[BOUNDARYSIZE+1];
+  if (!inline_pgp && ((body.size() && n_att_usable) || n_att_usable > 1))
+    {
+      /* A body and at least one attachment or more than one attachment  */
+      generate_boundary (inner_boundary);
+      if (write_multistring (encsink,
+                             "Content-Type: multipart/mixed;\r\n",
+                             "\tboundary=\"", inner_boundary, "\"\r\n",
+                             NULL))
+        {
+          log_error ("%s:%s: Failed to write boundary",
+                     SRCNAME, __func__);
+          return 1;
+        }
+    }
+  else
+    {
+      /* Only one part */
+      *inner_boundary = 0;
+    }
+
+  TRACEPOINT;
+  if (!body.empty())
+    {
+      if (write_part (encsink, body.c_str(), body.size(),
+                      *inner_boundary ? inner_boundary : NULL, NULL, 1))
+        {
+          log_error ("%s:%s: Failed to write body",
+                     SRCNAME, __func__);
+          return 1;
+        }
+    }
+
+  // TODO attachments
+  /* Finish the possible multipart/mixed. */
+  if (!inline_pgp && *inner_boundary &&
+      write_boundary (encsink, inner_boundary, 1))
+    {
+      log_error ("%s:%s: Failed to write boundary",
+                 SRCNAME, __func__);
+      return 1;
+    }
+
+  /* Flush the sink and wait for the encryption to get
+     ready.  */
+  if (write_buffer (encsink, NULL, 0))
+    {
+      log_error ("%s:%s: Failed to flush tmpsink",
+                 SRCNAME, __func__);
+      return 1;
+    }
+
+  log_debug ("%s:%s: Waiting for crypto",
+             SRCNAME, __func__);
+  if (engine_wait (filter))
+    {
+      log_error ("%s:%s: Failed to wait for engine",
+                 SRCNAME, __func__);
+      return 1;
+    }
+  log_debug ("%s:%s: Wait done",
+             SRCNAME, __func__);
+
+  if (inline_pgp)
+    {
+      return put_oom_string (mail->item (), "Body",
+                             attach->get_data_string ().c_str ());
+    }
+  if (put_oom_string (mail->item (), "Body", ""))
+    {
+      log_error ("%s:%s: Failed to update body",
+                 SRCNAME, __func__);
+      return 1;
+    }
+  if (mail->remove_attachments ())
+    {
+      log_error ("%s:%s: Failed to remove attachments",
+                 SRCNAME, __func__);
+      return 1;
+    }
+  attach->set_display_name("gpgol.dat");
+  std::vector<std::shared_ptr <Attachment> > attachments;
+  attachments.push_back (attach);
+  if (mail->add_attachments (attachments))
+    {
+      log_error ("%s:%s: Failed to add attachments",
+                 SRCNAME, __func__);
+      return 1;
+    }
+  if (put_oom_string (mail->item(), "MessageClass", "IPM.Note.SMIME.MultipartSigned"))
+    {
+      log_error ("%s:%s: Failed to change message class",
+                 SRCNAME, __func__);
+      return 1;
+    }
+  return 0;
+}
index f483120..c2ce42b 100644 (file)
@@ -69,5 +69,9 @@ int restore_msg_from_moss (LPMESSAGE message, LPDISPATCH moss_att,
 
 #ifdef __cplusplus
 }
+
+class Mail;
+int encrypt_sign_mail (Mail *, bool encrypt, bool sign, protocol_t protocol,
+                       HWND hwnd);
 #endif
 #endif /*MIMEMAKER_H*/