[git] GpgOL - branch, outlook14, created. gpgol-1.1.3-4-ge185317

by Andre Heinecke cvs at cvs.gnupg.org
Fri Jul 5 10:25:55 CEST 2013


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GnuPG extension for MS Outlook".

The branch, outlook14 has been created
        at  e1853176eaf2e0411f471f1cc30a783676a5d912 (commit)

- Log -----------------------------------------------------------------
commit e1853176eaf2e0411f471f1cc30a783676a5d912
Author: Andre Heinecke <aheinecke at intevation.de>
Date:   Fri Jul 5 08:00:58 2013 +0000

    Prototype Outlook 14 support
    
        This adds a prototype "Save and decrypt" action for attachments.
        The main advancement is that gpgOl can now interact with
        Outlook 14 using the Ribbon extension interface.
    
        * src/gpgoladdin.cpp, src/gpgoladdin.h: New. Classes implementing
        the COM Objects to interact with outlook.
        * src/gpgoladdin.cpp, src/gpgol.def (DllGetClassObject),
        (DllCanUnloadNow): New entry points into the library.
        * src/olflange.cpp (DllRegisterServer, DllUnregisterServer):
        Register / Unregister gpgOl as an Outlook Addin.
        (GpgolExt): Use VERSION macro.
        (install_sinks, install_forms): Expose for other classes.
        * src/olflange.h: Move GUID and some string definitions into
        header. Add function declarations.
    
    --
        In outlook 14 the exchange extension interface was removed. Now
        we have to use the IDT_EXTENSIBILITY2 interface to interact with
        outlook (or other office applications).
        Similarly the context menu events in the OOM have been removed now
        we must use the Ribbon Extensibility Interface to extend context
        Menus. This is also the only way to avoid having all added buttons
        placed in a generic "Addins" tab and gives us some more flexibility
        how we can modify the UI.
        There are still some fixmes and todos in there but basically it
        works.
        Not tested with older outlook versions where the Exchange interface
        also works. In that case it might be best to check the Version in
        OnConnection and disable all functionality in case it is < 14.

diff --git a/src/Makefile.am b/src/Makefile.am
index 220a013..3f73dba 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -79,7 +79,8 @@ gpgol_SOURCES = \
 	inspectors.cpp inspectors.h \
 	mailitem.cpp mailitem.h \
 	cmdbarcontrols.cpp cmdbarcontrols.h \
-	w32-gettext.c w32-gettext.h 
+	w32-gettext.c w32-gettext.h \
+	gpgoladdin.cpp gpgoladdin.h
 
 
 #treeview_SOURCES = treeview.c
@@ -110,7 +111,8 @@ clean-local:
 gpgol_LDADD = $(srcdir)/gpgol.def  \
 	-L . -lgpgme -lassuan -lgpg-error \
 	-lmapi32 -lshell32 -lgdi32 -lcomdlg32 \
-        -lole32 -loleaut32 -lws2_32 -ladvapi32
+	-lole32 -loleaut32 -lws2_32 -ladvapi32 \
+	-luuid
 
 resource.o: resource.rc versioninfo.rc dialogs.rc 
 
diff --git a/src/gpgol-ids.h b/src/gpgol-ids.h
index 0c02123..ff8f206 100644
--- a/src/gpgol-ids.h
+++ b/src/gpgol-ids.h
@@ -1,6 +1,6 @@
 /* gpgol-ids.h
 
-  Resource IDs  used by gpgol-rsrcs.rc. 
+  Resource IDs  used by gpgol resource files
 */
 
 #ifndef GPGOL_IDS_H
@@ -32,6 +32,7 @@
 #define IDC_OPT_HOMEDIR                 1027
 #define IDC_OPT_SELHOMEDIR              1028
 
+#define IDR_XML_EXPLORER                203
 
 #endif /*GPGOL_IDS_H*/
 
diff --git a/src/gpgol.def b/src/gpgol.def
index 4d319ed..79cf727 100644
--- a/src/gpgol.def
+++ b/src/gpgol.def
@@ -6,6 +6,8 @@ EXPORTS
     ExchEntryPoint = ExchEntryPoint at 0            @1
     DllRegisterServer = DllRegisterServer at 0      @2	PRIVATE
     DllUnregisterServer = DllUnregisterServer at 0  @3	PRIVATE
