2dbe80e01221f0647b008b24bb5f57b53d48a81a
[gpgol.git] / src / ribbon-callbacks.cpp
1 /* ribbon-callbacks.h - Callbacks for the ribbon extension interface
2  *    Copyright (C) 2013 Intevation GmbH
3  *
4  * This file is part of GpgOL.
5  *
6  * GpgOL is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * GpgOL is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <windows.h>
25 #include <olectl.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <gdiplus.h>
29
30 #include <objidl.h>
31
32 #include "ribbon-callbacks.h"
33 #include "gpgoladdin.h"
34 #include "common.h"
35
36 #include "mymapi.h"
37 #include "mymapitags.h"
38 #include "myexchext.h"
39
40 #include "common.h"
41 #include "display.h"
42 #include "msgcache.h"
43 #include "engine.h"
44 #include "engine-assuan.h"
45 #include "mapihelp.h"
46 #include "mimemaker.h"
47 #include "filetype.h"
48 #include "gpgolstr.h"
49 #include "message.h"
50 #include "mail.h"
51
52 #include <gpgme++/context.h>
53 #include <gpgme++/data.h>
54
55 using namespace GpgME;
56
57 #define OPAQUE_SIGNED_MARKER "-----BEGIN PGP MESSAGE-----"
58
59 /* Gets the context of a ribbon control. And prints some
60    useful debug output */
61 HRESULT getContext (LPDISPATCH ctrl, LPDISPATCH *context)
62 {
63   *context = get_oom_object (ctrl, "get_Context");
64   if (*context)
65     {
66       char *name = get_object_name (*context);
67       log_debug ("%s:%s: contextObj: %s",
68                  SRCNAME, __func__, name);
69       xfree (name);
70     }
71
72   return context ? S_OK : E_FAIL;
73 }
74
75 #define OP_ENCRYPT     1 /* Encrypt the data */
76 #define OP_SIGN        2 /* Sign the data */
77 #define OP_DECRYPT     1 /* Decrypt the data */
78 #define OP_VERIFY      2 /* Verify the data */
79 #define DATA_BODY      4 /* Use text body as data */
80 #define DATA_SELECTION 8 /* Use selection as data */
81
82 /* Read hfile in chunks of 4KB and writes them to the sink */
83 static int
84 copyFileToSink (HANDLE hFile, sink_t sink)
85 {
86   char buf[4096];
87   DWORD bytesRead = 0;
88   do
89     {
90       if (!ReadFile (hFile, buf, sizeof buf, &bytesRead, NULL))
91         {
92           log_error ("%s:%s: Could not read source file.",
93                      SRCNAME, __func__);
94           return -1;
95         }
96       if (write_buffer (sink, bytesRead ? buf : NULL, bytesRead))
97         {
98           log_error ("%s:%s: Could not write out buffer",
99                      SRCNAME, __func__);
100           return -1;
101         }
102     }
103   while (bytesRead);
104   return 0;
105 }
106
107 static int
108 attachSignature (LPDISPATCH mailItem, char *subject, HANDLE hFileToSign,
109                  protocol_t protocol, unsigned int session_number,
110                  HWND curWindow, wchar_t *fileNameToSign, char *sender)
111 {
112   wchar_t *sigName = NULL;
113   wchar_t *sigFileName = NULL;
114   HANDLE hSigFile = NULL;
115   int rc = 0;
116   struct sink_s encsinkmem;
117   sink_t encsink = &encsinkmem;
118   struct sink_s sinkmem;
119   sink_t sink = &sinkmem;
120   engine_filter_t filter = NULL;
121
122   memset (encsink, 0, sizeof *encsink);
123   memset (sink, 0, sizeof *sink);
124
125   /* Prepare a fresh filter */
126   if ((rc = engine_create_filter (&filter, write_buffer_for_cb, sink)))
127     {
128       goto failure;
129     }
130   encsink->cb_data = filter;
131   encsink->writefnc = sink_encryption_write;
132   engine_set_session_number (filter, session_number);
133   engine_set_session_title (filter, subject ? subject :_("GpgOL"));
134
135   if (engine_sign_start (filter, curWindow, protocol, sender, &protocol))
136     goto failure;
137
138   sigName = get_pretty_attachment_name (fileNameToSign, protocol, 1);
139
140   /* If we are unlucky the number of temporary file artifacts might
141      differ for the signature and the encrypted file but we have
142      to live with that. */
143   sigFileName = get_tmp_outfile (sigName, &hSigFile);
144   sink->cb_data = hSigFile;
145   sink->writefnc = sink_file_write;
146
147   if (!sigFileName)
148     {
149       log_error ("%s:%s: Could not get a decent attachment name",
150                  SRCNAME, __func__);
151       goto failure;
152     }
153
154   /* Reset the file to sign handle to the beginning of the file and
155      copy it to the signature buffer */
156   SetFilePointer (hFileToSign, 0, NULL, 0);
157   if ((rc=copyFileToSink (hFileToSign, encsink)))
158     goto failure;
159
160   /* Lets hope the user did not select a huge file. We are hanging
161      here until encryption is completed.. */
162   if ((rc = engine_wait (filter)))
163     goto failure;
164
165   filter = NULL; /* Not valid anymore.  */
166   encsink->cb_data = NULL; /* Not needed anymore.  */
167
168   if (!sink->enc_counter)
169     {
170       log_error ("%s:%s: nothing received from engine", SRCNAME, __func__);
171       goto failure;
172     }
173
174   /* Now we have an encrypted file behind encryptedFile. Let's add it */
175   add_oom_attachment (mailItem, sigFileName, nullptr);
176
177 failure:
178   xfree (sigFileName);
179   xfree (sigName);
180   if (hSigFile)
181     {
182       CloseHandle (hSigFile);
183       DeleteFileW (sigFileName);
184     }
185   return rc;
186 }
187
188 /* do_composer_action
189    Encrypts / Signs text in an IInspector context.
190    Depending on the flags either the
191    active selection or the full body is encrypted.
192    Combine OP_ENCRYPT and OP_SIGN if you want both.
193 */
194
195 HRESULT
196 do_composer_action (LPDISPATCH ctrl, int flags)
197 {
198   LPDISPATCH context = NULL;
199   LPDISPATCH selection = NULL;
200   LPDISPATCH wordEditor = NULL;
201   LPDISPATCH application = NULL;
202   LPDISPATCH mailItem = NULL;
203   LPDISPATCH sender = NULL;
204   LPDISPATCH recipients = NULL;
205
206   struct sink_s encsinkmem;
207   sink_t encsink = &encsinkmem;
208   struct sink_s sinkmem;
209   sink_t sink = &sinkmem;
210   char* senderAddr = NULL;
211   char** recipientAddrs = NULL;
212   LPSTREAM tmpstream = NULL;
213   engine_filter_t filter = NULL;
214   char* plaintext = NULL;
215   int rc = 0;
216   HRESULT hr;
217   HWND curWindow;
218   protocol_t protocol;
219   unsigned int session_number;
220   int i;
221   STATSTG tmpStat;
222
223   log_debug ("%s:%s: enter", SRCNAME, __func__);
224
225   hr = getContext (ctrl, &context);
226   if (FAILED(hr))
227       return hr;
228
229   memset (encsink, 0, sizeof *encsink);
230   memset (sink, 0, sizeof *sink);
231
232   curWindow = get_oom_context_window (context);
233
234   wordEditor = get_oom_object (context, "WordEditor");
235   application = get_oom_object (wordEditor, "get_Application");
236   selection = get_oom_object (application, "get_Selection");
237   mailItem = get_oom_object (context, "CurrentItem");
238   sender = get_oom_object (mailItem, "Session.CurrentUser");
239   recipients = get_oom_object (mailItem, "Recipients");
240
241   if (!wordEditor || !application || !selection || !mailItem ||
242       !sender || !recipients)
243     {
244       MessageBox (NULL,
245                   "Internal error in GpgOL.\n"
246                   "Could not find all objects.",
247                   _("GpgOL"),
248                   MB_ICONINFORMATION|MB_OK);
249       log_error ("%s:%s: Could not find all objects.",
250                  SRCNAME, __func__);
251       goto failure;
252     }
253
254   if (flags & DATA_SELECTION)
255     {
256       plaintext = get_oom_string (selection, "Text");
257
258       if (!plaintext || strlen (plaintext) <= 1)
259         {
260           MessageBox (NULL,
261                       _("Please select text to encrypt."),
262                       _("GpgOL"),
263                       MB_ICONINFORMATION|MB_OK);
264           goto failure;
265         }
266     }
267   else if (flags & DATA_BODY)
268     {
269       plaintext = get_oom_string (mailItem, "Body");
270       if (!plaintext || strlen (plaintext) <= 1)
271         {
272           MessageBox (NULL,
273                       _("Textbody empty."),
274                       _("GpgOL"),
275                       MB_ICONINFORMATION|MB_OK);
276           goto failure;
277         }
278     }
279
280   /* Create a temporary sink to construct the encrypted data.  */
281   hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
282                          (SOF_UNIQUEFILENAME | STGM_DELETEONRELEASE
283                           | STGM_CREATE | STGM_READWRITE),
284                          NULL, GpgOLStr("GPG"), &tmpstream);
285
286   if (FAILED (hr))
287     {
288       log_error ("%s:%s: can't create temp file: hr=%#lx\n",
289                  SRCNAME, __func__, hr);
290       rc = -1;
291       goto failure;
292     }
293
294   sink->cb_data = tmpstream;
295   sink->writefnc = sink_std_write;
296
297   /* Now lets prepare our encryption */
298   session_number = engine_new_session_number ();
299
300   /* Prepare the encryption sink */
301
302   if (engine_create_filter (&filter, write_buffer_for_cb, sink))
303     {
304       goto failure;
305     }
306
307   encsink->cb_data = filter;
308   encsink->writefnc = sink_encryption_write;
309
310   engine_set_session_number (filter, session_number);
311   engine_set_session_title (filter, _("GpgOL"));
312
313   senderAddr = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
314
315   if (flags & OP_ENCRYPT)
316     {
317       recipientAddrs = get_oom_recipients (recipients);
318
319       if (!recipientAddrs || !(*recipientAddrs))
320         {
321           MessageBox (NULL,
322                       _("Please add at least one recipent."),
323                       _("GpgOL"),
324                       MB_ICONINFORMATION|MB_OK);
325           goto failure;
326         }
327
328       if ((rc=engine_encrypt_prepare (filter, curWindow,
329                                       PROTOCOL_UNKNOWN,
330                                       (flags & OP_SIGN) ?
331                                       ENGINE_FLAG_SIGN_FOLLOWS : 0,
332                                       senderAddr, recipientAddrs,
333                                       &protocol)))
334         {
335           log_error ("%s:%s: engine encrypt prepare failed : %s",
336                      SRCNAME, __func__, gpg_strerror (rc));
337           goto failure;
338         }
339
340       if ((rc=engine_encrypt_start (filter, 0)))
341         {
342           log_error ("%s:%s: engine encrypt start failed: %s",
343                      SRCNAME, __func__, gpg_strerror (rc));
344           goto failure;
345         }
346     }
347   else
348     {
349       /* We could do some kind of clearsign / sign text as attachment here
350       but it is error prone */
351       if ((rc=engine_sign_opaque_start (filter, curWindow, PROTOCOL_UNKNOWN,
352                                         senderAddr, &protocol)))
353         {
354           log_error ("%s:%s: engine sign start failed: %s",
355                      SRCNAME, __func__, gpg_strerror (rc));
356           goto failure;
357         }
358     }
359
360   /* Write the text in the encryption sink. */
361   rc = write_buffer (encsink, plaintext, strlen (plaintext));
362
363   if (rc)
364     {
365       log_error ("%s:%s: writing tmpstream to encsink failed: %s",
366                  SRCNAME, __func__, gpg_strerror (rc));
367       goto failure;
368     }
369   /* Flush the encryption sink and wait for the encryption to get
370      ready.  */
371   if ((rc = write_buffer (encsink, NULL, 0)))
372     goto failure;
373   if ((rc = engine_wait (filter)))
374     goto failure;
375   filter = NULL; /* Not valid anymore.  */
376   encsink->cb_data = NULL; /* Not needed anymore.  */
377
378   if (!sink->enc_counter)
379     {
380       log_debug ("%s:%s: nothing received from engine", SRCNAME, __func__);
381       goto failure;
382     }
383
384   /* Check the size of the encrypted data */
385   tmpstream->Stat (&tmpStat, 0);
386
387   if (tmpStat.cbSize.QuadPart > UINT_MAX)
388     {
389       log_error ("%s:%s: No one should write so large mails.",
390                  SRCNAME, __func__);
391       goto failure;
392     }
393
394   /* Copy the encrypted stream to the message editor.  */
395   {
396     LARGE_INTEGER off;
397     ULONG nread;
398
399     char buffer[(unsigned int)tmpStat.cbSize.QuadPart + 1];
400
401     memset (buffer, 0, sizeof buffer);
402
403     off.QuadPart = 0;
404     hr = tmpstream->Seek (off, STREAM_SEEK_SET, NULL);
405     if (hr)
406       {
407         log_error ("%s:%s: seeking back to the begin failed: hr=%#lx",
408                    SRCNAME, __func__, hr);
409         rc = gpg_error (GPG_ERR_EIO);
410         goto failure;
411       }
412     hr = tmpstream->Read (buffer, sizeof (buffer) - 1, &nread);
413     if (hr)
414       {
415         log_error ("%s:%s: IStream::Read failed: hr=%#lx",
416                    SRCNAME, __func__, hr);
417         rc = gpg_error (GPG_ERR_EIO);
418         goto failure;
419       }
420     if (strlen (buffer) > 1)
421       {
422         if (flags & OP_SIGN)
423           {
424             /* When signing we append the signature after the body */
425             unsigned int combinedSize = strlen (buffer) +
426               strlen (plaintext) + 5;
427             char combinedBody[combinedSize];
428             memset (combinedBody, 0, combinedSize);
429             snprintf (combinedBody, combinedSize, "%s\r\n\r\n%s", plaintext,
430                       buffer);
431             if (flags & DATA_SELECTION)
432               put_oom_string (selection, "Text", combinedBody);
433             else if (flags & DATA_BODY)
434               put_oom_string (mailItem, "Body", combinedBody);
435
436           }
437         else if (protocol == PROTOCOL_SMIME)
438           {
439             unsigned int enclosedSize = strlen (buffer) + 34 + 31 + 1;
440             char enclosedData[enclosedSize];
441             snprintf (enclosedData, sizeof enclosedData,
442                       "-----BEGIN ENCRYPTED MESSAGE-----\r\n"
443                       "%s"
444                       "-----END ENCRYPTED MESSAGE-----\r\n", buffer);
445             if (flags & DATA_SELECTION)
446               put_oom_string (selection, "Text", enclosedData);
447             else if (flags & DATA_BODY)
448               put_oom_string (mailItem, "Body", enclosedData);
449
450           }
451         else
452           {
453             if (flags & DATA_SELECTION)
454               put_oom_string (selection, "Text", buffer);
455             else if (flags & DATA_BODY)
456               {
457                 put_oom_string (mailItem, "Body", buffer);
458               }
459           }
460       }
461     else
462       {
463         /* Just to be save not to overwrite the selection with
464            an empty buffer */
465         log_error ("%s:%s: unexpected problem ", SRCNAME, __func__);
466         goto failure;
467       }
468   }
469
470 failure:
471   if (rc)
472     log_debug ("%s:%s: failed rc=%d (%s) <%s>", SRCNAME, __func__, rc,
473                gpg_strerror (rc), gpg_strsource (rc));
474   engine_cancel (filter);
475   gpgol_release(wordEditor);
476   gpgol_release(application);
477   gpgol_release(selection);
478   gpgol_release(sender);
479   gpgol_release(recipients);
480   gpgol_release(mailItem);
481   gpgol_release(tmpstream);
482   xfree (plaintext);
483   xfree (senderAddr);
484   if (recipientAddrs)
485     {
486       for (i=0; recipientAddrs && recipientAddrs[i]; i++)
487         xfree (recipientAddrs[i]);
488       xfree (recipientAddrs);
489     }
490   log_debug ("%s:%s: leave", SRCNAME, __func__);
491
492   return S_OK;
493 }
494
495 HRESULT
496 decryptAttachments (LPDISPATCH ctrl)
497 {
498   LPDISPATCH context = NULL;
499   LPDISPATCH attachmentSelection;
500   int attachmentCount;
501   HRESULT hr = 0;
502   int i = 0;
503   HWND curWindow;
504   int err;
505
506   hr = getContext(ctrl, &context);
507
508   attachmentSelection = get_oom_object (context, "AttachmentSelection");
509   if (!attachmentSelection)
510     {
511       /* We can be called from a context menu, in that case we
512          directly have an AttachmentSelection context. Otherwise
513          we have an Explorer context with an Attachment Selection property. */
514       attachmentSelection = context;
515     }
516
517   attachmentCount = get_oom_int (attachmentSelection, "Count");
518
519   curWindow = get_oom_context_window (context);
520
521   {
522     char *filenames[attachmentCount + 1];
523     filenames[attachmentCount] = NULL;
524     /* Yes the items start at 1! */
525     for (i = 1; i <= attachmentCount; i++)
526       {
527         char buf[16];
528         char *filename;
529         wchar_t *wcsOutFilename;
530         DISPPARAMS saveParams;
531         VARIANT aVariant[1];
532         LPDISPATCH attachmentObj;
533         DISPID saveID;
534
535         snprintf (buf, sizeof (buf), "Item(%i)", i);
536         attachmentObj = get_oom_object (attachmentSelection, buf);
537         if (!attachmentObj)
538           {
539             /* Should be impossible */
540             filenames[i-1] = NULL;
541             log_error ("%s:%s: could not find Item %i;",
542                        SRCNAME, __func__, i);
543             break;
544           }
545         filename = get_oom_string (attachmentObj, "FileName");
546
547         saveID = lookup_oom_dispid (attachmentObj, "SaveAsFile");
548
549         saveParams.rgvarg = aVariant;
550         saveParams.rgvarg[0].vt = VT_BSTR;
551         filenames[i-1] = get_save_filename (NULL, filename);
552         xfree (filename);
553
554         if (!filenames [i-1])
555           continue;
556
557         wcsOutFilename = utf8_to_wchar2 (filenames[i-1],
558                                          strlen(filenames[i-1]));
559         saveParams.rgvarg[0].bstrVal = SysAllocString (wcsOutFilename);
560         saveParams.cArgs = 1;
561         saveParams.cNamedArgs = 0;
562
563         hr = attachmentObj->Invoke (saveID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
564                                     DISPATCH_METHOD, &saveParams,
565                                     NULL, NULL, NULL);
566         SysFreeString (saveParams.rgvarg[0].bstrVal);
567         gpgol_release (attachmentObj);
568         if (FAILED(hr))
569           {
570             int j;
571             log_debug ("%s:%s: Saving to file failed. hr: %x",
572                        SRCNAME, __func__, (unsigned int) hr);
573             for (j = 0; j < i; j++)
574               xfree (filenames[j]);
575             gpgol_release (attachmentSelection);
576             return hr;
577           }
578       }
579     gpgol_release (attachmentSelection);
580     err = op_assuan_start_decrypt_files (curWindow, filenames);
581     for (i = 0; i < attachmentCount; i++)
582       xfree (filenames[i]);
583   }
584
585   log_debug ("%s:%s: Leaving. Err: %i",
586              SRCNAME, __func__, err);
587
588   return S_OK; /* If we return an error outlook will show that our
589                   callback function failed in an ugly window. */
590 }
591
592 /* MIME erify mail helper. Returns 0 if it
593   was not called with a MIME crypto message or on error. */
594 static int
595 verify_mime (LPDISPATCH mailitem)
596 {
597   int ret = 0;
598
599   LPMESSAGE message = get_oom_base_message (mailitem);
600   if (!message)
601     {
602       log_error ("%s:%s: Failed to get the base message",
603                  SRCNAME, __func__);
604       return 0;
605     }
606   ret = message_incoming_handler (message, NULL, true /*force */);
607   gpgol_release (message);
608
609   return ret;
610 }
611
612 /* do_reader_action
613    decrypts the content of an inspector. Controled by flags
614    similary to the do_composer_action.
615 */
616
617 HRESULT
618 do_reader_action (LPDISPATCH ctrl, int flags)
619 {
620   LPDISPATCH context = NULL;
621   LPDISPATCH selection = NULL;
622   LPDISPATCH wordEditor = NULL;
623   LPDISPATCH mailItem = NULL;
624   LPDISPATCH wordApplication = NULL;
625
626   struct sink_s decsinkmem;
627   sink_t decsink = &decsinkmem;
628   struct sink_s sinkmem;
629   sink_t sink = &sinkmem;
630
631   LPSTREAM tmpstream = NULL;
632   engine_filter_t filter = NULL;
633   HWND curWindow;
634   char* encData = NULL;
635   char* senderAddr = NULL;
636   char* subject = NULL;
637   int encDataLen = 0;
638   int rc = 0;
639   unsigned int session_number;
640   HRESULT hr;
641   STATSTG tmpStat;
642
643   protocol_t protocol;
644
645   hr = getContext (ctrl, &context);
646   if (FAILED(hr))
647       return hr;
648
649   memset (decsink, 0, sizeof *decsink);
650   memset (sink, 0, sizeof *sink);
651
652   curWindow = get_oom_context_window (context);
653
654   if (!(flags & DATA_BODY))
655     {
656       wordEditor = get_oom_object (context, "WordEditor");
657       wordApplication = get_oom_object (wordEditor, "get_Application");
658       selection = get_oom_object (wordApplication, "get_Selection");
659     }
660   mailItem = get_oom_object (context, "CurrentItem");
661
662   if ((!wordEditor || !wordApplication || !selection || !mailItem) &&
663       !(flags & DATA_BODY))
664     {
665       MessageBox (NULL,
666                   "Internal error in GpgOL.\n"
667                     "Could not find all objects.",
668                   _("GpgOL"),
669                   MB_ICONINFORMATION|MB_OK);
670       log_error ("%s:%s: Could not find all objects.",
671                  SRCNAME, __func__);
672       goto failure;
673     }
674
675   if (!mailItem)
676     {
677       /* This happens when we try to decrypt the body of a mail in the
678          explorer context. */
679       mailItem = get_oom_object (context, "Selection.Item(1)");
680
681       if (!mailItem)
682         {
683           MessageBox (NULL,
684                       _("Please select a Mail."),
685                       _("GpgOL"),
686                       MB_ICONINFORMATION|MB_OK);
687           goto failure;
688         }
689     }
690
691   if (flags & DATA_SELECTION)
692     {
693       encData = get_oom_string (selection, "Text");
694
695       if (!encData || (encDataLen = strlen (encData)) <= 1)
696         {
697           MessageBox (NULL,
698                       _("Please select the data you wish to decrypt."),
699                       _("GpgOL"),
700                       MB_ICONINFORMATION|MB_OK);
701           goto failure;
702         }
703     }
704   else if (flags & DATA_BODY)
705     {
706       encData = get_oom_string (mailItem, "Body");
707
708       if (!encData || (encDataLen = strlen (encData)) <= 1)
709         {
710           MessageBox (NULL,
711                       _("Nothing to decrypt."),
712                       _("GpgOL"),
713                       MB_ICONINFORMATION|MB_OK);
714           goto failure;
715         }
716     }
717
718   fix_linebreaks (encData, &encDataLen);
719
720   /* We check if the data we work on was opaque signed. This is
721      true for signed stuff created by ribbon-callbacks and not a
722      decent MIME implementation. So in that case we don't use
723      verify_mime */
724   if (!strstr (encData, OPAQUE_SIGNED_MARKER) && verify_mime (mailItem))
725     {
726       log_debug ("%s:%s: This was a mime message.",
727                  SRCNAME, __func__);
728
729       if (flags & OP_DECRYPT)
730         {
731           MessageBox (NULL,
732                       "This message is in MIME format. Due to technical restrictions "
733                       "it can only be decrypted once per session. To decrypt it again "
734                       "please restart Outlook and open the message.",
735                       _("GpgOL"),
736                       MB_ICONINFORMATION|MB_OK);
737         }
738       goto failure;
739     }
740
741   subject = get_oom_string (mailItem, "Subject");
742   if (get_oom_bool (mailItem, "Sent"))
743     {
744       char *addrType = get_oom_string (mailItem, "SenderEmailType");
745       if (addrType && strcmp("SMTP", addrType) == 0)
746         {
747           senderAddr = get_oom_string (mailItem, "SenderEmailAddress");
748         }
749       else
750         {
751           /* Not SMTP, fall back to try getting the property. */
752           LPDISPATCH sender = get_oom_object (mailItem, "Sender");
753           senderAddr = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
754           gpgol_release (sender);
755         }
756       xfree (addrType);
757     }
758   else
759     {
760       /* If the message has not been sent we might be composing
761          in this case use the current address */
762       LPDISPATCH sender = get_oom_object (mailItem, "Session.CurrentUser");
763       senderAddr = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
764       gpgol_release (sender);
765     }
766
767   /* Determine the protocol based on the content */
768   protocol = is_cms_data (encData, encDataLen) ? PROTOCOL_SMIME :
769     PROTOCOL_OPENPGP;
770
771   hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
772                          (SOF_UNIQUEFILENAME | STGM_DELETEONRELEASE
773                           | STGM_CREATE | STGM_READWRITE),
774                          NULL, GpgOLStr("GPG"), &tmpstream);
775
776   if (FAILED (hr))
777     {
778       log_error ("%s:%s: can't create temp file: hr=%#lx\n",
779                  SRCNAME, __func__, hr);
780       rc = -1;
781       goto failure;
782     }
783
784   sink->cb_data = tmpstream;
785   sink->writefnc = sink_std_write;
786
787   session_number = engine_new_session_number ();
788   if (engine_create_filter (&filter, write_buffer_for_cb, sink))
789     goto failure;
790
791   decsink->cb_data = filter;
792   decsink->writefnc = sink_encryption_write;
793
794   engine_set_session_number (filter, session_number);
795   engine_set_session_title (filter, subject ? subject : _("GpgOL"));
796
797   if (flags & OP_DECRYPT)
798     {
799       if ((rc=engine_decrypt_start (filter, curWindow,
800                                     protocol,
801                                     1, NULL)))
802         {
803           log_error ("%s:%s: engine decrypt start failed: %s",
804                      SRCNAME, __func__, gpg_strerror (rc));
805           goto failure;
806         }
807     }
808   else if (flags & OP_VERIFY)
809     {
810       log_debug ("Starting verify");
811       if ((rc=engine_verify_start (filter, curWindow,
812                                    NULL, 0, protocol, senderAddr)))
813         {
814           log_error ("%s:%s: engine verify start failed: %s",
815                      SRCNAME, __func__, gpg_strerror (rc));
816           goto failure;
817         }
818     }
819
820   /* Write the text in the decryption sink. */
821   rc = write_buffer (decsink, encData, encDataLen);
822
823   /* Flush the decryption sink and wait for the decryption to get
824      ready.  */
825   if ((rc = write_buffer (decsink, NULL, 0)))
826     goto failure;
827   if ((rc = engine_wait (filter)))
828     goto failure;
829   filter = NULL; /* Not valid anymore.  */
830   decsink->cb_data = NULL; /* Not needed anymore.  */
831
832   if (!sink->enc_counter)
833     {
834       log_debug ("%s:%s: nothing received from engine", SRCNAME, __func__);
835       goto failure;
836     }
837
838   /* Check the size of the decrypted data */
839   tmpstream->Stat (&tmpStat, 0);
840
841   if (tmpStat.cbSize.QuadPart > UINT_MAX)
842     {
843       log_error ("%s:%s: No one should write so large mails.",
844                  SRCNAME, __func__);
845       goto failure;
846     }
847
848   /* Copy the decrypted stream to the message editor.  */
849   {
850     LARGE_INTEGER off;
851     ULONG nread;
852     char buffer[(unsigned int)tmpStat.cbSize.QuadPart + 1];
853
854     memset (buffer, 0, sizeof buffer);
855
856     off.QuadPart = 0;
857     hr = tmpstream->Seek (off, STREAM_SEEK_SET, NULL);
858     if (hr)
859       {
860         log_error ("%s:%s: seeking back to the begin failed: hr=%#lx",
861                    SRCNAME, __func__, hr);
862         rc = gpg_error (GPG_ERR_EIO);
863         goto failure;
864       }
865     hr = tmpstream->Read (buffer, sizeof (buffer) - 1, &nread);
866     if (hr)
867       {
868         log_error ("%s:%s: IStream::Read failed: hr=%#lx",
869                    SRCNAME, __func__, hr);
870         rc = gpg_error (GPG_ERR_EIO);
871         goto failure;
872       }
873     if (strlen (buffer) > 1)
874       {
875         /* Now replace the crypto data with the decrypted data or show it
876         somehow.*/
877         int err = 0;
878         if (flags & DATA_SELECTION)
879           {
880             err = put_oom_string (selection, "Text", buffer);
881           }
882         else if (flags & DATA_BODY)
883           {
884             err = put_oom_string (mailItem, "Body", buffer);
885           }
886
887         if (err)
888           {
889             MessageBox (NULL, buffer,
890                         flags & OP_DECRYPT ? _("Plain text") :
891                         _("Signed text"),
892                         MB_ICONINFORMATION|MB_OK);
893           }
894       }
895     else
896       {
897         /* Just to be save not to overwrite the selection with
898            an empty buffer */
899         log_error ("%s:%s: unexpected problem ", SRCNAME, __func__);
900         goto failure;
901       }
902   }
903
904  failure:
905   if (rc)
906     log_debug ("%s:%s: failed rc=%d (%s) <%s>", SRCNAME, __func__, rc,
907                gpg_strerror (rc), gpg_strsource (rc));
908   engine_cancel (filter);
909   gpgol_release (mailItem);
910   gpgol_release (selection);
911   gpgol_release (wordEditor);
912   gpgol_release (wordApplication);
913   xfree (encData);
914   xfree (senderAddr);
915   xfree (subject);
916   if (tmpstream)
917     gpgol_release (tmpstream);
918
919   return S_OK;
920 }
921
922
923 /* getIcon
924    Loads a PNG image from the resurce converts it into a Bitmap
925    and Wraps it in an PictureDispatcher that is returned as result.
926
927    Based on documentation from:
928    http://www.codeproject.com/Articles/3537/Loading-JPG-PNG-resources-using-GDI
929 */
930
931 HRESULT
932 getIcon (int id, VARIANT* result)
933 {
934   PICTDESC pdesc;
935   LPDISPATCH pPict;
936   HRESULT hr;
937   Gdiplus::GdiplusStartupInput gdiplusStartupInput;
938   Gdiplus::Bitmap* pbitmap;
939   ULONG_PTR gdiplusToken;
940   HRSRC hResource;
941   DWORD imageSize;
942   const void* pResourceData;
943   HGLOBAL hBuffer;
944
945   memset (&pdesc, 0, sizeof pdesc);
946   pdesc.cbSizeofstruct = sizeof pdesc;
947   pdesc.picType = PICTYPE_BITMAP;
948
949   if (!result)
950     {
951       log_error ("getIcon called without result variant.");
952       return E_POINTER;
953     }
954
955   /* Initialize GDI */
956   gdiplusStartupInput.DebugEventCallback = NULL;
957   gdiplusStartupInput.SuppressBackgroundThread = FALSE;
958   gdiplusStartupInput.SuppressExternalCodecs = FALSE;
959   gdiplusStartupInput.GdiplusVersion = 1;
960   GdiplusStartup (&gdiplusToken, &gdiplusStartupInput, NULL);
961
962   /* Get the image from the resource file */
963   hResource = FindResource (glob_hinst, MAKEINTRESOURCE(id), RT_RCDATA);
964   if (!hResource)
965     {
966       log_error ("%s:%s: failed to find image: %i",
967                  SRCNAME, __func__, id);
968       return E_FAIL;
969     }
970
971   imageSize = SizeofResource (glob_hinst, hResource);
972   if (!imageSize)
973     return E_FAIL;
974
975   pResourceData = LockResource (LoadResource(glob_hinst, hResource));
976
977   if (!pResourceData)
978     {
979       log_error ("%s:%s: failed to load image: %i",
980                  SRCNAME, __func__, id);
981       return E_FAIL;
982     }
983
984   hBuffer = GlobalAlloc (GMEM_MOVEABLE, imageSize);
985
986   if (hBuffer)
987     {
988       void* pBuffer = GlobalLock (hBuffer);
989       if (pBuffer)
990         {
991           IStream* pStream = NULL;
992           CopyMemory (pBuffer, pResourceData, imageSize);
993
994           if (CreateStreamOnHGlobal (hBuffer, FALSE, &pStream) == S_OK)
995             {
996               pbitmap = Gdiplus::Bitmap::FromStream (pStream);
997               gpgol_release (pStream);
998               if (!pbitmap || pbitmap->GetHBITMAP (0, &pdesc.bmp.hbitmap))
999                 {
1000                   log_error ("%s:%s: failed to get PNG.",
1001                              SRCNAME, __func__);
1002                 }
1003             }
1004         }
1005       GlobalUnlock (pBuffer);
1006     }
1007   GlobalFree (hBuffer);
1008
1009   Gdiplus::GdiplusShutdown (gdiplusToken);
1010
1011   /* Wrap the image into an OLE object.  */
1012   hr = OleCreatePictureIndirect (&pdesc, IID_IPictureDisp,
1013                                  TRUE, (void **) &pPict);
1014   if (hr != S_OK || !pPict)
1015     {
1016       log_error ("%s:%s: OleCreatePictureIndirect failed: hr=%#lx\n",
1017                  SRCNAME, __func__, hr);
1018       return -1;
1019     }
1020
1021   result->pdispVal = pPict;
1022   result->vt = VT_DISPATCH;
1023
1024   return S_OK;
1025 }
1026
1027 /* Adds an encrypted attachment if the flag OP_SIGN is set
1028    a detached signature of the encrypted file is also added. */
1029 static HRESULT
1030 attachEncryptedFile (LPDISPATCH ctrl, int flags)
1031 {
1032   LPDISPATCH context = NULL;
1033   LPDISPATCH mailItem = NULL;
1034   LPDISPATCH sender = NULL;
1035   LPDISPATCH recipients = NULL;
1036   HRESULT hr;
1037   char* senderAddr = NULL;
1038   char** recipientAddrs = NULL;
1039   char* subject = NULL;
1040
1041   HWND curWindow;
1042   char *fileToEncrypt = NULL;
1043   wchar_t *fileToEncryptW = NULL;
1044   wchar_t *encryptedFile = NULL;
1045   wchar_t *attachName = NULL;
1046   HANDLE hFile = NULL;
1047   HANDLE hEncFile = NULL;
1048
1049   unsigned int session_number;
1050   struct sink_s encsinkmem;
1051   sink_t encsink = &encsinkmem;
1052   struct sink_s sinkmem;
1053   sink_t sink = &sinkmem;
1054   engine_filter_t filter = NULL;
1055   protocol_t protocol;
1056   int rc = 0;
1057   int i = 0;
1058
1059   memset (encsink, 0, sizeof *encsink);
1060   memset (sink, 0, sizeof *sink);
1061
1062   hr = getContext (ctrl, &context);
1063   if (FAILED(hr))
1064       return hr;
1065
1066   /* First do the check for recipients as this is likely
1067      to fail */
1068   mailItem = get_oom_object (context, "CurrentItem");
1069   sender = get_oom_object (mailItem, "Session.CurrentUser");
1070   recipients = get_oom_object (mailItem, "Recipients");
1071   recipientAddrs = get_oom_recipients (recipients);
1072
1073   if (!recipientAddrs || !(*recipientAddrs))
1074     {
1075       MessageBox (NULL,
1076                   _("Please add at least one recipent."),
1077                   _("GpgOL"),
1078                   MB_ICONINFORMATION|MB_OK);
1079       goto failure;
1080     }
1081
1082   /* Get a file handle to read from */
1083   fileToEncrypt = get_open_filename (NULL, _("Select file to encrypt"));
1084
1085   if (!fileToEncrypt)
1086     {
1087       log_debug ("No file selected");
1088       goto failure;
1089     }
1090
1091   fileToEncryptW = utf8_to_wchar2 (fileToEncrypt, strlen(fileToEncrypt));
1092   xfree (fileToEncrypt);
1093
1094   hFile = CreateFileW (fileToEncryptW,
1095                        GENERIC_READ,
1096                        FILE_SHARE_READ,
1097                        NULL,
1098                        OPEN_EXISTING,
1099                        FILE_ATTRIBUTE_NORMAL,
1100                        NULL);
1101   if (hFile == INVALID_HANDLE_VALUE)
1102     {
1103       /* Should not happen as the Open File dialog
1104          should have prevented this.
1105          Maybe this also happens when a file is
1106          not readable. In that case we might want
1107          to switch to a localized error naming the file. */
1108       MessageBox (NULL,
1109                   "Internal error in GpgOL.\n"
1110                   "Could not open File.",
1111                   _("GpgOL"),
1112                   MB_ICONERROR|MB_OK);
1113       return S_OK;
1114     }
1115
1116   /* Now do the encryption preperations */
1117
1118   if (!mailItem || !sender || !recipients)
1119     {
1120       MessageBox (NULL,
1121                   "Internal error in GpgOL.\n"
1122                   "Could not find all objects.",
1123                   _("GpgOL"),
1124                   MB_ICONERROR|MB_OK);
1125       log_error ("%s:%s: Could not find all objects.",
1126                  SRCNAME, __func__);
1127       goto failure;
1128     }
1129
1130   senderAddr = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
1131
1132   curWindow = get_oom_context_window (context);
1133
1134   session_number = engine_new_session_number ();
1135
1136   subject = get_oom_string (mailItem, "Subject");
1137
1138   /* Prepare the encryption sink */
1139   if ((rc = engine_create_filter (&filter, write_buffer_for_cb, sink)))
1140     {
1141       goto failure;
1142     }
1143
1144   encsink->cb_data = filter;
1145   encsink->writefnc = sink_encryption_write;
1146
1147   engine_set_session_number (filter, session_number);
1148   engine_set_session_title (filter, subject ? subject :_("GpgOL"));
1149   if ((rc=engine_encrypt_prepare (filter, curWindow,
1150                                   PROTOCOL_UNKNOWN,
1151                                   ENGINE_FLAG_BINARY_OUTPUT,
1152                                   senderAddr, recipientAddrs, &protocol)))
1153     {
1154       log_error ("%s:%s: engine encrypt prepare failed : %s",
1155                  SRCNAME, __func__, gpg_strerror (rc));
1156       goto failure;
1157     }
1158
1159   attachName = get_pretty_attachment_name (fileToEncryptW, protocol, 0);
1160
1161   if (!attachName)
1162     {
1163       log_error ("%s:%s: Could not get a decent attachment name",
1164                  SRCNAME, __func__);
1165       goto failure;
1166     }
1167
1168   encryptedFile = get_tmp_outfile (attachName, &hEncFile);
1169   sink->cb_data = hEncFile;
1170   sink->writefnc = sink_file_write;
1171
1172   if ((rc=engine_encrypt_start (filter, 0)))
1173     {
1174       log_error ("%s:%s: engine encrypt start failed: %s",
1175                  SRCNAME, __func__, gpg_strerror (rc));
1176       goto failure;
1177     }
1178
1179   if ((rc=copyFileToSink (hFile, encsink)))
1180     goto failure;
1181
1182   /* Lets hope the user did not select a huge file. We are hanging
1183    here until encryption is completed.. */
1184   if ((rc = engine_wait (filter)))
1185     goto failure;
1186
1187   filter = NULL; /* Not valid anymore.  */
1188   encsink->cb_data = NULL; /* Not needed anymore.  */
1189
1190   if (!sink->enc_counter)
1191     {
1192       log_error ("%s:%s: nothing received from engine", SRCNAME, __func__);
1193       goto failure;
1194     }
1195
1196   /* Now we have an encrypted file behind encryptedFile. Let's add it */
1197   add_oom_attachment (mailItem, encryptedFile, nullptr);
1198
1199   if (flags & OP_SIGN)
1200     {
1201       attachSignature (mailItem, subject, hEncFile, protocol, session_number,
1202                        curWindow, encryptedFile, senderAddr);
1203     }
1204
1205 failure:
1206   if (filter)
1207     engine_cancel (filter);
1208
1209   if (hEncFile)
1210     {
1211       CloseHandle (hEncFile);
1212       DeleteFileW (encryptedFile);
1213     }
1214   xfree (senderAddr);
1215   xfree (encryptedFile);
1216   xfree (fileToEncryptW);
1217   xfree (attachName);
1218   xfree (subject);
1219   gpgol_release (mailItem);
1220   gpgol_release (sender);
1221   gpgol_release (recipients);
1222
1223   if (hFile)
1224     CloseHandle (hFile);
1225   if (recipientAddrs)
1226     {
1227       for (i=0; recipientAddrs && recipientAddrs[i]; i++)
1228         xfree (recipientAddrs[i]);
1229       xfree (recipientAddrs);
1230     }
1231
1232   return S_OK;
1233 }
1234
1235 HRESULT
1236 startCertManager (LPDISPATCH ctrl)
1237 {
1238   HRESULT hr;
1239   LPDISPATCH context;
1240   HWND curWindow;
1241
1242   hr = getContext (ctrl, &context);
1243   if (FAILED(hr))
1244       return hr;
1245
1246   curWindow = get_oom_context_window (context);
1247
1248   engine_start_keymanager (curWindow);
1249   return S_OK;
1250 }
1251
1252 HRESULT
1253 decryptBody (LPDISPATCH ctrl)
1254 {
1255   return do_reader_action (ctrl, OP_DECRYPT | DATA_BODY);
1256 }
1257
1258 HRESULT
1259 decryptSelection (LPDISPATCH ctrl)
1260 {
1261   return do_reader_action (ctrl, OP_DECRYPT | DATA_SELECTION);
1262 }
1263
1264 HRESULT
1265 encryptBody (LPDISPATCH ctrl)
1266 {
1267   return do_composer_action (ctrl, OP_ENCRYPT | DATA_BODY);
1268 }
1269
1270 HRESULT
1271 encryptSelection (LPDISPATCH ctrl)
1272 {
1273   return do_composer_action (ctrl, OP_ENCRYPT | DATA_SELECTION);
1274 }
1275
1276 HRESULT
1277 addEncSignedAttachment (LPDISPATCH ctrl)
1278 {
1279   return attachEncryptedFile (ctrl, OP_SIGN);
1280 }
1281
1282 HRESULT
1283 addEncAttachment (LPDISPATCH ctrl)
1284 {
1285   return attachEncryptedFile (ctrl, 0);
1286 }
1287
1288 HRESULT signBody (LPDISPATCH ctrl)
1289 {
1290   return do_composer_action (ctrl, DATA_BODY | OP_SIGN);
1291 }
1292
1293 HRESULT verifyBody (LPDISPATCH ctrl)
1294 {
1295   return do_reader_action (ctrl, DATA_BODY | OP_VERIFY);
1296 }
1297
1298 HRESULT
1299 mark_mime_action (LPDISPATCH ctrl, int flags, bool is_explorer)
1300 {
1301   HRESULT hr;
1302   HRESULT rc = E_FAIL;
1303   LPDISPATCH context = NULL,
1304              mailitem = NULL;
1305   LPMESSAGE message = NULL;
1306   int oldflags,
1307       newflags;
1308
1309   log_debug ("%s:%s: enter", SRCNAME, __func__);
1310   hr = getContext (ctrl, &context);
1311   if (FAILED(hr))
1312       return hr;
1313
1314   mailitem = get_oom_object (context, is_explorer ? "ActiveInlineResponse" :
1315                                                     "CurrentItem");
1316
1317   if (!mailitem)
1318     {
1319       log_error ("%s:%s: Failed to get mailitem.",
1320                  SRCNAME, __func__);
1321       goto done;
1322     }
1323
1324   message = get_oom_base_message (mailitem);
1325
1326   if (!message)
1327     {
1328       log_error ("%s:%s: Failed to get message.",
1329                  SRCNAME, __func__);
1330       goto done;
1331     }
1332
1333   oldflags = get_gpgol_draft_info_flags (message);
1334
1335   newflags = oldflags xor flags;
1336
1337   if (set_gpgol_draft_info_flags (message, newflags))
1338     {
1339       log_error ("%s:%s: Failed to set draft flags.",
1340                  SRCNAME, __func__);
1341     }
1342
1343   rc = S_OK;
1344
1345   /*  We need to invalidate the UI to update the toggle
1346       states of the subbuttons and the top button. Yeah,
1347       we invalidate a lot *sigh* */
1348   gpgoladdin_invalidate_ui ();
1349
1350 done:
1351   gpgol_release (context);
1352   gpgol_release (mailitem);
1353   gpgol_release (message);
1354
1355   return rc;
1356 }
1357
1358 /* Get the state of encrypt / sign toggle buttons.
1359   flag values: 1 get the state of the encrypt button.
1360                2 get the state of the sign button.
1361   If is_explorer is set to true we look at the inline response.
1362 */
1363 HRESULT get_crypt_pressed (LPDISPATCH ctrl, int flags, VARIANT *result,
1364                            bool is_explorer)
1365 {
1366   HRESULT hr;
1367   bool value;
1368   LPDISPATCH context = NULL,
1369              mailitem = NULL;
1370   LPMESSAGE message = NULL;
1371
1372   result->vt = VT_BOOL | VT_BYREF;
1373   result->pboolVal = (VARIANT_BOOL*) xmalloc (sizeof (VARIANT_BOOL));
1374   *(result->pboolVal) = VARIANT_FALSE;
1375
1376   /* First the usual defensive check about our parameters */
1377   if (!ctrl || !result)
1378     {
1379       log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__);
1380       return E_FAIL;
1381     }
1382
1383   hr = getContext (ctrl, &context);
1384
1385   if (hr)
1386     {
1387       log_error ("%s:%s:%i : hresult %lx", SRCNAME, __func__, __LINE__,
1388                  hr);
1389       return E_FAIL;
1390     }
1391
1392   mailitem = get_oom_object (context, is_explorer ? "ActiveInlineResponse" :
1393                                                     "CurrentItem");
1394
1395   if (!mailitem)
1396     {
1397       log_error ("%s:%s: Failed to get mailitem.",
1398                  SRCNAME, __func__);
1399       goto done;
1400     }
1401
1402   message = get_oom_base_message (mailitem);
1403
1404   if (!message)
1405     {
1406       log_error ("%s:%s: No message found.",
1407                  SRCNAME, __func__);
1408       goto done;
1409     }
1410
1411   value = (get_gpgol_draft_info_flags (message) & flags) == flags;
1412
1413   *(result->pboolVal) = value ? VARIANT_TRUE : VARIANT_FALSE;
1414
1415 done:
1416   gpgol_release (context);
1417   gpgol_release (mailitem);
1418   gpgol_release (message);
1419
1420   return S_OK;
1421 }
1422
1423 static Mail *
1424 get_mail_from_control (LPDISPATCH ctrl)
1425 {
1426   HRESULT hr;
1427   LPDISPATCH context = NULL,
1428              mailitem = NULL;
1429   if (!ctrl)
1430     {
1431       log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__);
1432       return NULL;
1433     }
1434   hr = getContext (ctrl, &context);
1435
1436   if (hr)
1437     {
1438       log_error ("%s:%s:%i : hresult %lx", SRCNAME, __func__, __LINE__,
1439                  hr);
1440       return NULL;
1441     }
1442
1443   char *ctx_name = get_object_name (context);
1444
1445   if (!ctx_name)
1446     {
1447       log_error ("%s:%s: Failed to get context name",
1448                  SRCNAME, __func__);
1449       gpgol_release (context);
1450       return NULL;
1451
1452     }
1453   if (!strcmp (ctx_name, "_Inspector"))
1454     {
1455       mailitem = get_oom_object (context, "CurrentItem");
1456     }
1457   else if (!strcmp (ctx_name, "_Explorer"))
1458     {
1459       mailitem = get_oom_object (context, "Selection.Item(1)");
1460     }
1461
1462   gpgol_release (context);
1463   if (!mailitem)
1464     {
1465       log_error ("%s:%s: Failed to get mailitem. From %s",
1466                  SRCNAME, __func__, ctx_name);
1467       xfree (ctx_name);
1468       return NULL;
1469     }
1470   xfree (ctx_name);
1471
1472   char *uid;
1473   /* Get the uid of this item. */
1474   uid = get_unique_id (mailitem, 0, nullptr);
1475   if (!uid)
1476     {
1477       LPMESSAGE msg = get_oom_base_message (mailitem);
1478       uid = mapi_get_uid (msg);
1479       gpgol_release (msg);
1480       if (!uid)
1481         {
1482           log_debug ("%s:%s: Failed to get uid for %p",
1483                    SRCNAME, __func__, mailitem);
1484           gpgol_release (mailitem);
1485           return NULL;
1486         }
1487     }
1488
1489   auto ret = Mail::get_mail_for_uuid (uid);
1490   xfree (uid);
1491   if (!ret)
1492     {
1493       log_error ("%s:%s: Failed to find mail %p in map.",
1494                  SRCNAME, __func__, mailitem);
1495     }
1496   gpgol_release (mailitem);
1497   return ret;
1498 }
1499
1500 /* Helper to reduce code duplication.*/
1501 #define MY_MAIL_GETTER \
1502   if (!ctrl) \
1503     { \
1504       log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__); \
1505       return E_FAIL; \
1506     } \
1507   const auto mail = get_mail_from_control (ctrl); \
1508   if (!mail) \
1509     { \
1510       log_oom ("%s:%s:%i Failed to get mail", \
1511                SRCNAME, __func__, __LINE__); \
1512     }
1513
1514 HRESULT get_is_signed (LPDISPATCH ctrl, VARIANT *result)
1515 {
1516   MY_MAIL_GETTER
1517
1518   result->vt = VT_BOOL | VT_BYREF;
1519   result->pboolVal = (VARIANT_BOOL*) xmalloc (sizeof (VARIANT_BOOL));
1520   *(result->pboolVal) = !mail ? VARIANT_FALSE :
1521                         mail->is_signed () ? VARIANT_TRUE : VARIANT_FALSE;
1522
1523   return S_OK;
1524 }
1525
1526 HRESULT get_sig_label (LPDISPATCH ctrl, VARIANT *result)
1527 {
1528   MY_MAIL_GETTER
1529
1530   result->vt = VT_BSTR;
1531   wchar_t *w_result;
1532   if (!mail)
1533     {
1534       log_debug ("%s:%s: No mail.",
1535                  SRCNAME, __func__);
1536       w_result = utf8_to_wchar (_("Not Trusted"));
1537       result->bstrVal = SysAllocString (w_result);
1538       xfree (w_result);
1539       return S_OK;
1540     }
1541   bool valid = mail->is_valid_sig ();
1542   const auto pair = mail->get_valid_sig ();
1543   bool fully = pair.first.validity() == GpgME::Signature::Validity::Full ||
1544                pair.first.validity() == GpgME::Signature::Validity::Ultimate;
1545   if (valid && fully)
1546     {
1547       w_result = utf8_to_wchar (_("Fully Trusted"));
1548     }
1549   else if (valid)
1550     {
1551       w_result = utf8_to_wchar (_("Trusted"));
1552     }
1553   else
1554     {
1555       w_result = utf8_to_wchar (_("Not Trusted"));
1556     }
1557   result->bstrVal = SysAllocString (w_result);
1558   xfree (w_result);
1559   return S_OK;
1560 }
1561
1562 HRESULT get_sig_ttip (LPDISPATCH ctrl, VARIANT *result)
1563 {
1564   MY_MAIL_GETTER
1565
1566   result->vt = VT_BSTR;
1567   wchar_t *w_result;
1568   if (mail && mail->is_signed ())
1569     {
1570       char *buf;
1571       gpgrt_asprintf (&buf, _("This is a signed %s message."),
1572                       mail->is_smime() ? _("S/MIME") : _("OpenPGP"));
1573       w_result = utf8_to_wchar (buf);
1574       xfree(buf);
1575     }
1576   else
1577     {
1578       w_result = utf8_to_wchar (_("This message is not cryptographically signed."));
1579     }
1580   result->bstrVal = SysAllocString (w_result);
1581   xfree (w_result);
1582   return S_OK;
1583 }
1584
1585 HRESULT get_sig_stip (LPDISPATCH ctrl, VARIANT *result)
1586 {
1587   MY_MAIL_GETTER
1588
1589   result->vt = VT_BSTR;
1590   if (!mail)
1591     {
1592       log_debug ("%s:%s: No mail.",
1593                  SRCNAME, __func__);
1594       wchar_t *w_result;
1595       w_result = utf8_to_wchar (_("You cannot be sure who wrote the message."));
1596       result->bstrVal = SysAllocString (w_result);
1597       xfree (w_result);
1598       return S_OK;
1599     }
1600   const auto message = mail->get_signature_status ();
1601   wchar_t *w_message = utf8_to_wchar (message.c_str());
1602   result->bstrVal = SysAllocString (w_message);
1603   xfree (w_message);
1604   return S_OK;
1605 }
1606
1607 HRESULT launch_cert_details (LPDISPATCH ctrl)
1608 {
1609   MY_MAIL_GETTER
1610
1611   if (!mail)
1612     {
1613       log_debug ("%s:%s: No mail.",
1614                  SRCNAME, __func__);
1615       return S_OK;
1616     }
1617
1618   char *uiserver = get_uiserver_name ();
1619   bool showError = false;
1620   if (uiserver)
1621     {
1622       std::string path (uiserver);
1623       xfree (uiserver);
1624       if (path.find("kleopatra.exe") != std::string::npos)
1625         {
1626         size_t dpos;
1627         if ((dpos = path.find(" --daemon")) != std::string::npos)
1628             {
1629               path.erase(dpos, strlen(" --daemon"));
1630             }
1631           auto ctx = Context::createForEngine(SpawnEngine);
1632           if (!ctx)
1633             {
1634               log_error ("%s:%s: No spawn engine.",
1635                          SRCNAME, __func__);
1636             }
1637             const char *argv[] = {path.c_str(),
1638                                   "--query",
1639                                   mail->get_sig_fpr(),
1640                                   NULL };
1641             log_debug ("%s:%s: Starting %s %s %s",
1642                        SRCNAME, __func__, path.c_str(), argv[1], argv[2]);
1643             Data d(Data::null);
1644             ctx->spawnAsync(path.c_str(), argv, d, d,
1645                             d, Context::SpawnNone);
1646         }
1647       else
1648         {
1649           showError = true;
1650         }
1651     }
1652   else
1653     {
1654       showError = true;
1655     }
1656
1657   if (showError)
1658     {
1659       MessageBox (NULL,
1660                   _("Could not find Kleopatra.\n"
1661                   "Please reinstall Gpg4win with the Kleopatra component enabled."),
1662                   _("GpgOL"),
1663                   MB_ICONINFORMATION|MB_OK);
1664     }
1665   return S_OK;
1666 }
1667
1668 HRESULT get_sigstate_icon (LPDISPATCH ctrl, VARIANT *result)
1669 {
1670   MY_MAIL_GETTER
1671
1672   if (mail)
1673     {
1674       return getIcon (mail->get_signature_icon_id (), result);
1675     }
1676   return getIcon (IDI_EMBLEM_INFORMATION_64_PNG, result);
1677 }