Add Attachment helper class
authorAndre Heinecke <aheinecke@intevation.de>
Tue, 16 Aug 2016 14:39:19 +0000 (16:39 +0200)
committerAndre Heinecke <aheinecke@intevation.de>
Tue, 16 Aug 2016 14:39:19 +0000 (16:39 +0200)
* src/attachment.cpp (Attachment): New helper class.
* src/attachment.h (Attachment): Declare class.
* src/mail.cpp (Mail::wipe): No longer encrypt attachments.

--
The Attachment class is similar to the Mail class in that
it is a wrapper around some Attachment related data. The
Attachment uses a temporary stream internally which ideally
is never even created as a file.

src/attachment.cpp
src/attachment.h
src/mail.cpp

index 6f31510..a9ad736 100644 (file)
 #include "config.h"
 #include "common.h"
 #include "attachment.h"
-#include "serpent.h"
-#include "oomhelp.h"
 #include "mymapitags.h"
 #include "mapihelp.h"
+#include "gpgolstr.h"
 
 #include <gpg-error.h>
 
-#define COPYBUFFERSIZE 4096
-
-#define IV_DEFAULT_LEN 16
-
-/** Decrypt the first 16 bytes of stream and check that it contains
-  our header. Return 0 on success. */
-static int
-check_header (LPSTREAM stream, symenc_t symenc)
+Attachment::Attachment()
 {
   HRESULT hr;
-  char tmpbuf[16];
-  ULONG nread;
-  hr = stream->Read (tmpbuf, 16, &nread);
-  if (hr || nread != 16)
-    {
-      log_error ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
-      return -1;
-    }
-  symenc_cfb_decrypt (symenc, tmpbuf, tmpbuf, 16);
-  if (memcmp (tmpbuf, "GpgOL attachment", 16))
+  hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
+                        (SOF_UNIQUEFILENAME | STGM_DELETEONRELEASE
+                         | STGM_CREATE | STGM_READWRITE),
+                        NULL, GpgOLStr("GPG"), &m_stream);
+  if (FAILED (hr))
     {
-      log_error ("%s:%s: Invalid header.",
-                 SRCNAME, __func__);
-      char buf2 [17];
-      snprintf (buf2, 17, "%s", tmpbuf);
-      log_error("Buf2: %s", buf2);
-      return -1;
+      log_error ("%s:%s: can't create attachment: hr=%#lx\n",
+                 SRCNAME, __func__, hr);
+      m_stream = NULL;
     }
-  return 0;
 }
 
-/** Encrypts or decrypts a stream in place using the symenc context.
-  Returns 0 on success. */
-static int
-do_crypt_stream (LPSTREAM stream, symenc_t symenc, bool encrypt)
+Attachment::~Attachment()
 {
-  char *buf = NULL;
-  HRESULT hr;
-  ULONG nread;
-  bool fixed_str_written = false;
-  int rc = -1;
-  ULONG written = 0;
-  /* The original intention was to use IStream::Clone to have
-     an independent read / write stream. But the MAPI attachment
-     stream returns E_NOT_IMPLMENTED for that :-)
-     So we manually track the read and writepos. Read is offset
-     at 16 because of the GpgOL message. */
-  LARGE_INTEGER readpos = {0, 0},
-                writepos = {0, 0};
-  ULARGE_INTEGER new_size = {0, 0};
-
-  if (!encrypt)
-    {
-      readpos.QuadPart = 16;
-    }
-
-  buf = (char*)xmalloc (COPYBUFFERSIZE);
-  do
-    {
-      hr = stream->Read (buf, COPYBUFFERSIZE, &nread);
-      if (hr)
-        {
-          log_error ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
-          goto done;
-        }
-      if (!nread)
-        {
-          break;
-        }
-      readpos.QuadPart += nread;
-      stream->Seek(writepos, STREAM_SEEK_SET, NULL);
-      if (nread && encrypt && !fixed_str_written)
-        {
-          char tmpbuf[16];
-          /* Write an encrypted fixed 16 byte string which we need to
-             check at decryption time to see whether we have actually
-             encrypted it using this session key.  */
-          symenc_cfb_encrypt (symenc, tmpbuf, "GpgOL attachment", 16);
-          stream->Write (tmpbuf, 16, NULL);
-          fixed_str_written = true;
-          writepos.QuadPart = 16;
-        }
-      if (encrypt)
-        {
-          symenc_cfb_encrypt (symenc, buf, buf, nread);
-        }
-      else
-        {
-          symenc_cfb_decrypt (symenc, buf, buf, nread);
-        }
-
-        hr = stream->Write (buf, nread, &written);
-        if (FAILED (hr) || written != nread)
-          {
-            log_error ("%s:%s: Write failed: %i", SRCNAME, __func__, __LINE__);
-            goto done;
-          }
-        writepos.QuadPart += written;
-        stream->Seek(readpos, STREAM_SEEK_SET, NULL);
-      }
-    while (nread == COPYBUFFERSIZE);
-
-  new_size.QuadPart = writepos.QuadPart;
-  hr = stream->SetSize (new_size);
-  if (FAILED (hr))
-    {
-      log_error ("%s:%s: Failed to update size", SRCNAME, __func__);
-      goto done;
-    }
-  rc = 0;
-
-done:
-  xfree (buf);
-
-  if (rc)
-    {
-      stream->Revert ();
-    }
-  else
-    {
-      stream->Commit (0);
-    }
-
-  return rc;
+  log_debug ("%s:%s", SRCNAME, __func__);
+  gpgol_release (m_stream);
 }
 