+    DllGetClassObject = DllGetClassObject at 12     @4 PRIVATE
+    DllCanUnloadNow = DllCanUnloadNow at 0          @5 PRIVATE
 
     gpgol_check_version = gpgol_check_version at 4     @11
     gpgol_message_revert = gpgol_message_revert at 12  @12
diff --git a/src/gpgoladdin.cpp b/src/gpgoladdin.cpp
new file mode 100644
index 0000000..7f55b69
--- /dev/null
+++ b/src/gpgoladdin.cpp
@@ -0,0 +1,636 @@
+/* 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"
+
+#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
+                                     SRCNAME, __func__, __LINE__); \
+                        } while (0)
+
+/* Id's of our callbacks */
+#define ID_CMD_DECRYPT_VERIFY   1
+#define ID_CMD_DECRYPT          2
+#define ID_CMD_VERIFY           3
+
+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)
+{
+  /* Create the COM Extension Object that handles the startup and
+     endinge initialization
+  */
+  m_gpgolext = new GpgolExt();
+
+  /* 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__);
+
+  engine_deinit ();
+  write_options ();
+  delete m_gpgolext;
+  delete m_ribbonExtender;
+
+  log_debug ("%s:%s: Object deleted\n", SRCNAME, __func__);
+}
+
+STDMETHODIMP
+GpgolAddin::QueryInterface (REFIID riid, LPVOID* ppvObj)
+{
+  HRESULT hr = S_OK;
+
+  *ppvObj = NULL;
+
+  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 = m_gpgolext->QueryInterface (riid, ppvObj);
+#if 0
+      if (FAILED(hr))
+        {
+          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;
+  TRACEPOINT();
+
+  if (!m_application)
+    {
+      m_application = Application;
+      m_application->AddRef();
+      m_addin = AddInInst;
+    }
+  else
+    {
+      /* This should not happen but happened during development when
+         the vtable was incorrect and the wrong function was called */
+      log_debug ("%s:%s: Application already set. Ignoring new value.",
+                 SRCNAME, __func__);
+      return S_OK;
+    }
+
+  if (ConnectMode != ext_cm_Startup)
+    {
+      OnStartupComplete (custom);
+    }
+  return S_OK;
+}
+
+STDMETHODIMP
+GpgolAddin::OnDisconnection (ext_DisconnectMode RemoveMode,
+                             SAFEARRAY** custom)
+{
+  (void)custom;
+  return S_OK;
+}
+
+STDMETHODIMP
+GpgolAddin::OnAddInsUpdate (SAFEARRAY** custom)
+{
+  (void)custom;
+  return S_OK;
+}
+
+STDMETHODIMP
+GpgolAddin::OnStartupComplete (SAFEARRAY** custom)
+{
+  (void)custom;
+  TRACEPOINT();
+
+  if (m_application)
+    {
+      /*
+         An install_sinks here works this but we
+         don't implement all the old extension feature
+         in the addin yet.
+         install_sinks ((LPEXCHEXTCALLBACK)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
+   */
+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[0]);
+      /* 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 */
+      if (!wcscmp (rgszNames[i], L"AttachmentDecryptCallback"))
+        {
+          found = true;
+          rgDispId[i] = ID_CMD_DECRYPT;
+        }
+    }
+
+  if (cNames > 1)
+    {
+      log_debug ("More then one name provided. Should not happen");
+    }
+
+  return found ? S_OK : E_NOINTERFACE;
+}
+
+HRESULT
+GpgolRibbonExtender::decryptAttachments(LPRIBBONCONTROL ctrl)
+{
+  BSTR idStr = NULL;
+  LPDISPATCH context = NULL;
+  int attachmentCount;
+  HRESULT hr = 0;
+  int i = 0;
+  HWND curWindow;
+  LPOLEWINDOW actExplorer;
+  int err;
+
+  /* We got the vtable right so we can save us the invoke and
+     property lookup hassle and call it directly */
+  hr = ctrl->get_Id (&idStr);
+  hr |= ctrl->get_Context (&context);
+
+  if (FAILED(hr))
+    {
+      log_debug ("%s:%s:Context / ID lookup failed. hr: %x",
+                 SRCNAME, __func__, (unsigned int) hr);
+      SysFreeString (idStr);
+      return E_FAIL;
+    }
+  else
+    {
+      log_debug ("%s:%s: contextId: %S, contextObj: %s",
+                 SRCNAME, __func__, idStr, get_object_name (context));
+      SysFreeString (idStr);
+    }
+
+  attachmentCount = get_oom_int (context, "Count");
+  log_debug ("Count: %i ", attachmentCount);
+
+  actExplorer = (LPOLEWINDOW) get_oom_object(context,
+                                             "Application.ActiveExplorer");
+  if (actExplorer)
+    actExplorer->GetWindow (&curWindow);
+  else
+    {
+      log_debug ("%s:%s: Could not find active window",
+                 SRCNAME, __func__);
+      curWindow = NULL;
+    }
+
+  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 (context, buf);
+      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);
+
+      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);
+      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]);
+          return hr;
+        }
+    }
+  err = op_assuan_start_decrypt_files (curWindow, filenames);
+
+  for (i = 0; i < attachmentCount; i++)
+    xfree (filenames[i]);
+
+  return err ? E_FAIL : S_OK;
+}
+
+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 ((LPRIBBONCONTROL)
+                                   parms->rgvarg[0].pdispVal);
+    }
+
+  log_debug ("%s:%s: leave", SRCNAME, __func__);
+
+  return DISP_E_MEMBERNOTFOUND;
+}
+
+BSTR
+loadXMLResource (int id)
+{
+  /* XXX I do not know how to get the handle of the currently
+     executed code as we never had a chance in DllMain to save
+     that handle. */
+
+  /* FIXME this does not work as intended */
+  HMODULE hModule = GetModuleHandle("gpgol.dll");
+
+  HRSRC hRsrc = FindResourceEx (hModule, MAKEINTRESOURCE(id), TEXT("XML"),
+                                MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));
+
+  if (!hRsrc)
+    {
+      log_error_w32 (-1, "%s:%s: FindResource(%d) failed\n",
+                     SRCNAME, __func__, id);
+      return NULL;
+    }
+
+  HGLOBAL hGlobal = LoadResource(hModule, hRsrc);
+
+  if (!hGlobal)
+    {
+      log_error_w32 (-1, "%s:%s: LoadResource(%d) failed\n",
+                     SRCNAME, __func__, id);
+      return NULL;
+    }
+
+  LPVOID xmlData = LockResource (hGlobal);
+
+  return SysAllocString (reinterpret_cast<OLECHAR*>(xmlData));
+}
+
+STDMETHODIMP
+GpgolRibbonExtender::GetCustomUI (BSTR RibbonID, BSTR * RibbonXml)
+{
+  log_debug ("%s:%s: GetCustomUI for id: %S", SRCNAME, __func__, RibbonID);
+
+  if (!RibbonXml)
+    return E_POINTER;
+
+  /*if (!wcscmp (RibbonID, L"Microsoft.Outlook.Explorer"))
+    {*/
+     // *RibbonXml = loadXMLResource (IDR_XML_EXPLORER);
+  /* TODO use callback for label's and Icons, load xml from resource */
+      *RibbonXml = SysAllocString (
+        L"<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\">"
+        L" <ribbon>"
+        L"   <tabs>"
+        L"    <tab id=\"gpgolTab\""
+        L"         label=\"GnuPG\">"
+        L"     <group id=\"general\""
+        L"            label=\"Allgemein\">"
+        L"       <button id=\"CustomButton\""
+        L"               imageMso=\"HappyFace\""
+        L"               size=\"large\""
+        L"               label=\"Zertifikatsverwaltung\""
+        L"               onAction=\"startCertManager\"/>"
+        L"     </group>"
+        L"    </tab>"
+        L"   </tabs>"
+        L" </ribbon>"
+        L" <contextMenus>"
+        L" <contextMenu idMso=\"ContextMenuAttachments\">"
+            L"<button id=\"gpgol_decrypt\""
+                L" label=\"Save and decrypt\""
+                L" onAction=\"AttachmentDecryptCallback\"/>"
+        L" </contextMenu>"
+        L" </contextMenus>"
+        L"</customUI>"
+        );
+  /*  } */
+
+  return S_OK;
+}
diff --git a/src/gpgoladdin.h b/src/gpgoladdin.h
new file mode 100644
index 0000000..ee4583a
--- /dev/null
+++ b/src/gpgoladdin.h
@@ -0,0 +1,233 @@
+/* gpgoladdin.h - 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/>.
+ */
+
+#ifndef GPGOLADDIN_H
+#define GPGOLADDIN_H
+
+#include <windows.h>
+
+class GpgolAddinRibbonExt;
+class GpgolExt;
+
+/* 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)
+{
+  STDMETHOD(OnConnection)(LPDISPATCH, ext_ConnectMode, LPDISPATCH,
+                          SAFEARRAY**);
+  STDMETHOD(OnDisconnection)(ext_DisconnectMode, SAFEARRAY**);
+  STDMETHOD(OnAddInsUpdate)(SAFEARRAY **);
+  STDMETHOD(OnStartupComplete)(SAFEARRAY**);
+  STDMETHOD(OnBeginShutdown)(SAFEARRAY**);
+};
+
+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)
+{
+  STDMETHOD(GetCustomUI)(BSTR RibbonID, BSTR * RibbonXml);
+};
+
+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)
+{
+  STDMETHOD(OnRibbonLoad)(IUnknown* pRibbonUIUnk);
+  STDMETHOD(ButtonClicked)(IDispatch* ribbon);
+};
+
+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)
+{
+  STDMETHOD(get_Id)(BSTR* id);
+  STDMETHOD(get_Context)(IDispatch** context);
+  STDMETHOD(get_Tag)(BSTR* Tag);
+};
+
+
+DEFINE_GUID(IID_ICustomTaskPaneConsumer, 0x000C033E, 0x0000, 0x0000, 0xC0,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+
+class GpgolRibbonExtender : public IRibbonExtensibility
+{
+public:
+  GpgolRibbonExtender(void);
+  ~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;
+
+  /* Callback implementations */
+  HRESULT decryptAttachments (LPRIBBONCONTROL ctrl);
+
+};
+
+class GpgolAddin : public IDTExtensibility2
+{
+public:
+  GpgolAddin(void);
+  ~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);
+
+private:
+  ULONG m_lRef;
+  GpgolExt* m_gpgolext;
+  GpgolRibbonExtender* m_ribbonExtender;
+
+  LPDISPATCH m_application;
+  LPDISPATCH m_addin;
+
+};
+
+class GpgolAddinFactory: public IClassFactory
+{
+public:
+  GpgolAddinFactory(): m_lRef(0){}
+  ~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);
+
+#endif /*GPGOLADDIN_H*/
diff --git a/src/olflange.cpp b/src/olflange.cpp
index a7fd5fe..e51521e 100644
--- a/src/olflange.cpp
+++ b/src/olflange.cpp
@@ -25,6 +25,9 @@
 #include <windows.h>
 
 #ifndef INITGUID
