Update signature status display
[gpgol.git] / src / gpgoladdin.cpp
1 /* gpgoladdin.cpp - Connect GpgOL to Outlook as an addin
2  *    Copyright (C) 2013, 2015 Intevation GmbH
3  *
4  * This file is part of GpgOL.
5  *
6  * GpgOL is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * GpgOL is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <windows.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "common.h"
29 #include "gpgoladdin.h"
30
31 #include "mymapi.h"
32 #include "mymapitags.h"
33 #include "myexchext.h"
34
35 #include "display.h"
36 #include "msgcache.h"
37 #include "engine.h"
38 #include "engine-assuan.h"
39 #include "mapihelp.h"
40
41 #include "oomhelp.h"
42
43 #include "olflange.h"
44
45 #include "gpgol-ids.h"
46 #include "ribbon-callbacks.h"
47 #include "eventsinks.h"
48 #include "eventsink.h"
49 #include "windowmessages.h"
50 #include "mail.h"
51 #include "addin-options.h"
52
53 #include <gpg-error.h>
54 #include <list>
55
56 #define ICON_SIZE_LARGE  32
57 #define ICON_SIZE_NORMAL 16
58
59 /* We use UTF-8 internally. */
60 #undef _
61 #define _(a) utf8_gettext (a)
62
63 ULONG addinLocks = 0;
64
65 bool can_unload = false;
66
67 /* Invalidating the interface does not take a nice effect so we store
68    this option in a global variable. */
69 bool use_mime_ui = false;
70
71 static std::list<LPDISPATCH> g_ribbon_uis;
72
73 static GpgolAddin * addin_instance = NULL;
74
75 /* This is the main entry point for the addin
76    Outlook uses this function to query for an Object implementing
77    the IClassFactory interface.
78 */
79 STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID* ppvObj)
80 {
81   if (!ppvObj)
82     return E_POINTER;
83
84   *ppvObj = NULL;
85   if (rclsid != CLSID_GPGOL)
86     return CLASS_E_CLASSNOTAVAILABLE;
87
88   /* Let the factory give the requested interface. */
89   GpgolAddinFactory* factory = new GpgolAddinFactory();
90   if (!factory)
91     return E_OUTOFMEMORY;
92
93   HRESULT hr = factory->QueryInterface (riid, ppvObj);
94   if(FAILED(hr))
95     {
96       *ppvObj = NULL;
97       delete factory;
98     }
99
100   return hr;
101 }
102
103
104 STDAPI DllCanUnloadNow()
105 {
106   /* This is called regularly to check if memory can be freed
107      by unloading the dll. The following unload will not call
108      any addin methods like disconnect etc. It will just
109      unload the Library. Any callbacks will become invalid.
110      So we _only_ say it's ok to unload if we were disconnected.
111      For the epic story behind the next line see GnuPG-Bug-Id 1837 */
112   return can_unload ? S_OK : S_FALSE;
113 }
114
115 /* Class factory */
116 STDMETHODIMP GpgolAddinFactory::QueryInterface (REFIID riid, LPVOID* ppvObj)
117 {
118   HRESULT hr = S_OK;
119
120   *ppvObj = NULL;
121
122   if ((IID_IUnknown == riid) || (IID_IClassFactory == riid))
123     *ppvObj = static_cast<IClassFactory*>(this);
124   else
125     {
126       hr = E_NOINTERFACE;
127       LPOLESTR sRiid = NULL;
128       StringFromIID (riid, &sRiid);
129       /* Should not happen */
130       log_debug ("GpgolAddinFactory queried for unknown interface: %S \n", sRiid);
131     }
132
133   if (*ppvObj)
134     ((LPUNKNOWN)*ppvObj)->AddRef();
135
136   return hr;
137 }
138
139
140 /* This actually creates the instance of our COM object */
141 STDMETHODIMP GpgolAddinFactory::CreateInstance (LPUNKNOWN punk, REFIID riid,
142                                                 LPVOID* ppvObj)
143 {
144   (void)punk;
145   *ppvObj = NULL;
146
147   GpgolAddin* obj = GpgolAddin::get_instance();
148   if (NULL == obj)
149     return E_OUTOFMEMORY;
150
151   HRESULT hr = obj->QueryInterface (riid, ppvObj);
152
153   if (FAILED(hr))
154     {
155       LPOLESTR sRiid = NULL;
156       StringFromIID (riid, &sRiid);
157       fprintf(stderr, "failed to create instance for: %S", sRiid);
158     }
159
160   return hr;
161 }
162
163 /* GpgolAddin definition */
164
165
166 /* Constructor of GpgolAddin
167
168    Initializes members and creates the interface objects for the new
169    context.  Does the DLL initialization if it has not been done
170    before.
171
172    The ref count is set by the factory after creation.
173 */
174 GpgolAddin::GpgolAddin (void) : m_lRef(0),
175   m_application(nullptr),
176   m_addin(nullptr),
177   m_applicationEventSink(nullptr),
178   m_explorersEventSink(nullptr),
179   m_disabled(false),
180   m_hook(nullptr)
181 {
182   read_options ();
183   use_mime_ui = opt.mime_ui;
184   /* RibbonExtender is it's own object to avoid the pitfalls of
185      multiple inheritance
186   */
187   m_ribbonExtender = new GpgolRibbonExtender();
188 }
189
190 GpgolAddin::~GpgolAddin (void)
191 {
192   if (m_disabled)
193     {
194       return;
195     }
196   log_debug ("%s:%s: Releasing Application Event Sink;",
197              SRCNAME, __func__);
198   gpgol_release (m_explorersEventSink);
199   gpgol_release (m_applicationEventSink);
200
201   engine_deinit ();
202   write_options ();
203
204   UnhookWindowsHookEx (m_hook);
205
206   addin_instance = NULL;
207
208   log_debug ("%s:%s: Object deleted\n", SRCNAME, __func__);
209 }
210
211 STDMETHODIMP
212 GpgolAddin::QueryInterface (REFIID riid, LPVOID* ppvObj)
213 {
214   HRESULT hr = S_OK;
215
216   *ppvObj = NULL;
217
218   if (m_disabled)
219     return E_NOINTERFACE;
220
221   if ((riid == IID_IUnknown) || (riid == IID_IDTExtensibility2) ||
222       (riid == IID_IDispatch))
223     {
224       *ppvObj = (LPUNKNOWN) this;
225     }
226   else if (riid == IID_IRibbonExtensibility)
227     {
228       return m_ribbonExtender->QueryInterface (riid, ppvObj);
229     }
230   else
231     {
232       hr = E_NOINTERFACE;
233 #if 0
234       LPOLESTR sRiid = NULL;
235       StringFromIID(riid, &sRiid);
236       log_debug ("%s:%s: queried for unimplmented interface: %S",
237                  SRCNAME, __func__, sRiid);
238 #endif
239     }
240
241   if (*ppvObj)
242     ((LPUNKNOWN)*ppvObj)->AddRef();
243
244   return hr;
245 }
246
247 STDMETHODIMP
248 GpgolAddin::OnConnection (LPDISPATCH Application, ext_ConnectMode ConnectMode,
249                           LPDISPATCH AddInInst, SAFEARRAY ** custom)
250 {
251   (void)custom;
252   char* version;
253
254   log_debug ("%s:%s: this is GpgOL %s\n",
255              SRCNAME, __func__, PACKAGE_VERSION);
256
257   can_unload = false;
258   m_application = Application;
259   m_application->AddRef();
260   m_addin = AddInInst;
261
262   version = get_oom_string (Application, "Version");
263
264   log_debug ("%s:%s:   using GPGME %s\n",
265              SRCNAME, __func__, gpgme_check_version (NULL));
266   log_debug ("%s:%s:   in Outlook %s\n",
267              SRCNAME, __func__, version);
268
269   g_ol_version_major = atoi (version);
270
271   if (!version || !strlen (version) ||
272       (strncmp (version, "14", 2) &&
273        strncmp (version, "15", 2) &&
274        strncmp (version, "16", 2)))
275     {
276       m_disabled = true;
277       log_debug ("%s:%s: Disabled addin for unsupported version.",
278                  SRCNAME, __func__);
279
280       xfree (version);
281       return S_OK;
282     }
283   engine_init ();
284
285   if (ConnectMode != ext_cm_Startup)
286     {
287       OnStartupComplete (custom);
288     }
289   return S_OK;
290 }
291
292 STDMETHODIMP
293 GpgolAddin::OnDisconnection (ext_DisconnectMode RemoveMode,
294                              SAFEARRAY** custom)
295 {
296   (void)custom;
297   (void)RemoveMode;
298   log_debug ("%s:%s: cleaning up GpgolAddin object;",
299              SRCNAME, __func__);
300
301   /* Doing the wipe in the dtor is too late. Outlook
302      does not allow us any OOM calls then and only returns
303      "Unexpected error" in that case. Weird. */
304
305   if (Mail::close_all_mails ())
306     {
307       MessageBox (NULL,
308                   "Failed to remove plaintext from at least one message.\n\n"
309                   "Until GpgOL is activated again it is possible that the "
310                   "plaintext of messages decrypted in this Session is saved "
311                   "or transfered back to your mailserver.",
312                   _("GpgOL"),
313                   MB_ICONINFORMATION|MB_OK);
314     }
315
316   write_options();
317   can_unload = true;
318   return S_OK;
319 }
320
321 STDMETHODIMP
322 GpgolAddin::OnAddInsUpdate (SAFEARRAY** custom)
323 {
324   (void)custom;
325   return S_OK;
326 }
327
328 static void
329 check_html_preferred()
330 {
331   /* Check if HTML Mail should be enabled. */
332   HKEY h;
333   std::string path = "Software\\Microsoft\\Office\\";
334   path += std::to_string (g_ol_version_major);
335   path += ".0\\Outlook\\Options\\Mail";
336   opt.prefer_html = 1;
337   int err = RegOpenKeyEx (HKEY_CURRENT_USER, path.c_str() , 0, KEY_READ, &h);
338   if (err != ERROR_SUCCESS)
339     {
340       log_debug ("%s:%s: no mail options under %s",
341                  SRCNAME, __func__, path.c_str());
342       return;
343     }
344   else
345     {
346       DWORD type;
347       err = RegQueryValueEx (h, "ReadAsPlain", NULL, &type, NULL, NULL);
348       if (err != ERROR_SUCCESS || type != REG_DWORD)
349         {
350           log_debug ("%s:%s: No type or key for ReadAsPlain",
351                      SRCNAME, __func__);
352           return;
353         }
354       else
355         {
356           DWORD data;
357           DWORD size = sizeof (DWORD);
358           err = RegQueryValueEx (h, "ReadAsPlain", NULL, NULL, (LPBYTE)&data,
359                                  &size);
360           if (err != ERROR_SUCCESS)
361             {
362               log_debug ("%s:%s: Failed to find out ReadAsPlain",
363                          SRCNAME, __func__);
364               return;
365             }
366           opt.prefer_html = data ? 0 : 1;
367           return;
368         }
369     }
370 }
371
372 static LPDISPATCH
373 install_explorer_sinks (LPDISPATCH application)
374 {
375
376   LPDISPATCH explorers = get_oom_object (application, "Explorers");
377
378   if (!explorers)
379     {
380       log_error ("%s:%s: No explorers object",
381                  SRCNAME, __func__);
382       return nullptr;
383     }
384   int count = get_oom_int (explorers, "Count");
385
386   for (int i = 1; i <= count; i++)
387     {
388       std::string item = "Item(";
389       item += std::to_string (i) + ")";
390       LPDISPATCH explorer = get_oom_object (explorers, item.c_str());
391       if (!explorer)
392         {
393           log_error ("%s:%s: failed to get explorer %i",
394                      SRCNAME, __func__, i);
395           continue;
396         }
397       /* Explorers delete themself in the close event of the explorer. */
398       LPDISPATCH sink = install_ExplorerEvents_sink (explorer);
399       if (!sink)
400         {
401           log_error ("%s:%s: failed to create eventsink for explorer %i",
402                      SRCNAME, __func__, i);
403
404         }
405       else
406         {
407           log_oom_extra ("%s:%s: created sink %p for explorer %i",
408                          SRCNAME, __func__, sink, i);
409         }
410       add_explorer (explorer);
411       gpgol_release (explorer);
412     }
413   /* Now install the event sink to handle new explorers */
414   return install_ExplorersEvents_sink (explorers);
415 }
416
417 STDMETHODIMP
418 GpgolAddin::OnStartupComplete (SAFEARRAY** custom)
419 {
420   (void)custom;
421   TRACEPOINT;
422
423   if (!create_responder_window())
424     {
425       log_error ("%s:%s: Failed to create the responder window;",
426                  SRCNAME, __func__);
427     }
428
429   if (!m_application)
430     {
431       /* Should not happen as OnConnection should be called before */
432       log_error ("%s:%s: no application set;",
433                  SRCNAME, __func__);
434       return E_NOINTERFACE;
435     }
436
437   if (!(m_hook = create_message_hook ()))
438     {
439       log_error ("%s:%s: Failed to create messagehook. ",
440                  SRCNAME, __func__);
441     }
442
443   /* Set up categories */
444   const char *decCategory = _("GpgOL: Encrypted Message");
445   const char *verifyCategory = _("GpgOL: Trusted Sender Address");
446   ensure_category_exists (m_application, decCategory, 8);
447   ensure_category_exists (m_application, verifyCategory, 5);
448   install_forms ();
449   m_applicationEventSink = install_ApplicationEvents_sink (m_application);
450   m_explorersEventSink = install_explorer_sinks (m_application);
451   check_html_preferred ();
452   return S_OK;
453 }
454
455 STDMETHODIMP
456 GpgolAddin::OnBeginShutdown (SAFEARRAY * * custom)
457 {
458   (void)custom;
459   TRACEPOINT;
460   return S_OK;
461 }
462
463 STDMETHODIMP
464 GpgolAddin::GetTypeInfoCount (UINT *r_count)
465 {
466   *r_count = 0;
467   TRACEPOINT; /* Should not happen */
468   return S_OK;
469 }
470
471 STDMETHODIMP
472 GpgolAddin::GetTypeInfo (UINT iTypeInfo, LCID lcid,
473                                   LPTYPEINFO *r_typeinfo)
474 {
475   (void)iTypeInfo;
476   (void)lcid;
477   (void)r_typeinfo;
478   TRACEPOINT; /* Should not happen */
479   return S_OK;
480 }
481
482 STDMETHODIMP
483 GpgolAddin::GetIDsOfNames (REFIID riid, LPOLESTR *rgszNames,
484                                     UINT cNames, LCID lcid,
485                                     DISPID *rgDispId)
486 {
487   (void)riid;
488   (void)rgszNames;
489   (void)cNames;
490   (void)lcid;
491   (void)rgDispId;
492   TRACEPOINT; /* Should not happen */
493   return E_NOINTERFACE;
494 }
495
496 STDMETHODIMP
497 GpgolAddin::Invoke (DISPID dispid, REFIID riid, LCID lcid,
498                     WORD flags, DISPPARAMS *parms, VARIANT *result,
499                     EXCEPINFO *exepinfo, UINT *argerr)
500 {
501   USE_INVOKE_ARGS
502   TRACEPOINT; /* Should not happen */
503   return DISP_E_MEMBERNOTFOUND;
504 }
505
506
507
508 /* Definition of GpgolRibbonExtender */
509
510 GpgolRibbonExtender::GpgolRibbonExtender (void) : m_lRef(0)
511 {
512 }
513
514 GpgolRibbonExtender::~GpgolRibbonExtender (void)
515 {
516   log_debug ("%s:%s: cleaning up GpgolRibbonExtender object;",
517              SRCNAME, __func__);
518   log_debug ("%s:%s: Object deleted\n", SRCNAME, __func__);
519 }
520
521 STDMETHODIMP
522 GpgolRibbonExtender::QueryInterface(REFIID riid, LPVOID* ppvObj)
523 {
524   HRESULT hr = S_OK;
525
526   *ppvObj = NULL;
527
528   if ((riid == IID_IUnknown) || (riid == IID_IRibbonExtensibility) ||
529       (riid == IID_IDispatch))
530     {
531       *ppvObj = (LPUNKNOWN) this;
532     }
533   else
534     {
535       LPOLESTR sRiid = NULL;
536       StringFromIID (riid, &sRiid);
537       log_debug ("%s:%s: queried for unknown interface: %S",
538                  SRCNAME, __func__, sRiid);
539     }
540
541   if (*ppvObj)
542     ((LPUNKNOWN)*ppvObj)->AddRef();
543
544   return hr;
545 }
546
547 STDMETHODIMP
548 GpgolRibbonExtender::GetTypeInfoCount (UINT *r_count)
549 {
550   *r_count = 0;
551   TRACEPOINT; /* Should not happen */
552   return S_OK;
553 }
554
555 STDMETHODIMP
556 GpgolRibbonExtender::GetTypeInfo (UINT iTypeInfo, LCID lcid,
557                                   LPTYPEINFO *r_typeinfo)
558 {
559   (void)iTypeInfo;
560   (void)lcid;
561   (void)r_typeinfo;
562   TRACEPOINT; /* Should not happen */
563   return S_OK;
564 }
565
566 /* Good documentation of what this function is supposed to do can
567    be found at: http://msdn.microsoft.com/en-us/library/cc237568.aspx
568
569    There is also a very good blog explaining how Ribbon Extensibility
570    is supposed to work.
571    http://blogs.msdn.com/b/andreww/archive/2007/03/09/
572 why-is-it-so-hard-to-shim-iribbonextensibility.aspx
573    */
574
575 #define ID_MAPPER(name,id)                      \
576   if (!wcscmp (rgszNames[i], name))             \
577     {                                           \
578       found = true;                             \
579       rgDispId[i] = id;                         \
580       break;                                    \
581     }                                           \
582
583
584 STDMETHODIMP
585 GpgolRibbonExtender::GetIDsOfNames (REFIID riid, LPOLESTR *rgszNames,
586                                     UINT cNames, LCID lcid,
587                                     DISPID *rgDispId)
588 {
589   (void)riid;
590   (void)lcid;
591   bool found = false;
592
593   if (!rgszNames || !cNames || !rgDispId)
594     {
595       return E_POINTER;
596     }
597
598   for (unsigned int i = 0; i < cNames; i++)
599     {
600       log_debug ("%s:%s: GetIDsOfNames for: %S",
601                  SRCNAME, __func__, rgszNames[i]);
602       /* How this is supposed to work with cNames > 1 is unknown,
603          but we can just say that we won't support callbacks with
604          different parameters and just match the name (the first element)
605          and we give it one of our own dispIds's that are later handled in
606          the invoke part */
607       ID_MAPPER (L"attachmentDecryptCallback", ID_CMD_DECRYPT)
608       ID_MAPPER (L"encryptSelection", ID_CMD_ENCRYPT_SELECTION)
609       ID_MAPPER (L"decryptSelection", ID_CMD_DECRYPT_SELECTION)
610       ID_MAPPER (L"startCertManager", ID_CMD_CERT_MANAGER)
611       ID_MAPPER (L"btnCertManager", ID_BTN_CERTMANAGER)
612       ID_MAPPER (L"btnDecrypt", ID_BTN_DECRYPT)
613       ID_MAPPER (L"btnDecryptLarge", ID_BTN_DECRYPT_LARGE)
614       ID_MAPPER (L"btnEncrypt", ID_BTN_ENCRYPT)
615       ID_MAPPER (L"btnEncryptLarge", ID_BTN_ENCRYPT_LARGE)
616       ID_MAPPER (L"btnEncryptSmall", IDI_ENCRYPT_20_PNG)
617       ID_MAPPER (L"btnSignSmall", IDI_SIGN_20_PNG)
618       ID_MAPPER (L"btnSignEncryptLarge", IDI_SIGN_ENCRYPT_40_PNG)
619       ID_MAPPER (L"btnEncryptFileLarge", ID_BTN_ENCSIGN_LARGE)
620       ID_MAPPER (L"btnSignLarge", ID_BTN_SIGN_LARGE)
621       ID_MAPPER (L"btnVerifyLarge", ID_BTN_VERIFY_LARGE)
622       ID_MAPPER (L"btnSigstateLarge", ID_BTN_SIGSTATE_LARGE)
623       ID_MAPPER (L"encryptBody", ID_CMD_ENCRYPT_BODY)
624       ID_MAPPER (L"decryptBody", ID_CMD_DECRYPT_BODY)
625       ID_MAPPER (L"addEncSignedAttachment", ID_CMD_ATT_ENCSIGN_FILE)
626       ID_MAPPER (L"addEncAttachment", ID_CMD_ATT_ENC_FILE)
627       ID_MAPPER (L"signBody", ID_CMD_SIGN_BODY)
628       ID_MAPPER (L"verifyBody", ID_CMD_VERIFY_BODY)
629
630       /* MIME support: */
631       ID_MAPPER (L"encryptMime", ID_CMD_MIME_ENCRYPT)
632       ID_MAPPER (L"encryptMimeEx", ID_CMD_MIME_ENCRYPT_EX)
633       ID_MAPPER (L"signMime", ID_CMD_MIME_SIGN)
634       ID_MAPPER (L"signMimeEx", ID_CMD_MIME_SIGN_EX)
635       ID_MAPPER (L"encryptSignMime", ID_CMD_SIGN_ENCRYPT_MIME)
636       ID_MAPPER (L"encryptSignMimeEx", ID_CMD_SIGN_ENCRYPT_MIME_EX)
637       ID_MAPPER (L"getEncryptPressed", ID_GET_ENCRYPT_PRESSED)
638       ID_MAPPER (L"getEncryptPressedEx", ID_GET_ENCRYPT_PRESSED_EX)
639       ID_MAPPER (L"getSignPressed", ID_GET_SIGN_PRESSED)
640       ID_MAPPER (L"getSignPressedEx", ID_GET_SIGN_PRESSED_EX)
641       ID_MAPPER (L"getSignEncryptPressed", ID_GET_SIGN_ENCRYPT_PRESSED)
642       ID_MAPPER (L"getSignEncryptPressedEx", ID_GET_SIGN_ENCRYPT_PRESSED_EX)
643       ID_MAPPER (L"ribbonLoaded", ID_ON_LOAD)
644       ID_MAPPER (L"openOptions", ID_CMD_OPEN_OPTIONS)
645       ID_MAPPER (L"getSigLabel", ID_GET_SIG_LABEL)
646       ID_MAPPER (L"getSigSTip", ID_GET_SIG_STIP)
647       ID_MAPPER (L"getSigTip", ID_GET_SIG_TTIP)
648       ID_MAPPER (L"launchDetails", ID_LAUNCH_CERT_DETAILS)
649       ID_MAPPER (L"getIsCrypto", ID_GET_IS_CRYPTO)
650     }
651
652   if (cNames > 1)
653     {
654       log_debug ("More then one name provided. Should not happen");
655     }
656
657   return found ? S_OK : E_NOINTERFACE;
658 }
659
660 STDMETHODIMP
661 GpgolRibbonExtender::Invoke (DISPID dispid, REFIID riid, LCID lcid,
662                              WORD flags, DISPPARAMS *parms, VARIANT *result,
663                              EXCEPINFO *exepinfo, UINT *argerr)
664 {
665   USE_INVOKE_ARGS
666   log_debug ("%s:%s: enter with dispid: %x",
667              SRCNAME, __func__, (int)dispid);
668
669   if (!(flags & DISPATCH_METHOD))
670     {
671       log_debug ("%s:%s: not called in method mode. Bailing out.",
672                  SRCNAME, __func__);
673       return DISP_E_MEMBERNOTFOUND;
674     }
675
676   switch (dispid)
677     {
678       case ID_CMD_DECRYPT:
679         /* We can assume that this points to an implementation of
680            IRibbonControl as we know the callback dispid. */
681         return decryptAttachments (parms->rgvarg[0].pdispVal);
682       case ID_CMD_ENCRYPT_SELECTION:
683         return encryptSelection (parms->rgvarg[0].pdispVal);
684       case ID_CMD_DECRYPT_SELECTION:
685         return decryptSelection (parms->rgvarg[0].pdispVal);
686       case ID_CMD_CERT_MANAGER:
687         return startCertManager (parms->rgvarg[0].pdispVal);
688       case ID_CMD_ENCRYPT_BODY:
689         return encryptBody (parms->rgvarg[0].pdispVal);
690       case ID_CMD_DECRYPT_BODY:
691         return decryptBody (parms->rgvarg[0].pdispVal);
692       case ID_CMD_ATT_ENCSIGN_FILE:
693         return addEncSignedAttachment (parms->rgvarg[0].pdispVal);
694       case ID_CMD_ATT_ENC_FILE:
695         return addEncAttachment (parms->rgvarg[0].pdispVal);
696       case ID_CMD_SIGN_BODY:
697         return signBody (parms->rgvarg[0].pdispVal);
698       case ID_CMD_VERIFY_BODY:
699         return verifyBody (parms->rgvarg[0].pdispVal);
700       case ID_CMD_SIGN_ENCRYPT_MIME:
701         return mark_mime_action (parms->rgvarg[1].pdispVal,
702                                  OP_SIGN|OP_ENCRYPT, false);
703       case ID_CMD_SIGN_ENCRYPT_MIME_EX:
704         return mark_mime_action (parms->rgvarg[1].pdispVal,
705                                  OP_SIGN|OP_ENCRYPT, true);
706       case ID_CMD_MIME_ENCRYPT:
707         return mark_mime_action (parms->rgvarg[1].pdispVal, OP_ENCRYPT,
708
709                                  false);
710       case ID_CMD_MIME_SIGN:
711         return mark_mime_action (parms->rgvarg[1].pdispVal, OP_SIGN,
712                                  false);
713       case ID_GET_ENCRYPT_PRESSED:
714         return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_ENCRYPT,
715                                   result, false);
716       case ID_GET_SIGN_PRESSED:
717         return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_SIGN,
718                                   result, false);
719       case ID_GET_SIGN_ENCRYPT_PRESSED:
720         return get_crypt_pressed (parms->rgvarg[0].pdispVal,
721                                   OP_SIGN | OP_ENCRYPT,
722                                   result, false);
723       case ID_CMD_MIME_SIGN_EX:
724         return mark_mime_action (parms->rgvarg[1].pdispVal, OP_SIGN, true);
725       case ID_CMD_MIME_ENCRYPT_EX:
726         return mark_mime_action (parms->rgvarg[1].pdispVal, OP_ENCRYPT, true);
727       case ID_GET_ENCRYPT_PRESSED_EX:
728         return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_ENCRYPT,
729                                   result, true);
730       case ID_GET_SIGN_PRESSED_EX:
731         return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_SIGN,
732                                   result, true);
733       case ID_GET_SIGN_ENCRYPT_PRESSED_EX:
734         return get_crypt_pressed (parms->rgvarg[0].pdispVal, OP_SIGN | OP_ENCRYPT,
735                                   result, true);
736       case ID_GET_SIG_STIP:
737         return get_sig_stip (parms->rgvarg[0].pdispVal, result);
738       case ID_GET_SIG_TTIP:
739         return get_sig_ttip (parms->rgvarg[0].pdispVal, result);
740       case ID_GET_SIG_LABEL:
741         return get_sig_label (parms->rgvarg[0].pdispVal, result);
742       case ID_LAUNCH_CERT_DETAILS:
743         return launch_cert_details (parms->rgvarg[0].pdispVal);
744       case ID_GET_IS_CRYPTO:
745         return get_is_crypto (parms->rgvarg[0].pdispVal, result);
746
747       case ID_ON_LOAD:
748           {
749             g_ribbon_uis.push_back (parms->rgvarg[0].pdispVal);
750             return S_OK;
751           }
752       case ID_CMD_OPEN_OPTIONS:
753           {
754             options_dialog_box (NULL);
755             return S_OK;
756           }
757       case ID_BTN_CERTMANAGER:
758       case ID_BTN_ENCRYPT:
759       case ID_BTN_DECRYPT:
760       case ID_BTN_DECRYPT_LARGE:
761       case ID_BTN_ENCRYPT_LARGE:
762       case ID_BTN_ENCSIGN_LARGE:
763       case ID_BTN_SIGN_LARGE:
764       case ID_BTN_VERIFY_LARGE:
765       case IDI_SIGN_ENCRYPT_40_PNG:
766       case IDI_ENCRYPT_20_PNG:
767       case IDI_SIGN_20_PNG:
768         return getIcon (dispid, result);
769       case ID_BTN_SIGSTATE_LARGE:
770         return get_crypto_icon (parms->rgvarg[0].pdispVal, result);
771     }
772
773   log_debug ("%s:%s: leave", SRCNAME, __func__);
774
775   return DISP_E_MEMBERNOTFOUND;
776 }
777
778 /* Returns the XML markup for the various RibbonID's
779
780    The custom ui syntax is documented at:
781    http://msdn.microsoft.com/en-us/library/dd926139%28v=office.12%29.aspx
782
783    The outlook specific elements are documented at:
784    http://msdn.microsoft.com/en-us/library/office/ee692172%28v=office.14%29.aspx
785 */
786 static STDMETHODIMP
787 GetCustomUI_MIME (BSTR RibbonID, BSTR * RibbonXml)
788 {
789   char * buffer = NULL;
790
791 /*  const char *certManagerTTip =
792     _("Start the Certificate Management Software");
793   const char *certManagerSTip =
794     _("Open GPA or Kleopatra to manage your certificates. "
795       "You can use this you to generate your "
796       "own certificates. ");*/
797   const char *encryptTTip =
798     _("Encrypt the message.");
799   const char *encryptSTip =
800     _("Encrypts the message and all attachments before sending.");
801   const char *signTTip =
802     _("Sign the message.");
803   const char *signSTip =
804     _("Sign the message and all attachments before sending.");
805
806   const char *secureTTip =
807     _("Encrypt and sign the message.");
808   const char *secureSTip =
809     _("Encrypting and cryptographically signing a message means that the "
810       "recipient can be sure that no one modified the message and only the "
811       "recipients can read it.\nNot even the NSA.");
812   const char *optsSTip =
813     _("Open the settings dialog for GpgOL.");
814   log_debug ("%s:%s: GetCustomUI_MIME for id: %ls", SRCNAME, __func__, RibbonID);
815
816   if (!RibbonXml || !RibbonID)
817     return E_POINTER;
818
819   if (!wcscmp (RibbonID, L"Microsoft.Outlook.Mail.Compose"))
820     {
821       gpgrt_asprintf (&buffer,
822         "<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\""
823         " onLoad=\"ribbonLoaded\">"
824         " <ribbon>"
825         "   <tabs>"
826         "    <tab idMso=\"TabNewMailMessage\">"
827         "     <group id=\"general\""
828         "            label=\"%s\">"
829         "       <splitButton id=\"splitty\" size=\"large\">"
830         "         <toggleButton id=\"mimeSignEncrypt\""
831         "                 label=\"%s\""
832         "                 screentip=\"%s\""
833         "                 supertip=\"%s\""
834         "                 getPressed=\"getSignEncryptPressed\""
835         "                 getImage=\"btnSignEncryptLarge\""
836         "                 onAction=\"encryptSignMime\"""/>"
837         "         <menu id=\"encMenu\" showLabel=\"true\">"
838         "         <toggleButton id=\"mimeSign\""
839         "                 getImage=\"btnSignSmall\""
840         "                 label=\"%s\""
841         "                 screentip=\"%s\""
842         "                 supertip=\"%s\""
843         "                 onAction=\"signMime\""
844         "                 getPressed=\"getSignPressed\"/>"
845         "         <toggleButton id=\"mimeEncrypt\""
846         "                 getImage=\"btnEncryptSmall\""
847         "                 label=\"%s\""
848         "                 screentip=\"%s\""
849         "                 supertip=\"%s\""
850         "                 onAction=\"encryptMime\""
851         "                 getPressed=\"getEncryptPressed\"/>"
852         "       </menu></splitButton>"
853         "       <dialogBoxLauncher>"
854         "         <button id=\"optsBtn\""
855         "                 onAction=\"openOptions\""
856         "                 screentip=\"%s\"/>"
857         "       </dialogBoxLauncher>"
858         "     </group>"
859         "    </tab>"
860         "   </tabs>"
861         " </ribbon>"
862         "</customUI>", _("GpgOL"),
863         _("Sign Encrypt"), secureTTip, secureSTip,
864         _("Sign"), signTTip, signSTip,
865         _("Encrypt"), encryptTTip, encryptSTip,
866         optsSTip
867         );
868     }
869   else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Mail.Read"))
870     {
871       gpgrt_asprintf (&buffer,
872         "<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\""
873         " onLoad=\"ribbonLoaded\">"
874         " <ribbon>"
875         "   <tabs>"
876         "    <tab idMso=\"TabReadMessage\">"
877         "     <group id=\"general\""
878         "            label=\"%s\">"
879         "       <button id=\"idSigned\""
880         "               getImage=\"btnSigstateLarge\""
881         "               size=\"large\""
882         "               getLabel=\"getSigLabel\""
883         "               getScreentip=\"getSigTip\""
884         "               getSupertip=\"getSigSTip\""
885         "               onAction=\"launchDetails\""
886         "               getEnabled=\"getIsCrypto\"/>"
887         "       <dialogBoxLauncher>"
888         "         <button id=\"optsBtn\""
889         "                 onAction=\"openOptions\""
890         "                 screentip=\"%s\"/>"
891         "       </dialogBoxLauncher>"
892         "     </group>"
893         "    </tab>"
894         "   </tabs>"
895         " </ribbon>"
896         "<contextMenus>"
897         " <contextMenu idMso=\"ContextMenuAttachments\">"
898         "   <button id=\"gpgol_decrypt\""
899         "           label=\"%s\""
900         "           getImage=\"btnDecrypt\""
901         "           onAction=\"attachmentDecryptCallback\"/>"
902         " </contextMenu>"
903         "</contextMenus>"
904         "</customUI>",
905         _("GpgOL"),
906         optsSTip,
907         _("Decrypt")
908         );
909     }
910   /* We don't use this code currently because calling the send
911      event for Inline Response mailitems fails. */
912   else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Explorer"))
913     {
914       gpgrt_asprintf (&buffer,
915         "<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\""
916         " onLoad=\"ribbonLoaded\">"
917         " <ribbon>"
918         "   <tabs>"
919         "    <tab idMso=\"TabMail\">"
920         "     <group id=\"general_read\""
921         "            label=\"%s\">"
922         "       <button id=\"idSigned\""
923         "               getImage=\"btnSigstateLarge\""
924         "               size=\"large\""
925         "               getLabel=\"getSigLabel\""
926         "               getScreentip=\"getSigTip\""
927         "               getSupertip=\"getSigSTip\""
928         "               onAction=\"launchDetails\""
929         "               getEnabled=\"getIsCrypto\"/>"
930         "       <dialogBoxLauncher>"
931         "         <button id=\"optsBtn_read\""
932         "                 onAction=\"openOptions\""
933         "                 screentip=\"%s\"/>"
934         "       </dialogBoxLauncher>"
935         "     </group>"
936         "    </tab>"
937         "   </tabs>"
938         "   <contextualTabs>"
939         "   <tabSet idMso=\"TabComposeTools\">"
940         "    <tab idMso=\"TabMessage\">"
941         "     <group id=\"general\""
942         "            label=\"%s\">"
943         "       <splitButton id=\"splitty\" size=\"large\">"
944         "         <toggleButton id=\"mimeSignEncrypt\""
945         "                 label=\"%s\""
946         "                 screentip=\"%s\""
947         "                 supertip=\"%s\""
948         "                 getPressed=\"getSignEncryptPressedEx\""
949         "                 getImage=\"btnSignEncryptLarge\""
950         "                 onAction=\"encryptSignMimeEx\"""/>"
951         "         <menu id=\"encMenu\" showLabel=\"true\">"
952         "         <toggleButton id=\"mimeSign\""
953         "                 getImage=\"btnSignSmall\""
954         "                 label=\"%s\""
955         "                 screentip=\"%s\""
956         "                 supertip=\"%s\""
957         "                 onAction=\"signMimeEx\""
958         "                 getPressed=\"getSignPressedEx\"/>"
959         "         <toggleButton id=\"mimeEncrypt\""
960         "                 getImage=\"btnEncryptSmall\""
961         "                 label=\"%s\""
962         "                 screentip=\"%s\""
963         "                 supertip=\"%s\""
964         "                 onAction=\"encryptMimeEx\""
965         "                 getPressed=\"getEncryptPressedEx\"/>"
966         "       </menu></splitButton>"
967         "       <dialogBoxLauncher>"
968         "         <button id=\"optsBtn\""
969         "                 onAction=\"openOptions\""
970         "                 screentip=\"%s\"/>"
971         "       </dialogBoxLauncher>"
972         "      </group>"
973         "    </tab>"
974         "   </tabSet>"
975         "   </contextualTabs>"
976         " </ribbon>"
977         "</customUI>",
978         _("GpgOL"),
979         optsSTip,
980         _("GpgOL"),
981         _("Sign Encrypt"), secureTTip, secureSTip,
982         _("Sign"), signTTip, signSTip,
983         _("Encrypt"), encryptTTip, encryptSTip,
984         optsSTip
985         );
986     }
987
988   if (buffer)
989     {
990       wchar_t *wbuf = utf8_to_wchar2 (buffer, strlen(buffer));
991       xfree (buffer);
992       *RibbonXml = SysAllocString (wbuf);
993       xfree (wbuf);
994     }
995   else
996     *RibbonXml = NULL;
997
998   return S_OK;
999 }
1000
1001 /* This is the old pre-mime adding UI code. It will be removed once we have a
1002    stable version that can also send mime messages.
1003 */
1004 static STDMETHODIMP
1005 GetCustomUI_old (BSTR RibbonID, BSTR * RibbonXml)
1006 {
1007   char *buffer = NULL;
1008   const char *certManagerTTip =
1009     _("Start the Certificate Management Software");
1010   const char *certManagerSTip =
1011     _("Open GPA or Kleopatra to manage your certificates. "
1012       "You can use this you to generate your "
1013       "own certificates. ");
1014   const char *encryptTextTTip =
1015     _("Encrypt the text of the message");
1016   const char *encryptTextSTip =
1017     _("Choose the certificates for which the message "
1018       "should be encrypted and replace the text "
1019       "with the encrypted message.");
1020   const char *encryptFileTTip =
1021     _("Add a file as an encrypted attachment");
1022   const char *encryptFileSTip =
1023     _("Encrypts a file and adds it as an attachment to the "
1024       "message. ");
1025   const char *encryptSignFileTTip =
1026     _("Add a file as an encrypted attachment with a signature");
1027   const char *encryptSignFileSTip =
1028     _("Encrypts a file, signs it and adds both the encrypted file "
1029       "and the signature as attachments to the message. ");
1030   const char *decryptTextTTip=
1031     _("Decrypt the message");
1032   const char *decryptTextSTip =
1033     _("Look for PGP or S/MIME encrypted data in the message text "
1034       "and decrypt it.");
1035   const char *signTextTTip =
1036     _("Add a signature of the message");
1037   const char *signTextSTip =
1038     _("Appends a signed copy of the message text in an opaque signature. "
1039       "An opaque signature ensures that the signed text is not modified by "
1040       "embedding it in the signature itself. "
1041       "The combination of the signed message text and your signature is "
1042       "added below the plain text. "
1043       "The message will not be encrypted!");
1044   const char *optsSTip =
1045     _("Open the settings dialog for GpgOL.");
1046
1047   log_debug ("%s:%s: GetCustomUI for id: %ls", SRCNAME, __func__, RibbonID);
1048
1049   if (!RibbonXml)
1050     return E_POINTER;
1051
1052   if (!wcscmp (RibbonID, L"Microsoft.Outlook.Mail.Compose"))
1053     {
1054       gpgrt_asprintf (&buffer,
1055         "<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\""
1056         " onLoad=\"ribbonLoaded\">"
1057         " <ribbon>"
1058         "   <tabs>"
1059         "    <tab id=\"gpgolTab\""
1060         "         label=\"%s\">"
1061         "     <group id=\"general\""
1062         "            label=\"%s\">"
1063         "       <button id=\"CustomButton\""
1064         "               getImage=\"btnCertManager\""
1065         "               size=\"large\""
1066         "               label=\"%s\""
1067         "               screentip=\"%s\""
1068         "               supertip=\"%s\""
1069         "               onAction=\"startCertManager\"/>"
1070         "       <dialogBoxLauncher>"
1071         "         <button id=\"optsBtn\""
1072         "                 onAction=\"openOptions\""
1073         "                 screentip=\"%s\"/>"
1074         "       </dialogBoxLauncher>"
1075         "     </group>"
1076         "     <group id=\"textGroup\""
1077         "            label=\"%s\">"
1078         "       <button id=\"fullTextEncrypt\""
1079         "               getImage=\"btnEncryptLarge\""
1080         "               size=\"large\""
1081         "               label=\"%s\""
1082         "               screentip=\"%s\""
1083         "               supertip=\"%s\""
1084         "               onAction=\"encryptBody\"/>"
1085         "       <button id=\"fullTextDecrypt\""
1086         "               getImage=\"btnDecryptLarge\""
1087         "               size=\"large\""
1088         "               label=\"%s\""
1089         "               screentip=\"%s\""
1090         "               supertip=\"%s\""
1091         "               onAction=\"decryptBody\"/>"
1092         "       <button id=\"fullTextSign\""
1093         "               getImage=\"btnSignLarge\""
1094         "               size=\"large\""
1095         "               label=\"%s\""
1096         "               screentip=\"%s\""
1097         "               supertip=\"%s\""
1098         "               onAction=\"signBody\"/>"
1099         "       <button id=\"fullTextVerify\""
1100         "               getImage=\"btnVerifyLarge\""
1101         "               size=\"large\""
1102         "               label=\"%s\""
1103         "               onAction=\"verifyBody\"/>"
1104         "     </group>"
1105         "     <group id=\"attachmentGroup\""
1106         "            label=\"%s\">"
1107         "       <button id=\"encryptedFile\""
1108         "               getImage=\"btnEncryptLarge\""
1109         "               size=\"large\""
1110         "               label=\"%s\""
1111         "               screentip=\"%s\""
1112         "               supertip=\"%s\""
1113         "               onAction=\"addEncAttachment\"/>"
1114         "       <button id=\"encryptSignFile\""
1115         "               getImage=\"btnEncryptFileLarge\""
1116         "               size=\"large\""
1117         "               label=\"%s\""
1118         "               screentip=\"%s\""
1119         "               supertip=\"%s\""
1120         "               onAction=\"addEncSignedAttachment\"/>"
1121         "     </group>"
1122         "    </tab>"
1123         "   </tabs>"
1124         " </ribbon>"
1125         " <contextMenus>"
1126         "  <contextMenu idMso=\"ContextMenuText\">"
1127         "    <button id=\"encryptButton\""
1128         "            label=\"%s\""
1129         "            getImage=\"btnEncrypt\""
1130         "            onAction=\"encryptSelection\"/>"
1131         "    <button id=\"decryptButton\""
1132         "            label=\"%s\""
1133         "            getImage=\"btnDecrypt\""
1134         "            onAction=\"decryptSelection\"/>"
1135         " </contextMenu>"
1136         "</contextMenus>"
1137         "</customUI>", _("GpgOL"), _("General"),
1138         _("Start Certificate Manager"), certManagerTTip, certManagerSTip,
1139         optsSTip,
1140         _("Textbody"),
1141         _("Encrypt"), encryptTextTTip, encryptTextSTip,
1142         _("Decrypt"), decryptTextTTip, decryptTextSTip,
1143         _("Sign"), signTextTTip, signTextSTip,
1144         _("Verify"),
1145         _("Attachments"),
1146         _("Encrypted file"), encryptFileTTip, encryptFileSTip,
1147         _("Encrypted file and Signature"), encryptSignFileTTip, encryptSignFileSTip,
1148         _("Encrypt"), _("Decrypt")
1149         );
1150     }
1151   else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Mail.Read"))
1152     {
1153       gpgrt_asprintf (&buffer,
1154         "<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\">"
1155         " <ribbon>"
1156         "   <tabs>"
1157         "    <tab id=\"gpgolTab\""
1158         "         label=\"%s\">"
1159         "     <group id=\"general\""
1160         "            label=\"%s\">"
1161         "       <button id=\"CustomButton\""
1162         "               getImage=\"btnCertManager\""
1163         "               size=\"large\""
1164         "               label=\"%s\""
1165         "               screentip=\"%s\""
1166         "               supertip=\"%s\""
1167         "               onAction=\"startCertManager\"/>"
1168         "       <dialogBoxLauncher>"
1169         "         <button id=\"optsBtn\""
1170         "                 onAction=\"openOptions\""
1171         "                 screentip=\"%s\"/>"
1172         "       </dialogBoxLauncher>"
1173         "     </group>"
1174         "     <group id=\"textGroup\""
1175         "            label=\"%s\">"
1176         "       <button id=\"fullTextDecrypt\""
1177         "               getImage=\"btnDecryptLarge\""
1178         "               size=\"large\""
1179         "               label=\"%s\""
1180         "               screentip=\"%s\""
1181         "               supertip=\"%s\""
1182         "               onAction=\"decryptBody\"/>"
1183         "       <button id=\"fullTextVerify\""
1184         "               getImage=\"btnVerifyLarge\""
1185         "               size=\"large\""
1186         "               label=\"%s\""
1187         "               onAction=\"verifyBody\"/>"
1188         "     </group>"
1189         "    </tab>"
1190         "   </tabs>"
1191         "  <contextualTabs>"
1192         "    <tabSet idMso=\"TabSetAttachments\">"
1193         "        <tab idMso=\"TabAttachments\">"
1194         "            <group label=\"%s\" id=\"gnupgLabel\">"
1195         "                <button id=\"gpgol_contextual_decrypt\""
1196         "                    size=\"large\""
1197         "                    label=\"%s\""
1198         "                    getImage=\"btnDecryptLarge\""
1199         "                    onAction=\"attachmentDecryptCallback\" />"
1200         "            </group>"
1201         "        </tab>"
1202         "    </tabSet>"
1203         "  </contextualTabs>"
1204         " </ribbon>"
1205         "<contextMenus>"
1206         "<contextMenu idMso=\"ContextMenuReadOnlyMailText\">"
1207         "   <button id=\"decryptReadButton\""
1208         "           label=\"%s\""
1209         "           getImage=\"btnDecrypt\""
1210         "           onAction=\"decryptSelection\"/>"
1211         " </contextMenu>"
1212         " <contextMenu idMso=\"ContextMenuAttachments\">"
1213         "   <button id=\"gpgol_decrypt\""
1214         "           label=\"%s\""
1215         "           getImage=\"btnDecrypt\""
1216         "           onAction=\"attachmentDecryptCallback\"/>"
1217         " </contextMenu>"
1218         "</contextMenus>"
1219         "</customUI>",
1220         _("GpgOL"), _("General"),
1221         _("Start Certificate Manager"), certManagerTTip, certManagerSTip,
1222         optsSTip,
1223         _("Textbody"),
1224         _("Decrypt"), decryptTextTTip, decryptTextSTip,
1225         _("Verify"),
1226         _("GpgOL"), _("Save and decrypt"),
1227         _("Decrypt"),
1228         _("Decrypt"));
1229     }
1230   else if (!wcscmp (RibbonID, L"Microsoft.Outlook.Explorer"))
1231     {
1232       gpgrt_asprintf (&buffer,
1233         "<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\">"
1234         " <ribbon>"
1235         "   <tabs>"
1236         "    <tab id=\"gpgolTab\""
1237         "         label=\"%s\">"
1238         "     <group id=\"general\""
1239         "            label=\"%s\">"
1240         "       <button id=\"CustomButton\""
1241         "               getImage=\"btnCertManager\""
1242         "               size=\"large\""
1243         "               label=\"%s\""
1244         "               screentip=\"%s\""
1245         "               supertip=\"%s\""
1246         "               onAction=\"startCertManager\"/>"
1247         "       <dialogBoxLauncher>"
1248         "         <button id=\"optsBtn\""
1249         "                 onAction=\"openOptions\""
1250         "                 screentip=\"%s\"/>"
1251         "       </dialogBoxLauncher>"
1252         "     </group>"
1253         /* This would be totally nice but Outlook
1254            saves the decrypted text aftewards automatically.
1255            Yay,..
1256         "     <group id=\"textGroup\""
1257         "            label=\"%s\">"
1258         "       <button id=\"fullTextDecrypt\""
1259         "               getImage=\"btnDecryptLarge\""
1260         "               size=\"large\""
1261         "               label=\"%s\""
1262         "               onAction=\"decryptBody\"/>"
1263         "     </group>"
1264         */
1265         "    </tab>"
1266         "   </tabs>"
1267         "  <contextualTabs>"
1268         "    <tabSet idMso=\"TabSetAttachments\">"
1269         "        <tab idMso=\"TabAttachments\">"
1270         "            <group label=\"%s\" id=\"gnupgLabel\">"
1271         "                <button id=\"gpgol_contextual_decrypt\""
1272         "                    size=\"large\""
1273         "                    label=\"%s\""
1274         "                    getImage=\"btnDecryptLarge\""
1275         "                    onAction=\"attachmentDecryptCallback\" />"
1276         "            </group>"
1277         "        </tab>"
1278         "    </tabSet>"
1279         "  </contextualTabs>"
1280         " </ribbon>"
1281         " <contextMenus>"
1282         /*
1283            There appears to be no way to access the word editor
1284            / get the selected text from that Context.
1285         " <contextMenu idMso=\"ContextMenuReadOnlyMailText\">"
1286         " <button id=\"decryptReadButton1\""
1287         "         label=\"%s\""
1288         "         onAction=\"decryptSelection\"/>"
1289         " </contextMenu>"
1290         */
1291         " <contextMenu idMso=\"ContextMenuAttachments\">"
1292         "   <button id=\"gpgol_decrypt\""
1293         "           label=\"%s\""
1294         "           getImage=\"btnDecrypt\""
1295         "           onAction=\"attachmentDecryptCallback\"/>"
1296         " </contextMenu>"
1297         " </contextMenus>"
1298         "</customUI>",
1299         _("GpgOL"), _("General"),
1300         _("Start Certificate Manager"), certManagerTTip, certManagerSTip,
1301         optsSTip,
1302         /*_("Mail Body"), _("Decrypt"),*/
1303         _("GpgOL"), _("Save and decrypt"),/*_("Decrypt"), */
1304         _("Save and decrypt"));
1305     }
1306
1307   if (buffer)
1308     {
1309       wchar_t *wbuf = utf8_to_wchar2 (buffer, strlen(buffer));
1310       xfree (buffer);
1311       *RibbonXml = SysAllocString (wbuf);
1312       xfree (wbuf);
1313     }
1314   else
1315     *RibbonXml = NULL;
1316
1317   return S_OK;
1318 }
1319
1320 STDMETHODIMP
1321 GpgolRibbonExtender::GetCustomUI (BSTR RibbonID, BSTR * RibbonXml)
1322 {
1323   if (use_mime_ui)
1324     {
1325       return GetCustomUI_MIME (RibbonID, RibbonXml);
1326     }
1327   else
1328     {
1329       return GetCustomUI_old (RibbonID, RibbonXml);
1330     }
1331 }
1332
1333 /* RibbonUi elements are created on demand but they are reused
1334    in different inspectors. So far and from all documentation
1335    I could find RibbonUi elments are never
1336    deleted. When they are created the onLoad callback is called
1337    to register them.
1338    The callbacks registered in the XML description are only
1339    executed on Load. So to have different information depending
1340    on the available mails we have to invalidate the UI ourself.
1341    This means that the callbacks will be reevaluated and the UI
1342    Updated. Sadly we don't know which ribbon_ui needs updates
1343    so we have to invalidate everything.
1344 */
1345 void gpgoladdin_invalidate_ui ()
1346 {
1347   std::list<LPDISPATCH>::iterator it;
1348
1349   for (it = g_ribbon_uis.begin(); it != g_ribbon_uis.end(); ++it)
1350     {
1351       log_debug ("%s:%s: Invalidating ribbon: %p",
1352                  SRCNAME, __func__, *it);
1353       invoke_oom_method (*it, "Invalidate", NULL);
1354     }
1355 }
1356
1357 GpgolAddin *
1358 GpgolAddin::get_instance ()
1359 {
1360   if (!addin_instance)
1361     {
1362       addin_instance = new GpgolAddin ();
1363     }
1364   return addin_instance;
1365 }