diff --git a/src/Makefile.am b/src/Makefile.am
index 599394f..7906794 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,118 +1,119 @@
# Makefile.am - makefile for GPGol
# Copyright (C) 2005 g10 Code GmbH
# Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik
# Software engineering by Intevation GmbH
# Copyright (C) 2018 Intevation 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
SUBDIRS = icons
bin_PROGRAMS = gpgol
EXTRA_DIST = \
versioninfo.rc.in mapi32.def Outlook.gpl \
dialogs.rc
EXEEXT = .dll
AM_CFLAGS = $(GPGME_CFLAGS) -shared
AM_CXXFLAGS = $(GPGME_CFLAGS) $(LIBASSUAN_CFLAGS) -shared -std=c++11
AM_CXXFLAGS += $(GPGMEPP_CXXFLAGS) -D_FILE_OFFSET_BITS=64
gpgol_SOURCES = \
addin-options.cpp addin-options.h \
application-events.cpp \
attachment.h attachment.cpp \
common.h common.cpp \
common_indep.h common_indep.c \
cpphelp.cpp cpphelp.h \
cryptcontroller.cpp cryptcontroller.h \
dialogs.h \
+ dispcache.h dispcache.cpp \
eventsink.h \
eventsinks.h \
exechelp.c exechelp.h \
explorer-events.cpp \
explorers-events.cpp \
filetype.c filetype.h \
folder-events.cpp \
gmime-table-private.h \
gpgoladdin.cpp gpgoladdin.h \
gpgol.def \
gpgol-ids.h \
keycache.cpp keycache.h \
mail.h mail.cpp \
mailitem-events.cpp \
main.c \
mapihelp.cpp mapihelp.h \
memdbg.cpp memdbg.h \
mimedataprovider.cpp mimedataprovider.h \
mimemaker.cpp mimemaker.h \
mlang-charset.cpp mlang-charset.h \
mymapi.h \
mymapitags.h \
olflange.cpp olflange.h \
oomhelp.cpp oomhelp.h \
overlay.cpp overlay.h \
parsecontroller.cpp parsecontroller.h \
parsetlv.h parsetlv.c \
resource.rc \
revert.cpp revert.h \
rfc2047parse.h rfc2047parse.c \
rfc822parse.c rfc822parse.h \
ribbon-callbacks.cpp ribbon-callbacks.h \
w32-gettext.cpp w32-gettext.h \
windowmessages.h windowmessages.cpp \
wks-helper.cpp wks-helper.h \
xmalloc.h
#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 libgpgmepp.a
if BUILD_W64
DLLTOOLFLAGS64=--as-flags=--64 -m i386:x86-64
endif
libmapi32.a: mapi32.def
$(DLLTOOL) $(DLLTOOLFLAGS64) --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 .
libgpgmepp.a:
ln -s $$($(GPGME_CONFIG) --prefix)/lib/libgpgmepp.a .
clean-local:
rm -f libmapi32.a libgpg-error.a libgpgme.a libassuan.a libgpgmepp.a
gpgol_LDFLAGS = -static-libgcc -static-libstdc++
gpgol_LDADD = $(srcdir)/gpgol.def \
-L . -lgpgmepp -lgpgme -lassuan -lgpg-error \
-lmapi32 -lshell32 -lgdi32 -lcomdlg32 \
-lole32 -loleaut32 -lws2_32 -ladvapi32 \
-luuid -lgdiplus -lrpcrt4
resource.o: resource.rc versioninfo.rc dialogs.rc dialogs.h
.rc.o:
$(WINDRES) -I $(srcdir) -I . -I .. `test -f '$<' || echo '$(srcdir)/'`$< $@
diff --git a/src/dispcache.cpp b/src/dispcache.cpp
new file mode 100644
index 0000000..7b1bbf3
--- /dev/null
+++ b/src/dispcache.cpp
@@ -0,0 +1,114 @@
+/* Copyright (C) 2018 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 .
+ */
+
+#include "dispcache.h"
+#include "gpg-error.h"
+
+#include "common.h"
+#include "gpgoladdin.h"
+
+#include
+
+GPGRT_LOCK_DEFINE (cache_lock);
+
+class DispCache::Private
+{
+public:
+ Private()
+ {
+
+ }
+
+ void addDisp (int id, LPDISPATCH obj)
+ {
+ if (!id || !obj)
+ {
+ TRACEPOINT;
+ return;
+ }
+ gpgrt_lock_lock (&cache_lock);
+ auto it = m_cache.find (id);
+ if (it != m_cache.end ())
+ {
+ log_debug ("%s:%s Item \"%i\" already cached. Replacing it.",
+ SRCNAME, __func__, id);
+ gpgol_release (it->second);
+ it->second = obj;
+ gpgrt_lock_unlock (&cache_lock);
+ return;
+ }
+ m_cache.insert (std::make_pair (id, obj));
+ gpgrt_lock_unlock (&cache_lock);
+ return;
+ }
+
+ LPDISPATCH getDisp (int id)
+ {
+ if (!id)
+ {
+ TRACEPOINT;
+ return nullptr;
+ }
+ gpgrt_lock_lock (&cache_lock);
+
+ const auto it = m_cache.find (id);
+ if (it != m_cache.end())
+ {
+ LPDISPATCH ret = it->second;
+ gpgrt_lock_unlock (&cache_lock);
+ return ret;
+ }
+ gpgrt_lock_unlock (&cache_lock);
+ return nullptr;
+ }
+
+ ~Private ()
+ {
+ gpgrt_lock_lock (&cache_lock);
+ for (const auto it: m_cache)
+ {
+ gpgol_release (it.second);
+ }
+ gpgrt_lock_unlock (&cache_lock);
+ }
+
+private:
+ std::unordered_map m_cache;
+};
+
+DispCache::DispCache (): d (new Private)
+{
+}
+
+void
+DispCache::addDisp (int id, LPDISPATCH obj)
+{
+ d->addDisp (id, obj);
+}
+
+LPDISPATCH
+DispCache::getDisp (int id)
+{
+ return d->getDisp (id);
+}
+
+DispCache *
+DispCache::instance ()
+{
+ return GpgolAddin::get_instance ()->get_dispcache ().get ();
+}
diff --git a/src/dispcache.h b/src/dispcache.h
new file mode 100644
index 0000000..465665d
--- /dev/null
+++ b/src/dispcache.h
@@ -0,0 +1,60 @@
+/* @file dispcache.h
+ * @brief Cache for IDispatch objects that are reusable.
+ *
+ * Copyright (C) 2018 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 .
+ */
+#ifndef DISPCACHE_H
+#define DISPCACHE_H
+
+#include "config.h"
+
+#include
+
+#include "oomhelp.h"
+
+class GpgolAddin;
+
+class DispCache
+{
+ friend class GpgolAddin;
+protected:
+ DispCache ();
+
+public:
+ /* Accessor. Returns the instance carried by
+ gpgoladdin. */
+ static DispCache *instance ();
+
+ /* Add a IDispatch with the id id to the cache.
+
+ The IDispatch is released on destruction of
+ the cache.
+
+ Id's are meant to be defined in dialogs.h
+ */
+ void addDisp (int id, LPDISPATCH obj);
+
+ /* Get the according IDispatch object. */
+ LPDISPATCH getDisp (int id);
+
+private:
+ class Private;
+ std::shared_ptr d;
+};
+
+#endif
diff --git a/src/gpgoladdin.cpp b/src/gpgoladdin.cpp
index abf8e71..e331b86 100644
--- a/src/gpgoladdin.cpp
+++ b/src/gpgoladdin.cpp
@@ -1,1198 +1,1200 @@
/* gpgoladdin.cpp - Connect GpgOL to Outlook as an addin
* Copyright (C) 2013 Intevation GmbH
* 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 .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include "common.h"
#include "gpgoladdin.h"
#include "mymapi.h"
#include "mymapitags.h"
#include "mapihelp.h"
#include "oomhelp.h"
#include "olflange.h"
#include "gpgol-ids.h"
#include "ribbon-callbacks.h"
#include "eventsinks.h"
#include "eventsink.h"
#include "windowmessages.h"
#include "mail.h"
#include "addin-options.h"
#include "cpphelp.h"
+#include "dispcache.h"
#include
#include
#define ICON_SIZE_LARGE 32
#define ICON_SIZE_NORMAL 16
/* We use UTF-8 internally. */
#undef _
#define _(a) utf8_gettext (a)
ULONG addinLocks = 0;
bool can_unload = false;
static std::list g_ribbon_uis;
static GpgolAddin * addin_instance = NULL;
/* 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()
{
/* This is called regularly to check if memory can be freed
by unloading the dll. The following unload will not call
any addin methods like disconnect etc. It will just
unload the Library. Any callbacks will become invalid.
So we _only_ say it's ok to unload if we were disconnected.
For the epic story behind the next line see GnuPG-Bug-Id 1837 */
TRACEPOINT;
return can_unload ? 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(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)
{
(void)punk;
*ppvObj = NULL;
GpgolAddin* obj = GpgolAddin::get_instance();
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;
}
GpgolAddinFactory::~GpgolAddinFactory()
{
log_debug ("%s:%s: Object deleted\n", SRCNAME, __func__);
}
/* 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(nullptr),
m_addin(nullptr),
m_applicationEventSink(nullptr),
m_explorersEventSink(nullptr),
m_disabled(false),
m_shutdown(false),
- m_hook(nullptr)
+ m_hook(nullptr),
+ m_dispcache(new DispCache)
{
read_options ();
/* RibbonExtender is it's own object to avoid the pitfalls of
multiple inheritance
*/
m_ribbonExtender = new GpgolRibbonExtender();
}
GpgolAddin::~GpgolAddin (void)
{
if (m_disabled)
{
return;
}
addin_instance = NULL;
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;
}
static void
addGpgOLToReg (const std::string &path)
{
HKEY h;
int err = RegOpenKeyEx (HKEY_CURRENT_USER, path.c_str(), 0,
KEY_ALL_ACCESS, &h);
if (err != ERROR_SUCCESS)
{
log_debug ("%s:%s: no DoNotDisableAddinList entry '%s' creating it",
SRCNAME, __func__, path.c_str ());
err = RegCreateKeyEx (HKEY_CURRENT_USER, path.c_str (), 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL,
&h, NULL);
}
if (err != ERROR_SUCCESS)
{
log_error ("%s:%s: failed to create key.",
SRCNAME, __func__);
return;
}
DWORD type;
err = RegQueryValueEx (h, GPGOL_PROGID, NULL, &type, NULL, NULL);
if (err == ERROR_SUCCESS)
{
log_debug ("%s:%s: Found gpgol reg key. Leaving it unchanged.",
SRCNAME, __func__);
RegCloseKey (h);
return;
}
// No key exists. Create one.
DWORD dwTemp = 1;
err = RegSetValueEx (h, GPGOL_PROGID, 0, REG_DWORD, (BYTE*)&dwTemp, 4);
RegCloseKey (h);
if (err != ERROR_SUCCESS)
{
log_error ("%s:%s: failed to set registry value.",
SRCNAME, __func__);
}
else
{
log_debug ("%s:%s: added gpgol to %s",
SRCNAME, __func__, path.c_str ());
}
}
/* This is a bit evil as we basically disable outlooks resiliency
for us. But users are still able to manually disable the addon
or change the donotdisable setting to zero and we won't change
it.
It has been much requested by users that we do this automatically.
*/
static void
setupDoNotDisable ()
{
std::string path = "Software\\Microsoft\\Office\\";
path += std::to_string (g_ol_version_major);
path += ".0\\Outlook\\Resiliency\\DoNotDisableAddinList";
addGpgOLToReg (path);
path = "Software\\Microsoft\\Office\\";
path += std::to_string (g_ol_version_major);
path += ".0\\Outlook\\Resiliency\\AddinList";
addGpgOLToReg (path);
}
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);
m_shutdown = false;
can_unload = false;
m_application = Application;
m_application->AddRef();
memdbg_addRef (m_application);
m_addin = AddInInst;
version = get_oom_string (Application, "Version");
log_debug ("%s:%s: using GPGME %s\n",
SRCNAME, __func__, gpgme_check_version (NULL));
log_debug ("%s:%s: in Outlook %s\n",
SRCNAME, __func__, version);
g_ol_version_major = atoi (version);
if (!version || !strlen (version) ||
(strncmp (version, "14", 2) &&
strncmp (version, "15", 2) &&
strncmp (version, "16", 2)))
{
m_disabled = true;
log_debug ("%s:%s: Disabled addin for unsupported version.",
SRCNAME, __func__);
xfree (version);
return S_OK;
}
xfree (version);
setupDoNotDisable ();
if (ConnectMode != ext_cm_Startup)
{
OnStartupComplete (custom);
}
return S_OK;
}
STDMETHODIMP
GpgolAddin::OnDisconnection (ext_DisconnectMode RemoveMode,
SAFEARRAY** custom)
{
(void)custom;
(void)RemoveMode;
log_debug ("%s:%s: cleaning up GpgolAddin object;",
SRCNAME, __func__);
/* Doing the wipe in the dtor is too late. Outlook
does not allow us any OOM calls then and only returns
"Unexpected error" in that case. Weird. */
shutdown ();
can_unload = true;
return S_OK;
}
STDMETHODIMP
GpgolAddin::OnAddInsUpdate (SAFEARRAY** custom)
{
(void)custom;
return S_OK;
}
static void
check_html_preferred()
{
/* Check if HTML Mail should be enabled. */
HKEY h;
std::string path = "Software\\Microsoft\\Office\\";
path += std::to_string (g_ol_version_major);
path += ".0\\Outlook\\Options\\Mail";
opt.prefer_html = 1;
int err = RegOpenKeyEx (HKEY_CURRENT_USER, path.c_str() , 0, KEY_READ, &h);
if (err != ERROR_SUCCESS)
{
log_debug ("%s:%s: no mail options under %s",
SRCNAME, __func__, path.c_str());
return;
}
else
{
DWORD type;
err = RegQueryValueEx (h, "ReadAsPlain", NULL, &type, NULL, NULL);
if (err != ERROR_SUCCESS || type != REG_DWORD)
{
log_debug ("%s:%s: No type or key for ReadAsPlain",
SRCNAME, __func__);
return;
}
else
{
DWORD data;
DWORD size = sizeof (DWORD);
err = RegQueryValueEx (h, "ReadAsPlain", NULL, NULL, (LPBYTE)&data,
&size);
if (err != ERROR_SUCCESS)
{
log_debug ("%s:%s: Failed to find out ReadAsPlain",
SRCNAME, __func__);
return;
}
opt.prefer_html = data ? 0 : 1;
return;
}
}
}
static LPDISPATCH
install_explorer_sinks (LPDISPATCH application)
{
LPDISPATCH explorers = get_oom_object (application, "Explorers");
if (!explorers)
{
log_error ("%s:%s: No explorers object",
SRCNAME, __func__);
return nullptr;
}
int count = get_oom_int (explorers, "Count");
for (int i = 1; i <= count; i++)
{
std::string item = "Item(";
item += std::to_string (i) + ")";
LPDISPATCH explorer = get_oom_object (explorers, item.c_str());
if (!explorer)
{
log_error ("%s:%s: failed to get explorer %i",
SRCNAME, __func__, i);
continue;
}
/* Explorers delete themself in the close event of the explorer. */
LPDISPATCH sink = install_ExplorerEvents_sink (explorer);
if (!sink)
{
log_error ("%s:%s: failed to create eventsink for explorer %i",
SRCNAME, __func__, i);
}
else
{
log_oom_extra ("%s:%s: created sink %p for explorer %i",
SRCNAME, __func__, sink, i);
GpgolAddin::get_instance ()->registerExplorerSink (sink);
}
gpgol_release (explorer);
}
/* Now install the event sink to handle new explorers */
LPDISPATCH ret = install_ExplorersEvents_sink (explorers);
gpgol_release (explorers);
return ret;
}
static DWORD WINAPI
init_gpgme_config (LPVOID)
{
/* This is a check we need to do anyway. GpgME++ caches
the configuration once it is accessed for the first time
so this call also initializes GpgME++ */
bool de_vs_mode = in_de_vs_mode ();
log_debug ("%s:%s: init_gpgme_config de_vs_mode %i",
SRCNAME, __func__, de_vs_mode);
return 0;
}
STDMETHODIMP
GpgolAddin::OnStartupComplete (SAFEARRAY** custom)
{
(void)custom;
TRACEPOINT;
i18n_init ();
if (!create_responder_window())
{
log_error ("%s:%s: Failed to create the responder window;",
SRCNAME, __func__);
}
if (!m_application)
{
/* Should not happen as OnConnection should be called before */
log_error ("%s:%s: no application set;",
SRCNAME, __func__);
return E_NOINTERFACE;
}
if (!(m_hook = create_message_hook ()))
{
log_error ("%s:%s: Failed to create messagehook. ",
SRCNAME, __func__);
}
/* Set up categories */
const char *decCategory = _("GpgOL: Encrypted Message");
const char *verifyCategory = _("GpgOL: Trusted Sender Address");
ensure_category_exists (m_application, decCategory, 8);
ensure_category_exists (m_application, verifyCategory, 5);
install_forms ();
m_applicationEventSink = install_ApplicationEvents_sink (m_application);
m_explorersEventSink = install_explorer_sinks (m_application);
check_html_preferred ();
CloseHandle (CreateThread (NULL, 0, init_gpgme_config, nullptr, 0,
NULL));
return S_OK;
}
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)
{
USE_INVOKE_ARGS
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__);
memdbg_dump ();
}
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"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"btnEncryptSmall", IDI_ENCRYPT_20_PNG)
ID_MAPPER (L"btnSignSmall", IDI_SIGN_20_PNG)
ID_MAPPER (L"btnSignEncryptLarge", IDI_SIGN_ENCRYPT_40_PNG)
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"btnSigstateLarge", ID_BTN_SIGSTATE_LARGE)
/* MIME support: */
ID_MAPPER (L"encryptMime", ID_CMD_MIME_ENCRYPT)
ID_MAPPER (L"encryptMimeEx", ID_CMD_MIME_ENCRYPT_EX)
ID_MAPPER (L"signMime", ID_CMD_MIME_SIGN)
ID_MAPPER (L"signMimeEx", ID_CMD_MIME_SIGN_EX)
ID_MAPPER (L"encryptSignMime", ID_CMD_SIGN_ENCRYPT_MIME)
ID_MAPPER (L"encryptSignMimeEx", ID_CMD_SIGN_ENCRYPT_MIME_EX)
ID_MAPPER (L"getEncryptPressed", ID_GET_ENCRYPT_PRESSED)
ID_MAPPER (L"getEncryptPressedEx", ID_GET_ENCRYPT_PRESSED_EX)
ID_MAPPER (L"getSignPressed", ID_GET_SIGN_PRESSED)
ID_MAPPER (L"getSignPressedEx", ID_GET_SIGN_PRESSED_EX)
ID_MAPPER (L"getSignEncryptPressed", ID_GET_SIGN_ENCRYPT_PRESSED)
ID_MAPPER (L"getSignEncryptPressedEx", ID_GET_SIGN_ENCRYPT_PRESSED_EX)
ID_MAPPER (L"ribbonLoaded", ID_ON_LOAD)
ID_MAPPER (L"openOptions", ID_CMD_OPEN_OPTIONS)
ID_MAPPER (L"getSigLabel", ID_GET_SIG_LABEL)
ID_MAPPER (L"getSigSTip", ID_GET_SIG_STIP)
ID_MAPPER (L"getSigTip", ID_GET_SIG_TTIP)
ID_MAPPER (L"launchDetails", ID_LAUNCH_CERT_DETAILS)
ID_MAPPER (L"getIsDetailsEnabled", ID_GET_IS_DETAILS_ENABLED)
ID_MAPPER (L"getIsCrypto", ID_GET_IS_CRYPTO_MAIL)
ID_MAPPER (L"printDecrypted", ID_CMD_PRINT_DECRYPTED)
}
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)
{
USE_INVOKE_ARGS
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_SIGN_ENCRYPT_MIME:
return mark_mime_action (parms->rgvarg[1].pdispVal,
OP_SIGN|OP_ENCRYPT, false);
case ID_CMD_SIGN_ENCRYPT_MIME_EX:
return mark_mime_action (parms->rgvarg[1].pdispVal,
OP_SIGN|OP_ENCRYPT, true);
case ID_CMD_MIME_ENCRYPT:
return mark_mime_action (parms->rgvarg[1].pdispVal, OP_ENCRYPT,
false);
case ID_CMD_MIME_SIGN:
return mark_mime_action (parms->rgvarg[1].pdispVal, OP_SIGN,
false);
case ID_GET_ENCRYPT_PRESSED:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_ENCRYPT,
result, false);
case ID_GET_SIGN_PRESSED:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_SIGN,
result, false);
case ID_GET_SIGN_ENCRYPT_PRESSED:
return get_crypt_pressed (parms->rgvarg[0].pdispVal,
OP_SIGN | OP_ENCRYPT,
result, false);
case ID_CMD_MIME_SIGN_EX:
return mark_mime_action (parms->rgvarg[1].pdispVal, OP_SIGN, true);
case ID_CMD_MIME_ENCRYPT_EX:
return mark_mime_action (parms->rgvarg[1].pdispVal, OP_ENCRYPT, true);
case ID_GET_ENCRYPT_PRESSED_EX:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_ENCRYPT,
result, true);
case ID_GET_SIGN_PRESSED_EX:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_SIGN,
result, true);
case ID_GET_SIGN_ENCRYPT_PRESSED_EX:
return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_SIGN | OP_ENCRYPT,
result, true);
case ID_GET_SIG_STIP:
return get_sig_stip (parms->rgvarg[0].pdispVal, result);
case ID_GET_SIG_TTIP:
return get_sig_ttip (parms->rgvarg[0].pdispVal, result);
case ID_GET_SIG_LABEL:
return get_sig_label (parms->rgvarg[0].pdispVal, result);
case ID_LAUNCH_CERT_DETAILS:
return launch_cert_details (parms->rgvarg[0].pdispVal);
case ID_GET_IS_DETAILS_ENABLED:
return get_is_details_enabled (parms->rgvarg[0].pdispVal, result);
case ID_ON_LOAD:
{
g_ribbon_uis.push_back (parms->rgvarg[0].pdispVal);
return S_OK;
}
case ID_CMD_OPEN_OPTIONS:
{
options_dialog_box (NULL);
return S_OK;
}
case ID_CMD_PRINT_DECRYPTED:
return print_decrypted (parms->rgvarg[0].pdispVal);
case ID_GET_IS_CRYPTO_MAIL:
return get_is_crypto_mail (parms->rgvarg[0].pdispVal, result);
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:
case IDI_SIGN_ENCRYPT_40_PNG:
case IDI_ENCRYPT_20_PNG:
case IDI_SIGN_20_PNG:
return getIcon (dispid, result);
case ID_BTN_SIGSTATE_LARGE:
return get_crypto_icon (parms->rgvarg[0].pdispVal, 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
*/
static STDMETHODIMP
GetCustomUI_MIME (BSTR RibbonID, BSTR * RibbonXml)
{
char * buffer = NULL;
/* 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 *encryptTTip =
_("Encrypt the message");
const char *encryptSTip =
_("Encrypts the message and all attachments before sending");
const char *signTTip =
_("Sign the message");
const char *signSTip =
_("Sign the message and all attachments before sending");
const char *secureTTip =
_("Sign and encrypt the message");
const char *secureSTip =
_("Encrypting and cryptographically signing a message means that the "
"recipients can be sure that no one modified the message and only the "
"recipients can read it");
const char *optsSTip =
_("Open the settings dialog for GpgOL");
log_debug ("%s:%s: GetCustomUI_MIME for id: %ls", SRCNAME, __func__, RibbonID);
if (!RibbonXml || !RibbonID)
return E_POINTER;
if (!wcscmp (RibbonID, L"Microsoft.Outlook.Mail.Compose"))
{
gpgrt_asprintf (&buffer,
""
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
"", _("GpgOL"),
_("Secure"), secureTTip, secureSTip,
_("Sign"), signTTip, signSTip,
_("Encrypt"), encryptTTip, encryptSTip,
optsSTip
);
}
else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Mail.Read"))
{
gpgrt_asprintf (&buffer,
""
" "
" "
" "
" "
" "
" "
" "
" "
"",
_("GpgOL"),
optsSTip
);
}
/* We don't use this code currently because calling the send
event for Inline Response mailitems fails. */
else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Explorer") && g_ol_version_major > 14)
{
gpgrt_asprintf (&buffer,
""
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
"",
_("GpgOL"),
optsSTip,
_("GpgOL"),
_("Secure"), secureTTip, secureSTip,
_("Sign"), signTTip, signSTip,
_("Encrypt"), encryptTTip, encryptSTip,
optsSTip,
_("&Print decrypted")
);
}
else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Explorer"))
{
// No TabComposeTools in Outlook 2010
gpgrt_asprintf (&buffer,
""
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
"",
_("GpgOL"),
optsSTip
);
}
if (buffer)
{
memdbg_alloc (buffer);
wchar_t *wbuf = utf8_to_wchar (buffer);
xfree (buffer);
*RibbonXml = SysAllocString (wbuf);
xfree (wbuf);
}
else
*RibbonXml = NULL;
return S_OK;
}
STDMETHODIMP
GpgolRibbonExtender::GetCustomUI (BSTR RibbonID, BSTR * RibbonXml)
{
return GetCustomUI_MIME (RibbonID, RibbonXml);
}
/* RibbonUi elements are created on demand but they are reused
in different inspectors. So far and from all documentation
I could find RibbonUi elments are never
deleted. When they are created the onLoad callback is called
to register them.
The callbacks registered in the XML description are only
executed on Load. So to have different information depending
on the available mails we have to invalidate the UI ourself.
This means that the callbacks will be reevaluated and the UI
Updated. Sadly we don't know which ribbon_ui needs updates
so we have to invalidate everything.
*/
void gpgoladdin_invalidate_ui ()
{
std::list::iterator it;
for (it = g_ribbon_uis.begin(); it != g_ribbon_uis.end(); ++it)
{
log_debug ("%s:%s: Invalidating ribbon: %p",
SRCNAME, __func__, *it);
invoke_oom_method (*it, "Invalidate", NULL);
}
}
GpgolAddin *
GpgolAddin::get_instance ()
{
if (!addin_instance)
{
addin_instance = new GpgolAddin ();
}
if (addin_instance->isShutdown ())
{
log_error ("%s:%s: Get instance after shutdown",
SRCNAME, __func__);
}
return addin_instance;
}
void
GpgolAddin::shutdown ()
{
if (m_shutdown)
{
log_debug ("%s:%s: Already shutdown",
SRCNAME, __func__);
return;
}
/* Disabling message hook */
UnhookWindowsHookEx (m_hook);
log_debug ("%s:%s: Releasing Application Event Sink;",
SRCNAME, __func__);
detach_ApplicationEvents_sink (m_applicationEventSink);
gpgol_release (m_applicationEventSink);
log_debug ("%s:%s: Releasing Explorers Event Sink;",
SRCNAME, __func__);
detach_ExplorersEvents_sink (m_explorersEventSink);
gpgol_release (m_explorersEventSink);
log_debug ("%s:%s: Releasing Explorer Event Sinks;",
SRCNAME, __func__);
for (auto sink: m_explorerEventSinks)
{
detach_ExplorerEvents_sink (sink);
gpgol_release (sink);
}
write_options ();
if (Mail::closeAllMails_o ())
{
MessageBox (NULL,
"Failed to remove plaintext from at least one message.\n\n"
"Until GpgOL is activated again it is possible that the "
"plaintext of messages decrypted in this Session is saved "
"or transfered back to your mailserver.",
_("GpgOL"),
MB_ICONINFORMATION|MB_OK);
}
m_shutdown = true;
gpgol_release (m_application);
m_application = nullptr;
}
void
GpgolAddin::registerExplorerSink (LPDISPATCH sink)
{
m_explorerEventSinks.push_back (sink);
}
void
GpgolAddin::unregisterExplorerSink (LPDISPATCH sink)
{
for (int i = 0; i < m_explorerEventSinks.size(); ++i)
{
if (m_explorerEventSinks[i] == sink)
{
m_explorerEventSinks.erase(m_explorerEventSinks.begin() + i);
return;
}
}
log_error ("%s:%s: Unregister %p which was not registered.",
SRCNAME, __func__, sink);
}
diff --git a/src/gpgoladdin.h b/src/gpgoladdin.h
index a8c121b..1641942 100644
--- a/src/gpgoladdin.h
+++ b/src/gpgoladdin.h
@@ -1,269 +1,273 @@
/* gpgoladdin.h - Connect GpgOL to Outlook as an addin
* Copyright (C) 2013 Intevation GmbH
* 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 .
*/
#ifndef GPGOLADDIN_H
#define GPGOLADDIN_H
#include
#include "mymapi.h"
#include
+#include
class GpgolAddinRibbonExt;
class ApplicationEventListener;
+class DispCache;
/* Enums for the IDTExtensibility2 interface*/
typedef enum
{
ext_cm_AfterStartup = 0,
ext_cm_Startup,
ext_cm_External,
ext_cm_CommandLine,
ext_cm_Solution,
ext_cm_UISetup
}
ext_ConnectMode;
typedef enum
{
ext_dm_HostShutdown = 0,
ext_dm_UserClosed,
ext_dm_UISetupComplete,
ext_dm_SolutionClosed
}
ext_DisconnectMode;
/* Global class locks */
extern ULONG addinLocks;
struct IDTExtensibility2;
typedef struct IDTExtensibility2 *LEXTENSIBILTY2;
/* Interface definitions */
DEFINE_GUID(IID_IDTExtensibility2, 0xB65AD801, 0xABAF, 0x11D0, 0xBB, 0x8B,
0x00, 0xA0, 0xC9, 0x0F, 0x27, 0x44);
#undef INTERFACE
#define INTERFACE IDTExtensibility2
DECLARE_INTERFACE_(IDTExtensibility2, IDispatch)
{
DECLARE_IUNKNOWN_METHODS;
DECLARE_IDISPATCH_METHODS;
/*** IDTExtensibility2 methods ***/
STDMETHOD(OnConnection)(LPDISPATCH, ext_ConnectMode, LPDISPATCH,
SAFEARRAY**) PURE;
STDMETHOD(OnDisconnection)(ext_DisconnectMode, SAFEARRAY**) PURE;
STDMETHOD(OnAddInsUpdate)(SAFEARRAY **) PURE;
STDMETHOD(OnStartupComplete)(SAFEARRAY**) PURE;
STDMETHOD(OnBeginShutdown)(SAFEARRAY**) PURE;
};
DEFINE_GUID(IID_IRibbonExtensibility, 0x000C0396, 0x0000, 0x0000, 0xC0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
struct IRibbonExtensibility;
typedef struct IRibbonExtensibility *LRIBBONEXTENSIBILITY;
#undef INTERFACE
#define INTERFACE IRibbonExtensibility
DECLARE_INTERFACE_(IRibbonExtensibility, IDispatch)
{
DECLARE_IUNKNOWN_METHODS;
DECLARE_IDISPATCH_METHODS;
/*** IRibbonExtensibility methods ***/
STDMETHOD(GetCustomUI)(BSTR RibbonID, BSTR * RibbonXml) PURE;
};
DEFINE_GUID(IID_IRibbonCallback, 0xCE895442, 0x9981, 0x4315, 0xAA, 0x85,
0x4B, 0x9A, 0x5C, 0x77, 0x39, 0xD8);
struct IRibbonCallback;
typedef struct IRibbonCallback *LRIBBONCALLBACK;
#undef INTERFACE
#define INTERFACE IRibbonCallback
DECLARE_INTERFACE_(IRibbonCallback, IUnknown)
{
DECLARE_IUNKNOWN_METHODS;
/*** IRibbonCallback methods ***/
STDMETHOD(OnRibbonLoad)(IUnknown* pRibbonUIUnk) PURE;
STDMETHOD(ButtonClicked)(IDispatch* ribbon) PURE;
};
DEFINE_GUID(IID_IRibbonControl, 0x000C0395, 0x0000, 0x0000, 0xC0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
struct IRibbonControl;
typedef struct IRibbonControl *LPRIBBONCONTROL;
#undef INTERFACE
#define INTERFACE IRibbonControl
DECLARE_INTERFACE_(IRibbonControl, IDispatch)
{
DECLARE_IUNKNOWN_METHODS;
DECLARE_IDISPATCH_METHODS;
STDMETHOD(get_Id)(BSTR* id) PURE;
STDMETHOD(get_Context)(IDispatch** context) PURE;
STDMETHOD(get_Tag)(BSTR* Tag) PURE;
};
DEFINE_GUID(IID_ICustomTaskPaneConsumer, 0x000C033E, 0x0000, 0x0000, 0xC0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
class GpgolRibbonExtender : public IRibbonExtensibility
{
public:
GpgolRibbonExtender(void);
virtual ~GpgolRibbonExtender();
/* IUnknown */
STDMETHODIMP QueryInterface (REFIID riid, LPVOID* ppvObj);
inline STDMETHODIMP_(ULONG) AddRef() { ++m_lRef; return m_lRef; };
inline STDMETHODIMP_(ULONG) Release()
{
ULONG lCount = --m_lRef;
if (!lCount)
delete this;
return lCount;
};
/* IDispatch */
STDMETHODIMP GetTypeInfoCount (UINT*);
STDMETHODIMP GetTypeInfo (UINT, LCID, LPTYPEINFO*);
STDMETHODIMP GetIDsOfNames (REFIID, LPOLESTR*, UINT, LCID, DISPID*);
STDMETHODIMP Invoke (DISPID, REFIID, LCID, WORD,
DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT*);
/* IRibbonExtensibility */
STDMETHODIMP GetCustomUI (BSTR RibbonID, BSTR* RibbonXml);
private:
ULONG m_lRef;
};
class GpgolAddin : public IDTExtensibility2
{
public:
GpgolAddin(void);
virtual ~GpgolAddin();
public:
/* IUnknown */
STDMETHODIMP QueryInterface (REFIID riid, LPVOID* ppvObj);
inline STDMETHODIMP_(ULONG) AddRef() { ++m_lRef; return m_lRef; };
inline STDMETHODIMP_(ULONG) Release()
{
ULONG lCount = --m_lRef;
if (!lCount)
delete this;
return lCount;
};
/* IDispatch */
STDMETHODIMP GetTypeInfoCount (UINT*);
STDMETHODIMP GetTypeInfo (UINT, LCID, LPTYPEINFO*);
STDMETHODIMP GetIDsOfNames (REFIID, LPOLESTR*, UINT, LCID, DISPID*);
STDMETHODIMP Invoke (DISPID, REFIID, LCID, WORD,
DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT*);
/* IDTExtensibility */
STDMETHODIMP OnConnection (LPDISPATCH Application,
ext_ConnectMode ConnectMode,
LPDISPATCH AddInInst,
SAFEARRAY** custom);
STDMETHODIMP OnDisconnection (ext_DisconnectMode RemoveMode,
SAFEARRAY** custom);
STDMETHODIMP OnAddInsUpdate (SAFEARRAY** custom);
STDMETHODIMP OnStartupComplete (SAFEARRAY** custom);
STDMETHODIMP OnBeginShutdown (SAFEARRAY** custom);
public:
static GpgolAddin * get_instance ();
void registerExplorerSink (LPDISPATCH sink);
void unregisterExplorerSink (LPDISPATCH sink);
/* Start the shutdown. Unregisters everything and closes all
crypto mails. */
void shutdown ();
LPDISPATCH get_application () { return m_application; }
+ std::shared_ptr get_dispcache () { return m_dispcache; }
bool isShutdown() { return m_shutdown; };
private:
ULONG m_lRef;
GpgolRibbonExtender* m_ribbonExtender;
LPDISPATCH m_application;
LPDISPATCH m_addin;
LPDISPATCH m_applicationEventSink;
LPDISPATCH m_explorersEventSink;
LPDISPATCH m_ribbon_control;
bool m_disabled;
bool m_shutdown;
HHOOK m_hook;
std::vector m_explorerEventSinks;
+ std::shared_ptr m_dispcache;
};
class GpgolAddinFactory: public IClassFactory
{
public:
GpgolAddinFactory(): m_lRef(0){}
virtual ~GpgolAddinFactory();
STDMETHODIMP QueryInterface (REFIID riid, LPVOID* ppvObj);
inline STDMETHODIMP_(ULONG) AddRef() { ++m_lRef; return m_lRef; };
inline STDMETHODIMP_(ULONG) Release()
{
ULONG lCount = --m_lRef;
if (!lCount)
delete this;
return lCount;
};
/* IClassFactory */
STDMETHODIMP CreateInstance (LPUNKNOWN unknown, REFIID riid,
LPVOID* ppvObj);
STDMETHODIMP LockServer (BOOL lock)
{
if (lock)
++addinLocks;
else
--addinLocks;
return S_OK;
}
private:
ULONG m_lRef;
};
STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID* ppvObj);
/* Invalidates the UI XML to trigger a reload of the UI Elements. */
void gpgoladdin_invalidate_ui ();
#endif /*GPGOLADDIN_H*/