+/* Include every header that defines a GUID below this
+   macro. Otherwise the GUID's will only be declared and
+   not defined. */
 #define INITGUID
 #endif
 
@@ -41,6 +44,7 @@
 
 #include "olflange-def.h"
 #include "olflange.h"
+#include "gpgoladdin.h"
 #include "ext-commands.h"
 #include "user-events.h"
 #include "session-events.h"
@@ -53,30 +57,14 @@
 #include "mailitem.h"
 #include "cmdbarcontrols.h"
 
-/* The GUID for this plugin.  */
-#define CLSIDSTR_GPGOL   "{42d30988-1a3a-11da-c687-000d6080e735}"
-DEFINE_GUID(CLSID_GPGOL, 0x42d30988, 0x1a3a, 0x11da,
-            0xc6, 0x87, 0x00, 0x0d, 0x60, 0x80, 0xe7, 0x35);
-
-/* For documentation: The GUID used for our custom properties:
-   {31805ab8-3e92-11dc-879c-00061b031004}
- */
-
-
 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
                                      SRCNAME, __func__, __LINE__); \
                         } while (0)
 
-
 static bool g_initdll = FALSE;
 
-static void install_forms (void);
-static void install_sinks (LPEXCHEXTCALLBACK eecb);
-
-
 static char *olversion;
 
