Add ref in get_strong_reference
[gpgol.git] / src / oomhelp.cpp
1 /* oomhelp.cpp - Helper functions for the Outlook Object Model
2  * Copyright (C) 2009 g10 Code GmbH
3  * Copyright (C) 2015 by Bundesamt für Sicherheit in der Informationstechnik
4  * Software engineering by Intevation GmbH
5  * 
6  * This file is part of GpgOL.
7  * 
8  * GpgOL is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  * 
13  * GpgOL is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  * 
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <windows.h>
27 #include <olectl.h>
28 #include <string>
29 #include <rpc.h>
30
31 #include "common.h"
32
33 #include "oomhelp.h"
34 #include "gpgoladdin.h"
35
36 HRESULT
37 gpgol_queryInterface (LPUNKNOWN pObj, REFIID riid, LPVOID FAR *ppvObj)
38 {
39   HRESULT ret = pObj->QueryInterface (riid, ppvObj);
40   if ((opt.enable_debug & DBG_OOM_EXTRA) && *ppvObj)
41     {
42       memdbg_addRef (*ppvObj);
43     }
44   return ret;
45 }
46
47 HRESULT
48 gpgol_openProperty (LPMAPIPROP obj, ULONG ulPropTag, LPCIID lpiid,
49                     ULONG ulInterfaceOptions, ULONG ulFlags,
50                     LPUNKNOWN FAR * lppUnk)
51 {
52   HRESULT ret = obj->OpenProperty (ulPropTag, lpiid,
53                                    ulInterfaceOptions, ulFlags,
54                                    lppUnk);
55   if ((opt.enable_debug & DBG_OOM_EXTRA) && *lppUnk)
56     {
57       memdbg_addRef (*lppUnk);
58       log_debug ("%s:%s: OpenProperty on %p prop %lx result %p",
59                  SRCNAME, __func__, obj,  ulPropTag, *lppUnk);
60     }
61   return ret;
62 }
63 /* Return a malloced string with the utf-8 encoded name of the object
64    or NULL if not available.  */
65 char *
66 get_object_name (LPUNKNOWN obj)
67 {
68   HRESULT hr;
69   LPDISPATCH disp = NULL;
70   LPTYPEINFO tinfo = NULL;
71   BSTR bstrname;
72   char *name = NULL;
73
74   if (!obj)
75     goto leave;
76
77   /* We can't use gpgol_queryInterface here to avoid recursion */
78   obj->QueryInterface (IID_IDispatch, (void **)&disp);
79   if (!disp)
80     goto leave;
81
82   disp->GetTypeInfo (0, 0, &tinfo);
83   if (!tinfo)
84     {
85       log_debug ("%s:%s: no typeinfo found for object\n", 
86                  SRCNAME, __func__);
87       goto leave;
88     }
89
90   bstrname = NULL;
91   hr = tinfo->GetDocumentation (MEMBERID_NIL, &bstrname, 0, 0, 0);
92   if (hr || !bstrname)
93     log_debug ("%s:%s: GetDocumentation failed: hr=%#lx\n", 
94                SRCNAME, __func__, hr);
95   if (bstrname)
96     {
97       name = wchar_to_utf8 (bstrname);
98       SysFreeString (bstrname);
99     }
100
101  leave:
102   if (tinfo)
103     tinfo->Release ();
104   if (disp)
105     disp->Release ();
106
107   return name;
108 }
109
110
111 /* Lookup the dispid of object PDISP for member NAME.  Returns
112    DISPID_UNKNOWN on error.  */
113 DISPID
114 lookup_oom_dispid (LPDISPATCH pDisp, const char *name)
115 {
116   HRESULT hr;
117   DISPID dispid;
118   wchar_t *wname;
119
120   if (!pDisp || !name)
121     return DISPID_UNKNOWN; /* Error: Invalid arg.  */
122
123   wname = utf8_to_wchar (name);
124   if (!wname)
125     return DISPID_UNKNOWN;/* Error:  Out of memory.  */
126
127   hr = pDisp->GetIDsOfNames (IID_NULL, &wname, 1, 
128                              LOCALE_SYSTEM_DEFAULT, &dispid);
129   xfree (wname);
130   if (hr != S_OK || dispid == DISPID_UNKNOWN)
131     log_debug ("%s:%s: error looking up dispid(%s)=%d: hr=0x%x\n",
132                SRCNAME, __func__, name, (int)dispid, (unsigned int)hr);
133   if (hr != S_OK)
134     dispid = DISPID_UNKNOWN;
135
136   return dispid;
137 }
138
139 static void
140 init_excepinfo (EXCEPINFO *err)
141 {
142   if (!err)
143     {
144       return;
145     }
146   err->wCode = 0;
147   err->wReserved = 0;
148   err->bstrSource = nullptr;
149   err->bstrDescription = nullptr;
150   err->bstrHelpFile = nullptr;
151   err->dwHelpContext = 0;
152   err->pvReserved = nullptr;
153   err->pfnDeferredFillIn = nullptr;
154   err->scode = 0;
155 }
156
157 void
158 dump_excepinfo (EXCEPINFO err)
159 {
160   log_debug ("%s:%s: Exception: \n"
161              "              wCode: 0x%x\n"
162              "              wReserved: 0x%x\n"
163              "              source: %S\n"
164              "              desc: %S\n"
165              "              help: %S\n"
166              "              helpCtx: 0x%x\n"
167              "              deferredFill: %p\n"
168              "              scode: 0x%x\n",
169              SRCNAME, __func__, (unsigned int) err.wCode,
170              (unsigned int) err.wReserved,
171              err.bstrSource ? err.bstrSource : L"null",
172              err.bstrDescription ? err.bstrDescription : L"null",
173              err.bstrHelpFile ? err.bstrDescription : L"null",
174              (unsigned int) err.dwHelpContext,
175              err.pfnDeferredFillIn,
176              (unsigned int) err.scode);
177 }
178
179 /* Return the OOM object's IDispatch interface described by FULLNAME.
180    Returns NULL if not found.  PSTART is the object where the search
181    starts.  FULLNAME is a dot delimited sequence of object names.  If
182    an object name has a "(foo)" suffix this passes it as a parameter
183    to the invoke function (i.e. using (DISPATCH|PROPERTYGET)).  Object
184    names including the optional suffix are truncated at 127 byte.  */
185 LPDISPATCH
186 get_oom_object (LPDISPATCH pStart, const char *fullname)
187 {
188   HRESULT hr;
189   LPDISPATCH pObj = pStart;
190   LPDISPATCH pDisp = NULL;
191
192   log_oom ("%s:%s: looking for %p->`%s'",
193            SRCNAME, __func__, pStart, fullname);
194
195   while (pObj)
196     {
197       DISPPARAMS dispparams;
198       VARIANT aVariant[4];
199       VARIANT vtResult;
200       wchar_t *wname;
201       char name[128];
202       int n_parms = 0;
203       BSTR parmstr = NULL;
204       INT  parmint = 0;
205       DISPID dispid;
206       char *p, *pend;
207       int dispmethod;
208       unsigned int argErr = 0;
209       EXCEPINFO execpinfo;
210
211       init_excepinfo (&execpinfo);
212
213       if (pDisp)
214         {
215           gpgol_release (pDisp);
216           pDisp = NULL;
217         }
218       if (gpgol_queryInterface (pObj, IID_IDispatch, (LPVOID*)&pDisp) != S_OK)
219         {
220           log_error ("%s:%s Object does not support IDispatch",
221                      SRCNAME, __func__);
222           gpgol_release (pObj);
223           return NULL;
224         }
225       /* Confirmed through testing that the retval needs a release */
226       if (pObj != pStart)
227         gpgol_release (pObj);
228       pObj = NULL;
229       if (!pDisp)
230         return NULL;  /* The object has no IDispatch interface.  */
231       if (!*fullname)
232         {
233           if ((opt.enable_debug & DBG_OOM))
234             {
235               pDisp->AddRef ();
236               int ref = pDisp->Release ();
237               log_oom ("%s:%s:         got %p with %i refs",
238                        SRCNAME, __func__, pDisp, ref);
239             }
240           return pDisp; /* Ready.  */
241         }
242       
243       /* Break out the next name part.  */
244       {
245         const char *dot;
246         size_t n;
247         
248         dot = strchr (fullname, '.');
249         if (dot == fullname)
250           {
251             gpgol_release (pDisp);
252             return NULL;  /* Empty name part: error.  */
253           }
254         else if (dot)
255           n = dot - fullname;
256         else
257           n = strlen (fullname);
258         
259         if (n >= sizeof name)
260           n = sizeof name - 1;
261         strncpy (name, fullname, n);
262         name[n] = 0;
263         
264         if (dot)
265           fullname = dot + 1;
266         else
267           fullname += strlen (fullname);
268       }
269       
270       if (!strncmp (name, "get_", 4) && name[4])
271         {
272           dispmethod = DISPATCH_PROPERTYGET;
273           memmove (name, name+4, strlen (name+4)+1);
274         }
275       else if ((p = strchr (name, '(')))
276         {
277           *p++ = 0;
278           pend = strchr (p, ')');
279           if (pend)
280             *pend = 0;
281
282           if (*p == ',' && p[1] != ',')
283             {
284               /* We assume this is "foo(,30007)".  I.e. the frst arg
285                  is not given and the second one is an integer.  */
286               parmint = (int)strtol (p+1, NULL, 10);
287               n_parms = 4;
288             }
289           else
290             {
291               wname = utf8_to_wchar (p);
292               if (wname)
293                 {
294                   parmstr = SysAllocString (wname);
295                   xfree (wname);
296                 }
297               if (!parmstr)
298                 {
299                   gpgol_release (pDisp);
300                   return NULL; /* Error:  Out of memory.  */
301                 }
302               n_parms = 1;
303             }
304           dispmethod = DISPATCH_METHOD|DISPATCH_PROPERTYGET;
305         }
306       else
307         dispmethod = DISPATCH_METHOD;
308
309       /* Lookup the dispid.  */
310       dispid = lookup_oom_dispid (pDisp, name);
311       if (dispid == DISPID_UNKNOWN)
312         {
313           if (parmstr)
314             SysFreeString (parmstr);
315           gpgol_release (pDisp);
316           return NULL;  /* Name not found.  */
317         }
318
319       /* Invoke the method.  */
320       dispparams.rgvarg = aVariant;
321       dispparams.cArgs = 0;
322       if (n_parms)
323         {
324           if (n_parms == 4)
325             {
326               dispparams.rgvarg[0].vt = VT_ERROR; 
327               dispparams.rgvarg[0].scode = DISP_E_PARAMNOTFOUND; 
328               dispparams.rgvarg[1].vt = VT_ERROR; 
329               dispparams.rgvarg[1].scode = DISP_E_PARAMNOTFOUND; 
330               dispparams.rgvarg[2].vt = VT_INT; 
331               dispparams.rgvarg[2].intVal = parmint; 
332               dispparams.rgvarg[3].vt = VT_ERROR; 
333               dispparams.rgvarg[3].scode = DISP_E_PARAMNOTFOUND; 
334               dispparams.cArgs = n_parms;
335             }
336           else if (n_parms == 1 && parmstr)
337             {
338               dispparams.rgvarg[0].vt = VT_BSTR;
339               dispparams.rgvarg[0].bstrVal = parmstr;
340               dispparams.cArgs++;
341             }
342         }
343       dispparams.cNamedArgs = 0;
344       VariantInit (&vtResult);
345       hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
346                           dispmethod, &dispparams,
347                           &vtResult, &execpinfo, &argErr);
348       if (parmstr)
349         SysFreeString (parmstr);
350       if (hr != S_OK || vtResult.vt != VT_DISPATCH)
351         {
352           log_debug ("%s:%s: failure: '%s' p=%p vt=%d hr=0x%x argErr=0x%x dispid=0x%x",
353                      SRCNAME, __func__,
354                      name, vtResult.pdispVal, vtResult.vt, (unsigned int)hr,
355                      (unsigned int)argErr, (unsigned int)dispid);
356           dump_excepinfo (execpinfo);
357           VariantClear (&vtResult);
358           gpgol_release (pDisp);
359           return NULL;  /* Invoke failed.  */
360         }
361
362       pObj = vtResult.pdispVal;
363       memdbg_addRef (pObj);
364     }
365   gpgol_release (pDisp);
366   log_debug ("%s:%s: no object", SRCNAME, __func__);
367   return NULL;
368 }
369
370
371 /* Helper for put_oom_icon.  */
372 static int
373 put_picture_or_mask (LPDISPATCH pDisp, int resource, int size, int is_mask)
374 {
375   HRESULT hr;
376   PICTDESC pdesc;
377   LPDISPATCH pPict;
378   DISPID dispid_put = DISPID_PROPERTYPUT;
379   UINT fuload;
380   DISPID dispid;
381   DISPPARAMS dispparams;
382   VARIANT aVariant[2];
383
384   /* When loading the mask we need to set the monochrome flag.  We
385      better create a DIB section to avoid possible rendering
386      problems.  */
387   fuload = LR_CREATEDIBSECTION | LR_SHARED;
388   if (is_mask)
389     fuload |= LR_MONOCHROME;
390   
391   memset (&pdesc, 0, sizeof pdesc);
392   pdesc.cbSizeofstruct = sizeof pdesc;
393   pdesc.picType = PICTYPE_BITMAP;
394   pdesc.bmp.hbitmap = (HBITMAP) LoadImage (glob_hinst,
395                                            MAKEINTRESOURCE (resource),
396                                            IMAGE_BITMAP, size, size, fuload);
397   if (!pdesc.bmp.hbitmap)
398     {
399       log_error_w32 (-1, "%s:%s: LoadImage(%d) failed\n", 
400                      SRCNAME, __func__, resource);
401       return -1;
402     }
403
404   /* Wrap the image into an OLE object.  */
405   hr = OleCreatePictureIndirect (&pdesc, IID_IPictureDisp, 
406                                  TRUE, (void **) &pPict);
407   if (hr != S_OK || !pPict)
408     {
409       log_error ("%s:%s: OleCreatePictureIndirect failed: hr=%#lx\n",
410                  SRCNAME, __func__, hr);
411       return -1;
412     }
413         
414   /* Store to the Picture or Mask property of the CommandBarButton.  */
415   dispid = lookup_oom_dispid (pDisp, is_mask? "Mask":"Picture");
416
417   dispparams.rgvarg = aVariant;
418   dispparams.rgvarg[0].vt = VT_DISPATCH;
419   dispparams.rgvarg[0].pdispVal = pPict;
420   dispparams.cArgs = 1;
421   dispparams.rgdispidNamedArgs = &dispid_put;
422   dispparams.cNamedArgs = 1;
423   hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
424                       DISPATCH_PROPERTYPUT, &dispparams,
425                       NULL, NULL, NULL);
426   if (hr != S_OK)
427     {
428       log_debug ("%s:%s: Putting icon failed: %#lx", SRCNAME, __func__, hr);
429       return -1;
430     }
431   return 0;
432 }
433
434
435 /* Update the icon of PDISP using the bitmap with RESOURCE ID.  The
436    function adds the system pixel size to the resource id to compute
437    the actual icon size.  The resource id of the mask is the N+1.  */
438 int
439 put_oom_icon (LPDISPATCH pDisp, int resource_id, int size)
440 {
441   int rc;
442
443   /* This code is only relevant for Outlook < 2010.
444     Ideally it should grab the system pixel size and use an
445     icon of the appropiate size (e.g. 32 or 64px)
446   */
447
448   rc = put_picture_or_mask (pDisp, resource_id, size, 0);
449   if (!rc)
450     rc = put_picture_or_mask (pDisp, resource_id + 1, size, 1);
451
452   return rc;
453 }
454
455
456 /* Set the boolean property NAME to VALUE.  */
457 int
458 put_oom_bool (LPDISPATCH pDisp, const char *name, int value)
459 {
460   HRESULT hr;
461   DISPID dispid_put = DISPID_PROPERTYPUT;
462   DISPID dispid;
463   DISPPARAMS dispparams;
464   VARIANT aVariant[1];
465
466   dispid = lookup_oom_dispid (pDisp, name);
467   if (dispid == DISPID_UNKNOWN)
468     return -1;
469
470   dispparams.rgvarg = aVariant;
471   dispparams.rgvarg[0].vt = VT_BOOL;
472   dispparams.rgvarg[0].boolVal = value? VARIANT_TRUE:VARIANT_FALSE;
473   dispparams.cArgs = 1;
474   dispparams.rgdispidNamedArgs = &dispid_put;
475   dispparams.cNamedArgs = 1;
476   hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
477                       DISPATCH_PROPERTYPUT, &dispparams,
478                       NULL, NULL, NULL);
479   if (hr != S_OK)
480     {
481       log_debug ("%s:%s: Putting '%s' failed: %#lx", 
482                  SRCNAME, __func__, name, hr);
483       return -1;
484     }
485   return 0;
486 }
487
488
489 /* Set the property NAME to VALUE.  */
490 int
491 put_oom_int (LPDISPATCH pDisp, const char *name, int value)
492 {
493   HRESULT hr;
494   DISPID dispid_put = DISPID_PROPERTYPUT;
495   DISPID dispid;
496   DISPPARAMS dispparams;
497   VARIANT aVariant[1];
498
499   dispid = lookup_oom_dispid (pDisp, name);
500   if (dispid == DISPID_UNKNOWN)
501     return -1;
502
503   dispparams.rgvarg = aVariant;
504   dispparams.rgvarg[0].vt = VT_INT;
505   dispparams.rgvarg[0].intVal = value;
506   dispparams.cArgs = 1;
507   dispparams.rgdispidNamedArgs = &dispid_put;
508   dispparams.cNamedArgs = 1;
509   hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
510                       DISPATCH_PROPERTYPUT, &dispparams,
511                       NULL, NULL, NULL);
512   if (hr != S_OK)
513     {
514       log_debug ("%s:%s: Putting '%s' failed: %#lx", 
515                  SRCNAME, __func__, name, hr);
516       return -1;
517     }
518   return 0;
519 }
520
521
522 /* Set the property NAME to STRING.  */
523 int
524 put_oom_string (LPDISPATCH pDisp, const char *name, const char *string)
525 {
526   HRESULT hr;
527   DISPID dispid_put = DISPID_PROPERTYPUT;
528   DISPID dispid;
529   DISPPARAMS dispparams;
530   VARIANT aVariant[1];
531   BSTR bstring;
532   EXCEPINFO execpinfo;
533
534   init_excepinfo (&execpinfo);
535   dispid = lookup_oom_dispid (pDisp, name);
536   if (dispid == DISPID_UNKNOWN)
537     return -1;
538
539   {
540     wchar_t *tmp = utf8_to_wchar (string);
541     bstring = tmp? SysAllocString (tmp):NULL;
542     xfree (tmp);
543     if (!bstring)
544       {
545         log_error_w32 (-1, "%s:%s: SysAllocString failed", SRCNAME, __func__);
546         return -1;
547       }
548   }
549
550   dispparams.rgvarg = aVariant;
551   dispparams.rgvarg[0].vt = VT_BSTR;
552   dispparams.rgvarg[0].bstrVal = bstring;
553   dispparams.cArgs = 1;
554   dispparams.rgdispidNamedArgs = &dispid_put;
555   dispparams.cNamedArgs = 1;
556   hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
557                       DISPATCH_PROPERTYPUT, &dispparams,
558                       NULL, &execpinfo, NULL);
559   SysFreeString (bstring);
560   if (hr != S_OK)
561     {
562       log_debug ("%s:%s: Putting '%s' failed: %#lx", 
563                  SRCNAME, __func__, name, hr);
564       dump_excepinfo (execpinfo);
565       return -1;
566     }
567   return 0;
568 }
569
570 /* Set the property NAME to DISP.  */
571 int
572 put_oom_disp (LPDISPATCH pDisp, const char *name, LPDISPATCH disp)
573 {
574   HRESULT hr;
575   DISPID dispid_put = DISPID_PROPERTYPUT;
576   DISPID dispid;
577   DISPPARAMS dispparams;
578   VARIANT aVariant[1];
579   EXCEPINFO execpinfo;
580
581   init_excepinfo (&execpinfo);
582   dispid = lookup_oom_dispid (pDisp, name);
583   if (dispid == DISPID_UNKNOWN)
584     return -1;
585
586   dispparams.rgvarg = aVariant;
587   dispparams.rgvarg[0].vt = VT_DISPATCH;
588   dispparams.rgvarg[0].pdispVal = disp;
589   dispparams.cArgs = 1;
590   dispparams.rgdispidNamedArgs = &dispid_put;
591   dispparams.cNamedArgs = 1;
592   hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
593                       DISPATCH_PROPERTYPUTREF, &dispparams,
594                       NULL, &execpinfo, NULL);
595   if (hr != S_OK)
596     {
597       log_debug ("%s:%s: Putting '%s' failed: %#lx",
598                  SRCNAME, __func__, name, hr);
599       dump_excepinfo (execpinfo);
600       return -1;
601     }
602   return 0;
603 }
604
605 /* Get the boolean property NAME of the object PDISP.  Returns False if
606    not found or if it is not a boolean property.  */
607 int
608 get_oom_bool (LPDISPATCH pDisp, const char *name)
609 {
610   HRESULT hr;      
611   int result = 0;
612   DISPID dispid;
613   
614   dispid = lookup_oom_dispid (pDisp, name);
615   if (dispid != DISPID_UNKNOWN)
616     {
617       DISPPARAMS dispparams = {NULL, NULL, 0, 0};
618       VARIANT rVariant;
619
620       VariantInit (&rVariant);
621       hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
622                           DISPATCH_PROPERTYGET, &dispparams,
623                           &rVariant, NULL, NULL);
624       if (hr != S_OK)
625         log_debug ("%s:%s: Property '%s' not found: %#lx",
626                    SRCNAME, __func__, name, hr);
627       else if (rVariant.vt != VT_BOOL)
628         log_debug ("%s:%s: Property `%s' is not a boolean (vt=%d)",
629                    SRCNAME, __func__, name, rVariant.vt);
630       else
631         result = !!rVariant.boolVal;
632       VariantClear (&rVariant);
633     }
634
635   return result;
636 }
637
638
639 /* Get the integer property NAME of the object PDISP.  Returns 0 if
640    not found or if it is not an integer property.  */
641 int
642 get_oom_int (LPDISPATCH pDisp, const char *name)
643 {
644   HRESULT hr;      
645   int result = 0;
646   DISPID dispid;
647   
648   dispid = lookup_oom_dispid (pDisp, name);
649   if (dispid != DISPID_UNKNOWN)
650     {
651       DISPPARAMS dispparams = {NULL, NULL, 0, 0};
652       VARIANT rVariant;
653
654       VariantInit (&rVariant);
655       hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
656                           DISPATCH_PROPERTYGET, &dispparams,
657                           &rVariant, NULL, NULL);
658       if (hr != S_OK)
659         log_debug ("%s:%s: Property '%s' not found: %#lx",
660                    SRCNAME, __func__, name, hr);
661       else if (rVariant.vt != VT_INT && rVariant.vt != VT_I4)
662         log_debug ("%s:%s: Property `%s' is not an integer (vt=%d)",
663                    SRCNAME, __func__, name, rVariant.vt);
664       else
665         result = rVariant.intVal;
666       VariantClear (&rVariant);
667     }
668
669   return result;
670 }
671
672
673 /* Get the string property NAME of the object PDISP.  Returns NULL if
674    not found or if it is not a string property.  */
675 char *
676 get_oom_string (LPDISPATCH pDisp, const char *name)
677 {
678   HRESULT hr;      
679   char *result = NULL;
680   DISPID dispid;
681   
682   dispid = lookup_oom_dispid (pDisp, name);
683   if (dispid != DISPID_UNKNOWN)
684     {
685       DISPPARAMS dispparams = {NULL, NULL, 0, 0};
686       VARIANT rVariant;
687
688       VariantInit (&rVariant);
689       hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
690                           DISPATCH_PROPERTYGET, &dispparams,
691                           &rVariant, NULL, NULL);
692       if (hr != S_OK)
693         log_debug ("%s:%s: Property '%s' not found: %#lx",
694                    SRCNAME, __func__, name, hr);
695       else if (rVariant.vt != VT_BSTR)
696         log_debug ("%s:%s: Property `%s' is not a string (vt=%d)",
697                    SRCNAME, __func__, name, rVariant.vt);
698       else if (rVariant.bstrVal)
699         result = wchar_to_utf8 (rVariant.bstrVal);
700       VariantClear (&rVariant);
701     }
702
703   return result;
704 }
705
706
707 /* Get the object property NAME of the object PDISP.  Returns NULL if
708    not found or if it is not an object perty.  */
709 LPUNKNOWN
710 get_oom_iunknown (LPDISPATCH pDisp, const char *name)
711 {
712   HRESULT hr;      
713   DISPID dispid;
714   
715   dispid = lookup_oom_dispid (pDisp, name);
716   if (dispid != DISPID_UNKNOWN)
717     {
718       DISPPARAMS dispparams = {NULL, NULL, 0, 0};
719       VARIANT rVariant;
720
721       VariantInit (&rVariant);
722       hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
723                           DISPATCH_PROPERTYGET, &dispparams,
724                           &rVariant, NULL, NULL);
725       if (hr != S_OK)
726         log_debug ("%s:%s: Property '%s' not found: %#lx",
727                    SRCNAME, __func__, name, hr);
728       else if (rVariant.vt != VT_UNKNOWN)
729         log_debug ("%s:%s: Property `%s' is not of class IUnknown (vt=%d)",
730                    SRCNAME, __func__, name, rVariant.vt);
731       else
732         {
733           memdbg_addRef (rVariant.punkVal);
734           return rVariant.punkVal;
735         }
736
737       VariantClear (&rVariant);
738     }
739
740   return NULL;
741 }
742
743
744 /* Return the control object described by the tag property with value
745    TAG. The object POBJ must support the FindControl method.  Returns
746    NULL if not found.  */
747 LPDISPATCH
748 get_oom_control_bytag (LPDISPATCH pDisp, const char *tag)
749 {
750   HRESULT hr;      
751   DISPID dispid;
752   DISPPARAMS dispparams;
753   VARIANT aVariant[4];
754   VARIANT rVariant;
755   BSTR bstring;
756   LPDISPATCH result = NULL;
757
758   dispid = lookup_oom_dispid (pDisp, "FindControl");
759   if (dispid == DISPID_UNKNOWN)
760     {
761       log_debug ("%s:%s: Object %p has no FindControl method",
762                  SRCNAME, __func__, pDisp);
763       return NULL;
764     }
765
766   {
767     wchar_t *tmp = utf8_to_wchar (tag);
768     bstring = tmp? SysAllocString (tmp):NULL;
769     xfree (tmp);
770     if (!bstring)
771       {
772         log_error_w32 (-1, "%s:%s: SysAllocString failed", SRCNAME, __func__);
773         return NULL;
774       }
775   }
776   dispparams.rgvarg = aVariant;
777   dispparams.rgvarg[0].vt = VT_ERROR; /* Visible */
778   dispparams.rgvarg[0].scode = DISP_E_PARAMNOTFOUND; 
779   dispparams.rgvarg[1].vt = VT_BSTR;  /* Tag */
780   dispparams.rgvarg[1].bstrVal = bstring;
781   dispparams.rgvarg[2].vt = VT_ERROR; /* Id */
782   dispparams.rgvarg[2].scode = DISP_E_PARAMNOTFOUND;
783   dispparams.rgvarg[3].vt = VT_ERROR;/* Type */
784   dispparams.rgvarg[3].scode = DISP_E_PARAMNOTFOUND; 
785   dispparams.cArgs = 4;
786   dispparams.cNamedArgs = 0;
787   VariantInit (&rVariant);
788   hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
789                       DISPATCH_METHOD, &dispparams,
790                       &rVariant, NULL, NULL);
791   SysFreeString (bstring);
792   if (hr == S_OK && rVariant.vt == VT_DISPATCH && rVariant.pdispVal)
793     {
794       gpgol_queryInterface (rVariant.pdispVal, IID_IDispatch,
795                             (LPVOID*)&result);
796       gpgol_release (rVariant.pdispVal);
797       if (!result)
798         log_debug ("%s:%s: Object with tag `%s' has no dispatch intf.",
799                    SRCNAME, __func__, tag);
800     }
801   else
802     {
803       log_debug ("%s:%s: No object with tag `%s' found: vt=%d hr=%#lx",
804                  SRCNAME, __func__, tag, rVariant.vt, hr);
805       VariantClear (&rVariant);
806     }
807
808   return result;
809 }
810
811
812 /* Add a new button to an object which supports the add method.
813    Returns the new object or NULL on error.  */
814 LPDISPATCH
815 add_oom_button (LPDISPATCH pObj)
816 {
817   HRESULT hr;      
818   DISPID dispid;
819   DISPPARAMS dispparams;
820   VARIANT aVariant[5];
821   VARIANT rVariant;
822
823   dispid = lookup_oom_dispid (pObj, "Add");
824
825   dispparams.rgvarg = aVariant;
826   dispparams.rgvarg[0].vt = VT_BOOL;  /* Temporary */
827   dispparams.rgvarg[0].boolVal = VARIANT_TRUE;
828   dispparams.rgvarg[1].vt = VT_ERROR;  /* Before */
829   dispparams.rgvarg[1].scode = DISP_E_PARAMNOTFOUND; 
830   dispparams.rgvarg[2].vt = VT_ERROR;  /* Parameter */
831   dispparams.rgvarg[2].scode = DISP_E_PARAMNOTFOUND; 
832   dispparams.rgvarg[3].vt = VT_ERROR;  /* Id */
833   dispparams.rgvarg[3].scode = DISP_E_PARAMNOTFOUND; 
834   dispparams.rgvarg[4].vt = VT_INT;    /* Type */
835   dispparams.rgvarg[4].intVal = MSOCONTROLBUTTON;
836   dispparams.cArgs = 5;
837   dispparams.cNamedArgs = 0;
838   VariantInit (&rVariant);
839   hr = pObj->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
840                      DISPATCH_METHOD, &dispparams,
841                      &rVariant, NULL, NULL);
842   if (hr != S_OK || rVariant.vt != VT_DISPATCH || !rVariant.pdispVal)
843     {
844       log_error ("%s:%s: Adding Control failed: %#lx - vt=%d",
845                  SRCNAME, __func__, hr, rVariant.vt);
846       VariantClear (&rVariant);
847       return NULL;
848     }
849   return rVariant.pdispVal;
850 }
851
852
853 /* Add a new button to an object which supports the add method.
854    Returns the new object or NULL on error.  */
855 void
856 del_oom_button (LPDISPATCH pObj)
857 {
858   HRESULT hr;      
859   DISPID dispid;
860   DISPPARAMS dispparams;
861   VARIANT aVariant[5];
862
863   dispid = lookup_oom_dispid (pObj, "Delete");
864
865   dispparams.rgvarg = aVariant;
866   dispparams.rgvarg[0].vt = VT_BOOL;  /* Temporary */
867   dispparams.rgvarg[0].boolVal = VARIANT_FALSE;
868   dispparams.cArgs = 1;
869   dispparams.cNamedArgs = 0;
870   hr = pObj->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
871                      DISPATCH_METHOD, &dispparams,
872                      NULL, NULL, NULL);
873   if (hr != S_OK)
874     log_error ("%s:%s: Deleting Control failed: %#lx",
875                SRCNAME, __func__, hr);
876 }
877
878 /* Gets the current contexts HWND. Returns NULL on error */
879 HWND
880 get_oom_context_window (LPDISPATCH context)
881 {
882   LPOLEWINDOW actExplorer;
883   HWND ret = NULL;
884   actExplorer = (LPOLEWINDOW) get_oom_object(context,
885                                              "Application.ActiveExplorer");
886   if (actExplorer)
887     actExplorer->GetWindow (&ret);
888   else
889     {
890       log_debug ("%s:%s: Could not find active window",
891                  SRCNAME, __func__);
892     }
893   gpgol_release (actExplorer);
894   return ret;
895 }
896
897 int
898 put_pa_variant (LPDISPATCH pDisp, const char *dasl_id, VARIANT *value)
899 {
900   LPDISPATCH propertyAccessor;
901   VARIANT cVariant[2];
902   VARIANT rVariant;
903   DISPID dispid;
904   DISPPARAMS dispparams;
905   HRESULT hr;
906   EXCEPINFO execpinfo;
907   BSTR b_property;
908   wchar_t *w_property;
909   unsigned int argErr = 0;
910
911   init_excepinfo (&execpinfo);
912
913   log_oom ("%s:%s: Looking up property: %s;",
914              SRCNAME, __func__, dasl_id);
915
916   propertyAccessor = get_oom_object (pDisp, "PropertyAccessor");
917   if (!propertyAccessor)
918     {
919       log_error ("%s:%s: Failed to look up property accessor.",
920                  SRCNAME, __func__);
921       return -1;
922     }
923
924   dispid = lookup_oom_dispid (propertyAccessor, "SetProperty");
925
926   if (dispid == DISPID_UNKNOWN)
927   {
928     log_error ("%s:%s: could not find SetProperty DISPID",
929                SRCNAME, __func__);
930     return -1;
931   }
932
933   /* Prepare the parameter */
934   w_property = utf8_to_wchar (dasl_id);
935   b_property = SysAllocString (w_property);
936   xfree (w_property);
937
938   /* Variant 0 carries the data. */
939   VariantInit (&cVariant[0]);
940   if (VariantCopy (&cVariant[0], value))
941     {
942       log_error ("%s:%s: Falied to copy value.",
943                  SRCNAME, __func__);
944       return -1;
945     }
946
947   /* Variant 1 is the DASL as found out by experiments. */
948   VariantInit (&cVariant[1]);
949   cVariant[1].vt = VT_BSTR;
950   cVariant[1].bstrVal = b_property;
951   dispparams.rgvarg = cVariant;
952   dispparams.cArgs = 2;
953   dispparams.cNamedArgs = 0;
954   VariantInit (&rVariant);
955
956   hr = propertyAccessor->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
957                                  DISPATCH_METHOD, &dispparams,
958                                  &rVariant, &execpinfo, &argErr);
959   VariantClear (&cVariant[0]);
960   VariantClear (&cVariant[1]);
961   gpgol_release (propertyAccessor);
962   if (hr != S_OK)
963     {
964       log_debug ("%s:%s: error: invoking SetProperty p=%p vt=%d"
965                  " hr=0x%x argErr=0x%x",
966                  SRCNAME, __func__,
967                  rVariant.pdispVal, rVariant.vt, (unsigned int)hr,
968                  (unsigned int)argErr);
969       VariantClear (&rVariant);
970       dump_excepinfo (execpinfo);
971       return -1;
972     }
973   VariantClear (&rVariant);
974   return 0;
975 }
976
977 int
978 put_pa_string (LPDISPATCH pDisp, const char *dasl_id, const char *value)
979 {
980   wchar_t *w_value = utf8_to_wchar (value);
981   BSTR b_value = SysAllocString(w_value);
982   xfree (w_value);
983   VARIANT var;
984   VariantInit (&var);
985   var.vt = VT_BSTR;
986   var.bstrVal = b_value;
987   int ret = put_pa_variant (pDisp, dasl_id, &var);
988   VariantClear (&var);
989   return ret;
990 }
991
992 int
993 put_pa_int (LPDISPATCH pDisp, const char *dasl_id, int value)
994 {
995   VARIANT var;
996   VariantInit (&var);
997   var.vt = VT_INT;
998   var.intVal = value;
999   int ret = put_pa_variant (pDisp, dasl_id, &var);
1000   VariantClear (&var);
1001   return ret;
1002 }
1003
1004 /* Get a MAPI property through OOM using the PropertyAccessor
1005  * interface and the DASL Uid. Returns -1 on error.
1006  * Variant has to be cleared with VariantClear.
1007  * rVariant must be a pointer to a Variant.
1008  */
1009 int get_pa_variant (LPDISPATCH pDisp, const char *dasl_id, VARIANT *rVariant)
1010 {
1011   LPDISPATCH propertyAccessor;
1012   VARIANT cVariant[1];
1013   DISPID dispid;
1014   DISPPARAMS dispparams;
1015   HRESULT hr;
1016   EXCEPINFO execpinfo;
1017   BSTR b_property;
1018   wchar_t *w_property;
1019   unsigned int argErr = 0;
1020
1021   init_excepinfo (&execpinfo);
1022   log_oom ("%s:%s: Looking up property: %s;",
1023              SRCNAME, __func__, dasl_id);
1024
1025   propertyAccessor = get_oom_object (pDisp, "PropertyAccessor");
1026   if (!propertyAccessor)
1027     {
1028       log_error ("%s:%s: Failed to look up property accessor.",
1029                  SRCNAME, __func__);
1030       return -1;
1031     }
1032
1033   dispid = lookup_oom_dispid (propertyAccessor, "GetProperty");
1034
1035   if (dispid == DISPID_UNKNOWN)
1036   {
1037     log_error ("%s:%s: could not find GetProperty DISPID",
1038                SRCNAME, __func__);
1039     return -1;
1040   }
1041
1042   /* Prepare the parameter */
1043   w_property = utf8_to_wchar (dasl_id);
1044   b_property = SysAllocString (w_property);
1045   xfree (w_property);
1046
1047   cVariant[0].vt = VT_BSTR;
1048   cVariant[0].bstrVal = b_property;
1049   dispparams.rgvarg = cVariant;
1050   dispparams.cArgs = 1;
1051   dispparams.cNamedArgs = 0;
1052   VariantInit (rVariant);
1053
1054   hr = propertyAccessor->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
1055                                  DISPATCH_METHOD, &dispparams,
1056                                  rVariant, &execpinfo, &argErr);
1057   SysFreeString (b_property);
1058   gpgol_release (propertyAccessor);
1059   if (hr != S_OK && strcmp (GPGOL_UID_DASL, dasl_id))
1060     {
1061       /* It often happens that mails don't have a uid by us e.g. if
1062          they are not crypto mails or just dont have one. This is
1063          not an error. */
1064       log_debug ("%s:%s: error: invoking GetProperty p=%p vt=%d"
1065                  " hr=0x%x argErr=0x%x",
1066                  SRCNAME, __func__,
1067                  rVariant->pdispVal, rVariant->vt, (unsigned int)hr,
1068                  (unsigned int)argErr);
1069       dump_excepinfo (execpinfo);
1070       VariantClear (rVariant);
1071       return -1;
1072     }
1073   return 0;
1074 }
1075
1076 /* Get a property string by using the PropertyAccessor of pDisp
1077  * returns NULL on error or a newly allocated result. */
1078 char *
1079 get_pa_string (LPDISPATCH pDisp, const char *property)
1080 {
1081   VARIANT rVariant;
1082   char *result = NULL;
1083
1084   if (get_pa_variant (pDisp, property, &rVariant))
1085     {
1086       return NULL;
1087     }
1088
1089   if (rVariant.vt == VT_BSTR && rVariant.bstrVal)
1090     {
1091       result = wchar_to_utf8 (rVariant.bstrVal);
1092     }
1093   else if (rVariant.vt & VT_ARRAY && !(rVariant.vt & VT_BYREF))
1094     {
1095       LONG uBound, lBound;
1096       VARTYPE vt;
1097       char *data;
1098       SafeArrayGetVartype(rVariant.parray, &vt);
1099
1100       if (SafeArrayGetUBound (rVariant.parray, 1, &uBound) != S_OK ||
1101           SafeArrayGetLBound (rVariant.parray, 1, &lBound) != S_OK ||
1102           vt != VT_UI1)
1103         {
1104           log_error ("%s:%s: Error: %i", SRCNAME, __func__, __LINE__);
1105           VariantClear (&rVariant);
1106           return NULL;
1107         }
1108
1109       result = (char *)xmalloc (uBound - lBound + 1);
1110       data = (char *) rVariant.parray->pvData;
1111       memcpy (result, data + lBound, uBound - lBound);
1112       result[uBound - lBound] = '\0';
1113     }
1114   else
1115     {
1116       log_debug ("%s:%s: Property `%s' is not a string (vt=%d)",
1117                  SRCNAME, __func__, property, rVariant.vt);
1118     }
1119
1120   VariantClear (&rVariant);
1121
1122   return result;
1123 }
1124
1125 int
1126 get_pa_int (LPDISPATCH pDisp, const char *property, int *rInt)
1127 {
1128   VARIANT rVariant;
1129
1130   if (get_pa_variant (pDisp, property, &rVariant))
1131     {
1132       return -1;
1133     }
1134
1135   if (rVariant.vt != VT_I4)
1136     {
1137       log_debug ("%s:%s: Property `%s' is not a int (vt=%d)",
1138                  SRCNAME, __func__, property, rVariant.vt);
1139       return -1;
1140     }
1141
1142   *rInt = rVariant.lVal;
1143
1144   VariantClear (&rVariant);
1145   return 0;
1146 }
1147
1148 /* Helper for exchange address lookup. */
1149 static char *
1150 get_recipient_addr_entry_fallbacks_ex (LPDISPATCH addr_entry)
1151 {
1152   /* Maybe check for type here? We are pretty sure that we are exchange */
1153
1154   /* According to MSDN Message Boards the PR_EMS_AB_PROXY_ADDRESSES_DASL
1155      is more avilable then the SMTP Address. */
1156   char *ret = get_pa_string (addr_entry, PR_EMS_AB_PROXY_ADDRESSES_DASL);
1157   if (ret)
1158     {
1159       log_debug ("%s:%s: Found recipient through AB_PROXY: %s",
1160                  SRCNAME, __func__, ret);
1161
1162       char *smtpbegin = strstr(ret, "SMTP:");
1163       if (smtpbegin == ret)
1164         {
1165           ret += 5;
1166         }
1167       return ret;
1168     }
1169   else
1170     {
1171       log_debug ("%s:%s: Failed AB_PROXY lookup.",
1172                  SRCNAME, __func__);
1173     }
1174
1175   LPDISPATCH ex_user = get_oom_object (addr_entry, "GetExchangeUser");
1176   if (!ex_user)
1177     {
1178       log_debug ("%s:%s: Failed to find ExchangeUser",
1179                  SRCNAME, __func__);
1180       return nullptr;
1181     }
1182
1183   ret = get_oom_string (ex_user, "PrimarySmtpAddress");
1184   gpgol_release (ex_user);
1185   if (ret)
1186     {
1187       log_debug ("%s:%s: Found recipient through exchange user primary smtp address: %s",
1188                  SRCNAME, __func__, ret);
1189       return ret;
1190     }
1191   return nullptr;
1192 }
1193
1194 /* Helper for additional fallbacks in recipient lookup */
1195 static char *
1196 get_recipient_addr_fallbacks (LPDISPATCH recipient)
1197 {
1198   if (!recipient)
1199     {
1200       return nullptr;
1201     }
1202   LPDISPATCH addr_entry = get_oom_object (recipient, "AddressEntry");
1203
1204   if (!addr_entry)
1205     {
1206       log_debug ("%s:%s: Failed to find AddressEntry",
1207                  SRCNAME, __func__);
1208       return nullptr;
1209     }
1210
1211   char *ret = get_recipient_addr_entry_fallbacks_ex (addr_entry);
1212
1213   gpgol_release (addr_entry);
1214
1215   return ret;
1216 }
1217
1218 /* Try to resolve a recipient group and add it to the recipients vector.
1219
1220    returns true on success.
1221 */
1222 static bool
1223 try_resolve_group (LPDISPATCH addrEntry, std::vector<std::string> &ret)
1224 {
1225   /* Get the name for debugging */
1226   std::string name;
1227   char *cname = get_oom_string (addrEntry, "Name");
1228   if (cname)
1229     {
1230       name = cname;
1231     }
1232   xfree (cname);
1233
1234   int type = get_oom_int (addrEntry, "AddressEntryUserType");
1235
1236   if (type != DISTRIBUTION_LIST_ADDRESS_ENTRY_TYPE)
1237     {
1238       log_mime_parser ("%s:%s: type of %s is %i",
1239                        SRCNAME, __func__, name.c_str(), type);
1240       return false;
1241     }
1242
1243   LPDISPATCH members = get_oom_object (addrEntry, "Members");
1244   addrEntry = nullptr;
1245
1246   if (!members)
1247     {
1248       TRACEPOINT;
1249       return false;
1250     }
1251
1252   int count = get_oom_int (members, "Count");
1253
1254   if (!count)
1255     {
1256       TRACEPOINT;
1257       gpgol_release (members);
1258       return false;
1259     }
1260
1261   bool foundOne = false;
1262   for (int i = 1; i <= count; i++)
1263     {
1264       auto item_str = std::string("Item(") + std::to_string (i) + ")";
1265       LPDISPATCH entry = get_oom_object (members, item_str.c_str());
1266       if (!entry)
1267         {
1268           TRACEPOINT;
1269           continue;
1270         }
1271       std::string entryName;
1272       char *entry_name = get_oom_string (entry, "Name");
1273       if (entry_name)
1274         {
1275           entryName = entry_name;
1276           xfree (entry_name);
1277         }
1278
1279       int subType = get_oom_int (entry, "AddressEntryUserType");
1280       /* Resolve recursively, yeah fun. */
1281       if (subType == DISTRIBUTION_LIST_ADDRESS_ENTRY_TYPE)
1282         {
1283           log_debug ("%s:%s: recursive address entry %s",
1284                      SRCNAME, __func__,
1285                      (opt.enable_debug & DBG_MIME_PARSER) ?
1286                      entryName.c_str() : "omitted");
1287           if (try_resolve_group (entry, ret))
1288             {
1289               foundOne = true;
1290               gpgol_release (entry);
1291               continue;
1292             }
1293         }
1294
1295       /* Resolve directly ? */
1296       char *addrtype = get_pa_string (entry, PR_ADDRTYPE_DASL);
1297       if (addrtype && !strcmp (addrtype, "SMTP"))
1298         {
1299           xfree (addrtype);
1300           char *resolved = get_pa_string (entry, PR_EMAIL_ADDRESS_DASL);
1301           if (resolved)
1302             {
1303               ret.push_back(resolved);
1304               foundOne = true;
1305               gpgol_release (entry);
1306               continue;
1307             }
1308         }
1309       xfree (addrtype);
1310
1311       /* Resolve through Exchange API */
1312       char *ex_resolved = get_recipient_addr_entry_fallbacks_ex (entry);
1313       if (ex_resolved)
1314         {
1315           ret.push_back (ex_resolved);
1316           foundOne = true;
1317           gpgol_release (entry);
1318           continue;
1319         }
1320
1321       gpgol_release (entry);
1322       log_debug ("%s:%s: failed to resolve name %s",
1323                  SRCNAME, __func__,
1324                  (opt.enable_debug & DBG_MIME_PARSER) ?
1325                  entryName.c_str() : "omitted");
1326     }
1327   gpgol_release (members);
1328   if (!foundOne)
1329     {
1330       log_debug ("%s:%s: failed to resolve group %s",
1331                  SRCNAME, __func__,
1332                  (opt.enable_debug & DBG_MIME_PARSER) ?
1333                  name.c_str() : "omitted");
1334     }
1335   return foundOne;
1336 }
1337
1338 /* Gets the resolved smtp addresses of the recpients. */
1339 std::vector<std::string>
1340 get_oom_recipients (LPDISPATCH recipients, bool *r_err)
1341 {
1342   int recipientsCnt = get_oom_int (recipients, "Count");
1343   std::vector<std::string> ret;
1344   int i;
1345
1346   if (!recipientsCnt)
1347     {
1348       return ret;
1349     }
1350
1351   /* Get the recipients */
1352   for (i = 1; i <= recipientsCnt; i++)
1353     {
1354       char buf[16];
1355       LPDISPATCH recipient;
1356       snprintf (buf, sizeof (buf), "Item(%i)", i);
1357       recipient = get_oom_object (recipients, buf);
1358       if (!recipient)
1359         {
1360           /* Should be impossible */
1361           log_error ("%s:%s: could not find Item %i;",
1362                      SRCNAME, __func__, i);
1363           if (r_err)
1364             {
1365               *r_err = true;
1366             }
1367           break;
1368         }
1369
1370       LPDISPATCH addrEntry = get_oom_object (recipient, "AddressEntry");
1371       if (addrEntry && try_resolve_group (addrEntry, ret))
1372         {
1373           log_debug ("%s:%s: Resolved recipient group",
1374                      SRCNAME, __func__);
1375           gpgol_release (recipient);
1376           gpgol_release (addrEntry);
1377           continue;
1378         }
1379       gpgol_release (addrEntry);
1380
1381       char *resolved = get_pa_string (recipient, PR_SMTP_ADDRESS_DASL);
1382       if (resolved)
1383         {
1384           ret.push_back (resolved);
1385           xfree (resolved);
1386           gpgol_release (recipient);
1387           continue;
1388         }
1389       /* No PR_SMTP_ADDRESS first fallback */
1390       resolved = get_recipient_addr_fallbacks (recipient);
1391       if (resolved)
1392         {
1393           ret.push_back (resolved);
1394           xfree (resolved);
1395           gpgol_release (recipient);
1396           continue;
1397         }
1398
1399       char *address = get_oom_string (recipient, "Address");
1400       gpgol_release (recipient);
1401       log_debug ("%s:%s: Failed to look up Address probably EX addr is returned",
1402                  SRCNAME, __func__);
1403       if (address)
1404         {
1405           ret.push_back (address);
1406           xfree (address);
1407         }
1408       else if (r_err)
1409         {
1410           *r_err = true;
1411         }
1412     }
1413
1414   return ret;
1415 }
1416
1417 /* Add an attachment to the outlook dispatcher disp
1418    that has an Attachment property.
1419    inFile is the path to the attachment. Name is the
1420    name that should be used in outlook. */
1421 int
1422 add_oom_attachment (LPDISPATCH disp, const wchar_t* inFileW,
1423                     const wchar_t* displayName)
1424 {
1425   LPDISPATCH attachments = get_oom_object (disp, "Attachments");
1426
1427   DISPID dispid;
1428   DISPPARAMS dispparams;
1429   VARIANT vtResult;
1430   VARIANT aVariant[4];
1431   HRESULT hr;
1432   BSTR inFileB = nullptr,
1433        dispNameB = nullptr;
1434   unsigned int argErr = 0;
1435   EXCEPINFO execpinfo;
1436
1437   init_excepinfo (&execpinfo);
1438   dispid = lookup_oom_dispid (attachments, "Add");
1439
1440   if (dispid == DISPID_UNKNOWN)
1441   {
1442     log_error ("%s:%s: could not find attachment dispatcher",
1443                SRCNAME, __func__);
1444     return -1;
1445   }
1446
1447   if (inFileW)
1448     {
1449       inFileB = SysAllocString (inFileW);
1450     }
1451   if (displayName)
1452     {
1453       dispNameB = SysAllocString (displayName);
1454     }
1455
1456   dispparams.rgvarg = aVariant;
1457
1458   /* Contrary to the documentation the Source is the last
1459      parameter and not the first. Additionally DisplayName
1460      is documented but gets ignored by Outlook since Outlook
1461      2003 */
1462   dispparams.rgvarg[0].vt = VT_BSTR; /* DisplayName */
1463   dispparams.rgvarg[0].bstrVal = dispNameB;
1464   dispparams.rgvarg[1].vt = VT_INT;  /* Position */
1465   dispparams.rgvarg[1].intVal = 1;
1466   dispparams.rgvarg[2].vt = VT_INT;  /* Type */
1467   dispparams.rgvarg[2].intVal = 1;
1468   dispparams.rgvarg[3].vt = VT_BSTR; /* Source */
1469   dispparams.rgvarg[3].bstrVal = inFileB;
1470   dispparams.cArgs = 4;
1471   dispparams.cNamedArgs = 0;
1472   VariantInit (&vtResult);
1473   hr = attachments->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
1474                             DISPATCH_METHOD, &dispparams,
1475                             &vtResult, &execpinfo, &argErr);
1476   if (hr != S_OK)
1477     {
1478       log_debug ("%s:%s: error: invoking Add p=%p vt=%d hr=0x%x argErr=0x%x",
1479                  SRCNAME, __func__,
1480                  vtResult.pdispVal, vtResult.vt, (unsigned int)hr,
1481                  (unsigned int)argErr);
1482       dump_excepinfo (execpinfo);
1483     }
1484
1485   if (inFileB)
1486     SysFreeString (inFileB);
1487   if (dispNameB)
1488     SysFreeString (dispNameB);
1489   VariantClear (&vtResult);
1490   gpgol_release (attachments);
1491
1492   return hr == S_OK ? 0 : -1;
1493 }
1494
1495 LPDISPATCH
1496 get_object_by_id (LPDISPATCH pDisp, REFIID id)
1497 {
1498   LPDISPATCH disp = NULL;
1499
1500   if (!pDisp)
1501     return NULL;
1502
1503   if (gpgol_queryInterface(pDisp, id, (void **)&disp) != S_OK)
1504     return NULL;
1505   return disp;
1506 }
1507
1508 LPDISPATCH
1509 get_strong_reference (LPDISPATCH mail)
1510 {
1511   VARIANT var;
1512   VariantInit (&var);
1513   DISPPARAMS args;
1514   VARIANT argvars[2];
1515   VariantInit (&argvars[0]);
1516   VariantInit (&argvars[1]);
1517   argvars[1].vt = VT_DISPATCH;
1518   argvars[1].pdispVal = mail;
1519   argvars[0].vt = VT_INT;
1520   argvars[0].intVal = 1;
1521   args.cArgs = 2;
1522   args.cNamedArgs = 0;
1523   args.rgvarg = argvars;
1524   LPDISPATCH ret = NULL;
1525   if (!invoke_oom_method_with_parms (
1526       GpgolAddin::get_instance()->get_application(),
1527       "GetObjectReference", &var, &args))
1528     {
1529       ret = var.pdispVal;
1530       log_oom ("%s:%s: Got strong ref %p for %p",
1531                SRCNAME, __func__, ret, mail);
1532       memdbg_addRef (ret);
1533     }
1534   else
1535     {
1536       log_error ("%s:%s: Failed to get strong ref.",
1537                  SRCNAME, __func__);
1538     }
1539   VariantClear (&var);
1540   return ret;
1541 }
1542
1543 LPMESSAGE
1544 get_oom_message (LPDISPATCH mailitem)
1545 {
1546   LPUNKNOWN mapi_obj = get_oom_iunknown (mailitem, "MapiObject");
1547   if (!mapi_obj)
1548     {
1549       log_error ("%s:%s: Failed to obtain MAPI Message.",
1550                  SRCNAME, __func__);
1551       return NULL;
1552     }
1553   return (LPMESSAGE) mapi_obj;
1554 }
1555
1556 static LPMESSAGE
1557 get_oom_base_message_from_mapi (LPDISPATCH mapi_message)
1558 {
1559   HRESULT hr;
1560   LPDISPATCH secureItem = NULL;
1561   LPMESSAGE message = NULL;
1562   LPMAPISECUREMESSAGE secureMessage = NULL;
1563
1564   secureItem = get_object_by_id (mapi_message,
1565                                  IID_IMAPISecureMessage);
1566   if (!secureItem)
1567     {
1568       log_error ("%s:%s: Failed to obtain SecureItem.",
1569                  SRCNAME, __func__);
1570       return NULL;
1571     }
1572
1573   secureMessage = (LPMAPISECUREMESSAGE) secureItem;
1574
1575   /* The call to GetBaseMessage is pretty much a jump
1576      in the dark. So it would not be surprising to get
1577      crashes here in the future. */
1578   log_oom_extra("%s:%s: About to call GetBaseMessage.",
1579                 SRCNAME, __func__);
1580   hr = secureMessage->GetBaseMessage (&message);
1581   memdbg_addRef (message);
1582   gpgol_release (secureMessage);
1583   if (hr != S_OK)
1584     {
1585       log_error_w32 (hr, "Failed to GetBaseMessage.");
1586       return NULL;
1587     }
1588
1589   return message;
1590 }
1591
1592 LPMESSAGE
1593 get_oom_base_message (LPDISPATCH mailitem)
1594 {
1595   LPMESSAGE mapi_message = get_oom_message (mailitem);
1596   LPMESSAGE ret = NULL;
1597   if (!mapi_message)
1598     {
1599       log_error ("%s:%s: Failed to obtain mapi_message.",
1600                  SRCNAME, __func__);
1601       return NULL;
1602     }
1603   ret = get_oom_base_message_from_mapi ((LPDISPATCH)mapi_message);
1604   gpgol_release (mapi_message);
1605   return ret;
1606 }
1607
1608 static int
1609 invoke_oom_method_with_parms_type (LPDISPATCH pDisp, const char *name,
1610                                    VARIANT *rVariant, DISPPARAMS *params,
1611                                    int type)
1612 {
1613   HRESULT hr;
1614   DISPID dispid;
1615
1616   dispid = lookup_oom_dispid (pDisp, name);
1617   if (dispid != DISPID_UNKNOWN)
1618     {
1619       EXCEPINFO execpinfo;
1620       init_excepinfo (&execpinfo);
1621       DISPPARAMS dispparams = {NULL, NULL, 0, 0};
1622
1623       hr = pDisp->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
1624                           type, params ? params : &dispparams,
1625                           rVariant, &execpinfo, NULL);
1626       if (hr != S_OK)
1627         {
1628           log_debug ("%s:%s: Method '%s' invokation failed: %#lx",
1629                      SRCNAME, __func__, name, hr);
1630           dump_excepinfo (execpinfo);
1631           return -1;
1632         }
1633     }
1634
1635   return 0;
1636 }
1637
1638 int
1639 invoke_oom_method_with_parms (LPDISPATCH pDisp, const char *name,
1640                               VARIANT *rVariant, DISPPARAMS *params)
1641 {
1642   return invoke_oom_method_with_parms_type (pDisp, name, rVariant, params,
1643                                             DISPATCH_METHOD);
1644 }
1645
1646 int
1647 invoke_oom_method (LPDISPATCH pDisp, const char *name, VARIANT *rVariant)
1648 {
1649   return invoke_oom_method_with_parms (pDisp, name, rVariant, NULL);
1650 }
1651
1652 LPMAPISESSION
1653 get_oom_mapi_session ()
1654 {
1655   LPDISPATCH application = GpgolAddin::get_instance ()->get_application ();
1656   LPDISPATCH oom_session = NULL;
1657   LPMAPISESSION session = NULL;
1658   LPUNKNOWN mapiobj = NULL;
1659   HRESULT hr;
1660
1661   if (!application)
1662     {
1663       log_debug ("%s:%s: Not implemented for Ol < 14", SRCNAME, __func__);
1664       return NULL;
1665     }
1666
1667   oom_session = get_oom_object (application, "Session");
1668   if (!oom_session)
1669     {
1670       log_error ("%s:%s: session object not found", SRCNAME, __func__);
1671       return NULL;
1672     }
1673   mapiobj = get_oom_iunknown (oom_session, "MAPIOBJECT");
1674   gpgol_release (oom_session);
1675
1676   if (!mapiobj)
1677     {
1678       log_error ("%s:%s: error getting Session.MAPIOBJECT", SRCNAME, __func__);
1679       return NULL;
1680     }
1681   session = NULL;
1682   hr = gpgol_queryInterface (mapiobj, IID_IMAPISession, (void**)&session);
1683   gpgol_release (mapiobj);
1684   if (hr != S_OK || !session)
1685     {
1686       log_error ("%s:%s: error getting IMAPISession: hr=%#lx",
1687                  SRCNAME, __func__, hr);
1688       return NULL;
1689     }
1690   return session;
1691 }
1692
1693 static int
1694 create_category (LPDISPATCH categories, const char *category, int color)
1695 {
1696   VARIANT cVariant[3];
1697   VARIANT rVariant;
1698   DISPID dispid;
1699   DISPPARAMS dispparams;
1700   HRESULT hr;
1701   EXCEPINFO execpinfo;
1702   BSTR b_name;
1703   wchar_t *w_name;
1704   unsigned int argErr = 0;
1705
1706   init_excepinfo (&execpinfo);
1707
1708   if (!categories || !category)
1709     {
1710       TRACEPOINT;
1711       return 1;
1712     }
1713
1714   dispid = lookup_oom_dispid (categories, "Add");
1715   if (dispid == DISPID_UNKNOWN)
1716   {
1717     log_error ("%s:%s: could not find Add DISPID",
1718                SRCNAME, __func__);
1719     return -1;
1720   }
1721
1722   /* Do the string dance */
1723   w_name = utf8_to_wchar (category);
1724   b_name = SysAllocString (w_name);
1725   xfree (w_name);
1726
1727   /* Variants are in reverse order
1728      ShortcutKey -> 0 / Int
1729      Color -> 1 / Int
1730      Name -> 2 / Bstr */
1731   VariantInit (&cVariant[2]);
1732   cVariant[2].vt = VT_BSTR;
1733   cVariant[2].bstrVal = b_name;
1734
1735   VariantInit (&cVariant[1]);
1736   cVariant[1].vt = VT_INT;
1737   cVariant[1].intVal = color;
1738
1739   VariantInit (&cVariant[0]);
1740   cVariant[0].vt = VT_INT;
1741   cVariant[0].intVal = 0;
1742
1743   dispparams.cArgs = 3;
1744   dispparams.cNamedArgs = 0;
1745   dispparams.rgvarg = cVariant;
1746
1747   hr = categories->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
1748                            DISPATCH_METHOD, &dispparams,
1749                            &rVariant, &execpinfo, &argErr);
1750   SysFreeString (b_name);
1751   VariantClear (&cVariant[0]);
1752   VariantClear (&cVariant[1]);
1753   VariantClear (&cVariant[2]);
1754   if (hr != S_OK)
1755     {
1756       log_debug ("%s:%s: error: invoking Add p=%p vt=%d"
1757                  " hr=0x%x argErr=0x%x",
1758                  SRCNAME, __func__,
1759                  rVariant.pdispVal, rVariant.vt, (unsigned int)hr,
1760                  (unsigned int)argErr);
1761       dump_excepinfo (execpinfo);
1762       VariantClear (&rVariant);
1763       return -1;
1764     }
1765   VariantClear (&rVariant);
1766   log_debug ("%s:%s: Created category '%s'",
1767              SRCNAME, __func__, category);
1768   return 0;
1769 }
1770
1771 void
1772 ensure_category_exists (LPDISPATCH application, const char *category, int color)
1773 {
1774   if (!application || !category)
1775     {
1776       TRACEPOINT;
1777       return;
1778     }
1779
1780   log_debug ("Ensure category exists called for %s, %i", category, color);
1781
1782   LPDISPATCH stores = get_oom_object (application, "Session.Stores");
1783   if (!stores)
1784     {
1785       log_error ("%s:%s: No stores found.",
1786                  SRCNAME, __func__);
1787       return;
1788     }
1789   auto store_count = get_oom_int (stores, "Count");
1790
1791   for (int n = 1; n <= store_count; n++)
1792     {
1793       const auto store_str = std::string("Item(") + std::to_string(n) + ")";
1794       LPDISPATCH store = get_oom_object (stores, store_str.c_str());
1795
1796       if (!store)
1797         {
1798           TRACEPOINT;
1799           continue;
1800         }
1801
1802       LPDISPATCH categories = get_oom_object (store, "Categories");
1803       gpgol_release (store);
1804       if (!categories)
1805         {
1806           categories = get_oom_object (application, "Session.Categories");
1807           if (!categories)
1808             {
1809               TRACEPOINT;
1810               continue;
1811             }
1812         }
1813
1814       auto count = get_oom_int (categories, "Count");
1815       bool found = false;
1816       for (int i = 1; i <= count && !found; i++)
1817         {
1818           const auto item_str = std::string("Item(") + std::to_string(i) + ")";
1819           LPDISPATCH category_obj = get_oom_object (categories, item_str.c_str());
1820           if (!category_obj)
1821             {
1822               TRACEPOINT;
1823               gpgol_release (categories);
1824               break;
1825             }
1826           char *name = get_oom_string (category_obj, "Name");
1827           if (name && !strcmp (category, name))
1828             {
1829               log_debug ("%s:%s: Found category '%s'",
1830                          SRCNAME, __func__, name);
1831               found = true;
1832             }
1833           /* We don't check the color here as the user may change that. */
1834           gpgol_release (category_obj);
1835           xfree (name);
1836         }
1837
1838       if (!found)
1839         {
1840           if (create_category (categories, category, color))
1841             {
1842               log_debug ("%s:%s: Found category '%s'",
1843                          SRCNAME, __func__, category);
1844             }
1845         }
1846       /* Otherwise we have to create the category */
1847       gpgol_release (categories);
1848     }
1849   gpgol_release (stores);
1850 }
1851
1852 int
1853 add_category (LPDISPATCH mail, const char *category)
1854 {
1855   char *tmp = get_oom_string (mail, "Categories");
1856   if (!tmp)
1857     {
1858       TRACEPOINT;
1859       return 1;
1860     }
1861
1862   if (strstr (tmp, category))
1863     {
1864       log_debug ("%s:%s: category '%s' already added.",
1865                  SRCNAME, __func__, category);
1866       return 0;
1867     }
1868
1869   std::string newstr (tmp);
1870   xfree (tmp);
1871   if (!newstr.empty ())
1872     {
1873       newstr += ", ";
1874     }
1875   newstr += category;
1876
1877   return put_oom_string (mail, "Categories", newstr.c_str ());
1878 }
1879
1880 int
1881 remove_category (LPDISPATCH mail, const char *category)
1882 {
1883   char *tmp = get_oom_string (mail, "Categories");
1884   if (!tmp)
1885     {
1886       TRACEPOINT;
1887       return 1;
1888     }
1889   std::string newstr (tmp);
1890   xfree (tmp);
1891   std::string cat (category);
1892
1893   size_t pos1 = newstr.find (cat);
1894   size_t pos2 = newstr.find (std::string(", ") + cat);
1895   if (pos1 == std::string::npos && pos2 == std::string::npos)
1896     {
1897       log_debug ("%s:%s: category '%s' not found.",
1898                  SRCNAME, __func__, category);
1899       return 0;
1900     }
1901
1902   size_t len = cat.size();
1903   if (pos2)
1904     {
1905       len += 2;
1906     }
1907   newstr.erase (pos2 != std::string::npos ? pos2 : pos1, len);
1908   log_debug ("%s:%s: removing category '%s'",
1909              SRCNAME, __func__, category);
1910
1911   return put_oom_string (mail, "Categories", newstr.c_str ());
1912 }
1913
1914 static char *
1915 generate_uid ()
1916 {
1917   UUID uuid;
1918   UuidCreate (&uuid);
1919
1920   unsigned char *str;
1921   UuidToStringA (&uuid, &str);
1922
1923   char *ret = xstrdup ((char*)str);
1924   RpcStringFreeA (&str);
1925
1926   return ret;
1927 }
1928
1929 char *
1930 get_unique_id (LPDISPATCH mail, int create, const char *uuid)
1931 {
1932   if (!mail)
1933     {
1934       return NULL;
1935     }
1936
1937   /* Get the User Properties. */
1938   if (!create)
1939     {
1940       char *uid = get_pa_string (mail, GPGOL_UID_DASL);
1941       if (!uid)
1942         {
1943           log_debug ("%s:%s: No uuid found in oom for '%p'",
1944                      SRCNAME, __func__, mail);
1945           return NULL;
1946         }
1947       else
1948         {
1949           log_debug ("%s:%s: Found uid '%s' for '%p'",
1950                      SRCNAME, __func__, uid, mail);
1951           return uid;
1952         }
1953     }
1954   char *newuid;
1955   if (!uuid)
1956     {
1957       newuid = generate_uid ();
1958     }
1959   else
1960     {
1961       newuid = xstrdup (uuid);
1962     }
1963   int ret = put_pa_string (mail, GPGOL_UID_DASL, newuid);
1964
1965   if (ret)
1966     {
1967       log_debug ("%s:%s: failed to set uid '%s' for '%p'",
1968                  SRCNAME, __func__, newuid, mail);
1969       xfree (newuid);
1970       return NULL;
1971     }
1972
1973
1974   log_debug ("%s:%s: '%p' has now the uid: '%s' ",
1975              SRCNAME, __func__, mail, newuid);
1976   return newuid;
1977 }
1978
1979 HWND
1980 get_active_hwnd ()
1981 {
1982   LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
1983
1984   if (!app)
1985     {
1986       TRACEPOINT;
1987       return nullptr;
1988     }
1989
1990   LPDISPATCH activeWindow = get_oom_object (app, "ActiveWindow");
1991   if (!activeWindow)
1992     {
1993       activeWindow = get_oom_object (app, "ActiveInspector");
1994       if (!activeWindow)
1995         {
1996           activeWindow = get_oom_object (app, "ActiveExplorer");
1997           if (!activeWindow)
1998             {
1999               TRACEPOINT;
2000               return nullptr;
2001             }
2002         }
2003     }
2004
2005   /* Both explorer and inspector have this. */
2006   char *caption = get_oom_string (activeWindow, "Caption");
2007   gpgol_release (activeWindow);
2008   if (!caption)
2009     {
2010       TRACEPOINT;
2011       return nullptr;
2012     }
2013   /* Might not be completly true for multiple explorers
2014      on the same folder but good enugh. */
2015   HWND hwnd = FindWindowExA(NULL, NULL, "rctrl_renwnd32",
2016                             caption);
2017   xfree (caption);
2018
2019   return hwnd;
2020 }
2021
2022 LPDISPATCH
2023 create_mail ()
2024 {
2025   LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
2026
2027   if (!app)
2028     {
2029       TRACEPOINT;
2030       return nullptr;
2031    }
2032
2033   VARIANT var;
2034   VariantInit (&var);
2035   VARIANT argvars[1];
2036   DISPPARAMS args;
2037   VariantInit (&argvars[0]);
2038   argvars[0].vt = VT_I2;
2039   argvars[0].intVal = 0;
2040   args.cArgs = 1;
2041   args.cNamedArgs = 0;
2042   args.rgvarg = argvars;
2043
2044   LPDISPATCH ret = nullptr;
2045
2046   if (invoke_oom_method_with_parms (app, "CreateItem", &var, &args))
2047     {
2048       log_error ("%s:%s: Failed to create mailitem.",
2049                  SRCNAME, __func__);
2050       return ret;
2051     }
2052
2053   ret = var.pdispVal;
2054   return ret;
2055 }
2056
2057 LPDISPATCH
2058 get_account_for_mail (const char *mbox)
2059 {
2060   LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
2061
2062   if (!app)
2063     {
2064       TRACEPOINT;
2065       return nullptr;
2066    }
2067
2068   LPDISPATCH accounts = get_oom_object (app, "Session.Accounts");
2069
2070   if (!accounts)
2071     {
2072       TRACEPOINT;
2073       return nullptr;
2074     }
2075
2076   int count = get_oom_int (accounts, "Count");
2077   for (int i = 1; i <= count; i++)
2078     {
2079       std::string item = std::string ("Item(") + std::to_string (i) + ")";
2080
2081       LPDISPATCH account = get_oom_object (accounts, item.c_str ());
2082
2083       if (!account)
2084         {
2085           TRACEPOINT;
2086           continue;
2087         }
2088       char *smtpAddr = get_oom_string (account, "SmtpAddress");
2089
2090       if (!smtpAddr)
2091         {
2092           gpgol_release (account);
2093           TRACEPOINT;
2094           continue;
2095         }
2096       if (!stricmp (mbox, smtpAddr))
2097         {
2098           gpgol_release (accounts);
2099           xfree (smtpAddr);
2100           return account;
2101         }
2102       gpgol_release (account);
2103       xfree (smtpAddr);
2104     }
2105   gpgol_release (accounts);
2106
2107   log_error ("%s:%s: Failed to find account for '%s'.",
2108              SRCNAME, __func__, mbox);
2109
2110   return nullptr;
2111 }
2112
2113 char *
2114 get_sender_SendUsingAccount (LPDISPATCH mailitem, bool *r_is_GSuite)
2115 {
2116   LPDISPATCH sender = get_oom_object (mailitem, "SendUsingAccount");
2117   if (!sender)
2118     {
2119       return nullptr;
2120     }
2121
2122   char *buf = get_oom_string (sender, "SmtpAddress");
2123   char *dispName = get_oom_string (sender, "DisplayName");
2124   gpgol_release (sender);
2125
2126   /* Check for G Suite account */
2127   if (dispName && !strcmp ("G Suite", dispName) && r_is_GSuite)
2128     {
2129       *r_is_GSuite = true;
2130     }
2131   xfree (dispName);
2132   if (buf && strlen (buf))
2133     {
2134       log_debug ("%s:%s: found sender", SRCNAME, __func__);
2135       return buf;
2136     }
2137   xfree (buf);
2138   return nullptr;
2139 }
2140
2141 char *
2142 get_sender_Sender (LPDISPATCH mailitem)
2143 {
2144   LPDISPATCH sender = get_oom_object (mailitem, "Sender");
2145   if (!sender)
2146     {
2147       return nullptr;
2148     }
2149   char *buf = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
2150   gpgol_release (sender);
2151   if (buf && strlen (buf))
2152     {
2153       log_debug ("%s:%s Sender fallback 2", SRCNAME, __func__);
2154       return buf;
2155     }
2156   xfree (buf);
2157   /* We have a sender object but not yet an smtp address likely
2158      exchange. Try some more propertys of the message. */
2159   buf = get_pa_string (mailitem, PR_TAG_SENDER_SMTP_ADDRESS);
2160   if (buf && strlen (buf))
2161     {
2162       log_debug ("%s:%s Sender fallback 3", SRCNAME, __func__);
2163       return buf;
2164     }
2165   xfree (buf);
2166   buf = get_pa_string (mailitem, PR_TAG_RECEIVED_REPRESENTING_SMTP_ADDRESS);
2167   if (buf && strlen (buf))
2168     {
2169       log_debug ("%s:%s Sender fallback 4", SRCNAME, __func__);
2170       return buf;
2171     }
2172   xfree (buf);
2173   return nullptr;
2174 }
2175
2176 char *
2177 get_sender_CurrentUser (LPDISPATCH mailitem)
2178 {
2179   LPDISPATCH sender = get_oom_object (mailitem,
2180                                       "Session.CurrentUser");
2181   if (!sender)
2182     {
2183       return nullptr;
2184     }
2185   char *buf = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
2186   gpgol_release (sender);
2187   if (buf && strlen (buf))
2188     {
2189       log_debug ("%s:%s Sender fallback 5", SRCNAME, __func__);
2190       return buf;
2191     }
2192   xfree (buf);
2193   return nullptr;
2194 }
2195
2196 char *
2197 get_sender_SenderEMailAddress (LPDISPATCH mailitem)
2198 {
2199
2200   char *type = get_oom_string (mailitem, "SenderEmailType");
2201   if (type && !strcmp ("SMTP", type))
2202     {
2203       char *senderMail = get_oom_string (mailitem, "SenderEmailAddress");
2204       if (senderMail)
2205         {
2206           log_debug ("%s:%s: Sender found", SRCNAME, __func__);
2207           xfree (type);
2208           return senderMail;
2209         }
2210     }
2211   xfree (type);
2212   return nullptr;
2213 }
2214
2215 char *
2216 get_inline_body ()
2217 {
2218   LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
2219   if (!app)
2220     {
2221       TRACEPOINT;
2222       return nullptr;
2223     }
2224
2225   LPDISPATCH explorer = get_oom_object (app, "ActiveExplorer");
2226
2227   if (!explorer)
2228     {
2229       TRACEPOINT;
2230       return nullptr;
2231     }
2232
2233   LPDISPATCH inlineResponse = get_oom_object (explorer, "ActiveInlineResponse");
2234   gpgol_release (explorer);
2235
2236   if (!inlineResponse)
2237     {
2238       return nullptr;
2239     }
2240
2241   char *body = get_oom_string (inlineResponse, "Body");
2242   gpgol_release (inlineResponse);
2243
2244   return body;
2245 }
2246
2247 int
2248 get_ex_major_version_for_addr (const char *mbox)
2249 {
2250   LPDISPATCH account = get_account_for_mail (mbox);
2251   if (!account)
2252     {
2253       TRACEPOINT;
2254       return -1;
2255     }
2256
2257   char *version_str = get_oom_string (account, "ExchangeMailboxServerVersion");
2258   gpgol_release (account);
2259
2260   if (!version_str)
2261     {
2262       return -1;
2263     }
2264   long int version = strtol (version_str, nullptr, 10);
2265   xfree (version_str);
2266
2267   return (int) version;
2268 }
2269
2270 int
2271 get_ol_ui_language ()
2272 {
2273   LPDISPATCH app = GpgolAddin::get_instance()->get_application();
2274   if (!app)
2275     {
2276       TRACEPOINT;
2277       return 0;
2278     }
2279
2280   LPDISPATCH langSettings = get_oom_object (app, "LanguageSettings");
2281   if (!langSettings)
2282     {
2283       TRACEPOINT;
2284       return 0;
2285     }
2286
2287   VARIANT var;
2288   VariantInit (&var);
2289
2290   VARIANT aVariant[1];
2291   DISPPARAMS dispparams;
2292
2293   dispparams.rgvarg = aVariant;
2294   dispparams.rgvarg[0].vt = VT_INT;
2295   dispparams.rgvarg[0].intVal = 2;
2296   dispparams.cArgs = 1;
2297   dispparams.cNamedArgs = 0;
2298
2299   int ret = invoke_oom_method_with_parms_type (langSettings, "LanguageID", &var,
2300                                                &dispparams,
2301                                                DISPATCH_PROPERTYGET);
2302   gpgol_release (langSettings);
2303   if (ret)
2304     {
2305       TRACEPOINT;
2306       return 0;
2307     }
2308   if (var.vt != VT_INT && var.vt != VT_I4)
2309     {
2310       TRACEPOINT;
2311       return 0;
2312     }
2313
2314   int result = var.intVal;
2315
2316   VariantClear (&var);
2317   return result;
2318 }
2319
2320 void
2321 log_addins ()
2322 {
2323   LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
2324
2325   if (!app)
2326     {
2327       TRACEPOINT;
2328       return;
2329    }
2330
2331   LPDISPATCH addins = get_oom_object (app, "COMAddins");
2332
2333   if (!addins)
2334     {
2335       TRACEPOINT;
2336       return;
2337     }
2338
2339   std::string activeAddins;
2340   int count = get_oom_int (addins, "Count");
2341   for (int i = 1; i <= count; i++)
2342     {
2343       std::string item = std::string ("Item(") + std::to_string (i) + ")";
2344
2345       LPDISPATCH addin = get_oom_object (addins, item.c_str ());
2346
2347       if (!addin)
2348         {
2349           TRACEPOINT;
2350           continue;
2351         }
2352       bool connected = get_oom_bool (addin, "Connect");
2353       if (!connected)
2354         {
2355           gpgol_release (addin);
2356           continue;
2357         }
2358
2359       char *progId = get_oom_string (addin, "ProgId");
2360       gpgol_release (addin);
2361
2362       if (!progId)
2363         {
2364           TRACEPOINT;
2365           continue;
2366         }
2367       activeAddins += std::string (progId) + "\n";
2368       xfree (progId);
2369     }
2370   gpgol_release (addins);
2371
2372   log_debug ("%s:%s:Active Addins:\n%s", SRCNAME, __func__,
2373              activeAddins.c_str ());
2374   return;
2375 }