-/** If encrypt is set to true this will encrypt the attachment
-  data with serpent otherwiese it will decrypt.
-  This function handles the mapi side of things.
-  */
-static int
-do_crypt_mapi (LPATTACH att, bool encrypt)
+LPSTREAM
+Attachment::get_stream()
 {
-  char *iv;
-  ULONG tag;
-  size_t ivlen = IV_DEFAULT_LEN;
-  symenc_t symenc = NULL;
-  HRESULT hr;
-  LPSTREAM stream = NULL;
-  int rc = -1;
-
-  if (!att)
-    {
-      log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
-      return -1;
-    }
-
-  /* Get or create a new IV */
-  if (get_gpgolprotectiv_tag ((LPMESSAGE)att, &tag) )
-    {
-      log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
-      return -1;
-    }
-  if (encrypt)
-    {
-      iv = (char*)create_initialization_vector (IV_DEFAULT_LEN);
-    }
-  else
-    {
-      iv = mapi_get_binary_prop ((LPMESSAGE)att, tag, &ivlen);
-    }
-  if (!iv)
-    {
-      log_error ("%s:%s: Error creating / getting IV: %i", SRCNAME,
-                 __func__, __LINE__);
-      goto done;
-    }
-
-  symenc = symenc_open (get_128bit_session_key (), 16, iv, ivlen);
-  xfree (iv);
-  if (!symenc)
-    {
-      log_error ("%s:%s: can't open encryption context", SRCNAME, __func__);
-      goto done;
-    }
-
-  hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream,
-                          0, MAPI_MODIFY, (LPUNKNOWN*) &stream);
-  if (FAILED (hr))
-    {
-      log_error ("%s:%s: can't open data stream of attachment: hr=%#lx",
-                 SRCNAME, __func__, hr);
-      goto done;
-    }
-
-  /* When decrypting check the first 16 bytes for the header */
-  if (!encrypt && check_header (stream, symenc))
-    {
-      goto done;
-    }
-
-  if (FAILED (hr))
-    {
-      log_error ("%s:%s: can't create temp file: hr=%#lx",
-                 SRCNAME, __func__, hr);
-      goto done;
-    }
+  return m_stream;
+}
 
-  if (do_crypt_stream (stream, symenc, encrypt))
-    {
-      log_error ("%s:%s: stream handling failed",
-                 SRCNAME, __func__);
-      goto done;
-    }
-  rc = 0;
+std::string
+Attachment::get_display_name() const
+{
+  return m_utf8DisplayName;
+}
 
-done:
-  if (symenc)
-    symenc_close (symenc);
-  gpgol_release (stream);
+std::string
+Attachment::get_tmp_file_name() const
+{
+  return m_utf8FileName;
+}
 
-  return rc;
+void
+Attachment::set_display_name(const char *name)
+{
+  m_utf8DisplayName = std::string(name);
 }
 
-/** Protect or unprotect attachments.*/
-static int
-do_crypt (LPDISPATCH mailitem, bool protect)
+void
+Attachment::set_attach_type(attachtype_t type)
 {
-  LPDISPATCH attachments = get_oom_object (mailitem, "Attachments");
-  LPMESSAGE message = get_oom_base_message (mailitem);
-  int count = 0;
-  int err = -1;
-  char *item_str;
-  int i;
-  ULONG tag_id;
+  m_type = type;
+}
 
-  if (!attachments || !message)
-    {
-      log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
-      return -1;
-    }
-  count = get_oom_int (attachments, "Count");
+void
+Attachment::set_hidden(bool value)
+{
+  m_hidden = value;
+}
 