-
 
 /* Return a string for the context NO.  This never return NULL. */
 const char *
@@ -126,8 +114,11 @@ get_ol_main_version (void)
 
 
 
-/* Registers this module as an Exchange extension. This basically updates
-   some Registry entries. */
+/* Registers this module as an Exchange extension and as an addin for
+   outlook 2010. This basically updates some Registry entries.
+   Documentation to be found at:
+   http://msdn.microsoft.com/en-us/library/bb386106%28v=vs.110%29.aspx
+   */
 STDAPI
 DllRegisterServer (void)
 {
@@ -221,8 +212,9 @@ DllRegisterServer (void)
   if (hkey != NULL)
     RegCloseKey (hkey);
 
+  /* Register the CLSID in the registry */
   hkey = NULL;
-  strcpy (szKeyBuf, "CLSID\\" CLSIDSTR_GPGOL );
+  strcpy (szKeyBuf, "CLSID\\" CLSIDSTR_GPGOL);
   ec = RegCreateKeyEx (HKEY_CLASSES_ROOT, szKeyBuf, 0, NULL,
                   REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL);
   if (ec != ERROR_SUCCESS)
@@ -231,10 +223,11 @@ DllRegisterServer (void)
       return E_ACCESSDENIED;
     }
 
-  strcpy (szEntry, "GpgOL - The GnuPG Outlook Plugin");
+  strcpy (szEntry, GPGOL_PRETTY);
   dwTemp = strlen (szEntry) + 1;
   RegSetValueEx (hkey, NULL, 0, REG_SZ, (BYTE*)szEntry, dwTemp);
 
