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