Implement forwarding crypto mails with attachments
authorAndre Heinecke <aheinecke@intevation.de>
Tue, 13 Mar 2018 15:41:45 +0000 (16:41 +0100)
committerAndre Heinecke <aheinecke@intevation.de>
Tue, 13 Mar 2018 15:41:45 +0000 (16:41 +0100)
* src/application-events.cpp (EVENT_SINK_INVOKE): Invalidate last
mail in the next UI Loop.
* src/mail.cpp (Mail::update_body): Safety check for parser. Keep
parser around.
(Mail::invalidate_last_mail): New. Clear cached last mail ptr.
* src/mail.h: Update accordingly.
* src/mailitem-events.cpp (EVENT_SINK_INVOKE): Extend write
event to detect forward of mails with attachments.
* src/oomhelp.cpp (get_inline_body): New.
* src/oomhelp.h: Update accordingly.
* src/windowmessages.cpp, src/windowmessages.h
(INVALIDATE_LAST_MAIL, REVERT_MAIL): New messages.
* src/wks-helper.cpp (WKSHelper::send_mail): Check if last
mail was invalidated.

--
This is a first working draft of forwarding crypto mails
with attachments. As we don't see the forward event before
we get the write event (that we have to cancel usually) we
now have some extra magic to detect the forward based on
an ItemLoad of an empty mail in the same UI loop as the
write event. This is pretty magic but it works.

Once we detect the forward we save the mail and then immediately
revert it and save it again. This has the usual uglyness of
reverting (sync) but works and allows to forward mails.

GnuPG-Bug-Id: T3836

src/application-events.cpp
src/mail.cpp
src/mail.h
src/mailitem-events.cpp
src/oomhelp.cpp
src/oomhelp.h
src/windowmessages.cpp
src/windowmessages.h
src/wks-helper.cpp

index c000a86..1100934 100644 (file)
@@ -33,6 +33,7 @@
 #include "oomhelp.h"
 #include "mail.h"
 #include "gpgoladdin.h"
+#include "windowmessages.h"
 
 /* Application Events */
 BEGIN_EVENT_SINK(ApplicationEvents, IDispatch)
@@ -91,6 +92,7 @@ EVENT_SINK_INVOKE(ApplicationEvents)
           log_debug ("%s:%s: Creating mail object for item: %p",
                      SRCNAME, __func__, mailItem);
           new Mail (mailItem);
+          do_in_ui_thread_async (INVALIDATE_LAST_MAIL, nullptr);
           break;
         }
       case Quit:
index 13b984d..d4e7e10 100644 (file)
@@ -922,6 +922,12 @@ void find_and_replace(std::string& source, const std::string &find,
 void
 Mail::update_body()
 {
+  if (!m_parser)
+    {
+      TRACEPOINT;
+      return;
+    }
+
   const auto error = m_parser->get_formatted_error ();
   if (!error.empty())
     {
@@ -1076,9 +1082,6 @@ Mail::parsing_done()
                  SRCNAME, __func__);
     }
 
-  /* Invalidate UI to set the correct sig status. */
-  m_parser = nullptr;
-
   log_debug ("%s:%s: Delayed invalidate to update sigstate.",
              SRCNAME, __func__);
   CloseHandle(CreateThread (NULL, 0, delayed_invalidate_ui, (LPVOID) this, 0,
@@ -2589,6 +2592,13 @@ Mail::get_last_mail ()
 
 // static
 void
+Mail::invalidate_last_mail ()
+{
+  s_last_mail = nullptr;
+}
+
+// static
+void
 Mail::locate_all_crypto_recipients()
 {
   if (!opt.autoresolve)
index f8cdbab..90f042e 100644 (file)
@@ -92,6 +92,8 @@ public:
   */
   static Mail* get_last_mail ();
 
+  static void invalidate_last_mail ();
+
   /** @brief looks for existing Mail objects.
 
     @returns A reference to an existing mailitem or NULL in case none
@@ -446,9 +448,9 @@ public:
   /** Get the mime data that should be used when sending. */
   std::string get_override_mime_data () const { return m_mime_data; }
 
+  void update_body ();
 private:
   void update_categories ();
-  void update_body ();
   void update_sigstate ();
 
   LPDISPATCH m_mailitem;
index 2360789..7933826 100644 (file)
@@ -480,6 +480,45 @@ EVENT_SINK_INVOKE(MailItemEvents)
 
           if (m_mail->is_crypto_mail () && !m_mail->needs_save ())
             {
+              Mail *last_mail = Mail::get_last_mail ();
+              if (Mail::is_valid_ptr (last_mail))
+                {
+                  /* We want to identify here if there was a mail created that
+                     should receive the contents of this mail. For this we check
+                     for a write in the same loop as a mail creation.
+                     Now when switching from one mail to another this is also what
+                     happens. The new mail is loaded and the old mail is written.
+                     To distinguish the two we check that the new mail does not have
+                     an entryID, a Subject and No Size. Maybe just size or entryID
+                     would be enough but better save then sorry.
+
+                     Security consideration: Worst case we pass the write here but
+                     an unload follows before we get the scheduled revert. This
+                     would leak plaintext.
+
+                     Similarly if we crash or Outlook is closed before we see this
+                     revert. */
+                  const std::string lastSubject = last_mail->get_subject ();
+                  char *lastEntryID = get_oom_string (last_mail->item (), "EntryID");
+                  int lastSize = get_oom_int (last_mail->item (), "Size");
+                  std::string lastEntryStr;
+                  if (lastEntryID)
+                    {
+                      lastEntryStr = lastEntryID;
+                      xfree (lastEntryID);
+                    }
+
+                  if (!lastSize && !lastEntryStr.size () && !lastSubject.size ())
+                    {
+                      log_debug ("%s:%s: Write in the same loop as empty load."
+                                 " Pass but schedule revert.",
+                                 SRCNAME, __func__);
+
+                      Mail::invalidate_last_mail ();
+                      do_in_ui_thread_async (REVERT_MAIL, m_mail);
+                      return S_OK;
+                    }
+                }
               /* We cancel the write event to stop outlook from excessively
                  syncing our changes.
                  if smime support is disabled and we still have an smime
index ba3d89f..51d025b 100644 (file)
@@ -2002,3 +2002,35 @@ get_sender_SenderEMailAddress (LPDISPATCH mailitem)
   xfree (type);
   return nullptr;
 }
+
+char *
+get_inline_body ()
+{
+  LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
+  if (!app)
+    {
+      TRACEPOINT;
+      return nullptr;
+    }
+
+  LPDISPATCH explorer = get_oom_object (app, "ActiveExplorer");
+
+  if (!explorer)
+    {
+      TRACEPOINT;
+      return nullptr;
+    }
+
+  LPDISPATCH inlineResponse = get_oom_object (explorer, "ActiveInlineResponse");
+  gpgol_release (explorer);
+
+  if (!inlineResponse)
+    {
+      return nullptr;
+    }
+
+  char *body = get_oom_string (inlineResponse, "Body");
+  gpgol_release (inlineResponse);
+
+  return body;
+}
index 2af096b..cbc36ca 100644 (file)
@@ -338,6 +338,9 @@ LPDISPATCH get_account_for_mail (const char *mbox);
 char *get_sender_CurrentUser (LPDISPATCH mailitem);
 char *get_sender_Sender (LPDISPATCH mailitem);
 char *get_sender_SenderEMailAddress (LPDISPATCH mailitem);
+
+/* Get the body of the active inline response */
+char *get_inline_body (void);
 #ifdef __cplusplus
 char *get_sender_SendUsingAccount (LPDISPATCH mailitem, bool *r_is_GSuite);
 }
index b0e2311..af55d11 100644 (file)
@@ -68,6 +68,34 @@ gpgol_window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
               mail->locate_keys();
               break;
             }
+          case (REVERT_MAIL):
+            {
+              auto mail = (Mail*) ctx->data;
+              if (!Mail::is_valid_ptr (mail))
+                {
+                  log_debug ("%s:%s: Revert mail for mail which is gone.",
+                             SRCNAME, __func__);
+                  break;
+                }
+
+              mail->set_needs_save (true);
+              /* Some magic here. Accessing any existing inline body cements
+                 it. Otherwise updating the body through the revert also changes
+                 the body of a inline mail. */
+              char *inlineBody = get_inline_body ();
+              xfree (inlineBody);
+
+              // Does the revert.
+              log_debug ("%s:%s: Revert mail. Invoking save.",
+                         SRCNAME, __func__);
+              invoke_oom_method (mail->item (), "Save", NULL);
+              log_debug ("%s:%s: Revert mail. Save done. Updating body..",
+                         SRCNAME, __func__);
+              mail->update_body ();
+              log_debug ("%s:%s: Revert mail done.",
+                         SRCNAME, __func__);
+              break;
+            }
           case (INVALIDATE_UI):
             {
               log_debug ("%s:%s: Invalidating UI",
@@ -77,6 +105,13 @@ gpgol_window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
                          SRCNAME, __func__);
               break;
             }
+          case (INVALIDATE_LAST_MAIL):
+            {
+              log_debug ("%s:%s: Invalidating last mail",
+                         SRCNAME, __func__);
+              Mail::invalidate_last_mail ();
+              break;
+            }
           case (CLOSE):
             {
               auto mail = (Mail*) ctx->data;
@@ -114,6 +149,8 @@ gpgol_window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
                 }
               // Finaly this should pass.
               invoke_oom_method (mail->item (), "Send", NULL);
+              log_debug ("%s:%s:  Send for %p completed.",
+                         SRCNAME, __func__, mail);
               // Allow the WKS helper to queue a notification.
               WKSHelper::instance()->allow_notify ();
               break;
index 979f0b4..56d9db8 100644 (file)
@@ -50,6 +50,8 @@ typedef enum _gpgol_wmsg_type
   CRYPTO_DONE, /* Sign / Encrypt done. */
   WKS_NOTIFY, /* Show a WKS Notification. */
   BRING_TO_FRONT, /* Bring the active Outlook window to the front. */
+  INVALIDATE_LAST_MAIL,
+  REVERT_MAIL,
 } gpgol_wmsg_type;
 
 typedef struct
index 16d42ba..52c05c1 100644 (file)
@@ -623,6 +623,12 @@ WKSHelper::send_mail (const std::string &mimeData) const
      a Hack! :-) */
   auto last_mail = Mail::get_last_mail ();
 
+  if (!Mail::is_valid_ptr (last_mail))
+    {
+      log_error ("%s:%s: Invalid last mail %p.",
+                 SRCNAME, __func__, last_mail);
+      return -1;
+    }
   last_mail->set_override_mime_data (mimeData);
   last_mail->set_crypt_state (Mail::NeedsSecondAfterWrite);