+  /* Set the Inproc server value */
   strcpy (szKeyBuf, "InprocServer32");
   ec = RegCreateKeyEx (hkey, szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE,
                        KEY_ALL_ACCESS, NULL, &hkey2, NULL);
@@ -248,10 +241,87 @@ DllRegisterServer (void)
   dwTemp = strlen (szEntry) + 1;
   RegSetValueEx (hkey2, NULL, 0, REG_SZ, (BYTE*)szEntry, dwTemp);
 
-  strcpy (szEntry, "Neutral");
+  /* Set the threading model used */
+  strcpy (szEntry, "Both");
   dwTemp = strlen (szEntry) + 1;
   RegSetValueEx (hkey2, "ThreadingModel", 0, REG_SZ, (BYTE*)szEntry, dwTemp);
 
+  /* Set the Prog ID */
+  strcpy (szKeyBuf, "ProgID");
+  ec = RegCreateKeyEx (hkey, szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE,
+                       KEY_ALL_ACCESS, NULL, &hkey2, NULL);
+  if (ec != ERROR_SUCCESS)
+    {
+      fprintf (stderr, "creating key `%s' failed: ec=%#lx\n", szKeyBuf, ec);
+      RegCloseKey (hkey);
+      return E_ACCESSDENIED;
+    }
+  strcpy (szEntry, GPGOL_PROGID);
+  dwTemp = strlen (szEntry) + 1;
+  RegSetValueEx (hkey2, NULL, 0, REG_SZ, (BYTE*)szEntry, dwTemp);
+
+  /* Make the Prog ID known. This is basically the same as above
+   * but necessary so we can refer to the Prog ID as an Outlook
+   * Extension
+   */
+  hkey = NULL;
+  strcpy (szKeyBuf, GPGOL_PROGID);
+  ec = RegCreateKeyEx (HKEY_CLASSES_ROOT, szKeyBuf, 0, NULL,
+                  REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL);
+  if (ec != ERROR_SUCCESS)
+    {
+      fprintf (stderr, "creating key `%s' failed: ec=%#lx\n", szKeyBuf, ec);
+      return E_ACCESSDENIED;
+    }
+
+  strcpy (szEntry, GPGOL_PRETTY);
+  dwTemp = strlen (szEntry) + 1;
+  RegSetValueEx (hkey, NULL, 0, REG_SZ, (BYTE*)szEntry, dwTemp);
+
+  /* Point from the Prog ID entry to the CSLID */
+
+  strcpy (szKeyBuf, "CLSID");
+  ec = RegCreateKeyEx (hkey, szKeyBuf, 0, NULL, REG_OPTION_NON_VOLATILE,
+                       KEY_ALL_ACCESS, NULL, &hkey2, NULL);
+  if (ec != ERROR_SUCCESS)
+    {
+      fprintf (stderr, "creating key `%s' failed: ec=%#lx\n", szKeyBuf, ec);
+      RegCloseKey (hkey);
+      return E_ACCESSDENIED;
+    }
+  strcpy (szEntry, CLSIDSTR_GPGOL);
+  dwTemp = strlen (szEntry) + 1;
+  RegSetValueEx (hkey2, NULL, 0, REG_SZ, (BYTE*)szEntry, dwTemp);
+
+  /* Register ourself as an extension for outlook >= 14 */
+
+  strcpy (szKeyBuf, "Software\\Microsoft\\Office\\Outlook\\Addins\\" GPGOL_PROGID);
+  ec = RegCreateKeyEx (HKEY_LOCAL_MACHINE, szKeyBuf, 0, NULL,
+                  REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL);
+  if (ec != ERROR_SUCCESS)
+    {
+      fprintf (stderr, "creating key `%s' failed: ec=%#lx\n", szKeyBuf, ec);
+      return E_ACCESSDENIED;
+    }
+
+  /* Load connected and load Bootload */
+  dwTemp = 0x01 | 0x02;
+  RegSetValueEx (hkey, "LoadBehavior", 0, REG_DWORD, (BYTE*)&dwTemp, 4);
+
+  /* We are not commandline save */
+  dwTemp = 0;
+  RegSetValueEx (hkey, "CommandLineSafe", 0, REG_DWORD, (BYTE*)&dwTemp, 4);
+
+  /* A friendly name (visible in outlook) */
+  strcpy (szEntry, GPGOL_PRETTY);
+  dwTemp = strlen (szEntry) + 1;
+  RegSetValueEx (hkey, "FriendlyName", 0, REG_SZ, (BYTE*)szEntry, dwTemp);
+
+  /* A short description (visible in outlook) */
+  strcpy (szEntry, GPGOL_DESCRIPTION);
+  dwTemp = strlen (szEntry) + 1;
+  RegSetValueEx (hkey, "Description", 0, REG_SZ, (BYTE*)szEntry, dwTemp);
+
   RegCloseKey (hkey2);
   RegCloseKey (hkey);
 
