1 /* ribbon-callbacks.h - Callbacks for the ribbon extension interface
2 * Copyright (C) 2013 Intevation GmbH
4 * This file is part of GpgOL.
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.
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.
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/>.
32 #include "ribbon-callbacks.h"
33 #include "gpgoladdin.h"
37 #include "mymapitags.h"
38 #include "myexchext.h"
44 #include "engine-assuan.h"
46 #include "mimemaker.h"
52 #include <gpgme++/context.h>
53 #include <gpgme++/data.h>
55 using namespace GpgME;
57 #define OPAQUE_SIGNED_MARKER "-----BEGIN PGP MESSAGE-----"
59 /* Gets the context of a ribbon control. And prints some
60 useful debug output */
61 HRESULT getContext (LPDISPATCH ctrl, LPDISPATCH *context)
63 *context = get_oom_object (ctrl, "get_Context");
66 char *name = get_object_name (*context);
67 log_debug ("%s:%s: contextObj: %s",
68 SRCNAME, __func__, name);
72 return context ? S_OK : E_FAIL;
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 */
82 /* Read hfile in chunks of 4KB and writes them to the sink */
84 copyFileToSink (HANDLE hFile, sink_t sink)
90 if (!ReadFile (hFile, buf, sizeof buf, &bytesRead, NULL))
92 log_error ("%s:%s: Could not read source file.",
96 if (write_buffer (sink, bytesRead ? buf : NULL, bytesRead))
98 log_error ("%s:%s: Could not write out buffer",
108 attachSignature (LPDISPATCH mailItem, char *subject, HANDLE hFileToSign,
109 protocol_t protocol, unsigned int session_number,
110 HWND curWindow, wchar_t *fileNameToSign, char *sender)
112 wchar_t *sigName = NULL;
113 wchar_t *sigFileName = NULL;
114 HANDLE hSigFile = NULL;
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;
122 memset (encsink, 0, sizeof *encsink);
123 memset (sink, 0, sizeof *sink);
125 /* Prepare a fresh filter */
126 if ((rc = engine_create_filter (&filter, write_buffer_for_cb, sink)))
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"));
135 if (engine_sign_start (filter, curWindow, protocol, sender, &protocol))
138 sigName = get_pretty_attachment_name (fileNameToSign, protocol, 1);
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;
149 log_error ("%s:%s: Could not get a decent attachment name",
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)))
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)))
165 filter = NULL; /* Not valid anymore. */
166 encsink->cb_data = NULL; /* Not needed anymore. */
168 if (!sink->enc_counter)
170 log_error ("%s:%s: nothing received from engine", SRCNAME, __func__);
174 /* Now we have an encrypted file behind encryptedFile. Let's add it */
175 add_oom_attachment (mailItem, sigFileName, nullptr);
182 CloseHandle (hSigFile);
183 DeleteFileW (sigFileName);
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.
196 do_composer_action (LPDISPATCH ctrl, int flags)
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;
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;
219 unsigned int session_number;
223 log_debug ("%s:%s: enter", SRCNAME, __func__);
225 hr = getContext (ctrl, &context);
229 memset (encsink, 0, sizeof *encsink);
230 memset (sink, 0, sizeof *sink);
232 curWindow = get_oom_context_window (context);
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");
241 if (!wordEditor || !application || !selection || !mailItem ||
242 !sender || !recipients)
245 "Internal error in GpgOL.\n"
246 "Could not find all objects.",
248 MB_ICONINFORMATION|MB_OK);
249 log_error ("%s:%s: Could not find all objects.",
254 if (flags & DATA_SELECTION)
256 plaintext = get_oom_string (selection, "Text");
258 if (!plaintext || strlen (plaintext) <= 1)
261 _("Please select text to encrypt."),
263 MB_ICONINFORMATION|MB_OK);
267 else if (flags & DATA_BODY)
269 plaintext = get_oom_string (mailItem, "Body");
270 if (!plaintext || strlen (plaintext) <= 1)
273 _("Textbody empty."),
275 MB_ICONINFORMATION|MB_OK);
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);
288 log_error ("%s:%s: can't create temp file: hr=%#lx\n",
289 SRCNAME, __func__, hr);
294 sink->cb_data = tmpstream;
295 sink->writefnc = sink_std_write;
297 /* Now lets prepare our encryption */
298 session_number = engine_new_session_number ();
300 /* Prepare the encryption sink */
302 if (engine_create_filter (&filter, write_buffer_for_cb, sink))
307 encsink->cb_data = filter;
308 encsink->writefnc = sink_encryption_write;
310 engine_set_session_number (filter, session_number);
311 engine_set_session_title (filter, _("GpgOL"));
313 senderAddr = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
315 if (flags & OP_ENCRYPT)
317 recipientAddrs = get_oom_recipients (recipients);
319 if (!recipientAddrs || !(*recipientAddrs))
322 _("Please add at least one recipent."),
324 MB_ICONINFORMATION|MB_OK);
328 if ((rc=engine_encrypt_prepare (filter, curWindow,
331 ENGINE_FLAG_SIGN_FOLLOWS : 0,
332 senderAddr, recipientAddrs,
335 log_error ("%s:%s: engine encrypt prepare failed : %s",
336 SRCNAME, __func__, gpg_strerror (rc));
340 if ((rc=engine_encrypt_start (filter, 0)))
342 log_error ("%s:%s: engine encrypt start failed: %s",
343 SRCNAME, __func__, gpg_strerror (rc));
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)))
354 log_error ("%s:%s: engine sign start failed: %s",
355 SRCNAME, __func__, gpg_strerror (rc));
360 /* Write the text in the encryption sink. */
361 rc = write_buffer (encsink, plaintext, strlen (plaintext));
365 log_error ("%s:%s: writing tmpstream to encsink failed: %s",
366 SRCNAME, __func__, gpg_strerror (rc));
369 /* Flush the encryption sink and wait for the encryption to get
371 if ((rc = write_buffer (encsink, NULL, 0)))
373 if ((rc = engine_wait (filter)))
375 filter = NULL; /* Not valid anymore. */
376 encsink->cb_data = NULL; /* Not needed anymore. */
378 if (!sink->enc_counter)
380 log_debug ("%s:%s: nothing received from engine", SRCNAME, __func__);
384 /* Check the size of the encrypted data */
385 tmpstream->Stat (&tmpStat, 0);
387 if (tmpStat.cbSize.QuadPart > UINT_MAX)
389 log_error ("%s:%s: No one should write so large mails.",
394 /* Copy the encrypted stream to the message editor. */
399 char buffer[(unsigned int)tmpStat.cbSize.QuadPart + 1];
401 memset (buffer, 0, sizeof buffer);
404 hr = tmpstream->Seek (off, STREAM_SEEK_SET, NULL);
407 log_error ("%s:%s: seeking back to the begin failed: hr=%#lx",
408 SRCNAME, __func__, hr);
409 rc = gpg_error (GPG_ERR_EIO);
412 hr = tmpstream->Read (buffer, sizeof (buffer) - 1, &nread);
415 log_error ("%s:%s: IStream::Read failed: hr=%#lx",
416 SRCNAME, __func__, hr);
417 rc = gpg_error (GPG_ERR_EIO);
420 if (strlen (buffer) > 1)
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,
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);
437 else if (protocol == PROTOCOL_SMIME)
439 unsigned int enclosedSize = strlen (buffer) + 34 + 31 + 1;
440 char enclosedData[enclosedSize];
441 snprintf (enclosedData, sizeof enclosedData,
442 "-----BEGIN ENCRYPTED MESSAGE-----\r\n"
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);
453 if (flags & DATA_SELECTION)
454 put_oom_string (selection, "Text", buffer);
455 else if (flags & DATA_BODY)
457 put_oom_string (mailItem, "Body", buffer);
463 /* Just to be save not to overwrite the selection with
465 log_error ("%s:%s: unexpected problem ", SRCNAME, __func__);
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);
486 for (i=0; recipientAddrs && recipientAddrs[i]; i++)
487 xfree (recipientAddrs[i]);
488 xfree (recipientAddrs);
490 log_debug ("%s:%s: leave", SRCNAME, __func__);
496 decryptAttachments (LPDISPATCH ctrl)
498 LPDISPATCH context = NULL;
499 LPDISPATCH attachmentSelection;
506 hr = getContext(ctrl, &context);
508 attachmentSelection = get_oom_object (context, "AttachmentSelection");
509 if (!attachmentSelection)
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;
517 attachmentCount = get_oom_int (attachmentSelection, "Count");
519 curWindow = get_oom_context_window (context);
522 char *filenames[attachmentCount + 1];
523 filenames[attachmentCount] = NULL;
524 /* Yes the items start at 1! */
525 for (i = 1; i <= attachmentCount; i++)
529 wchar_t *wcsOutFilename;
530 DISPPARAMS saveParams;
532 LPDISPATCH attachmentObj;
535 snprintf (buf, sizeof (buf), "Item(%i)", i);
536 attachmentObj = get_oom_object (attachmentSelection, buf);
539 /* Should be impossible */
540 filenames[i-1] = NULL;
541 log_error ("%s:%s: could not find Item %i;",
542 SRCNAME, __func__, i);
545 filename = get_oom_string (attachmentObj, "FileName");
547 saveID = lookup_oom_dispid (attachmentObj, "SaveAsFile");
549 saveParams.rgvarg = aVariant;
550 saveParams.rgvarg[0].vt = VT_BSTR;
551 filenames[i-1] = get_save_filename (NULL, filename);
554 if (!filenames [i-1])
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;
563 hr = attachmentObj->Invoke (saveID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
564 DISPATCH_METHOD, &saveParams,
566 SysFreeString (saveParams.rgvarg[0].bstrVal);
567 gpgol_release (attachmentObj);
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);
579 gpgol_release (attachmentSelection);
580 err = op_assuan_start_decrypt_files (curWindow, filenames);
581 for (i = 0; i < attachmentCount; i++)
582 xfree (filenames[i]);
585 log_debug ("%s:%s: Leaving. Err: %i",
586 SRCNAME, __func__, err);
588 return S_OK; /* If we return an error outlook will show that our
589 callback function failed in an ugly window. */
592 /* MIME erify mail helper. Returns 0 if it
593 was not called with a MIME crypto message or on error. */
595 verify_mime (LPDISPATCH mailitem)
599 LPMESSAGE message = get_oom_base_message (mailitem);
602 log_error ("%s:%s: Failed to get the base message",
606 ret = message_incoming_handler (message, NULL, true /*force */);
607 gpgol_release (message);
613 decrypts the content of an inspector. Controled by flags
614 similary to the do_composer_action.
618 do_reader_action (LPDISPATCH ctrl, int flags)
620 LPDISPATCH context = NULL;
621 LPDISPATCH selection = NULL;
622 LPDISPATCH wordEditor = NULL;
623 LPDISPATCH mailItem = NULL;
624 LPDISPATCH wordApplication = NULL;
626 struct sink_s decsinkmem;
627 sink_t decsink = &decsinkmem;
628 struct sink_s sinkmem;
629 sink_t sink = &sinkmem;
631 LPSTREAM tmpstream = NULL;
632 engine_filter_t filter = NULL;
634 char* encData = NULL;
635 char* senderAddr = NULL;
636 char* subject = NULL;
639 unsigned int session_number;
645 hr = getContext (ctrl, &context);
649 memset (decsink, 0, sizeof *decsink);
650 memset (sink, 0, sizeof *sink);
652 curWindow = get_oom_context_window (context);
654 if (!(flags & DATA_BODY))
656 wordEditor = get_oom_object (context, "WordEditor");
657 wordApplication = get_oom_object (wordEditor, "get_Application");
658 selection = get_oom_object (wordApplication, "get_Selection");
660 mailItem = get_oom_object (context, "CurrentItem");
662 if ((!wordEditor || !wordApplication || !selection || !mailItem) &&
663 !(flags & DATA_BODY))
666 "Internal error in GpgOL.\n"
667 "Could not find all objects.",
669 MB_ICONINFORMATION|MB_OK);
670 log_error ("%s:%s: Could not find all objects.",
677 /* This happens when we try to decrypt the body of a mail in the
679 mailItem = get_oom_object (context, "Selection.Item(1)");
684 _("Please select a Mail."),
686 MB_ICONINFORMATION|MB_OK);
691 if (flags & DATA_SELECTION)
693 encData = get_oom_string (selection, "Text");
695 if (!encData || (encDataLen = strlen (encData)) <= 1)
698 _("Please select the data you wish to decrypt."),
700 MB_ICONINFORMATION|MB_OK);
704 else if (flags & DATA_BODY)
706 encData = get_oom_string (mailItem, "Body");
708 if (!encData || (encDataLen = strlen (encData)) <= 1)
711 _("Nothing to decrypt."),
713 MB_ICONINFORMATION|MB_OK);
718 fix_linebreaks (encData, &encDataLen);
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
724 if (!strstr (encData, OPAQUE_SIGNED_MARKER) && verify_mime (mailItem))
726 log_debug ("%s:%s: This was a mime message.",
729 if (flags & OP_DECRYPT)
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.",
736 MB_ICONINFORMATION|MB_OK);
741 subject = get_oom_string (mailItem, "Subject");
742 if (get_oom_bool (mailItem, "Sent"))
744 char *addrType = get_oom_string (mailItem, "SenderEmailType");
745 if (addrType && strcmp("SMTP", addrType) == 0)
747 senderAddr = get_oom_string (mailItem, "SenderEmailAddress");
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);
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);
767 /* Determine the protocol based on the content */
768 protocol = is_cms_data (encData, encDataLen) ? PROTOCOL_SMIME :
771 hr = OpenStreamOnFile (MAPIAllocateBuffer, MAPIFreeBuffer,
772 (SOF_UNIQUEFILENAME | STGM_DELETEONRELEASE
773 | STGM_CREATE | STGM_READWRITE),
774 NULL, GpgOLStr("GPG"), &tmpstream);
778 log_error ("%s:%s: can't create temp file: hr=%#lx\n",
779 SRCNAME, __func__, hr);
784 sink->cb_data = tmpstream;
785 sink->writefnc = sink_std_write;
787 session_number = engine_new_session_number ();
788 if (engine_create_filter (&filter, write_buffer_for_cb, sink))
791 decsink->cb_data = filter;
792 decsink->writefnc = sink_encryption_write;
794 engine_set_session_number (filter, session_number);
795 engine_set_session_title (filter, subject ? subject : _("GpgOL"));
797 if (flags & OP_DECRYPT)
799 if ((rc=engine_decrypt_start (filter, curWindow,
803 log_error ("%s:%s: engine decrypt start failed: %s",
804 SRCNAME, __func__, gpg_strerror (rc));
808 else if (flags & OP_VERIFY)
810 log_debug ("Starting verify");
811 if ((rc=engine_verify_start (filter, curWindow,
812 NULL, 0, protocol, senderAddr)))
814 log_error ("%s:%s: engine verify start failed: %s",
815 SRCNAME, __func__, gpg_strerror (rc));
820 /* Write the text in the decryption sink. */
821 rc = write_buffer (decsink, encData, encDataLen);
823 /* Flush the decryption sink and wait for the decryption to get
825 if ((rc = write_buffer (decsink, NULL, 0)))
827 if ((rc = engine_wait (filter)))
829 filter = NULL; /* Not valid anymore. */
830 decsink->cb_data = NULL; /* Not needed anymore. */
832 if (!sink->enc_counter)
834 log_debug ("%s:%s: nothing received from engine", SRCNAME, __func__);
838 /* Check the size of the decrypted data */
839 tmpstream->Stat (&tmpStat, 0);
841 if (tmpStat.cbSize.QuadPart > UINT_MAX)
843 log_error ("%s:%s: No one should write so large mails.",
848 /* Copy the decrypted stream to the message editor. */
852 char buffer[(unsigned int)tmpStat.cbSize.QuadPart + 1];
854 memset (buffer, 0, sizeof buffer);
857 hr = tmpstream->Seek (off, STREAM_SEEK_SET, NULL);
860 log_error ("%s:%s: seeking back to the begin failed: hr=%#lx",
861 SRCNAME, __func__, hr);
862 rc = gpg_error (GPG_ERR_EIO);
865 hr = tmpstream->Read (buffer, sizeof (buffer) - 1, &nread);
868 log_error ("%s:%s: IStream::Read failed: hr=%#lx",
869 SRCNAME, __func__, hr);
870 rc = gpg_error (GPG_ERR_EIO);
873 if (strlen (buffer) > 1)
875 /* Now replace the crypto data with the decrypted data or show it
878 if (flags & DATA_SELECTION)
880 err = put_oom_string (selection, "Text", buffer);
882 else if (flags & DATA_BODY)
884 err = put_oom_string (mailItem, "Body", buffer);
889 MessageBox (NULL, buffer,
890 flags & OP_DECRYPT ? _("Plain text") :
892 MB_ICONINFORMATION|MB_OK);
897 /* Just to be save not to overwrite the selection with
899 log_error ("%s:%s: unexpected problem ", SRCNAME, __func__);
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);
917 gpgol_release (tmpstream);
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.
927 Based on documentation from:
928 http://www.codeproject.com/Articles/3537/Loading-JPG-PNG-resources-using-GDI
932 getIcon (int id, VARIANT* result)
937 Gdiplus::GdiplusStartupInput gdiplusStartupInput;
938 Gdiplus::Bitmap* pbitmap;
939 ULONG_PTR gdiplusToken;
942 const void* pResourceData;
945 memset (&pdesc, 0, sizeof pdesc);
946 pdesc.cbSizeofstruct = sizeof pdesc;
947 pdesc.picType = PICTYPE_BITMAP;
951 log_error ("getIcon called without result variant.");
956 gdiplusStartupInput.DebugEventCallback = NULL;
957 gdiplusStartupInput.SuppressBackgroundThread = FALSE;
958 gdiplusStartupInput.SuppressExternalCodecs = FALSE;
959 gdiplusStartupInput.GdiplusVersion = 1;
960 GdiplusStartup (&gdiplusToken, &gdiplusStartupInput, NULL);
962 /* Get the image from the resource file */
963 hResource = FindResource (glob_hinst, MAKEINTRESOURCE(id), RT_RCDATA);
966 log_error ("%s:%s: failed to find image: %i",
967 SRCNAME, __func__, id);
971 imageSize = SizeofResource (glob_hinst, hResource);
975 pResourceData = LockResource (LoadResource(glob_hinst, hResource));
979 log_error ("%s:%s: failed to load image: %i",
980 SRCNAME, __func__, id);
984 hBuffer = GlobalAlloc (GMEM_MOVEABLE, imageSize);
988 void* pBuffer = GlobalLock (hBuffer);
991 IStream* pStream = NULL;
992 CopyMemory (pBuffer, pResourceData, imageSize);
994 if (CreateStreamOnHGlobal (hBuffer, FALSE, &pStream) == S_OK)
996 pbitmap = Gdiplus::Bitmap::FromStream (pStream);
997 gpgol_release (pStream);
998 if (!pbitmap || pbitmap->GetHBITMAP (0, &pdesc.bmp.hbitmap))
1000 log_error ("%s:%s: failed to get PNG.",
1005 GlobalUnlock (pBuffer);
1007 GlobalFree (hBuffer);
1009 Gdiplus::GdiplusShutdown (gdiplusToken);
1011 /* Wrap the image into an OLE object. */
1012 hr = OleCreatePictureIndirect (&pdesc, IID_IPictureDisp,
1013 TRUE, (void **) &pPict);
1014 if (hr != S_OK || !pPict)
1016 log_error ("%s:%s: OleCreatePictureIndirect failed: hr=%#lx\n",
1017 SRCNAME, __func__, hr);
1021 result->pdispVal = pPict;
1022 result->vt = VT_DISPATCH;
1027 /* Adds an encrypted attachment if the flag OP_SIGN is set
1028 a detached signature of the encrypted file is also added. */
1030 attachEncryptedFile (LPDISPATCH ctrl, int flags)
1032 LPDISPATCH context = NULL;
1033 LPDISPATCH mailItem = NULL;
1034 LPDISPATCH sender = NULL;
1035 LPDISPATCH recipients = NULL;
1037 char* senderAddr = NULL;
1038 char** recipientAddrs = NULL;
1039 char* subject = NULL;
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;
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;
1059 memset (encsink, 0, sizeof *encsink);
1060 memset (sink, 0, sizeof *sink);
1062 hr = getContext (ctrl, &context);
1066 /* First do the check for recipients as this is likely
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);
1073 if (!recipientAddrs || !(*recipientAddrs))
1076 _("Please add at least one recipent."),
1078 MB_ICONINFORMATION|MB_OK);
1082 /* Get a file handle to read from */
1083 fileToEncrypt = get_open_filename (NULL, _("Select file to encrypt"));
1087 log_debug ("No file selected");
1091 fileToEncryptW = utf8_to_wchar2 (fileToEncrypt, strlen(fileToEncrypt));
1092 xfree (fileToEncrypt);
1094 hFile = CreateFileW (fileToEncryptW,
1099 FILE_ATTRIBUTE_NORMAL,
1101 if (hFile == INVALID_HANDLE_VALUE)
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. */
1109 "Internal error in GpgOL.\n"
1110 "Could not open File.",
1112 MB_ICONERROR|MB_OK);
1116 /* Now do the encryption preperations */
1118 if (!mailItem || !sender || !recipients)
1121 "Internal error in GpgOL.\n"
1122 "Could not find all objects.",
1124 MB_ICONERROR|MB_OK);
1125 log_error ("%s:%s: Could not find all objects.",
1130 senderAddr = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
1132 curWindow = get_oom_context_window (context);
1134 session_number = engine_new_session_number ();
1136 subject = get_oom_string (mailItem, "Subject");
1138 /* Prepare the encryption sink */
1139 if ((rc = engine_create_filter (&filter, write_buffer_for_cb, sink)))
1144 encsink->cb_data = filter;
1145 encsink->writefnc = sink_encryption_write;
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,
1151 ENGINE_FLAG_BINARY_OUTPUT,
1152 senderAddr, recipientAddrs, &protocol)))
1154 log_error ("%s:%s: engine encrypt prepare failed : %s",
1155 SRCNAME, __func__, gpg_strerror (rc));
1159 attachName = get_pretty_attachment_name (fileToEncryptW, protocol, 0);
1163 log_error ("%s:%s: Could not get a decent attachment name",
1168 encryptedFile = get_tmp_outfile (attachName, &hEncFile);
1169 sink->cb_data = hEncFile;
1170 sink->writefnc = sink_file_write;
1172 if ((rc=engine_encrypt_start (filter, 0)))
1174 log_error ("%s:%s: engine encrypt start failed: %s",
1175 SRCNAME, __func__, gpg_strerror (rc));
1179 if ((rc=copyFileToSink (hFile, encsink)))
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)))
1187 filter = NULL; /* Not valid anymore. */
1188 encsink->cb_data = NULL; /* Not needed anymore. */
1190 if (!sink->enc_counter)
1192 log_error ("%s:%s: nothing received from engine", SRCNAME, __func__);
1196 /* Now we have an encrypted file behind encryptedFile. Let's add it */
1197 add_oom_attachment (mailItem, encryptedFile, nullptr);
1199 if (flags & OP_SIGN)
1201 attachSignature (mailItem, subject, hEncFile, protocol, session_number,
1202 curWindow, encryptedFile, senderAddr);
1207 engine_cancel (filter);
1211 CloseHandle (hEncFile);
1212 DeleteFileW (encryptedFile);
1215 xfree (encryptedFile);
1216 xfree (fileToEncryptW);
1219 gpgol_release (mailItem);
1220 gpgol_release (sender);
1221 gpgol_release (recipients);
1224 CloseHandle (hFile);
1227 for (i=0; recipientAddrs && recipientAddrs[i]; i++)
1228 xfree (recipientAddrs[i]);
1229 xfree (recipientAddrs);
1236 startCertManager (LPDISPATCH ctrl)
1242 hr = getContext (ctrl, &context);
1246 curWindow = get_oom_context_window (context);
1248 engine_start_keymanager (curWindow);
1253 decryptBody (LPDISPATCH ctrl)
1255 return do_reader_action (ctrl, OP_DECRYPT | DATA_BODY);
1259 decryptSelection (LPDISPATCH ctrl)
1261 return do_reader_action (ctrl, OP_DECRYPT | DATA_SELECTION);
1265 encryptBody (LPDISPATCH ctrl)
1267 return do_composer_action (ctrl, OP_ENCRYPT | DATA_BODY);
1271 encryptSelection (LPDISPATCH ctrl)
1273 return do_composer_action (ctrl, OP_ENCRYPT | DATA_SELECTION);
1277 addEncSignedAttachment (LPDISPATCH ctrl)
1279 return attachEncryptedFile (ctrl, OP_SIGN);
1283 addEncAttachment (LPDISPATCH ctrl)
1285 return attachEncryptedFile (ctrl, 0);
1288 HRESULT signBody (LPDISPATCH ctrl)
1290 return do_composer_action (ctrl, DATA_BODY | OP_SIGN);
1293 HRESULT verifyBody (LPDISPATCH ctrl)
1295 return do_reader_action (ctrl, DATA_BODY | OP_VERIFY);
1299 mark_mime_action (LPDISPATCH ctrl, int flags, bool is_explorer)
1302 HRESULT rc = E_FAIL;
1303 LPDISPATCH context = NULL,
1305 LPMESSAGE message = NULL;
1309 log_debug ("%s:%s: enter", SRCNAME, __func__);
1310 hr = getContext (ctrl, &context);
1314 mailitem = get_oom_object (context, is_explorer ? "ActiveInlineResponse" :
1319 log_error ("%s:%s: Failed to get mailitem.",
1324 message = get_oom_base_message (mailitem);
1328 log_error ("%s:%s: Failed to get message.",
1333 oldflags = get_gpgol_draft_info_flags (message);
1335 newflags = oldflags xor flags;
1337 if (set_gpgol_draft_info_flags (message, newflags))
1339 log_error ("%s:%s: Failed to set draft flags.",
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 ();
1351 gpgol_release (context);
1352 gpgol_release (mailitem);
1353 gpgol_release (message);
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.
1363 HRESULT get_crypt_pressed (LPDISPATCH ctrl, int flags, VARIANT *result,
1368 LPDISPATCH context = NULL,
1370 LPMESSAGE message = NULL;
1372 result->vt = VT_BOOL | VT_BYREF;
1373 result->pboolVal = (VARIANT_BOOL*) xmalloc (sizeof (VARIANT_BOOL));
1374 *(result->pboolVal) = VARIANT_FALSE;
1376 /* First the usual defensive check about our parameters */
1377 if (!ctrl || !result)
1379 log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__);
1383 hr = getContext (ctrl, &context);
1387 log_error ("%s:%s:%i : hresult %lx", SRCNAME, __func__, __LINE__,
1392 mailitem = get_oom_object (context, is_explorer ? "ActiveInlineResponse" :
1397 log_error ("%s:%s: Failed to get mailitem.",
1402 message = get_oom_base_message (mailitem);
1406 log_error ("%s:%s: No message found.",
1411 value = (get_gpgol_draft_info_flags (message) & flags) == flags;
1413 *(result->pboolVal) = value ? VARIANT_TRUE : VARIANT_FALSE;
1416 gpgol_release (context);
1417 gpgol_release (mailitem);
1418 gpgol_release (message);
1424 get_mail_from_control (LPDISPATCH ctrl)
1427 LPDISPATCH context = NULL,
1431 log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__);
1434 hr = getContext (ctrl, &context);
1438 log_error ("%s:%s:%i : hresult %lx", SRCNAME, __func__, __LINE__,
1443 char *ctx_name = get_object_name (context);
1447 log_error ("%s:%s: Failed to get context name",
1449 gpgol_release (context);
1453 if (!strcmp (ctx_name, "_Inspector"))
1455 mailitem = get_oom_object (context, "CurrentItem");
1457 else if (!strcmp (ctx_name, "_Explorer"))
1459 mailitem = get_oom_object (context, "Selection.Item(1)");
1462 gpgol_release (context);
1465 log_error ("%s:%s: Failed to get mailitem. From %s",
1466 SRCNAME, __func__, ctx_name);
1473 /* Get the uid of this item. */
1474 uid = get_unique_id (mailitem, 0, nullptr);
1477 LPMESSAGE msg = get_oom_base_message (mailitem);
1478 uid = mapi_get_uid (msg);
1479 gpgol_release (msg);
1482 log_debug ("%s:%s: Failed to get uid for %p",
1483 SRCNAME, __func__, mailitem);
1484 gpgol_release (mailitem);
1489 auto ret = Mail::get_mail_for_uuid (uid);
1493 log_error ("%s:%s: Failed to find mail %p in map.",
1494 SRCNAME, __func__, mailitem);
1496 gpgol_release (mailitem);
1500 /* Helper to reduce code duplication.*/
1501 #define MY_MAIL_GETTER \
1504 log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__); \
1507 const auto mail = get_mail_from_control (ctrl); \
1510 log_oom ("%s:%s:%i Failed to get mail", \
1511 SRCNAME, __func__, __LINE__); \
1514 HRESULT get_is_signed (LPDISPATCH ctrl, VARIANT *result)
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;
1526 HRESULT get_sig_label (LPDISPATCH ctrl, VARIANT *result)
1530 result->vt = VT_BSTR;
1534 log_debug ("%s:%s: No mail.",
1536 w_result = utf8_to_wchar (_("Not Trusted"));
1537 result->bstrVal = SysAllocString (w_result);
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;
1547 w_result = utf8_to_wchar (_("Fully Trusted"));
1551 w_result = utf8_to_wchar (_("Trusted"));
1555 w_result = utf8_to_wchar (_("Not Trusted"));
1557 result->bstrVal = SysAllocString (w_result);
1562 HRESULT get_sig_ttip (LPDISPATCH ctrl, VARIANT *result)
1566 result->vt = VT_BSTR;
1568 if (mail && mail->is_signed ())
1571 gpgrt_asprintf (&buf, _("This is a signed %s message."),
1572 mail->is_smime() ? _("S/MIME") : _("OpenPGP"));
1573 w_result = utf8_to_wchar (buf);
1578 w_result = utf8_to_wchar (_("This message is not cryptographically signed."));
1580 result->bstrVal = SysAllocString (w_result);
1585 HRESULT get_sig_stip (LPDISPATCH ctrl, VARIANT *result)
1589 result->vt = VT_BSTR;
1592 log_debug ("%s:%s: No mail.",
1595 w_result = utf8_to_wchar (_("You cannot be sure who wrote the message."));
1596 result->bstrVal = SysAllocString (w_result);
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);
1607 HRESULT launch_cert_details (LPDISPATCH ctrl)
1613 log_debug ("%s:%s: No mail.",
1618 char *uiserver = get_uiserver_name ();
1619 bool showError = false;
1622 std::string path (uiserver);
1624 if (path.find("kleopatra.exe") != std::string::npos)
1627 if ((dpos = path.find(" --daemon")) != std::string::npos)
1629 path.erase(dpos, strlen(" --daemon"));
1631 auto ctx = Context::createForEngine(SpawnEngine);
1634 log_error ("%s:%s: No spawn engine.",
1637 const char *argv[] = {path.c_str(),
1639 mail->get_sig_fpr(),
1641 log_debug ("%s:%s: Starting %s %s %s",
1642 SRCNAME, __func__, path.c_str(), argv[1], argv[2]);
1644 ctx->spawnAsync(path.c_str(), argv, d, d,
1645 d, Context::SpawnNone);
1660 _("Could not find Kleopatra.\n"
1661 "Please reinstall Gpg4win with the Kleopatra component enabled."),
1663 MB_ICONINFORMATION|MB_OK);
1668 HRESULT get_sigstate_icon (LPDISPATCH ctrl, VARIANT *result)
1674 return getIcon (mail->get_signature_icon_id (), result);
1676 return getIcon (IDI_EMBLEM_INFORMATION_64_PNG, result);