-  if (get_gpgolattachtype_tag (message, &tag_id))
+int
+Attachment::write(const char *data, size_t size)
+{
+  if (!data || !size)
     {
-      log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
-      goto done;
+      return 0;
     }
-
-  if (count < 1)
+  if (!m_stream && m_stream->Write (data, size, NULL) != S_OK)
     {
-      log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
-      goto done;
+      return 1;
     }
-
-  /* Yes the items start at 1! */
-  for (i = 1; i <= count; i++)
+  if (m_stream->Commit (0) != S_OK)
     {
-      LPDISPATCH attachment;
-      LPATTACH mapi_attachment;
-      attachtype_t att_type;
-
-      if (gpgrt_asprintf (&item_str, "Item(%i)", i) == -1)
-        {
-          log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
-          goto done;
-        }
-
-      attachment = get_oom_object (attachments, item_str);
-      xfree (item_str);
-      if (!attachment)
-        {
-          log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
-        }
-      mapi_attachment = (LPATTACH) get_oom_iunknown (attachment,
-                                                     "MapiObject");
-      if (!mapi_attachment)
-        {
-          log_debug ("%s:%s: Failed to get MapiObject of attachment: %p",
-                     SRCNAME, __func__, attachment);
-          gpgol_release (attachment);
-          continue;
-        }
-
-      att_type = get_gpgolattachtype (mapi_attachment, tag_id);
-      if ((protect && att_type == ATTACHTYPE_FROMMOSS_DEC) ||
-          (!protect && att_type == ATTACHTYPE_FROMMOSS))
-        {
-          if (do_crypt_mapi (mapi_attachment, protect))
-            {
-              log_error ("%s:%s: Error: Session crypto failed.",
-                         SRCNAME, __func__);
-              gpgol_release (mapi_attachment);
-              gpgol_release (attachment);
-              goto done;
-            }
-        }
-      gpgol_release (mapi_attachment);
-      gpgol_release (attachment);
+      return 1;
     }
-  err = 0;
-
-done:
-
-  gpgol_release (message);
-  gpgol_release (attachments);
-  return err;
-}
-
-int
-protect_attachments (LPDISPATCH mailitem)
-{
-  return do_crypt (mailitem, true);
-}
-
-int
-unprotect_attachments (LPDISPATCH mailitem)
-{
-  return do_crypt (mailitem, false);
+  return 0;
 }
index 932a21a..68821ff 100644 (file)
 #define ATTACHMENT_H
 
 #include <windows.h>
+#include "oomhelp.h"
+#include "mapihelp.h"
+#include <string>
 
-/** Protect attachments so that it can be stored
-  by outlook. This means to symetrically encrypt the
-  data with the session key.
+/** Helper class for attachment actions. */
+class Attachment
+{
+public:
+  /** Creates and opens a new temporary stream. */
+  Attachment();
 
-  This will change the messagetype back to
-  ATTACHTYPE_FROMMOSS it is only supposed to be
-  called on attachments with the Attachmentype
-  ATTACHTYPE_FROMMOSS_DEC.
+  /** Deletes the attachment and the underlying temporary file. */
+  ~Attachment();
 
-  The dispatch paramenter should be a mailitem.
+  /** Get an assoicated ISteam ptr or NULL. */
+  LPSTREAM get_stream();
 
-  Returns 0 on success.
-*/
-int
-protect_attachments (LPDISPATCH mailitem);
+  /** Writes data to the attachment stream.
+   * Calling this method automatically commits the stream.
+   *
+   * Returns 0 on success. */
+  int write(const char *data, size_t size);
 
-/** Remove the symetric session encryption of the attachments.
+  /** Set the display name */
+  void set_display_name(const char *name);
+  std::string get_display_name() const;
 
-  The dispatch paramenter should be a mailitem.
+  std::string get_tmp_file_name() const;
 
-  This will change the messsagetype to
-  ATTACHTYPE_FROMMOSS_DEC it should only be called
-  with attachments of the type ATTACHTYPE_FROMMOSS.
+  void set_attach_type(attachtype_t type);
+
+  void set_hidden(bool value);
+private:
+  LPSTREAM m_stream;
+  std::string m_utf8FileName;
+  std::string m_utf8DisplayName;
+  attachtype_t m_type;
+  bool m_hidden;
+};
 
-  Returns 0 on success. */
-int
-unprotect_attachments (LPDISPATCH mailitem);
 #endif // ATTACHMENT_H
index 0559c93..7da636c 100644 (file)
@@ -352,8 +352,7 @@ Mail::wipe ()
   log_debug ("%s:%s: Removing plaintext from mailitem: %p.",
              SRCNAME, __func__, m_mailitem);
   if (put_oom_string (m_mailitem, "HTMLBody",
-                      HTML_TEMPLATE) ||
-      protect_attachments (m_mailitem))
+                      HTML_TEMPLATE))
     {
       log_debug ("%s:%s: Failed to wipe mailitem: %p.",
                  SRCNAME, __func__, m_mailitem);