@@ -261,7 +331,7 @@ DllRegisterServer (void)
 }
 
 
-/* Unregisters this module as an Exchange extension. */
+/* Unregisters this module as an Exchange extension / Addin. */
 STDAPI
 DllUnregisterServer (void)
 {
@@ -292,9 +362,21 @@ DllUnregisterServer (void)
   /* Delete CLSIDs. */
   strcpy (buf, "CLSID\\" CLSIDSTR_GPGOL "\\InprocServer32");
   RegDeleteKey (HKEY_CLASSES_ROOT, buf);
+  strcpy (buf, "CLSID\\" CLSIDSTR_GPGOL "\\ProgID");
+  RegDeleteKey (HKEY_CLASSES_ROOT, buf);
   strcpy (buf, "CLSID\\" CLSIDSTR_GPGOL);
   RegDeleteKey (HKEY_CLASSES_ROOT, buf);
 
+  /* Delete ProgID */
+  strcpy (buf, GPGOL_PROGID "\\CLSID");
+  RegDeleteKey (HKEY_CLASSES_ROOT, buf);
+  strcpy (buf, GPGOL_PROGID);
+  RegDeleteKey (HKEY_CLASSES_ROOT, buf);
+
+  /* Delete Addin entry */
+  strcpy (buf, "Software\\Microsoft\\Office\\Outlook\\Addins\\" GPGOL_PROGID);
+  RegDeleteKey (HKEY_LOCAL_MACHINE, buf);
+
   return S_OK;
 }
 
