diff --git a/src/Makefile.am b/src/Makefile.am index 71246a3..93312cd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,132 +1,133 @@ # Makefile.am - main makefile for dialogs part of GPGol # Copyright (C) 2005 g10 Code GmbH # # This file is free software; as a special exception the author gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ## Process this file with automake to produce Makefile.in unused_sources = item-events.cpp bin_PROGRAMS = gpgol #treeview EXTRA_DIST = \ versioninfo.rc.in mapi32.def $(unused_sources) Outlook.gpl \ encrypt-16.bmp encrypt-16m.bmp \ encrypt-32.bmp encrypt-32m.bmp \ sign-16.bmp sign-16m.bmp \ sign-32.bmp sign-32m.bmp \ key-manager-16.bmp key-manager-16m.bmp \ key-manager-32.bmp key-manager-32m.bmp \ key-manager-64.bmp key-manager-64m.bmp \ decrypt-16.bmp decrypt-16m.bmp \ decrypt-32.bmp decrypt-32m.bmp \ verify-16.bmp verify-16m.bmp \ verify-32.bmp verify-32m.bmp \ decrypt-verify-16.bmp decrypt-verify-16m.bmp \ decrypt-verify-32.bmp decrypt-verify-32m.bmp \ encrypt-16.png encrypt-48.png \ key-manager-64.png \ decrypt-16.png decrypt-48.png \ encrypt-sign-file-48.png \ sign-48.png verify-48.png \ logo.bmp README.icons EXEEXT = .dll AM_CFLAGS = $(GPGME_CFLAGS) $(LIBASSUAN_CFLAGS) -shared AM_CXXFLAGS = $(GPGME_CFLAGS) $(LIBASSUAN_CFLAGS) -shared gpgol_SOURCES = \ main.c gpgol.def \ resource.rc \ gpgol-ids.h \ olflange.cpp olflange.h \ olflange-def.h \ olflange-dlgs.cpp \ dialogs.rc dialogs.h \ myexchext.h \ display.cpp display.h \ message.cpp message.h \ revert.cpp revert.h \ mimeparser.c mimeparser.h \ mimemaker.c mimemaker.h \ msgcache.c msgcache.h \ engine.c engine.h \ engine-assuan.c engine-assuan.h \ engine-gpgme.c engine-gpgme.h \ rfc822parse.c rfc822parse.h \ common.h common.c util.h \ xmalloc.h \ passcache.c passcache.h \ config-dialog.c \ passphrase-dialog.c \ recipient-dialog.c \ verify-dialog.c \ mapihelp.cpp mapihelp.h \ mymapi.h mymapitags.h \ serpent.c serpent.h \ ext-commands.cpp ext-commands.h \ user-events.cpp user-events.h \ session-events.cpp session-events.h \ message-events.cpp message-events.h \ attached-file-events.cpp attached-file-events.h \ property-sheets.cpp property-sheets.h \ item-events.h \ oomhelp.cpp oomhelp.h eventsink.h \ explorers.cpp explorers.h \ inspectors.cpp inspectors.h \ mailitem.cpp mailitem.h \ cmdbarcontrols.cpp cmdbarcontrols.h \ w32-gettext.c w32-gettext.h \ gpgoladdin.cpp gpgoladdin.h \ ribbon-callbacks.cpp ribbon-callbacks.h \ parsetlv.c parsetlv.h \ filetype.c filetype.h \ eventsinks.h application-events.cpp \ mailitem-events.cpp \ - attachment.h attachment.cpp + attachment.h attachment.cpp \ + windowmessages.h windowmessages.cpp #treeview_SOURCES = treeview.c # W32API 3.2 comes with an unusable libmapi32.a. We build our own # version. Note the omission of -k (--kill-at) from the DLLTOOL # command line. We also create our own virtual copies to the _static_ # versions of GPGME and gpg-error, because we want to link to them # statically, and not dynamically (otherwise Outlook would not find # them). gpgol_DEPENDENCIES = libmapi32.a libgpg-error.a libgpgme.a libassuan.a libmapi32.a: mapi32.def $(DLLTOOL) --output-lib $@ --def $< libgpg-error.a: ln -s $$($(GPG_ERROR_CONFIG) --prefix)/lib/libgpg-error.a . libgpgme.a: ln -s $$($(GPGME_CONFIG) --prefix)/lib/libgpgme.a . libassuan.a: ln -s $$($(LIBASSUAN_CONFIG) --prefix)/lib/libassuan.a . clean-local: rm -f libmapi32.a libgpg-error.a libgpgme.a libassuan.a gpgol_LDFLAGS = -static-libgcc -static-libstdc++ gpgol_LDADD = $(srcdir)/gpgol.def \ -L . -lgpgme -lassuan -lgpg-error \ -lmapi32 -lshell32 -lgdi32 -lcomdlg32 \ -lole32 -loleaut32 -lws2_32 -ladvapi32 \ -luuid -lgdiplus resource.o: resource.rc versioninfo.rc dialogs.rc .rc.o: $(WINDRES) -I $(srcdir) -I . `test -f '$<' || echo '$(srcdir)/'`$< $@ diff --git a/src/gpgoladdin.cpp b/src/gpgoladdin.cpp index 7323674..72b2f60 100644 --- a/src/gpgoladdin.cpp +++ b/src/gpgoladdin.cpp @@ -1,823 +1,830 @@ /* gpgoladdin.cpp - Connect GpgOL to Outlook as an addin * Copyright (C) 2013 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 <stdio.h> #include <string.h> #include "util.h" #include "gpgoladdin.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 "oomhelp.h" #include "olflange.h" #include "gpgol-ids.h" #include "ribbon-callbacks.h" #include "eventsinks.h" +#include "windowmessages.h" #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \ SRCNAME, __func__, __LINE__); \ } while (0) #define ICON_SIZE_LARGE 32 #define ICON_SIZE_NORMAL 16 ULONG addinLocks = 0; /* This is the main entry point for the addin Outlook uses this function to query for an Object implementing the IClassFactory interface. */ STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID* ppvObj) { if (!ppvObj) return E_POINTER; *ppvObj = NULL; if (rclsid != CLSID_GPGOL) return CLASS_E_CLASSNOTAVAILABLE; /* Let the factory give the requested interface. */ GpgolAddinFactory* factory = new GpgolAddinFactory(); if (!factory) return E_OUTOFMEMORY; HRESULT hr = factory->QueryInterface (riid, ppvObj); if(FAILED(hr)) { *ppvObj = NULL; delete factory; } return hr; } STDAPI DllCanUnloadNow() { return addinLocks == 0 ? S_OK : S_FALSE; } /* Class factory */ STDMETHODIMP GpgolAddinFactory::QueryInterface (REFIID riid, LPVOID* ppvObj) { HRESULT hr = S_OK; *ppvObj = NULL; if ((IID_IUnknown == riid) || (IID_IClassFactory == riid)) *ppvObj = static_cast<IClassFactory*>(this); else { hr = E_NOINTERFACE; LPOLESTR sRiid = NULL; StringFromIID (riid, &sRiid); /* Should not happen */ log_debug ("GpgolAddinFactory queried for unknown interface: %S \n", sRiid); } if (*ppvObj) ((LPUNKNOWN)*ppvObj)->AddRef(); return hr; } /* This actually creates the instance of our COM object */ STDMETHODIMP GpgolAddinFactory::CreateInstance (LPUNKNOWN punk, REFIID riid, LPVOID* ppvObj) { *ppvObj = NULL; GpgolAddin* obj = new GpgolAddin(); if (NULL == obj) return E_OUTOFMEMORY; HRESULT hr = obj->QueryInterface (riid, ppvObj); if (FAILED(hr)) { LPOLESTR sRiid = NULL; StringFromIID (riid, &sRiid); fprintf(stderr, "failed to create instance for: %S", sRiid); } return hr; } /* GpgolAddin definition */ /* Constructor of GpgolAddin Initializes members and creates the interface objects for the new context. Does the DLL initialization if it has not been done before. The ref count is set by the factory after creation. */ GpgolAddin::GpgolAddin (void) : m_lRef(0), m_application(0), m_addin(0), m_applicationEventSink(0), m_disabled(false) { read_options (); /* RibbonExtender is it's own object to avoid the pitfalls of multiple inheritance */ m_ribbonExtender = new GpgolRibbonExtender(); } GpgolAddin::~GpgolAddin (void) { log_debug ("%s:%s: cleaning up GpgolAddin object;", SRCNAME, __func__); delete m_ribbonExtender; delete m_applicationEventSink; if (!m_disabled) { engine_deinit (); write_options (); } log_debug ("%s:%s: Object deleted\n", SRCNAME, __func__); } STDMETHODIMP GpgolAddin::QueryInterface (REFIID riid, LPVOID* ppvObj) { HRESULT hr = S_OK; *ppvObj = NULL; if (m_disabled) return E_NOINTERFACE; if ((riid == IID_IUnknown) || (riid == IID_IDTExtensibility2) || (riid == IID_IDispatch)) { *ppvObj = (LPUNKNOWN) this; } else if (riid == IID_IRibbonExtensibility) { return m_ribbonExtender->QueryInterface (riid, ppvObj); } else { hr = E_NOINTERFACE; #if 0 LPOLESTR sRiid = NULL; StringFromIID(riid, &sRiid); log_debug ("%s:%s: queried for unimplmented interface: %S", SRCNAME, __func__, sRiid); #endif } if (*ppvObj) ((LPUNKNOWN)*ppvObj)->AddRef(); return hr; } STDMETHODIMP GpgolAddin::OnConnection (LPDISPATCH Application, ext_ConnectMode ConnectMode, LPDISPATCH AddInInst, SAFEARRAY ** custom) { (void)custom; char* version; log_debug ("%s:%s: this is GpgOL %s\n", SRCNAME, __func__, PACKAGE_VERSION); log_debug ("%s:%s: in Outlook %s\n", SRCNAME, __func__, gpgme_check_version (NULL)); m_application = Application; m_application->AddRef(); m_addin = AddInInst; version = get_oom_string (Application, "Version"); log_debug ("%s:%s: using GPGME %s\n", SRCNAME, __func__, version); if (!version || !strlen (version) || (strncmp (version, "14", 2) && strncmp (version, "15", 2))) { m_disabled = true; log_debug ("%s:%s: Disabled addin for unsupported version.", SRCNAME, __func__); xfree (version); return S_OK; } engine_init (); if (ConnectMode != ext_cm_Startup) { OnStartupComplete (custom); } return S_OK; } STDMETHODIMP GpgolAddin::OnDisconnection (ext_DisconnectMode RemoveMode, SAFEARRAY** custom) { (void)custom; (void)RemoveMode; write_options(); return S_OK; } STDMETHODIMP GpgolAddin::OnAddInsUpdate (SAFEARRAY** custom) { (void)custom; return S_OK; } STDMETHODIMP GpgolAddin::OnStartupComplete (SAFEARRAY** custom) { (void)custom; TRACEPOINT(); + if (!create_responder_window()) + { + log_error ("%s:%s: Failed to create the responder window;", + SRCNAME, __func__); + } + if (m_application) { m_applicationEventSink = install_ApplicationEvents_sink(m_application); return S_OK; } /* Should not happen as OnConnection should be called before */ log_error ("%s:%s: no application set;", SRCNAME, __func__); return E_NOINTERFACE; } STDMETHODIMP GpgolAddin::OnBeginShutdown (SAFEARRAY * * custom) { (void)custom; TRACEPOINT(); return S_OK; } STDMETHODIMP GpgolAddin::GetTypeInfoCount (UINT *r_count) { *r_count = 0; TRACEPOINT(); /* Should not happen */ return S_OK; } STDMETHODIMP GpgolAddin::GetTypeInfo (UINT iTypeInfo, LCID lcid, LPTYPEINFO *r_typeinfo) { (void)iTypeInfo; (void)lcid; (void)r_typeinfo; TRACEPOINT(); /* Should not happen */ return S_OK; } STDMETHODIMP GpgolAddin::GetIDsOfNames (REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { (void)riid; (void)rgszNames; (void)cNames; (void)lcid; (void)rgDispId; TRACEPOINT(); /* Should not happen */ return E_NOINTERFACE; } STDMETHODIMP GpgolAddin::Invoke (DISPID dispid, REFIID riid, LCID lcid, WORD flags, DISPPARAMS *parms, VARIANT *result, EXCEPINFO *exepinfo, UINT *argerr) { TRACEPOINT(); /* Should not happen */ return DISP_E_MEMBERNOTFOUND; } /* Definition of GpgolRibbonExtender */ GpgolRibbonExtender::GpgolRibbonExtender (void) : m_lRef(0) { } GpgolRibbonExtender::~GpgolRibbonExtender (void) { log_debug ("%s:%s: cleaning up GpgolRibbonExtender object;", SRCNAME, __func__); log_debug ("%s:%s: Object deleted\n", SRCNAME, __func__); } STDMETHODIMP GpgolRibbonExtender::QueryInterface(REFIID riid, LPVOID* ppvObj) { HRESULT hr = S_OK; *ppvObj = NULL; if ((riid == IID_IUnknown) || (riid == IID_IRibbonExtensibility) || (riid == IID_IDispatch)) { *ppvObj = (LPUNKNOWN) this; } else { LPOLESTR sRiid = NULL; StringFromIID (riid, &sRiid); log_debug ("%s:%s: queried for unknown interface: %S", SRCNAME, __func__, sRiid); } if (*ppvObj) ((LPUNKNOWN)*ppvObj)->AddRef(); return hr; } STDMETHODIMP GpgolRibbonExtender::GetTypeInfoCount (UINT *r_count) { *r_count = 0; TRACEPOINT(); /* Should not happen */ return S_OK; } STDMETHODIMP GpgolRibbonExtender::GetTypeInfo (UINT iTypeInfo, LCID lcid, LPTYPEINFO *r_typeinfo) { (void)iTypeInfo; (void)lcid; (void)r_typeinfo; TRACEPOINT(); /* Should not happen */ return S_OK; } /* Good documentation of what this function is supposed to do can be found at: http://msdn.microsoft.com/en-us/library/cc237568.aspx There is also a very good blog explaining how Ribbon Extensibility is supposed to work. http://blogs.msdn.com/b/andreww/archive/2007/03/09/ why-is-it-so-hard-to-shim-iribbonextensibility.aspx */ #define ID_MAPPER(name,id) \ if (!wcscmp (rgszNames[i], name)) \ { \ found = true; \ rgDispId[i] = id; \ break; \ } \ STDMETHODIMP GpgolRibbonExtender::GetIDsOfNames (REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { (void)riid; (void)lcid; bool found = false; if (!rgszNames || !cNames || !rgDispId) { return E_POINTER; } for (unsigned int i = 0; i < cNames; i++) { log_debug ("%s:%s: GetIDsOfNames for: %S", SRCNAME, __func__, rgszNames[i]); /* How this is supposed to work with cNames > 1 is unknown, but we can just say that we won't support callbacks with different parameters and just match the name (the first element) and we give it one of our own dispIds's that are later handled in the invoke part */ ID_MAPPER (L"attachmentDecryptCallback", ID_CMD_DECRYPT) ID_MAPPER (L"encryptSelection", ID_CMD_ENCRYPT_SELECTION) ID_MAPPER (L"decryptSelection", ID_CMD_DECRYPT_SELECTION) ID_MAPPER (L"startCertManager", ID_CMD_CERT_MANAGER) ID_MAPPER (L"btnCertManager", ID_BTN_CERTMANAGER) ID_MAPPER (L"btnDecrypt", ID_BTN_DECRYPT) ID_MAPPER (L"btnDecryptLarge", ID_BTN_DECRYPT_LARGE) ID_MAPPER (L"btnEncrypt", ID_BTN_ENCRYPT) ID_MAPPER (L"btnEncryptLarge", ID_BTN_ENCRYPT_LARGE) ID_MAPPER (L"btnEncryptFileLarge", ID_BTN_ENCSIGN_LARGE) ID_MAPPER (L"btnSignLarge", ID_BTN_SIGN_LARGE) ID_MAPPER (L"btnVerifyLarge", ID_BTN_VERIFY_LARGE) ID_MAPPER (L"encryptBody", ID_CMD_ENCRYPT_BODY) ID_MAPPER (L"decryptBody", ID_CMD_DECRYPT_BODY) ID_MAPPER (L"addEncSignedAttachment", ID_CMD_ATT_ENCSIGN_FILE) ID_MAPPER (L"addEncAttachment", ID_CMD_ATT_ENC_FILE) ID_MAPPER (L"signBody", ID_CMD_SIGN_BODY) ID_MAPPER (L"verifyBody", ID_CMD_VERIFY_BODY) } if (cNames > 1) { log_debug ("More then one name provided. Should not happen"); } return found ? S_OK : E_NOINTERFACE; } STDMETHODIMP GpgolRibbonExtender::Invoke (DISPID dispid, REFIID riid, LCID lcid, WORD flags, DISPPARAMS *parms, VARIANT *result, EXCEPINFO *exepinfo, UINT *argerr) { log_debug ("%s:%s: enter with dispid: %x", SRCNAME, __func__, (int)dispid); if (!(flags & DISPATCH_METHOD)) { log_debug ("%s:%s: not called in method mode. Bailing out.", SRCNAME, __func__); return DISP_E_MEMBERNOTFOUND; } switch (dispid) { case ID_CMD_DECRYPT: /* We can assume that this points to an implementation of IRibbonControl as we know the callback dispid. */ return decryptAttachments (parms->rgvarg[0].pdispVal); case ID_CMD_ENCRYPT_SELECTION: return encryptSelection (parms->rgvarg[0].pdispVal); case ID_CMD_DECRYPT_SELECTION: return decryptSelection (parms->rgvarg[0].pdispVal); case ID_CMD_CERT_MANAGER: return startCertManager (parms->rgvarg[0].pdispVal); case ID_CMD_ENCRYPT_BODY: return encryptBody (parms->rgvarg[0].pdispVal); case ID_CMD_DECRYPT_BODY: return decryptBody (parms->rgvarg[0].pdispVal); case ID_CMD_ATT_ENCSIGN_FILE: return addEncSignedAttachment (parms->rgvarg[0].pdispVal); case ID_CMD_ATT_ENC_FILE: return addEncAttachment (parms->rgvarg[0].pdispVal); case ID_CMD_SIGN_BODY: return signBody (parms->rgvarg[0].pdispVal); case ID_CMD_VERIFY_BODY: return verifyBody (parms->rgvarg[0].pdispVal); case ID_BTN_CERTMANAGER: case ID_BTN_ENCRYPT: case ID_BTN_DECRYPT: case ID_BTN_DECRYPT_LARGE: case ID_BTN_ENCRYPT_LARGE: case ID_BTN_ENCSIGN_LARGE: case ID_BTN_SIGN_LARGE: case ID_BTN_VERIFY_LARGE: return getIcon (dispid, result); } log_debug ("%s:%s: leave", SRCNAME, __func__); return DISP_E_MEMBERNOTFOUND; } /* Returns the XML markup for the various RibbonID's The custom ui syntax is documented at: http://msdn.microsoft.com/en-us/library/dd926139%28v=office.12%29.aspx The outlook specific elements are documented at: http://msdn.microsoft.com/en-us/library/office/ee692172%28v=office.14%29.aspx */ STDMETHODIMP GpgolRibbonExtender::GetCustomUI (BSTR RibbonID, BSTR * RibbonXml) { wchar_t buffer[8192]; const char *certManagerTTip = _("Start the Certificate Management Software"); const char *certManagerSTip = _("Open GPA or Kleopatra to manage your certificates. " "You can use this you to generate your " "own certificates. "); const char *encryptTextTTip = _("Encrypt the text of the message"); const char *encryptTextSTip = _("Choose the certificates for which the message " "should be encrypted and replace the text " "with the encrypted message."); const char *encryptFileTTip = _("Add a file as an encrypted attachment"); const char *encryptFileSTip = _("Encrypts a file and adds it as an attachment to the " "message. "); const char *encryptSignFileTTip = _("Add a file as an encrypted attachment with a signature"); const char *encryptSignFileSTip = _("Encrypts a file, signs it and adds both the encrypted file " "and the signature as attachments to the message. "); const char *decryptTextTTip= _("Decrypt the message"); const char *decryptTextSTip = _("Look for PGP or S/MIME encrypted data in the message text " "and decrypt it."); const char *signTextTTip = _("Add a signature of the message"); const char *signTextSTip = _("Appends a signed copy of the message text in an opaque signature. " "An opaque signature ensures that the signed text is not modified by " "embedding it in the signature itself. " "The combination of the signed message text and your signature is " "added below the plain text. " "The message will not be encrypted!"); memset(buffer, 0, sizeof buffer); log_debug ("%s:%s: GetCustomUI for id: %ls", SRCNAME, __func__, RibbonID); if (!RibbonXml) return E_POINTER; if (!wcscmp (RibbonID, L"Microsoft.Outlook.Mail.Compose")) { swprintf (buffer, L"<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\">" L" <ribbon>" L" <tabs>" L" <tab id=\"gpgolTab\"" L" label=\"%hs\">" L" <group id=\"general\"" L" label=\"%hs\">" L" <button id=\"CustomButton\"" L" getImage=\"btnCertManager\"" L" size=\"large\"" L" label=\"%hs\"" L" screentip=\"%hs\"" L" supertip=\"%hs\"" L" onAction=\"startCertManager\"/>" L" </group>" L" <group id=\"textGroup\"" L" label=\"%hs\">" L" <button id=\"fullTextEncrypt\"" L" getImage=\"btnEncryptLarge\"" L" size=\"large\"" L" label=\"%hs\"" L" screentip=\"%hs\"" L" supertip=\"%hs\"" L" onAction=\"encryptBody\"/>" L" <button id=\"fullTextDecrypt\"" L" getImage=\"btnDecryptLarge\"" L" size=\"large\"" L" label=\"%hs\"" L" screentip=\"%hs\"" L" supertip=\"%hs\"" L" onAction=\"decryptBody\"/>" L" <button id=\"fullTextSign\"" L" getImage=\"btnSignLarge\"" L" size=\"large\"" L" label=\"%hs\"" L" screentip=\"%hs\"" L" supertip=\"%hs\"" L" onAction=\"signBody\"/>" L" <button id=\"fullTextVerify\"" L" getImage=\"btnVerifyLarge\"" L" size=\"large\"" L" label=\"%hs\"" L" onAction=\"verifyBody\"/>" L" </group>" L" <group id=\"attachmentGroup\"" L" label=\"%hs\">" L" <button id=\"encryptedFile\"" L" getImage=\"btnEncryptLarge\"" L" size=\"large\"" L" label=\"%hs\"" L" screentip=\"%hs\"" L" supertip=\"%hs\"" L" onAction=\"addEncAttachment\"/>" L" <button id=\"encryptSignFile\"" L" getImage=\"btnEncryptFileLarge\"" L" size=\"large\"" L" label=\"%hs\"" L" screentip=\"%hs\"" L" supertip=\"%hs\"" L" onAction=\"addEncSignedAttachment\"/>" L" </group>" L" </tab>" L" </tabs>" L" </ribbon>" L" <contextMenus>" L" <contextMenu idMso=\"ContextMenuText\">" L" <button id=\"encryptButton\"" L" label=\"%hs\"" L" getImage=\"btnEncrypt\"" L" onAction=\"encryptSelection\"/>" L" <button id=\"decryptButton\"" L" label=\"%hs\"" L" getImage=\"btnDecrypt\"" L" onAction=\"decryptSelection\"/>" L" </contextMenu>" L"</contextMenus>" L"</customUI>", _("GpgOL"), _("General"), _("Start Certificate Manager"), certManagerTTip, certManagerSTip, _("Textbody"), _("Encrypt"), encryptTextTTip, encryptTextSTip, _("Decrypt"), decryptTextTTip, decryptTextSTip, _("Sign"), signTextTTip, signTextSTip, _("Verify"), _("Attachments"), _("Encrypted file"), encryptFileTTip, encryptFileSTip, _("Encrypted file and Signature"), encryptSignFileTTip, encryptSignFileSTip, _("Encrypt"), _("Decrypt") ); } else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Mail.Read")) { swprintf (buffer, L"<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\">" L" <ribbon>" L" <tabs>" L" <tab id=\"gpgolTab\"" L" label=\"%hs\">" L" <group id=\"general\"" L" label=\"%hs\">" L" <button id=\"CustomButton\"" L" getImage=\"btnCertManager\"" L" size=\"large\"" L" label=\"%hs\"" L" screentip=\"%hs\"" L" supertip=\"%hs\"" L" onAction=\"startCertManager\"/>" L" </group>" L" <group id=\"textGroup\"" L" label=\"%hs\">" L" <button id=\"fullTextDecrypt\"" L" getImage=\"btnDecryptLarge\"" L" size=\"large\"" L" label=\"%hs\"" L" screentip=\"%hs\"" L" supertip=\"%hs\"" L" onAction=\"decryptBody\"/>" L" <button id=\"fullTextVerify\"" L" getImage=\"btnVerifyLarge\"" L" size=\"large\"" L" label=\"%hs\"" L" onAction=\"verifyBody\"/>" L" </group>" L" </tab>" L" </tabs>" L" <contextualTabs>" L" <tabSet idMso=\"TabSetAttachments\">" L" <tab idMso=\"TabAttachments\">" L" <group label=\"%hs\" id=\"gnupgLabel\">" L" <button id=\"gpgol_contextual_decrypt\"" L" size=\"large\"" L" label=\"%hs\"" L" getImage=\"btnDecryptLarge\"" L" onAction=\"attachmentDecryptCallback\" />" L" </group>" L" </tab>" L" </tabSet>" L" </contextualTabs>" L" </ribbon>" L"<contextMenus>" L"<contextMenu idMso=\"ContextMenuReadOnlyMailText\">" L" <button id=\"decryptReadButton\"" L" label=\"%hs\"" L" getImage=\"btnDecrypt\"" L" onAction=\"decryptSelection\"/>" L" </contextMenu>" L" <contextMenu idMso=\"ContextMenuAttachments\">" L" <button id=\"gpgol_decrypt\"" L" label=\"%hs\"" L" getImage=\"btnDecrypt\"" L" onAction=\"attachmentDecryptCallback\"/>" L" </contextMenu>" L"</contextMenus>" L"</customUI>", _("GpgOL"), _("General"), _("Start Certificate Manager"), certManagerTTip, certManagerSTip, _("Textbody"), _("Decrypt"), decryptTextTTip, decryptTextSTip, _("Verify"), _("GpgOL"), _("Save and decrypt"), _("Decrypt"), _("Decrypt")); } else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Explorer")) { swprintf (buffer, L"<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\">" L" <ribbon>" L" <tabs>" L" <tab id=\"gpgolTab\"" L" label=\"%hs\">" L" <group id=\"general\"" L" label=\"%hs\">" L" <button id=\"CustomButton\"" L" getImage=\"btnCertManager\"" L" size=\"large\"" L" label=\"%hs\"" L" screentip=\"%hs\"" L" supertip=\"%hs\"" L" onAction=\"startCertManager\"/>" L" </group>" /* This would be totally nice but Outlook saves the decrypted text aftewards automatically. Yay,.. L" <group id=\"textGroup\"" L" label=\"%hs\">" L" <button id=\"fullTextDecrypt\"" L" getImage=\"btnDecryptLarge\"" L" size=\"large\"" L" label=\"%hs\"" L" onAction=\"decryptBody\"/>" L" </group>" */ L" </tab>" L" </tabs>" L" <contextualTabs>" L" <tabSet idMso=\"TabSetAttachments\">" L" <tab idMso=\"TabAttachments\">" L" <group label=\"%hs\" id=\"gnupgLabel\">" L" <button id=\"gpgol_contextual_decrypt\"" L" size=\"large\"" L" label=\"%hs\"" L" getImage=\"btnDecryptLarge\"" L" onAction=\"attachmentDecryptCallback\" />" L" </group>" L" </tab>" L" </tabSet>" L" </contextualTabs>" L" </ribbon>" L" <contextMenus>" /* There appears to be no way to access the word editor / get the selected text from that Context. L" <contextMenu idMso=\"ContextMenuReadOnlyMailText\">" L" <button id=\"decryptReadButton1\"" L" label=\"%hs\"" L" onAction=\"decryptSelection\"/>" L" </contextMenu>" */ L" <contextMenu idMso=\"ContextMenuAttachments\">" L" <button id=\"gpgol_decrypt\"" L" label=\"%hs\"" L" getImage=\"btnDecrypt\"" L" onAction=\"attachmentDecryptCallback\"/>" L" </contextMenu>" L" </contextMenus>" L"</customUI>", _("GpgOL"), _("General"), _("Start Certificate Manager"), certManagerTTip, certManagerSTip, /*_("Mail Body"), _("Decrypt"),*/ _("GpgOL"), _("Save and decrypt"),/*_("Decrypt"), */ _("Save and decrypt")); } if (wcslen (buffer)) *RibbonXml = SysAllocString (buffer); else *RibbonXml = NULL; return S_OK; } diff --git a/src/windowmessages.cpp b/src/windowmessages.cpp new file mode 100644 index 0000000..ce1afa5 --- /dev/null +++ b/src/windowmessages.cpp @@ -0,0 +1,110 @@ +#include "windowmessages.h" + +#include "util.h" +#include "oomhelp.h" + +#include <stdio.h> + +#define RESPONDER_CLASS_NAME "GpgOLResponder" + +/* Singleton window */ +static HWND g_responder_window = NULL; + +static int +request_send_mail (LPDISPATCH mailitem) +{ + if (invoke_oom_method (mailitem, "Send", NULL)) + { + log_debug ("%s:%s: Failed to resend message.", + SRCNAME, __func__); + return -1; + } + log_debug ("%s:%s: Message %p sent.", + SRCNAME, __func__, mailitem); + return 0; +} + +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 (REQUEST_SEND_MAIL): + { + ctx->err = request_send_mail ((LPDISPATCH) ctx->data); + break; + } + default: + log_debug ("Unknown msg"); + } + return 0; + } + + 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 = {0}; + ctx.wmsg_type = type; + ctx.data = data; + if (send_msg_to_ui_thread (&ctx)) + { + return -1; + } + return ctx.err; +} diff --git a/src/windowmessages.h b/src/windowmessages.h new file mode 100644 index 0000000..c9355d5 --- /dev/null +++ b/src/windowmessages.h @@ -0,0 +1,66 @@ +/* windowmessages.h - Helper functions for Window message exchange. + * Copyright (C) 2015 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, + REQUEST_SEND_MAIL = 1 /* Request to send a mail. + Data should be LPMAILITEM */ +} 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); +#endif // WINDOWMESSAGES_H