Page MenuHome GnuPG

No OneTemporary

diff --git a/src/explorer-events.cpp b/src/explorer-events.cpp
index 8ed0784..03969b1 100644
--- a/src/explorer-events.cpp
+++ b/src/explorer-events.cpp
@@ -1,149 +1,108 @@
/* explorer-events.cpp - Event handling for the application.
* Copyright (C) 2016 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation 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.1 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, see <http://www.gnu.org/licenses/>.
*/
/* The event handler classes defined in this file follow the
general pattern that they implment the IDispatch interface
through the eventsink macros and handle event invocations
in their invoke methods.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "eventsink.h"
#include "ocidl.h"
#include "common.h"
#include "oomhelp.h"
#include "mail.h"
#include "gpgoladdin.h"
#include "windowmessages.h"
/* Explorer Events */
BEGIN_EVENT_SINK(ExplorerEvents, IDispatch)
EVENT_SINK_DEFAULT_CTOR(ExplorerEvents)
EVENT_SINK_DEFAULT_DTOR(ExplorerEvents)
typedef enum
{
Activate = 0xF001,
AttachmentSelectionChange = 0xFC79,
BeforeFolderSwitch = 0xF003,
BeforeItemCopy = 0xFA0E,
BeforeItemCut = 0xFA0F,
BeforeItemPaste = 0xFA10,
BeforeMaximize = 0xFA11,
BeforeMinimize = 0xFA12,
BeforeMove = 0xFA13,
BeforeSize = 0xFA14,
BeforeViewSwitch = 0xF005,
Close = 0xF008,
Deactivate = 0xF006,
DisplayModeChange = 0xFC98,
FolderSwitch = 0xF002,
InlineResponse = 0xFC92,
InlineResponseClose = 0xFC96,
SelectionChange = 0xF007,
ViewSwitch = 0xF004
} ExplorerEvent;
-static DWORD WINAPI
-invalidate_ui (LPVOID)
-{
- /* We sleep here a bit to prevent invalidtion immediately
- after the selection change before we have started processing
- the mail. */
- Sleep (1000);
- do_in_ui_thread (INVALIDATE_UI, nullptr);
- return 0;
-}
EVENT_SINK_INVOKE(ExplorerEvents)
{
USE_INVOKE_ARGS
switch(dispid)
{
case SelectionChange:
{
log_oom_extra ("%s:%s: Selection change in explorer: %p",
SRCNAME, __func__, this);
- /* Somehow latest Outlook 2016 crashes when accessing the current view
- of the Explorer. This is even reproducible with
- GpgOL disabled and only with Outlook Spy active. If you select
- the explorer of an Outlook.com resource and then access
- the CurrentView and close the CurrentView again in Outlook Spy
- outlook crashes. */
-
- if (g_ol_version_major <= 15)
- {
- LPDISPATCH tableView = get_oom_object (m_object, "CurrentView");
- if (!tableView)
- {
- TRACEPOINT;
- break;
- }
- int hasReadingPane = get_oom_bool (tableView, "ShowReadingPane");
- gpgol_release (tableView);
- if (!hasReadingPane)
- {
- break;
- }
- }
- else
- {
- LPDISPATCH prevEdit = get_oom_object (m_object, "PreviewPane.WordEditor");
- gpgol_release (prevEdit);
- if (!prevEdit)
- {
- break;
- }
- }
- HANDLE thread = CreateThread (NULL, 0, invalidate_ui, (LPVOID) this, 0,
+ HANDLE thread = CreateThread (NULL, 0, delayed_invalidate_ui, (LPVOID) this, 0,
NULL);
if (!thread)
{
log_error ("%s:%s: Failed to create invalidate_ui thread.",
SRCNAME, __func__);
}
else
{
CloseHandle (thread);
}
break;
}
case Close:
{
log_oom_extra ("%s:%s: Deleting event handler: %p",
SRCNAME, __func__, this);
remove_explorer (m_object);
delete this;
return S_OK;
}
default:
break;
#if 0
log_oom_extra ("%s:%s: Unhandled Event: %lx \n",
SRCNAME, __func__, dispid);
#endif
}
return S_OK;
}
END_EVENT_SINK(ExplorerEvents, IID_ExplorerEvents)
diff --git a/src/mailitem-events.cpp b/src/mailitem-events.cpp
index 7797b1d..d703348 100644
--- a/src/mailitem-events.cpp
+++ b/src/mailitem-events.cpp
@@ -1,565 +1,566 @@
/* mailitem-events.h - Event handling for mails.
* Copyright (C) 2015 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation 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.1 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "common.h"
#include "eventsink.h"
#include "eventsinks.h"
#include "mymapi.h"
#include "oomhelp.h"
#include "ocidl.h"
#include "windowmessages.h"
#include "mail.h"
#include "mapihelp.h"
#include "gpgoladdin.h"
#undef _
#define _(a) utf8_gettext (a)
const wchar_t *prop_blacklist[] = {
L"Body",
L"HTMLBody",
L"To", /* Somehow this is done when a mail is opened */
L"CC", /* Ditto */
L"BCC", /* Ditto */
L"Categories",
L"UnRead",
L"OutlookVersion",
L"OutlookInternalVersion",
NULL };
typedef enum
{
AfterWrite = 0xFC8D,
AttachmentAdd = 0xF00B,
AttachmentRead = 0xF00C,
AttachmentRemove = 0xFBAE,
BeforeAttachmentAdd = 0xFBB0,
BeforeAttachmentPreview = 0xFBAF,
BeforeAttachmentRead = 0xFBAB,
BeforeAttachmentSave = 0xF00D,
BeforeAttachmentWriteToTempFile = 0xFBB2,
BeforeAutoSave = 0xFC02,
BeforeCheckNames = 0xF00A,
BeforeDelete = 0xFA75,
BeforeRead = 0xFC8C,
Close = 0xF004,
CustomAction = 0xF006,
CustomPropertyChange = 0xF008,
Forward = 0xF468,
Open = 0xF003,
PropertyChange = 0xF009,
Read = 0xF001,
ReadComplete = 0xFC8F,
Reply = 0xF466,
ReplyAll = 0xF467,
Send = 0xF005,
Unload = 0xFBAD,
Write = 0xF002
} MailEvent;
/* Mail Item Events */
BEGIN_EVENT_SINK(MailItemEvents, IDispatch)
/* We are still in the class declaration */
private:
Mail * m_mail; /* The mail object related to this mailitem */
};
MailItemEvents::MailItemEvents() :
m_object(NULL),
m_pCP(NULL),
m_cookie(0),
m_ref(1),
m_mail(NULL)
{
}
MailItemEvents::~MailItemEvents()
{
if (m_pCP)
m_pCP->Unadvise(m_cookie);
if (m_object)
gpgol_release (m_object);
}
static bool propchangeWarnShown = false;
static DWORD WINAPI
do_delayed_locate (LPVOID arg)
{
Sleep(100);
do_in_ui_thread (RECIPIENT_ADDED, arg);
return 0;
}
/* The main Invoke function. The return value of this
function does not appear to have any effect on outlook
although I have read in an example somewhere that you
should return S_OK so that outlook continues to handle
the event I have not yet seen any effect by returning
error values here and no MSDN documentation about the
return values.
*/
EVENT_SINK_INVOKE(MailItemEvents)
{
USE_INVOKE_ARGS
if (!m_mail)
{
m_mail = Mail::get_mail_for_item (m_object);
if (!m_mail)
{
log_error ("%s:%s: mail event without mail object known. Bug.",
SRCNAME, __func__);
return S_OK;
}
}
switch(dispid)
{
case Open:
{
log_oom_extra ("%s:%s: Open : %p",
SRCNAME, __func__, m_mail);
LPMESSAGE message;
if (g_ol_version_major < 14 && m_mail->set_uuid ())
{
/* In Outlook 2007 we need the uid for every
open mail to track the message in case
it is sent and crypto is required. */
log_debug ("%s:%s: Failed to set uuid.",
SRCNAME, __func__);
delete m_mail; /* deletes this, too */
return S_OK;
}
int draft_flags = 0;
if (!opt.encrypt_default && !opt.sign_default)
{
return S_OK;
}
message = get_oom_base_message (m_object);
if (!message)
{
log_error ("%s:%s: Failed to get message.",
SRCNAME, __func__);
break;
}
if (opt.encrypt_default)
{
draft_flags = 1;
}
if (opt.sign_default)
{
draft_flags += 2;
}
set_gpgol_draft_info_flags (message, draft_flags);
gpgol_release (message);
break;
}
case BeforeRead:
{
log_oom_extra ("%s:%s: BeforeRead : %p",
SRCNAME, __func__, m_mail);
if (m_mail->pre_process_message ())
{
log_error ("%s:%s: Pre process message failed.",
SRCNAME, __func__);
}
break;
}
case Read:
{
if (g_ol_version_major < 14)
{
/* In Outlook 2007 there is no Before read event.
We change the message class in message-events to
prevent that outlook parses the mail itself but
we still need to update our mail object accordingly.
So we call pre_process here gain although the message
class already was changed. */
if (m_mail->pre_process_message ())
{
log_error ("%s:%s: Pre process message failed.",
SRCNAME, __func__);
}
}
log_oom_extra ("%s:%s: Read : %p",
SRCNAME, __func__, m_mail);
if (!m_mail->is_crypto_mail())
{
/* Ensure that no wrong sigstatus is shown */
- gpgoladdin_invalidate_ui ();
+ CloseHandle(CreateThread (NULL, 0, delayed_invalidate_ui, (LPVOID) this, 0,
+ NULL));
break;
}
if (m_mail->set_uuid ())
{
log_debug ("%s:%s: Failed to set uuid.",
SRCNAME, __func__);
delete m_mail; /* deletes this, too */
return S_OK;
}
if (m_mail->decrypt_verify ())
{
log_error ("%s:%s: Decrypt message failed.",
SRCNAME, __func__);
}
if (!opt.enable_smime && m_mail->is_smime ())
{
/* We want to save the mail when it's an smime mail and smime
is disabled to revert it. */
log_debug ("%s:%s: S/MIME mail but S/MIME is disabled."
" Need save.",
SRCNAME, __func__);
m_mail->set_needs_save (true);
}
break;
}
case PropertyChange:
{
if (!parms || parms->cArgs != 1 ||
parms->rgvarg[0].vt != VT_BSTR ||
!parms->rgvarg[0].bstrVal)
{
log_error ("%s:%s: Unexpected params.",
SRCNAME, __func__);
break;
}
const wchar_t *prop_name = parms->rgvarg[0].bstrVal;
if (!m_mail->is_crypto_mail ())
{
if (!opt.autoresolve)
{
break;
}
if (!wcscmp (prop_name, L"To") /* ||
!wcscmp (prop_name, L"BCC") ||
!wcscmp (prop_name, L"CC")
Testing shows that Outlook always sends these three in a row
*/)
{
if ((m_mail->needs_crypto() & 1))
{
/* XXX Racy race. This is a fix for crashes
that happend if a resolved recipient is copied an pasted.
If we then access the recipients object in the Property
Change event we crash. Thus we do the delay dance. */
HANDLE thread = CreateThread (NULL, 0, do_delayed_locate,
(LPVOID) m_mail, 0,
NULL);
CloseHandle(thread);
}
}
break;
}
for (const wchar_t **cur = prop_blacklist; *cur; cur++)
{
if (!wcscmp (prop_name, *cur))
{
log_oom ("%s:%s: Message %p propchange: %ls discarded.",
SRCNAME, __func__, m_object, prop_name);
return S_OK;
}
}
log_oom ("%s:%s: Message %p propchange: %ls.",
SRCNAME, __func__, m_object, prop_name);
/* We have tried several scenarios to handle propery changes.
Only save the property in MAPI and call MAPI SaveChanges
worked and did not leak plaintext but this caused outlook
still to break the attachments of PGP/MIME Mails into two
attachments and add them as winmail.dat so other clients
are broken.
Alternatively reverting the mail, saving the property and
then decrypt again also worked a bit but there were some
weird side effects and breakages. But this has the usual
problem of a revert that the mail is created by outlook and
e.g. multipart/signed signatures from most MUA's are broken.
Some things to try out might be the close approach and then
another open or a selection change. But for now we just warn.
As a workardound a user should make property changes when
the mail was not read by us. */
if (propchangeWarnShown)
{
return S_OK;
}
wchar_t *title = utf8_to_wchar (_("Sorry, that's not possible, yet"));
char *fmt;
gpgrt_asprintf (&fmt, _("GpgOL has prevented the change to the \"%s\" property.\n"
"Property changes are not yet handled for crypto messages.\n\n"
"To workaround this limitation please change the property when the "
"message is not open in any window and not selected in the "
"messagelist.\n\nFor example by right clicking but not selecting the message.\n"),
wchar_to_utf8(prop_name));
wchar_t *msg = utf8_to_wchar (fmt);
xfree (fmt);
MessageBoxW (get_active_hwnd(), msg, title,
MB_ICONINFORMATION | MB_OK);
xfree (msg);
xfree (title);
propchangeWarnShown = true;
return S_OK;
}
case CustomPropertyChange:
{
log_oom_extra ("%s:%s: CustomPropertyChange : %p",
SRCNAME, __func__, m_mail);
/* TODO */
break;
}
case Send:
{
/* This is the only event where we can cancel the send of a
mailitem. But it is too early for us to encrypt as the MAPI
structures are not yet filled. Crypto based on the
Outlook Object Model data did not work as the messages
were only sent out empty. See 2b376a48 for a try of
this.
This is why we store send_seen and invoke a save which
may result in an error but only after triggering all the
behavior we need -> filling mapi structures and invoking the
AfterWrite handler where we encrypt.
If this encryption is successful and we pass the send
as then the encrypted data is sent.
*/
log_oom_extra ("%s:%s: Send : %p",
SRCNAME, __func__, m_mail);
if (!m_mail->needs_crypto ())
{
log_debug ("%s:%s: No crypto neccessary. Passing send for unencrypted %p",
SRCNAME, __func__, m_mail);
break;
}
if (parms->cArgs != 1 || parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF))
{
log_debug ("%s:%s: Uncancellable send event.",
SRCNAME, __func__);
break;
}
m_mail->update_oom_data ();
m_mail->set_needs_encrypt (true);
invoke_oom_method (m_object, "Save", NULL);
if (m_mail->crypto_successful ())
{
/* Fishy behavior catcher: Sometimes the save does not clean
out the body. Weird. Happened at least for one user.
The following code checks for plain text leaks and
prevents send in case the body can't be wiped. */
if (!m_mail->get_body().empty() || !m_mail->get_html_body().empty())
{
log_debug ("%s:%s: Body found after encryption %p.",
SRCNAME, __func__, m_object);
const auto body = m_mail->get_body();
if (body.size() > 10 && !strncmp (body.c_str(), "-----BEGIN", 10))
{
log_debug ("%s:%s: Looks like inline body. You can pass %p.",
SRCNAME, __func__, m_object);
break;
}
if (!m_mail->wipe (true))
{
log_debug ("%s:%s: Wipe succeded. %p.",
SRCNAME, __func__, m_object);
log_debug ("%s:%s: Passing send event for message %p.",
SRCNAME, __func__, m_object);
break;
}
log_debug ("%s:%s: Cancel send for %p.",
SRCNAME, __func__, m_object);
wchar_t *title = utf8_to_wchar (_("GpgOL: Encryption not possible!"));
wchar_t *msg = utf8_to_wchar (_(
"Outlook returned an error when trying to send the encrypted mail.\n\n"
"Please restart Outlook and try again.\n\n"
"If it still fails consider using an encrypted attachment or\n"
"switching to PGP/Inline in GpgOL's options."));
MessageBoxW (get_active_hwnd(), msg, title,
MB_ICONERROR | MB_OK);
xfree (msg);
xfree (title);
*(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
return S_OK;
}
log_debug ("%s:%s: Passing send event for message %p.",
SRCNAME, __func__, m_object);
break;
}
else
{
log_debug ("%s:%s: Message %p cancelling send - crypto failed.",
SRCNAME, __func__, m_object);
*(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
}
return S_OK;
}
case Write:
{
log_oom_extra ("%s:%s: Write : %p",
SRCNAME, __func__, m_mail);
/* This is a bit strange. We sometimes get multiple write events
without a read in between. When we access the message in
the second event it fails and if we cancel the event outlook
crashes. So we have keep the m_needs_wipe state variable
to keep track of that. */
if (parms->cArgs != 1 || parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF))
{
/* This happens in the weird case */
log_debug ("%s:%s: Uncancellable write event.",
SRCNAME, __func__);
break;
}
if (m_mail->is_crypto_mail () && !m_mail->needs_save ())
{
/* We cancel the write event to stop outlook from excessively
syncing our changes.
if smime support is disabled and we still have an smime
mail we also don't want to cancel the write event
to enable reverting this mails.
*/
*(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
log_debug ("%s:%s: Canceling write event.",
SRCNAME, __func__);
return S_OK;
}
if (m_mail->is_crypto_mail () && m_mail->needs_save () &&
m_mail->revert ())
{
/* An error cleaning the mail should not happen normally.
But just in case there is an error we cancel the
write here. */
log_debug ("%s:%s: Failed to remove plaintext.",
SRCNAME, __func__);
*(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
}
log_debug ("%s:%s: Passing write event.",
SRCNAME, __func__);
m_mail->set_needs_save (false);
break;
}
case AfterWrite:
{
log_oom_extra ("%s:%s: AfterWrite : %p",
SRCNAME, __func__, m_mail);
if (m_mail->needs_encrypt ())
{
m_mail->encrypt_sign ();
return S_OK;
}
break;
}
case Close:
{
log_oom_extra ("%s:%s: Close : %p",
SRCNAME, __func__, m_mail);
if (m_mail->is_crypto_mail ())
{
/* Close. This happens when an Opened mail is closed.
To prevent the question of wether or not to save the changes
(Which would save the decrypted data without an event to
prevent it) we cancel the close and then either close it
with discard changes or revert / save it.
Contrary to documentation we can invoke close from
close.
*/
if (parms->cArgs != 1 || parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF))
{
/* This happens in the weird case */
log_debug ("%s:%s: Uncancellable close event.",
SRCNAME, __func__);
break;
}
if (m_mail->get_close_triggered ())
{
/* Our close with discard changes, pass through */
m_mail->set_close_triggered (false);
return S_OK;
}
*(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
log_oom_extra ("%s:%s: Canceling close event.",
SRCNAME, __func__);
if (Mail::close(m_mail))
{
log_debug ("%s:%s: Close request failed.",
SRCNAME, __func__);
}
}
return S_OK;
}
case Unload:
{
log_oom_extra ("%s:%s: Unload : %p",
SRCNAME, __func__, m_mail);
log_debug ("%s:%s: Removing Mail for message: %p.",
SRCNAME, __func__, m_object);
delete m_mail;
return S_OK;
}
case Forward:
case Reply:
case ReplyAll:
{
log_oom_extra ("%s:%s: Reply Forward ReplyAll: %p",
SRCNAME, __func__, m_mail);
if (!opt.reply_crypt)
{
break;
}
int crypto_flags = 0;
if (!(crypto_flags = m_mail->get_crypto_flags ()))
{
break;
}
if (parms->cArgs != 2 || parms->rgvarg[1].vt != (VT_DISPATCH) ||
parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF))
{
/* This happens in the weird case */
log_debug ("%s:%s: Unexpected args %i %x %x named: %i",
SRCNAME, __func__, parms->cArgs, parms->rgvarg[0].vt, parms->rgvarg[1].vt,
parms->cNamedArgs);
break;
}
LPMESSAGE msg = get_oom_base_message (parms->rgvarg[1].pdispVal);
if (!msg)
{
log_debug ("%s:%s: Failed to get base message",
SRCNAME, __func__);
break;
}
set_gpgol_draft_info_flags (msg, crypto_flags);
gpgol_release (msg);
break;
}
default:
log_oom_extra ("%s:%s: Message:%p Unhandled Event: %lx \n",
SRCNAME, __func__, m_object, dispid);
}
return S_OK;
}
END_EVENT_SINK(MailItemEvents, IID_MailItemEvents)
diff --git a/src/ribbon-callbacks.cpp b/src/ribbon-callbacks.cpp
index f787622..df5eb76 100644
--- a/src/ribbon-callbacks.cpp
+++ b/src/ribbon-callbacks.cpp
@@ -1,1733 +1,1741 @@
/* ribbon-callbacks.h - Callbacks for the ribbon extension interface
* Copyright (C) 2013 Intevation GmbH
* Software engineering by Intevation 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.1 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, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <windows.h>
#include <olectl.h>
#include <stdio.h>
#include <string.h>
#include <gdiplus.h>
#include <objidl.h>
#include "ribbon-callbacks.h"
#include "gpgoladdin.h"
#include "common.h"
#include "mymapi.h"
#include "mymapitags.h"
#include "myexchext.h"
#include "common.h"
#include "display.h"
#include "msgcache.h"
#include "engine.h"
#include "engine-assuan.h"
#include "mapihelp.h"
#include "mimemaker.h"
#include "filetype.h"
#include "gpgolstr.h"
#include "message.h"
#include "mail.h"
#include <gpgme++/context.h>
#include <gpgme++/data.h>
using namespace GpgME;
#define OPAQUE_SIGNED_MARKER "-----BEGIN PGP MESSAGE-----"
/* Gets the context of a ribbon control. And prints some
useful debug output */
HRESULT getContext (LPDISPATCH ctrl, LPDISPATCH *context)
{
*context = get_oom_object (ctrl, "get_Context");
if (*context)
{
char *name = get_object_name (*context);
log_debug ("%s:%s: contextObj: %s",
SRCNAME, __func__, name);
xfree (name);
}
return context ? S_OK : E_FAIL;
}
#define OP_ENCRYPT 1 /* Encrypt the data */
#define OP_SIGN 2 /* Sign the data */
#define OP_DECRYPT 1 /* Decrypt the data */
#define OP_VERIFY 2 /* Verify the data */
#define DATA_BODY 4 /* Use text body as data */
#define DATA_SELECTION 8 /* Use selection as data */
/* Read hfile in chunks of 4KB and writes them to the sink */
static int
copyFileToSink (HANDLE hFile, sink_t sink)
{
char buf[4096];
DWORD bytesRead = 0;
do
{
if (!ReadFile (hFile, buf, sizeof buf, &bytesRead, NULL))
{
log_error ("%s:%s: Could not read source file.",
SRCNAME, __func__);
return -1;
}
if (write_buffer (sink, bytesRead ? buf : NULL, bytesRead))
{
log_error ("%s:%s: Could not write out buffer",
SRCNAME, __func__);
return -1;
}
}
while (bytesRead);
return 0;
}
static int
attachSignature (LPDISPATCH mailItem, char *subject, HANDLE hFileToSign,
protocol_t protocol, unsigned int session_number,
HWND curWindow, wchar_t *fileNameToSign, char *sender)
{
wchar_t *sigName = NULL;
wchar_t *sigFileName = NULL;
HANDLE hSigFile = NULL;
int rc = 0;
struct sink_s encsinkmem;
sink_t encsink = &encsinkmem;
struct sink_s sinkmem;
sink_t sink = &sinkmem;
engine_filter_t filter = NULL;
memset (encsink, 0, sizeof *encsink);
memset (sink, 0, sizeof *sink);
/* Prepare a fresh filter */
if ((rc = engine_create_filter (&filter, write_buffer_for_cb, sink)))
{
goto failure;
}
encsink->cb_data = filter;
encsink->writefnc = sink_encryption_write;
engine_set_session_number (filter, session_number);
engine_set_session_title (filter, subject ? subject :_("GpgOL"));
if (engine_sign_start (filter, curWindow, protocol, sender, &protocol))
goto failure;
sigName = get_pretty_attachment_name (fileNameToSign, protocol, 1);
/* If we are unlucky the number of temporary file artifacts might
differ for the signature and the encrypted file but we have
to live with that. */
sigFileName = get_tmp_outfile (sigName, &hSigFile);
sink->cb_data = hSigFile;
sink->writefnc = sink_file_write;
if (!sigFileName)
{
log_error ("%s:%s: Could not get a decent attachment name",
SRCNAME, __func__);
goto failure;
}
/* Reset the file to sign handle to the beginning of the file and
copy it to the signature buffer */
SetFilePointer (hFileToSign, 0, NULL, 0);
if ((rc=copyFileToSink (hFileToSign, encsink)))
goto failure;
/* Lets hope the user did not select a huge file. We are hanging
here until encryption is completed.. */
if ((rc = engine_wait (filter)))
goto failure;
filter = NULL; /* Not valid anymore. */
encsink->cb_data = NULL; /* Not needed anymore. */
if (!sink->enc_counter)
{
log_error ("%s:%s: nothing received from engine", SRCNAME, __func__);
goto failure;
}
/* Now we have an encrypted file behind encryptedFile. Let's add it */
add_oom_attachment (mailItem, sigFileName, nullptr);
failure:
xfree (sigFileName);
xfree (sigName);
if (hSigFile)
{
CloseHandle (hSigFile);
DeleteFileW (sigFileName);
}
return rc;
}
/* do_composer_action
Encrypts / Signs text in an IInspector context.
Depending on the flags either the
active selection or the full body is encrypted.
Combine OP_ENCRYPT and OP_SIGN if you want both.
*/
HRESULT
do_composer_action (LPDISPATCH ctrl, int flags)
{
LPDISPATCH context = NULL;
LPDISPATCH selection = NULL;
LPDISPATCH wordEditor = NULL;
LPDISPATCH application = NULL;
LPDISPATCH mailItem = NULL;
LPDISPATCH sender = NULL;
LPDISPATCH recipients = NULL;
struct sink_s encsinkmem;
sink_t encsink = &encsinkmem;
struct sink_s sinkmem;
sink_t sink = &sinkmem;
char* senderAddr = NULL;
char** recipientAddrs = NULL;
LPSTREAM tmpstream = NULL;
engine_filter_t filter = NULL;
char* plaintext = NULL;
int rc = 0;
HRESULT hr;
HWND curWindow;
protocol_t protocol;
unsigned int session_number;
int i;
STATSTG tmpStat;
log_debug ("%s:%s: enter", SRCNAME, __func__);
hr = getContext (ctrl, &context);
if (FAILED(hr))
return hr;
memset (encsink, 0, sizeof *encsink);
memset (sink, 0, sizeof *sink);
curWindow = get_oom_context_window (context);
wordEditor = get_oom_object (context, "WordEditor");
application = get_oom_object (wordEditor, "get_Application");
selection = get_oom_object (application, "get_Selection");
mailItem = get_oom_object (context, "CurrentItem");
sender = get_oom_object (mailItem, "Session.CurrentUser");
recipients = get_oom_object (mailItem, "Recipients");
if (!wordEditor || !application || !selection || !mailItem ||
!sender || !recipients)
{
MessageBox (NULL,
"Internal error in GpgOL.\n"
"Could not find all objects.",
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
log_error ("%s:%s: Could not find all objects.",
SRCNAME, __func__);
goto failure;
}
if (flags & DATA_SELECTION)
{
plaintext = get_oom_string (selection, "Text");
if (!plaintext || strlen (plaintext) <= 1)
{
MessageBox (NULL,
_("Please select text to encrypt."),
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
goto failure;
}
}
else if (flags & DATA_BODY)
{
plaintext = get_oom_string (mailItem, "Body");
if (!plaintext || strlen (plaintext) <= 1)
{
MessageBox (NULL,
_("Textbody empty."),
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
goto failure;
}
}
/* Create a temporary sink to construct the encrypted data. */
hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
(SOF_UNIQUEFILENAME | STGM_DELETEONRELEASE
| STGM_CREATE | STGM_READWRITE),
NULL, GpgOLStr("GPG"), &tmpstream);
if (FAILED (hr))
{
log_error ("%s:%s: can't create temp file: hr=%#lx\n",
SRCNAME, __func__, hr);
rc = -1;
goto failure;
}
sink->cb_data = tmpstream;
sink->writefnc = sink_std_write;
/* Now lets prepare our encryption */
session_number = engine_new_session_number ();
/* Prepare the encryption sink */
if (engine_create_filter (&filter, write_buffer_for_cb, sink))
{
goto failure;
}
encsink->cb_data = filter;
encsink->writefnc = sink_encryption_write;
engine_set_session_number (filter, session_number);
engine_set_session_title (filter, _("GpgOL"));
senderAddr = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
if (flags & OP_ENCRYPT)
{
recipientAddrs = get_oom_recipients (recipients);
if (!recipientAddrs || !(*recipientAddrs))
{
MessageBox (NULL,
_("Please add at least one recipent."),
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
goto failure;
}
if ((rc=engine_encrypt_prepare (filter, curWindow,
PROTOCOL_UNKNOWN,
(flags & OP_SIGN) ?
ENGINE_FLAG_SIGN_FOLLOWS : 0,
senderAddr, recipientAddrs,
&protocol)))
{
log_error ("%s:%s: engine encrypt prepare failed : %s",
SRCNAME, __func__, gpg_strerror (rc));
goto failure;
}
if ((rc=engine_encrypt_start (filter, 0)))
{
log_error ("%s:%s: engine encrypt start failed: %s",
SRCNAME, __func__, gpg_strerror (rc));
goto failure;
}
}
else
{
/* We could do some kind of clearsign / sign text as attachment here
but it is error prone */
if ((rc=engine_sign_opaque_start (filter, curWindow, PROTOCOL_UNKNOWN,
senderAddr, &protocol)))
{
log_error ("%s:%s: engine sign start failed: %s",
SRCNAME, __func__, gpg_strerror (rc));
goto failure;
}
}
/* Write the text in the encryption sink. */
rc = write_buffer (encsink, plaintext, strlen (plaintext));
if (rc)
{
log_error ("%s:%s: writing tmpstream to encsink failed: %s",
SRCNAME, __func__, gpg_strerror (rc));
goto failure;
}
/* Flush the encryption sink and wait for the encryption to get
ready. */
if ((rc = write_buffer (encsink, NULL, 0)))
goto failure;
if ((rc = engine_wait (filter)))
goto failure;
filter = NULL; /* Not valid anymore. */
encsink->cb_data = NULL; /* Not needed anymore. */
if (!sink->enc_counter)
{
log_debug ("%s:%s: nothing received from engine", SRCNAME, __func__);
goto failure;
}
/* Check the size of the encrypted data */
tmpstream->Stat (&tmpStat, 0);
if (tmpStat.cbSize.QuadPart > UINT_MAX)
{
log_error ("%s:%s: No one should write so large mails.",
SRCNAME, __func__);
goto failure;
}
/* Copy the encrypted stream to the message editor. */
{
LARGE_INTEGER off;
ULONG nread;
char buffer[(unsigned int)tmpStat.cbSize.QuadPart + 1];
memset (buffer, 0, sizeof buffer);
off.QuadPart = 0;
hr = tmpstream->Seek (off, STREAM_SEEK_SET, NULL);
if (hr)
{
log_error ("%s:%s: seeking back to the begin failed: hr=%#lx",
SRCNAME, __func__, hr);
rc = gpg_error (GPG_ERR_EIO);
goto failure;
}
hr = tmpstream->Read (buffer, sizeof (buffer) - 1, &nread);
if (hr)
{
log_error ("%s:%s: IStream::Read failed: hr=%#lx",
SRCNAME, __func__, hr);
rc = gpg_error (GPG_ERR_EIO);
goto failure;
}
if (strlen (buffer) > 1)
{
if (flags & OP_SIGN)
{
/* When signing we append the signature after the body */
unsigned int combinedSize = strlen (buffer) +
strlen (plaintext) + 5;
char combinedBody[combinedSize];
memset (combinedBody, 0, combinedSize);
snprintf (combinedBody, combinedSize, "%s\r\n\r\n%s", plaintext,
buffer);
if (flags & DATA_SELECTION)
put_oom_string (selection, "Text", combinedBody);
else if (flags & DATA_BODY)
put_oom_string (mailItem, "Body", combinedBody);
}
else if (protocol == PROTOCOL_SMIME)
{
unsigned int enclosedSize = strlen (buffer) + 34 + 31 + 1;
char enclosedData[enclosedSize];
snprintf (enclosedData, sizeof enclosedData,
"-----BEGIN ENCRYPTED MESSAGE-----\r\n"
"%s"
"-----END ENCRYPTED MESSAGE-----\r\n", buffer);
if (flags & DATA_SELECTION)
put_oom_string (selection, "Text", enclosedData);
else if (flags & DATA_BODY)
put_oom_string (mailItem, "Body", enclosedData);
}
else
{
if (flags & DATA_SELECTION)
put_oom_string (selection, "Text", buffer);
else if (flags & DATA_BODY)
{
put_oom_string (mailItem, "Body", buffer);
}
}
}
else
{
/* Just to be save not to overwrite the selection with
an empty buffer */
log_error ("%s:%s: unexpected problem ", SRCNAME, __func__);
goto failure;
}
}
failure:
if (rc)
log_debug ("%s:%s: failed rc=%d (%s) <%s>", SRCNAME, __func__, rc,
gpg_strerror (rc), gpg_strsource (rc));
engine_cancel (filter);
gpgol_release(wordEditor);
gpgol_release(application);
gpgol_release(selection);
gpgol_release(sender);
gpgol_release(recipients);
gpgol_release(mailItem);
gpgol_release(tmpstream);
xfree (plaintext);
xfree (senderAddr);
if (recipientAddrs)
{
for (i=0; recipientAddrs && recipientAddrs[i]; i++)
xfree (recipientAddrs[i]);
xfree (recipientAddrs);
}
log_debug ("%s:%s: leave", SRCNAME, __func__);
return S_OK;
}
HRESULT
decryptAttachments (LPDISPATCH ctrl)
{
LPDISPATCH context = NULL;
LPDISPATCH attachmentSelection;
int attachmentCount;
HRESULT hr = 0;
int i = 0;
HWND curWindow;
int err;
hr = getContext(ctrl, &context);
attachmentSelection = get_oom_object (context, "AttachmentSelection");
if (!attachmentSelection)
{
/* We can be called from a context menu, in that case we
directly have an AttachmentSelection context. Otherwise
we have an Explorer context with an Attachment Selection property. */
attachmentSelection = context;
}
attachmentCount = get_oom_int (attachmentSelection, "Count");
curWindow = get_oom_context_window (context);
{
char *filenames[attachmentCount + 1];
filenames[attachmentCount] = NULL;
/* Yes the items start at 1! */
for (i = 1; i <= attachmentCount; i++)
{
char buf[16];
char *filename;
wchar_t *wcsOutFilename;
DISPPARAMS saveParams;
VARIANT aVariant[1];
LPDISPATCH attachmentObj;
DISPID saveID;
snprintf (buf, sizeof (buf), "Item(%i)", i);
attachmentObj = get_oom_object (attachmentSelection, buf);
if (!attachmentObj)
{
/* Should be impossible */
filenames[i-1] = NULL;
log_error ("%s:%s: could not find Item %i;",
SRCNAME, __func__, i);
break;
}
filename = get_oom_string (attachmentObj, "FileName");
saveID = lookup_oom_dispid (attachmentObj, "SaveAsFile");
saveParams.rgvarg = aVariant;
saveParams.rgvarg[0].vt = VT_BSTR;
filenames[i-1] = get_save_filename (NULL, filename);
xfree (filename);
if (!filenames [i-1])
continue;
wcsOutFilename = utf8_to_wchar2 (filenames[i-1],
strlen(filenames[i-1]));
saveParams.rgvarg[0].bstrVal = SysAllocString (wcsOutFilename);
saveParams.cArgs = 1;
saveParams.cNamedArgs = 0;
hr = attachmentObj->Invoke (saveID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, &saveParams,
NULL, NULL, NULL);
SysFreeString (saveParams.rgvarg[0].bstrVal);
gpgol_release (attachmentObj);
if (FAILED(hr))
{
int j;
log_debug ("%s:%s: Saving to file failed. hr: %x",
SRCNAME, __func__, (unsigned int) hr);
for (j = 0; j < i; j++)
xfree (filenames[j]);
gpgol_release (attachmentSelection);
return hr;
}
}
gpgol_release (attachmentSelection);
err = op_assuan_start_decrypt_files (curWindow, filenames);
for (i = 0; i < attachmentCount; i++)
xfree (filenames[i]);
}
log_debug ("%s:%s: Leaving. Err: %i",
SRCNAME, __func__, err);
return S_OK; /* If we return an error outlook will show that our
callback function failed in an ugly window. */
}
/* MIME erify mail helper. Returns 0 if it
was not called with a MIME crypto message or on error. */
static int
verify_mime (LPDISPATCH mailitem)
{
int ret = 0;
LPMESSAGE message = get_oom_base_message (mailitem);
if (!message)
{
log_error ("%s:%s: Failed to get the base message",
SRCNAME, __func__);
return 0;
}
ret = message_incoming_handler (message, NULL, true /*force */);
gpgol_release (message);
return ret;
}
/* do_reader_action
decrypts the content of an inspector. Controled by flags
similary to the do_composer_action.
*/
HRESULT
do_reader_action (LPDISPATCH ctrl, int flags)
{
LPDISPATCH context = NULL;
LPDISPATCH selection = NULL;
LPDISPATCH wordEditor = NULL;
LPDISPATCH mailItem = NULL;
LPDISPATCH wordApplication = NULL;
struct sink_s decsinkmem;
sink_t decsink = &decsinkmem;
struct sink_s sinkmem;
sink_t sink = &sinkmem;
LPSTREAM tmpstream = NULL;
engine_filter_t filter = NULL;
HWND curWindow;
char* encData = NULL;
char* senderAddr = NULL;
char* subject = NULL;
int encDataLen = 0;
int rc = 0;
unsigned int session_number;
HRESULT hr;
STATSTG tmpStat;
protocol_t protocol;
hr = getContext (ctrl, &context);
if (FAILED(hr))
return hr;
memset (decsink, 0, sizeof *decsink);
memset (sink, 0, sizeof *sink);
curWindow = get_oom_context_window (context);
if (!(flags & DATA_BODY))
{
wordEditor = get_oom_object (context, "WordEditor");
wordApplication = get_oom_object (wordEditor, "get_Application");
selection = get_oom_object (wordApplication, "get_Selection");
}
mailItem = get_oom_object (context, "CurrentItem");
if ((!wordEditor || !wordApplication || !selection || !mailItem) &&
!(flags & DATA_BODY))
{
MessageBox (NULL,
"Internal error in GpgOL.\n"
"Could not find all objects.",
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
log_error ("%s:%s: Could not find all objects.",
SRCNAME, __func__);
goto failure;
}
if (!mailItem)
{
/* This happens when we try to decrypt the body of a mail in the
explorer context. */
mailItem = get_oom_object (context, "Selection.Item(1)");
if (!mailItem)
{
MessageBox (NULL,
_("Please select a Mail."),
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
goto failure;
}
}
if (flags & DATA_SELECTION)
{
encData = get_oom_string (selection, "Text");
if (!encData || (encDataLen = strlen (encData)) <= 1)
{
MessageBox (NULL,
_("Please select the data you wish to decrypt."),
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
goto failure;
}
}
else if (flags & DATA_BODY)
{
encData = get_oom_string (mailItem, "Body");
if (!encData || (encDataLen = strlen (encData)) <= 1)
{
MessageBox (NULL,
_("Nothing to decrypt."),
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
goto failure;
}
}
fix_linebreaks (encData, &encDataLen);
/* We check if the data we work on was opaque signed. This is
true for signed stuff created by ribbon-callbacks and not a
decent MIME implementation. So in that case we don't use
verify_mime */
if (!strstr (encData, OPAQUE_SIGNED_MARKER) && verify_mime (mailItem))
{
log_debug ("%s:%s: This was a mime message.",
SRCNAME, __func__);
if (flags & OP_DECRYPT)
{
MessageBox (NULL,
"This message is in MIME format. Due to technical restrictions "
"it can only be decrypted once per session. To decrypt it again "
"please restart Outlook and open the message.",
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
}
goto failure;
}
subject = get_oom_string (mailItem, "Subject");
if (get_oom_bool (mailItem, "Sent"))
{
char *addrType = get_oom_string (mailItem, "SenderEmailType");
if (addrType && strcmp("SMTP", addrType) == 0)
{
senderAddr = get_oom_string (mailItem, "SenderEmailAddress");
}
else
{
/* Not SMTP, fall back to try getting the property. */
LPDISPATCH sender = get_oom_object (mailItem, "Sender");
senderAddr = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
gpgol_release (sender);
}
xfree (addrType);
}
else
{
/* If the message has not been sent we might be composing
in this case use the current address */
LPDISPATCH sender = get_oom_object (mailItem, "Session.CurrentUser");
senderAddr = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
gpgol_release (sender);
}
/* Determine the protocol based on the content */
protocol = is_cms_data (encData, encDataLen) ? PROTOCOL_SMIME :
PROTOCOL_OPENPGP;
hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
(SOF_UNIQUEFILENAME | STGM_DELETEONRELEASE
| STGM_CREATE | STGM_READWRITE),
NULL, GpgOLStr("GPG"), &tmpstream);
if (FAILED (hr))
{
log_error ("%s:%s: can't create temp file: hr=%#lx\n",
SRCNAME, __func__, hr);
rc = -1;
goto failure;
}
sink->cb_data = tmpstream;
sink->writefnc = sink_std_write;
session_number = engine_new_session_number ();
if (engine_create_filter (&filter, write_buffer_for_cb, sink))
goto failure;
decsink->cb_data = filter;
decsink->writefnc = sink_encryption_write;
engine_set_session_number (filter, session_number);
engine_set_session_title (filter, subject ? subject : _("GpgOL"));
if (flags & OP_DECRYPT)
{
if ((rc=engine_decrypt_start (filter, curWindow,
protocol,
1, NULL)))
{
log_error ("%s:%s: engine decrypt start failed: %s",
SRCNAME, __func__, gpg_strerror (rc));
goto failure;
}
}
else if (flags & OP_VERIFY)
{
log_debug ("Starting verify");
if ((rc=engine_verify_start (filter, curWindow,
NULL, 0, protocol, senderAddr)))
{
log_error ("%s:%s: engine verify start failed: %s",
SRCNAME, __func__, gpg_strerror (rc));
goto failure;
}
}
/* Write the text in the decryption sink. */
rc = write_buffer (decsink, encData, encDataLen);
/* Flush the decryption sink and wait for the decryption to get
ready. */
if ((rc = write_buffer (decsink, NULL, 0)))
goto failure;
if ((rc = engine_wait (filter)))
goto failure;
filter = NULL; /* Not valid anymore. */
decsink->cb_data = NULL; /* Not needed anymore. */
if (!sink->enc_counter)
{
log_debug ("%s:%s: nothing received from engine", SRCNAME, __func__);
goto failure;
}
/* Check the size of the decrypted data */
tmpstream->Stat (&tmpStat, 0);
if (tmpStat.cbSize.QuadPart > UINT_MAX)
{
log_error ("%s:%s: No one should write so large mails.",
SRCNAME, __func__);
goto failure;
}
/* Copy the decrypted stream to the message editor. */
{
LARGE_INTEGER off;
ULONG nread;
char buffer[(unsigned int)tmpStat.cbSize.QuadPart + 1];
memset (buffer, 0, sizeof buffer);
off.QuadPart = 0;
hr = tmpstream->Seek (off, STREAM_SEEK_SET, NULL);
if (hr)
{
log_error ("%s:%s: seeking back to the begin failed: hr=%#lx",
SRCNAME, __func__, hr);
rc = gpg_error (GPG_ERR_EIO);
goto failure;
}
hr = tmpstream->Read (buffer, sizeof (buffer) - 1, &nread);
if (hr)
{
log_error ("%s:%s: IStream::Read failed: hr=%#lx",
SRCNAME, __func__, hr);
rc = gpg_error (GPG_ERR_EIO);
goto failure;
}
if (strlen (buffer) > 1)
{
/* Now replace the crypto data with the decrypted data or show it
somehow.*/
int err = 0;
if (flags & DATA_SELECTION)
{
err = put_oom_string (selection, "Text", buffer);
}
else if (flags & DATA_BODY)
{
err = put_oom_string (mailItem, "Body", buffer);
}
if (err)
{
MessageBox (NULL, buffer,
flags & OP_DECRYPT ? _("Plain text") :
_("Signed text"),
MB_ICONINFORMATION|MB_OK);
}
}
else
{
/* Just to be save not to overwrite the selection with
an empty buffer */
log_error ("%s:%s: unexpected problem ", SRCNAME, __func__);
goto failure;
}
}
failure:
if (rc)
log_debug ("%s:%s: failed rc=%d (%s) <%s>", SRCNAME, __func__, rc,
gpg_strerror (rc), gpg_strsource (rc));
engine_cancel (filter);
gpgol_release (mailItem);
gpgol_release (selection);
gpgol_release (wordEditor);
gpgol_release (wordApplication);
xfree (encData);
xfree (senderAddr);
xfree (subject);
if (tmpstream)
gpgol_release (tmpstream);
return S_OK;
}
/* getIcon
Loads a PNG image from the resurce converts it into a Bitmap
and Wraps it in an PictureDispatcher that is returned as result.
Based on documentation from:
http://www.codeproject.com/Articles/3537/Loading-JPG-PNG-resources-using-GDI
*/
HRESULT
getIcon (int id, VARIANT* result)
{
PICTDESC pdesc;
LPDISPATCH pPict;
HRESULT hr;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::Bitmap* pbitmap;
ULONG_PTR gdiplusToken;
HRSRC hResource;
DWORD imageSize;
const void* pResourceData;
HGLOBAL hBuffer;
memset (&pdesc, 0, sizeof pdesc);
pdesc.cbSizeofstruct = sizeof pdesc;
pdesc.picType = PICTYPE_BITMAP;
if (!result)
{
log_error ("getIcon called without result variant.");
return E_POINTER;
}
/* Initialize GDI */
gdiplusStartupInput.DebugEventCallback = NULL;
gdiplusStartupInput.SuppressBackgroundThread = FALSE;
gdiplusStartupInput.SuppressExternalCodecs = FALSE;
gdiplusStartupInput.GdiplusVersion = 1;
GdiplusStartup (&gdiplusToken, &gdiplusStartupInput, NULL);
/* Get the image from the resource file */
hResource = FindResource (glob_hinst, MAKEINTRESOURCE(id), RT_RCDATA);
if (!hResource)
{
log_error ("%s:%s: failed to find image: %i",
SRCNAME, __func__, id);
return E_FAIL;
}
imageSize = SizeofResource (glob_hinst, hResource);
if (!imageSize)
return E_FAIL;
pResourceData = LockResource (LoadResource(glob_hinst, hResource));
if (!pResourceData)
{
log_error ("%s:%s: failed to load image: %i",
SRCNAME, __func__, id);
return E_FAIL;
}
hBuffer = GlobalAlloc (GMEM_MOVEABLE, imageSize);
if (hBuffer)
{
void* pBuffer = GlobalLock (hBuffer);
if (pBuffer)
{
IStream* pStream = NULL;
CopyMemory (pBuffer, pResourceData, imageSize);
if (CreateStreamOnHGlobal (hBuffer, FALSE, &pStream) == S_OK)
{
pbitmap = Gdiplus::Bitmap::FromStream (pStream);
gpgol_release (pStream);
if (!pbitmap || pbitmap->GetHBITMAP (0, &pdesc.bmp.hbitmap))
{
log_error ("%s:%s: failed to get PNG.",
SRCNAME, __func__);
}
}
}
GlobalUnlock (pBuffer);
}
GlobalFree (hBuffer);
Gdiplus::GdiplusShutdown (gdiplusToken);
/* Wrap the image into an OLE object. */
hr = OleCreatePictureIndirect (&pdesc, IID_IPictureDisp,
TRUE, (void **) &pPict);
if (hr != S_OK || !pPict)
{
log_error ("%s:%s: OleCreatePictureIndirect failed: hr=%#lx\n",
SRCNAME, __func__, hr);
return -1;
}
result->pdispVal = pPict;
result->vt = VT_DISPATCH;
return S_OK;
}
/* Adds an encrypted attachment if the flag OP_SIGN is set
a detached signature of the encrypted file is also added. */
static HRESULT
attachEncryptedFile (LPDISPATCH ctrl, int flags)
{
LPDISPATCH context = NULL;
LPDISPATCH mailItem = NULL;
LPDISPATCH sender = NULL;
LPDISPATCH recipients = NULL;
HRESULT hr;
char* senderAddr = NULL;
char** recipientAddrs = NULL;
char* subject = NULL;
HWND curWindow;
char *fileToEncrypt = NULL;
wchar_t *fileToEncryptW = NULL;
wchar_t *encryptedFile = NULL;
wchar_t *attachName = NULL;
HANDLE hFile = NULL;
HANDLE hEncFile = NULL;
unsigned int session_number;
struct sink_s encsinkmem;
sink_t encsink = &encsinkmem;
struct sink_s sinkmem;
sink_t sink = &sinkmem;
engine_filter_t filter = NULL;
protocol_t protocol;
int rc = 0;
int i = 0;
memset (encsink, 0, sizeof *encsink);
memset (sink, 0, sizeof *sink);
hr = getContext (ctrl, &context);
if (FAILED(hr))
return hr;
/* First do the check for recipients as this is likely
to fail */
mailItem = get_oom_object (context, "CurrentItem");
sender = get_oom_object (mailItem, "Session.CurrentUser");
recipients = get_oom_object (mailItem, "Recipients");
recipientAddrs = get_oom_recipients (recipients);
if (!recipientAddrs || !(*recipientAddrs))
{
MessageBox (NULL,
_("Please add at least one recipent."),
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
goto failure;
}
/* Get a file handle to read from */
fileToEncrypt = get_open_filename (NULL, _("Select file to encrypt"));
if (!fileToEncrypt)
{
log_debug ("No file selected");
goto failure;
}
fileToEncryptW = utf8_to_wchar2 (fileToEncrypt, strlen(fileToEncrypt));
xfree (fileToEncrypt);
hFile = CreateFileW (fileToEncryptW,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
/* Should not happen as the Open File dialog
should have prevented this.
Maybe this also happens when a file is
not readable. In that case we might want
to switch to a localized error naming the file. */
MessageBox (NULL,
"Internal error in GpgOL.\n"
"Could not open File.",
_("GpgOL"),
MB_ICONERROR|MB_OK);
return S_OK;
}
/* Now do the encryption preperations */
if (!mailItem || !sender || !recipients)
{
MessageBox (NULL,
"Internal error in GpgOL.\n"
"Could not find all objects.",
_("GpgOL"),
MB_ICONERROR|MB_OK);
log_error ("%s:%s: Could not find all objects.",
SRCNAME, __func__);
goto failure;
}
senderAddr = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
curWindow = get_oom_context_window (context);
session_number = engine_new_session_number ();
subject = get_oom_string (mailItem, "Subject");
/* Prepare the encryption sink */
if ((rc = engine_create_filter (&filter, write_buffer_for_cb, sink)))
{
goto failure;
}
encsink->cb_data = filter;
encsink->writefnc = sink_encryption_write;
engine_set_session_number (filter, session_number);
engine_set_session_title (filter, subject ? subject :_("GpgOL"));
if ((rc=engine_encrypt_prepare (filter, curWindow,
PROTOCOL_UNKNOWN,
ENGINE_FLAG_BINARY_OUTPUT,
senderAddr, recipientAddrs, &protocol)))
{
log_error ("%s:%s: engine encrypt prepare failed : %s",
SRCNAME, __func__, gpg_strerror (rc));
goto failure;
}
attachName = get_pretty_attachment_name (fileToEncryptW, protocol, 0);
if (!attachName)
{
log_error ("%s:%s: Could not get a decent attachment name",
SRCNAME, __func__);
goto failure;
}
encryptedFile = get_tmp_outfile (attachName, &hEncFile);
sink->cb_data = hEncFile;
sink->writefnc = sink_file_write;
if ((rc=engine_encrypt_start (filter, 0)))
{
log_error ("%s:%s: engine encrypt start failed: %s",
SRCNAME, __func__, gpg_strerror (rc));
goto failure;
}
if ((rc=copyFileToSink (hFile, encsink)))
goto failure;
/* Lets hope the user did not select a huge file. We are hanging
here until encryption is completed.. */
if ((rc = engine_wait (filter)))
goto failure;
filter = NULL; /* Not valid anymore. */
encsink->cb_data = NULL; /* Not needed anymore. */
if (!sink->enc_counter)
{
log_error ("%s:%s: nothing received from engine", SRCNAME, __func__);
goto failure;
}
/* Now we have an encrypted file behind encryptedFile. Let's add it */
add_oom_attachment (mailItem, encryptedFile, nullptr);
if (flags & OP_SIGN)
{
attachSignature (mailItem, subject, hEncFile, protocol, session_number,
curWindow, encryptedFile, senderAddr);
}
failure:
if (filter)
engine_cancel (filter);
if (hEncFile)
{
CloseHandle (hEncFile);
DeleteFileW (encryptedFile);
}
xfree (senderAddr);
xfree (encryptedFile);
xfree (fileToEncryptW);
xfree (attachName);
xfree (subject);
gpgol_release (mailItem);
gpgol_release (sender);
gpgol_release (recipients);
if (hFile)
CloseHandle (hFile);
if (recipientAddrs)
{
for (i=0; recipientAddrs && recipientAddrs[i]; i++)
xfree (recipientAddrs[i]);
xfree (recipientAddrs);
}
return S_OK;
}
HRESULT
startCertManager (LPDISPATCH ctrl)
{
HRESULT hr;
LPDISPATCH context;
HWND curWindow;
hr = getContext (ctrl, &context);
if (FAILED(hr))
return hr;
curWindow = get_oom_context_window (context);
engine_start_keymanager (curWindow);
return S_OK;
}
HRESULT
decryptBody (LPDISPATCH ctrl)
{
return do_reader_action (ctrl, OP_DECRYPT | DATA_BODY);
}
HRESULT
decryptSelection (LPDISPATCH ctrl)
{
return do_reader_action (ctrl, OP_DECRYPT | DATA_SELECTION);
}
HRESULT
encryptBody (LPDISPATCH ctrl)
{
return do_composer_action (ctrl, OP_ENCRYPT | DATA_BODY);
}
HRESULT
encryptSelection (LPDISPATCH ctrl)
{
return do_composer_action (ctrl, OP_ENCRYPT | DATA_SELECTION);
}
HRESULT
addEncSignedAttachment (LPDISPATCH ctrl)
{
return attachEncryptedFile (ctrl, OP_SIGN);
}
HRESULT
addEncAttachment (LPDISPATCH ctrl)
{
return attachEncryptedFile (ctrl, 0);
}
HRESULT signBody (LPDISPATCH ctrl)
{
return do_composer_action (ctrl, DATA_BODY | OP_SIGN);
}
HRESULT verifyBody (LPDISPATCH ctrl)
{
return do_reader_action (ctrl, DATA_BODY | OP_VERIFY);
}
HRESULT
mark_mime_action (LPDISPATCH ctrl, int flags, bool is_explorer)
{
HRESULT hr;
HRESULT rc = E_FAIL;
LPDISPATCH context = NULL,
mailitem = NULL;
LPMESSAGE message = NULL;
int oldflags,
newflags;
log_debug ("%s:%s: enter", SRCNAME, __func__);
hr = getContext (ctrl, &context);
if (FAILED(hr))
return hr;
mailitem = get_oom_object (context, is_explorer ? "ActiveInlineResponse" :
"CurrentItem");
if (!mailitem)
{
log_error ("%s:%s: Failed to get mailitem.",
SRCNAME, __func__);
goto done;
}
message = get_oom_base_message (mailitem);
if (!message)
{
log_error ("%s:%s: Failed to get message.",
SRCNAME, __func__);
goto done;
}
oldflags = get_gpgol_draft_info_flags (message);
newflags = oldflags xor flags;
if (set_gpgol_draft_info_flags (message, newflags))
{
log_error ("%s:%s: Failed to set draft flags.",
SRCNAME, __func__);
}
rc = S_OK;
/* We need to invalidate the UI to update the toggle
states of the subbuttons and the top button. Yeah,
we invalidate a lot *sigh* */
gpgoladdin_invalidate_ui ();
done:
gpgol_release (context);
gpgol_release (mailitem);
gpgol_release (message);
return rc;
}
/* Get the state of encrypt / sign toggle buttons.
flag values: 1 get the state of the encrypt button.
2 get the state of the sign button.
If is_explorer is set to true we look at the inline response.
*/
HRESULT get_crypt_pressed (LPDISPATCH ctrl, int flags, VARIANT *result,
bool is_explorer)
{
HRESULT hr;
bool value;
LPDISPATCH context = NULL,
mailitem = NULL;
LPMESSAGE message = NULL;
result->vt = VT_BOOL | VT_BYREF;
result->pboolVal = (VARIANT_BOOL*) xmalloc (sizeof (VARIANT_BOOL));
*(result->pboolVal) = VARIANT_FALSE;
/* First the usual defensive check about our parameters */
if (!ctrl || !result)
{
log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__);
return E_FAIL;
}
hr = getContext (ctrl, &context);
if (hr)
{
log_error ("%s:%s:%i : hresult %lx", SRCNAME, __func__, __LINE__,
hr);
return E_FAIL;
}
mailitem = get_oom_object (context, is_explorer ? "ActiveInlineResponse" :
"CurrentItem");
if (!mailitem)
{
log_error ("%s:%s: Failed to get mailitem.",
SRCNAME, __func__);
goto done;
}
message = get_oom_base_message (mailitem);
if (!message)
{
log_error ("%s:%s: No message found.",
SRCNAME, __func__);
goto done;
}
value = (get_gpgol_draft_info_flags (message) & flags) == flags;
*(result->pboolVal) = value ? VARIANT_TRUE : VARIANT_FALSE;
done:
gpgol_release (context);
gpgol_release (mailitem);
gpgol_release (message);
return S_OK;
}
static Mail *
get_mail_from_control (LPDISPATCH ctrl, bool *none_selected)
{
HRESULT hr;
LPDISPATCH context = NULL,
mailitem = NULL;
*none_selected = false;
if (!ctrl)
{
log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__);
return NULL;
}
hr = getContext (ctrl, &context);
if (hr)
{
log_error ("%s:%s:%i : hresult %lx", SRCNAME, __func__, __LINE__,
hr);
return NULL;
}
char *ctx_name = get_object_name (context);
if (!ctx_name)
{
log_error ("%s:%s: Failed to get context name",
SRCNAME, __func__);
gpgol_release (context);
return NULL;
}
if (!strcmp (ctx_name, "_Inspector"))
{
mailitem = get_oom_object (context, "CurrentItem");
}
else if (!strcmp (ctx_name, "_Explorer"))
{
mailitem = get_oom_object (context, "Selection.Item(1)");
if (!mailitem)
{
*none_selected = true;
}
else if (g_ol_version_major >= 16)
{
// Avoid showing wrong crypto state if we don't have a reading
// pane. In that case the parser will finish for a mail which is gone
// and the crypto state will not get updated.
+
+ //
+ // Somehow latest Outlook 2016 crashes when accessing the current view
+ // of the Explorer. This is even reproducible with
+ // GpgOL disabled and only with Outlook Spy active. If you select
+ // the explorer of an Outlook.com resource and then access
+ // the CurrentView and close the CurrentView again in Outlook Spy
+ // outlook crashes.
LPDISPATCH prevEdit = get_oom_object (context, "PreviewPane.WordEditor");
gpgol_release (prevEdit);
if (!prevEdit)
{
*none_selected = true;
gpgol_release (mailitem);
mailitem = nullptr;
}
}
else
{
// Preview Pane is not available in older outlooks
LPDISPATCH tableView = get_oom_object (context, "CurrentView");
if (!tableView)
{
// Woops, should not happen.
TRACEPOINT;
*none_selected = true;
gpgol_release (mailitem);
mailitem = nullptr;
}
else
{
int hasReadingPane = get_oom_bool (tableView, "ShowReadingPane");
gpgol_release (tableView);
if (!hasReadingPane)
{
*none_selected = true;
gpgol_release (mailitem);
mailitem = nullptr;
}
}
}
}
gpgol_release (context);
if (!mailitem)
{
log_error ("%s:%s: Failed to get mailitem. From %s",
SRCNAME, __func__, ctx_name);
xfree (ctx_name);
return NULL;
}
xfree (ctx_name);
char *uid;
/* Get the uid of this item. */
uid = get_unique_id (mailitem, 0, nullptr);
if (!uid)
{
LPMESSAGE msg = get_oom_base_message (mailitem);
uid = mapi_get_uid (msg);
gpgol_release (msg);
if (!uid)
{
log_debug ("%s:%s: Failed to get uid for %p",
SRCNAME, __func__, mailitem);
gpgol_release (mailitem);
return NULL;
}
}
auto ret = Mail::get_mail_for_uuid (uid);
xfree (uid);
if (!ret)
{
log_error ("%s:%s: Failed to find mail %p in map.",
SRCNAME, __func__, mailitem);
}
gpgol_release (mailitem);
return ret;
}
/* Helper to reduce code duplication.*/
#define MY_MAIL_GETTER \
if (!ctrl) \
{ \
log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__); \
return E_FAIL; \
} \
bool none_selected; \
const auto mail = get_mail_from_control (ctrl, &none_selected); \
(void)none_selected; \
if (!mail) \
{ \
log_oom ("%s:%s:%i Failed to get mail", \
SRCNAME, __func__, __LINE__); \
}
HRESULT get_is_details_enabled (LPDISPATCH ctrl, VARIANT *result)
{
MY_MAIL_GETTER
result->vt = VT_BOOL | VT_BYREF;
result->pboolVal = (VARIANT_BOOL*) xmalloc (sizeof (VARIANT_BOOL));
*(result->pboolVal) = none_selected ? VARIANT_FALSE : VARIANT_TRUE;
return S_OK;
}
HRESULT get_sig_label (LPDISPATCH ctrl, VARIANT *result)
{
MY_MAIL_GETTER
result->vt = VT_BSTR;
wchar_t *w_result;
if (!mail)
{
log_debug ("%s:%s: No mail.",
SRCNAME, __func__);
w_result = utf8_to_wchar (_("Insecure"));
result->bstrVal = SysAllocString (w_result);
xfree (w_result);
return S_OK;
}
w_result = utf8_to_wchar (mail->get_crypto_summary ().c_str ());
result->bstrVal = SysAllocString (w_result);
xfree (w_result);
return S_OK;
}
HRESULT get_sig_ttip (LPDISPATCH ctrl, VARIANT *result)
{
MY_MAIL_GETTER
result->vt = VT_BSTR;
wchar_t *w_result;
if (mail)
{
w_result = utf8_to_wchar (mail->get_crypto_one_line().c_str());
}
else if (!none_selected)
{
w_result = utf8_to_wchar (_("Insecure message"));
}
else
{
w_result = utf8_to_wchar (_("No message selected"));
}
result->bstrVal = SysAllocString (w_result);
xfree (w_result);
return S_OK;
}
HRESULT get_sig_stip (LPDISPATCH ctrl, VARIANT *result)
{
MY_MAIL_GETTER
result->vt = VT_BSTR;
if (none_selected)
{
result->bstrVal = SysAllocString (L"");
return S_OK;
}
if (!mail || (!mail->is_signed () && !mail->is_encrypted ()))
{
wchar_t *w_result;
w_result = utf8_to_wchar (utf8_gettext ("You cannot be sure who sent, "
"modified and read the message in transit.\n\n"
"Click here to learn more."));
result->bstrVal = SysAllocString (w_result);
xfree (w_result);
return S_OK;
}
const auto message = mail->get_crypto_details ();
wchar_t *w_message = utf8_to_wchar (message.c_str());
result->bstrVal = SysAllocString (w_message);
xfree (w_message);
return S_OK;
}
HRESULT launch_cert_details (LPDISPATCH ctrl)
{
MY_MAIL_GETTER
if (!mail || (!mail->is_signed () && !mail->is_encrypted ()))
{
ShellExecuteA(NULL, NULL, "https://emailselfdefense.fsf.org/infographic",
0, 0, SW_SHOWNORMAL);
return S_OK;
}
if (!mail->is_signed () && mail->is_encrypted ())
{
/* Encrypt only, no information but show something. because
we want the button to be active.
Aheinecke: I don't think we should show to which keys the message
is encrypted here. This would confuse users if they see keyids
of unknown keys and the information can't be "true" because the
sender could have sent the same information to other people or
used throw keyids etc.
*/
char * buf;
gpgrt_asprintf (&buf, _("The message was not cryptographically signed.\n"
"There is no additional information available if it "
"was actually sent by '%s' or if someone faked the sender address."), mail->get_sender ().c_str());
MessageBox (NULL, buf, _("GpgOL"),
MB_ICONINFORMATION|MB_OK);
xfree (buf);
return S_OK;
}
char *uiserver = get_uiserver_name ();
bool showError = false;
if (uiserver)
{
std::string path (uiserver);
xfree (uiserver);
if (path.find("kleopatra.exe") != std::string::npos)
{
size_t dpos;
if ((dpos = path.find(" --daemon")) != std::string::npos)
{
path.erase(dpos, strlen(" --daemon"));
}
auto ctx = Context::createForEngine(SpawnEngine);
if (!ctx)
{
log_error ("%s:%s: No spawn engine.",
SRCNAME, __func__);
}
const char *argv[] = {path.c_str(),
"--query",
mail->get_sig_fpr(),
NULL };
log_debug ("%s:%s: Starting %s %s %s",
SRCNAME, __func__, path.c_str(), argv[1], argv[2]);
Data d(Data::null);
ctx->spawnAsync(path.c_str(), argv, d, d,
d, Context::SpawnNone);
}
else
{
showError = true;
}
}
else
{
showError = true;
}
if (showError)
{
MessageBox (NULL,
_("Could not find Kleopatra.\n"
"Please reinstall Gpg4win with the Kleopatra component enabled."),
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
}
return S_OK;
}
HRESULT get_crypto_icon (LPDISPATCH ctrl, VARIANT *result)
{
MY_MAIL_GETTER
if (mail)
{
return getIcon (mail->get_crypto_icon_id (), result);
}
return getIcon (IDI_LEVEL_0, result);
}
diff --git a/src/windowmessages.cpp b/src/windowmessages.cpp
index 316e00e..1259496 100644
--- a/src/windowmessages.cpp
+++ b/src/windowmessages.cpp
@@ -1,233 +1,257 @@
/* @file windowmessages.h
* @brief Helper class to work with the windowmessage handler thread.
*
* Copyright (C) 2015, 2016 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation 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.1 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, see <http://www.gnu.org/licenses/>.
*/
#include "windowmessages.h"
#include "common.h"
#include "oomhelp.h"
#include "mail.h"
#include "gpgoladdin.h"
#include <stdio.h>
#define RESPONDER_CLASS_NAME "GpgOLResponder"
/* Singleton window */
static HWND g_responder_window = NULL;
LONG_PTR WINAPI
gpgol_window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_USER + 1)
{
wm_ctx_t *ctx = (wm_ctx_t *) lParam;
log_debug ("%s:%s: Recieved user msg: %i",
SRCNAME, __func__, ctx->wmsg_type);
switch (ctx->wmsg_type)
{
case (PARSING_DONE):
{
auto mail = (Mail*) ctx->data;
if (!Mail::is_valid_ptr (mail))
{
log_debug ("%s:%s: Parsing done for mail which is gone.",
SRCNAME, __func__);
break;
}
mail->parsing_done();
break;
}
case (RECIPIENT_ADDED):
{
auto mail = (Mail*) ctx->data;
if (!Mail::is_valid_ptr (mail))
{
log_debug ("%s:%s: Recipient add for mail which is gone.",
SRCNAME, __func__);
break;
}
mail->locate_keys();
break;
}
case (INVALIDATE_UI):
{
log_debug ("%s:%s: Invalidating UI",
SRCNAME, __func__);
gpgoladdin_invalidate_ui();
log_debug ("%s:%s: Invalidation done",
SRCNAME, __func__);
break;
}
default:
log_debug ("Unknown msg");
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
HWND
create_responder_window ()
{
size_t cls_name_len = strlen(RESPONDER_CLASS_NAME) + 1;
char cls_name[cls_name_len];
if (g_responder_window)
{
return g_responder_window;
}
/* Create Window wants a mutable string as the first parameter */
snprintf (cls_name, cls_name_len, "%s", RESPONDER_CLASS_NAME);
WNDCLASS windowClass;
windowClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
windowClass.lpfnWndProc = gpgol_window_proc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = (HINSTANCE) GetModuleHandle(NULL);
windowClass.hIcon = 0;
windowClass.hCursor = 0;
windowClass.hbrBackground = 0;
windowClass.lpszMenuName = 0;
windowClass.lpszClassName = cls_name;
RegisterClass(&windowClass);
g_responder_window = CreateWindow (cls_name, RESPONDER_CLASS_NAME, 0, 0, 0,
0, 0, 0, (HMENU) 0,
(HINSTANCE) GetModuleHandle(NULL), 0);
return g_responder_window;
}
int
send_msg_to_ui_thread (wm_ctx_t *ctx)
{
size_t cls_name_len = strlen(RESPONDER_CLASS_NAME) + 1;
char cls_name[cls_name_len];
snprintf (cls_name, cls_name_len, "%s", RESPONDER_CLASS_NAME);
HWND responder = FindWindow (cls_name, RESPONDER_CLASS_NAME);
if (!responder)
{
log_error ("%s:%s: Failed to find responder window.",
SRCNAME, __func__);
return -1;
}
SendMessage (responder, WM_USER + 1, 0, (LPARAM) ctx);
return 0;
}
int
do_in_ui_thread (gpgol_wmsg_type type, void *data)
{
wm_ctx_t ctx = {NULL, UNKNOWN, 0};
ctx.wmsg_type = type;
ctx.data = data;
if (send_msg_to_ui_thread (&ctx))
{
return -1;
}
return ctx.err;
}
static std::vector <LPDISPATCH> explorers;
void
add_explorer (LPDISPATCH explorer)
{
explorers.push_back (explorer);
}
void remove_explorer (LPDISPATCH explorer)
{
explorers.erase(std::remove(explorers.begin(),
explorers.end(),
explorer),
explorers.end());
}
LRESULT CALLBACK
gpgol_hook(int code, WPARAM wParam, LPARAM lParam)
{
/* Once we are in the close events we don't have enough
control to revert all our changes so we have to do it
with this nice little hack by catching the WM_CLOSE message
before it reaches outlook. */
LPCWPSTRUCT cwp = (LPCWPSTRUCT) lParam;
switch (cwp->message)
{
case WM_CLOSE:
{
HWND lastChild = NULL;
for (const auto explorer: explorers)
{
/* Casting to LPOLEWINDOW and calling GetWindow
succeeded in Outlook 2016 but always returned
the number 1. So we need this hack. */
char *caption = get_oom_string (explorer, "Caption");
if (!caption)
{
log_debug ("%s:%s: No caption.",
SRCNAME, __func__);
continue;
}
/* rctrl_renwnd32 is the window class of outlook. */
HWND hwnd = FindWindowExA(NULL, lastChild, "rctrl_renwnd32",
caption);
xfree (caption);
lastChild = hwnd;
if (hwnd == cwp->hwnd)
{
log_debug ("%s:%s: WM_CLOSE windowmessage for explorer. "
"Closing all mails.",
SRCNAME, __func__);
Mail::close_all_mails();
break;
}
}
break;
}
case WM_SYSCOMMAND:
/*
This comes to often and when we are closed from the icon
we also get WM_CLOSE
if (cwp->wParam == SC_CLOSE)
{
log_debug ("%s:%s: SC_CLOSE syscommand. Closing all mails.",
SRCNAME, __func__);
Mail::close_all_mails();
} */
break;
default:
break;
}
return CallNextHookEx (NULL, code, wParam, lParam);
}
/* Create the message hook for outlook's windowmessages
we are especially interested in WM_QUIT to do cleanups
and prevent the "Item has changed" question. */
HHOOK
create_message_hook()
{
return SetWindowsHookEx (WH_CALLWNDPROC,
gpgol_hook,
NULL,
GetCurrentThreadId());
}
+
+GPGRT_LOCK_DEFINE(invalidate_lock);
+static bool invalidation_in_progress;
+
+DWORD WINAPI
+delayed_invalidate_ui (LPVOID)
+{
+ if (invalidation_in_progress)
+ {
+ log_debug ("%s:%s: Invalidation canceled as it is in progress.",
+ SRCNAME, __func__);
+ return 0;
+ }
+ gpgrt_lock_lock(&invalidate_lock);
+ invalidation_in_progress = true;
+ /* We sleep here a bit to prevent invalidation immediately
+ after the selection change before we have started processing
+ the mail. */
+ Sleep (500);
+ do_in_ui_thread (INVALIDATE_UI, nullptr);
+ invalidation_in_progress = false;
+ gpgrt_lock_unlock(&invalidate_lock);
+ return 0;
+}
diff --git a/src/windowmessages.h b/src/windowmessages.h
index 6e875d7..cc1cb62 100644
--- a/src/windowmessages.h
+++ b/src/windowmessages.h
@@ -1,76 +1,79 @@
/* windowmessages.h - Helper functions for Window message exchange.
* Copyright (C) 2015 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation 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.1 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef WINDOWMESSAGES_H
#define WINDOWMESSAGES_H
#include <windows.h>
/** Window Message handling for GpgOL.
In Outlook only one thread has access to the Outlook Object model
and this is the UI Thread. We can work in other threads but
to do something with outlooks data we neet to be in the UI Thread.
So we create a hidden Window in this thread and use the fact
that SendMessage handles Window messages in the thread where the
Window was created.
This way we can go back to interactct with the Outlook from another
thread without working with COM Multithreading / Marshaling.
The Responder Window should be initalized on startup.
*/
typedef enum _gpgol_wmsg_type
{
UNKNOWN = 0,
INVALIDATE_UI = 1, /* The UI should be invalidated. */
PARSING_DONE = 2, /* A mail was parsed. Data should be a pointer
to the mail object. */
RECIPIENT_ADDED = 3, /* A recipient was added. Data should be ptr
to mail */
} gpgol_wmsg_type;
typedef struct
{
void *data; /* Pointer to arbitrary data depending on msg type */
gpgol_wmsg_type wmsg_type; /* Type of the msg. */
int err; /* Set to true on error */
} wm_ctx_t;
/** Create and register the responder window.
The responder window should be */
HWND
create_responder_window ();
/** Send a message to the UI thread through the responder Window.
Returns 0 on success. */
int
send_msg_to_ui_thread (wm_ctx_t *ctx);
/** Uses send_msg_to_ui_thread to execute the request
in the ui thread. Returns the result. */
int
do_in_ui_thread (gpgol_wmsg_type type, void *data);
/** Create our filter before outlook Window Messages. */
HHOOK
create_message_hook();
+DWORD WINAPI
+delayed_invalidate_ui (LPVOID);
+
void add_explorer (LPDISPATCH explorer);
void remove_explorer (LPDISPATCH explorer);
#endif // WINDOWMESSAGES_H

File Metadata

Mime Type
text/x-diff
Expires
Fri, Mar 14, 4:42 AM (1 d, 11 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
23/27/a7bc19c2ba198e43df7610d8ddcb

Event Timeline