@@ -441,7 +523,7 @@ GpgolExt::GpgolExt (void)
              that a user will see this message only once.  */
           MessageBox
             (NULL,
-             _("Welcome to GpgOL 1.0\n"
+             _("Welcome to GpgOL " VERSION "\n"
                "\n"
                "GpgOL adds integrated OpenPGP and S/MIME encryption "
                "and digital signing support to Outlook 2003 and 2007.\n"
@@ -706,7 +788,7 @@ GpgolExt::Install(LPEXCHEXTCALLBACK pEECB, ULONG lContext, ULONG lFlags)
 }
 
 
-static void
+void
 install_forms (void)
 {
   HRESULT hr;
@@ -782,8 +864,7 @@ install_forms (void)
 }
 
 
-
-static void
+void
 install_sinks (LPEXCHEXTCALLBACK eecb)
 {
   static int done;
@@ -802,6 +883,12 @@ install_sinks (LPEXCHEXTCALLBACK eecb)
   eecb->QueryInterface (IID_IOutlookExtCallback, (LPVOID*)&pCb);
   if (pCb)
     pCb->GetObject (&rootobj);
+  else
+    {
+      /* If we did not get an ExtCallback interface we might
+         as well try to find Application.Explorers directly */
+      rootobj = eecb;
+    }
   if (rootobj)
     {
       LPDISPATCH disp;
diff --git a/src/olflange.h b/src/olflange.h
index 895cf7b..b4ab534 100644
--- a/src/olflange.h
+++ b/src/olflange.h
@@ -20,11 +20,33 @@
 #ifndef OLFLANGE_H
 #define OLFLANGE_H
 
+#include "mymapi.h"
+#include "mymapitags.h"
+#include "myexchext.h"
 #include "mapihelp.h"
 
+#include "olflange-def.h"
+
+/* The GUID for this plugin.  */
+#define CLSIDSTR_GPGOL   "{42d30988-1a3a-11da-c687-000d6080e735}"
+DEFINE_GUID(CLSID_GPGOL, 0x42d30988, 0x1a3a, 0x11da,
+            0xc6, 0x87, 0x00, 0x0d, 0x60, 0x80, 0xe7, 0x35);
+
+/* For documentation: The GUID used for our custom properties:
+   {31805ab8-3e92-11dc-879c-00061b031004}
+ */
+
+/* The ProgID used by us */
+#define GPGOL_PROGID "GNU.GpgOL"
+/* User friendly add in name */
+#define GPGOL_PRETTY "GpgOL - The GnuPG Outlook Plugin"
+/* Short description of the addin */
+#define GPGOL_DESCRIPTION "Cryptography for Outlook"
+
+
 
 /*
- GpgolExt 
+ GpgolExt
 
  The GpgolExt class is the main exchange extension class. The other 
  extensions will be created in the constructor of this class.
@@ -71,5 +93,10 @@ EXTERN_C const char * __stdcall gpgol_check_version (const char *req_version);
 
 EXTERN_C int get_ol_main_version (void);
 
+void install_sinks (LPEXCHEXTCALLBACK eecb);
+
+void install_forms (void);
+
+LPDISPATCH get_eecb_object (LPEXCHEXTCALLBACK eecb);
 
 #endif /*OLFLANGE_H*/

commit bf9f567b67512b8b75785941eafdcf47fb5a7cf5
Author: Andre Heinecke <aheinecke at intevation.de>
Date:   Fri Jul 5 07:22:37 2013 +0000

    Add decrypt_files operation to the assuan engine
    
        To be used to decrypt a list of filenames.
    
        * src/common.c (percent_escape): New. Taken from gpgEx to escape
        file names.
        * src/engine-assuan.c (op_assuan_start_decrypt_files): New. Handles
        the assuan transactions.
        * src/engine-assuan.h, src/util.h: Add prototypes
        * src/util.h (tohex_lower): New. Taken from gpgEx

diff --git a/src/common.c b/src/common.c
index 68cae2d..b422154 100644
--- a/src/common.c
+++ b/src/common.c
@@ -999,4 +999,51 @@ parse_tlv (char const **buffer, size_t *size, tlvinfo_t *ti)
   return 0;
 }
 
+/* Percent-escape the string STR by replacing colons with '%3a'.  If
+   EXTRA is not NULL all characters in it are also escaped. */
+char *
+percent_escape (const char *str, const char *extra)
+{
+  int i, j;
+  char *ptr;
+
+  if (!str)
+    return NULL;
+
+  for (i=j=0; str[i]; i++)
+    if (str[i] == ':' || str[i] == '%' || (extra && strchr (extra, str[i])))
+      j++;
+  ptr = (char *) malloc (i + 2 * j + 1);
+  i = 0;
+  while (*str)
+    {
+      /* FIXME: Work around a bug in Kleo.  */
+      if (*str == ':')
+        {
+          ptr[i++] = '%';
+          ptr[i++] = '3';
+          ptr[i++] = 'a';
+        }
+      else
+        {
+          if (*str == '%')
+            {
+              ptr[i++] = '%';
+              ptr[i++] = '2';
+              ptr[i++] = '5';
+            }
+          else if (extra && strchr (extra, *str))
+            {
+              ptr[i++] = '%';
+              ptr[i++] = tohex_lower ((*str >> 4) & 15);
+              ptr[i++] = tohex_lower (*str & 15);
+            }
+          else
+            ptr[i++] = *str;
+        }
+      str++;
+    }
+  ptr[i] = '\0';
 
+  return ptr;
+}
diff --git a/src/engine-assuan.c b/src/engine-assuan.c
index ff17cf4..8e0737f 100644
--- a/src/engine-assuan.c
+++ b/src/engine-assuan.c
@@ -33,6 +33,7 @@
 #include "common.h"
 #include "engine.h"
 #include "engine-assuan.h"
+#include "util.h"
 
 
 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
@@ -2207,3 +2208,35 @@ op_assuan_start_confdialog (void *hwnd)
     }
   return err;
 }
+
+/* Send a decrypt files command to the server.
+   Filenames should be a null terminated array of char*
+*/
+int
+op_assuan_start_decrypt_files (void *hwnd, char **filenames)
+{
+  gpg_error_t err;
+  assuan_context_t ctx;
+  ULONG cmdid;
+  pid_t pid;
+  char line[1024];
+
+  err = connect_uiserver (&ctx, &pid, &cmdid, hwnd);
+  if (!err)
+    {
+      while (*filenames != NULL)
+        {
+          snprintf(line, sizeof(line), "FILE %s",
+                   percent_escape(*filenames, NULL));
+          err = assuan_transact (ctx, line,
+                                 NULL, NULL, NULL, NULL, NULL, NULL);
+          if (err)
+            return err;
+          filenames++;
+        }
+      err = assuan_transact (ctx, "DECRYPT_FILES",
+                             NULL, NULL, NULL, NULL, NULL, NULL);
+      assuan_release (ctx);
+    }
+  return err;
+}
diff --git a/src/engine-assuan.h b/src/engine-assuan.h
index 7e9d0cb..de2a144 100644
--- a/src/engine-assuan.h
+++ b/src/engine-assuan.h
@@ -64,6 +64,8 @@ int op_assuan_start_keymanager (void *hwnd);
 
 int op_assuan_start_confdialog (void *hwnd);
 
+int op_assuan_start_decrypt_files (void *hwnd, char **filenames);
+
 
 #ifdef __cplusplus
 }
diff --git a/src/util.h b/src/util.h
index f50b9d7..77cdef0 100644
--- a/src/util.h
+++ b/src/util.h
@@ -70,7 +70,7 @@ char *trim_spaces (char *string);
 char *trim_trailing_spaces (char *string);
 char *read_w32_registry_string (const char *root, const char *dir,
                                 const char *name);
-
+char *percent_escape (const char *str, const char *extra);
 
 /*-- main.c --*/
 const void *get_128bit_session_key (void);
@@ -123,6 +123,7 @@ int write_options (void);
 
 #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
 
+#define tohex_lower(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'a'))
 /***** Inline functions.  ****/
 
 /* Return true if LINE consists only of white space (up to and

commit 5e2da8b3e8b65cd9642b830b2978dee269737d0d
Author: Andre Heinecke <aheinecke at intevation.de>
Date:   Fri Jul 5 07:20:54 2013 +0000

    Update copyright year in resource file
    
        * src/versioninfo.rc.in: Update copyright

diff --git a/src/versioninfo.rc.in b/src/versioninfo.rc.in
index c88d6e3..bc0da84 100644
--- a/src/versioninfo.rc.in
+++ b/src/versioninfo.rc.in
@@ -39,7 +39,7 @@ BEGIN
             VALUE "FileDescription", "GpgOL - GnuPG plugin for Outlook\0"
             VALUE "FileVersion", "@VERSION@\0"
             VALUE "InternalName", "gpgol\0"
-            VALUE "LegalCopyright", "Copyright © 2009 g10 Code GmbH\0"
+            VALUE "LegalCopyright", "Copyright © 2013 g10 Code GmbH\0"
             VALUE "LegalTrademarks", "\0"
             VALUE "OriginalFilename", "gpgol.dll\0"
             VALUE "PrivateBuild", "\0"

-----------------------------------------------------------------------


hooks/post-receive
-- 
GnuPG extension for MS Outlook
http://git.gnupg.org




More information about the Gnupg-commits mailing list