Do not reset draft info for drafts
[gpgol.git] / src / cryptcontroller.cpp
1 /* @file cryptcontroller.cpp
2  * @brief Helper to do crypto on a mail.
3  *
4  * Copyright (C) 2018 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 #include "config.h"
22
23 #include "common.h"
24 #include "cpphelp.h"
25 #include "cryptcontroller.h"
26 #include "mail.h"
27 #include "mapihelp.h"
28 #include "mimemaker.h"
29 #include "wks-helper.h"
30 #include "overlay.h"
31 #include "keycache.h"
32 #include "mymapitags.h"
33
34 #include <gpgme++/context.h>
35 #include <gpgme++/signingresult.h>
36 #include <gpgme++/encryptionresult.h>
37
38 #include "common.h"
39
40 #include <sstream>
41
42 static int
43 sink_data_write (sink_t sink, const void *data, size_t datalen)
44 {
45   GpgME::Data *d = static_cast<GpgME::Data *>(sink->cb_data);
46   d->write (data, datalen);
47   return 0;
48 }
49
50 static int
51 create_sign_attach (sink_t sink, protocol_t protocol,
52                     GpgME::Data &signature,
53                     GpgME::Data &signedData,
54                     const char *micalg);
55
56 /** We have some C Style cruft in here as this was historically how
57   GpgOL worked directly in the MAPI data objects. To reduce the regression
58   risk the new object oriented way for crypto reused as much as possible
59   from this.
60 */
61 CryptController::CryptController (Mail *mail, bool encrypt, bool sign,
62                                   GpgME::Protocol proto):
63     m_mail (mail),
64     m_encrypt (encrypt),
65     m_sign (sign),
66     m_crypto_success (false),
67     m_proto (proto)
68 {
69   TSTART;
70   memdbg_ctor ("CryptController");
71   log_debug ("%s:%s: CryptController ctor for %p encrypt %i sign %i inline %i.",
72              SRCNAME, __func__, mail, encrypt, sign, mail->getDoPGPInline ());
73   m_recipient_addrs = mail->getCachedRecipients ();
74   TRETURN;
75 }
76
77 CryptController::~CryptController()
78 {
79   TSTART;
80   memdbg_dtor ("CryptController");
81   log_debug ("%s:%s:%p",
82              SRCNAME, __func__, m_mail);
83   TRETURN;
84 }
85
86 int
87 CryptController::collect_data ()
88 {
89   TSTART;
90   /* Get the attachment info and the body.  We need to do this before
91      creating the engine's filter because sending the cancel to
92      the engine with nothing for the engine to process.  Will result
93      in an error. This is actually a bug in our engine code but
94      we better avoid triggering this bug because the engine
95      sometimes hangs.  Fixme: Needs a proper fix. */
96
97
98   /* Take the Body from the mail if possible. This is a fix for
99      GnuPG-Bug-ID: T3614 because the body is not always properly
100      updated in MAPI when sending. */
101   char *body = m_mail->takeCachedPlainBody ();
102   if (body && !*body)
103     {
104       xfree (body);
105       body = nullptr;
106     }
107
108   LPMESSAGE message = get_oom_base_message (m_mail->item ());
109   if (!message)
110     {
111       log_error ("%s:%s: Failed to get base message.",
112                  SRCNAME, __func__);
113     }
114
115   auto att_table = mapi_create_attach_table (message, 0);
116   int n_att_usable = count_usable_attachments (att_table);
117   if (!n_att_usable && !body)
118     {
119       gpgol_message_box (m_mail->getWindow (),
120                          utf8_gettext ("Can't encrypt / sign an empty message."),
121                          utf8_gettext ("GpgOL"), MB_OK);
122       gpgol_release (message);
123       mapi_release_attach_table (att_table);
124       xfree (body);
125       TRETURN -1;
126     }
127
128   bool do_inline = m_mail->getDoPGPInline ();
129
130   if (n_att_usable && do_inline)
131     {
132       log_debug ("%s:%s: PGP Inline not supported for attachments."
133                  " Using PGP MIME",
134                  SRCNAME, __func__);
135       do_inline = false;
136       m_mail->setDoPGPInline (false);
137     }
138   else if (do_inline)
139     {
140       /* Inline. Use Body as input.
141         We need to collect also our mime structure for S/MIME
142         as we don't know yet if we are S/MIME or OpenPGP */
143       m_bodyInput.write (body, strlen (body));
144       log_debug ("%s:%s: Inline. Caching body.",
145                  SRCNAME, __func__);
146       /* Set the input buffer to start. */
147       m_bodyInput.seek (0, SEEK_SET);
148     }
149
150   /* Set up the sink object to collect the mime structure */
151   struct sink_s sinkmem;
152   sink_t sink = &sinkmem;
153   memset (sink, 0, sizeof *sink);
154   sink->cb_data = &m_input;
155   sink->writefnc = sink_data_write;
156
157   /* Collect the mime strucutre */
158   int err = add_body_and_attachments (sink, message, att_table, m_mail,
159                                       body, n_att_usable);
160   xfree (body);
161
162   if (err)
163     {
164       log_error ("%s:%s: Collecting body and attachments failed.",
165                  SRCNAME, __func__);
166       gpgol_release (message);
167       mapi_release_attach_table (att_table);
168       TRETURN -1;
169     }
170
171   /* Message is no longer needed */
172   gpgol_release (message);
173
174   mapi_release_attach_table (att_table);
175
176   /* Set the input buffer to start. */
177   m_input.seek (0, SEEK_SET);
178   TRETURN 0;
179 }
180
181 int
182 CryptController::lookup_fingerprints (const std::string &sigFpr,
183                                       const std::vector<std::string> recpFprs)
184 {
185   TSTART;
186   auto ctx = std::shared_ptr<GpgME::Context> (GpgME::Context::createForProtocol (m_proto));
187
188   if (!ctx)
189     {
190       log_error ("%s:%s: failed to create context with protocol '%s'",
191                  SRCNAME, __func__,
192                  m_proto == GpgME::CMS ? "smime" :
193                  m_proto == GpgME::OpenPGP ? "openpgp" :
194                  "unknown");
195       TRETURN -1;
196     }
197
198   ctx->setKeyListMode (GpgME::Local);
199   GpgME::Error err;
200
201   if (!sigFpr.empty()) {
202       m_signer_key = ctx->key (sigFpr.c_str (), err, true);
203       if (err || m_signer_key.isNull ()) {
204           log_error ("%s:%s: failed to lookup key for '%s' with protocol '%s'",
205                      SRCNAME, __func__, anonstr (sigFpr.c_str ()),
206                      m_proto == GpgME::CMS ? "smime" :
207                      m_proto == GpgME::OpenPGP ? "openpgp" :
208                      "unknown");
209           TRETURN -1;
210       }
211       // reset context
212       ctx = std::shared_ptr<GpgME::Context> (GpgME::Context::createForProtocol (m_proto));
213       ctx->setKeyListMode (GpgME::Local);
214   }
215
216   if (!recpFprs.size()) {
217      TRETURN 0;
218   }
219
220   // Convert recipient fingerprints
221   char **cRecps = vector_to_cArray (recpFprs);
222
223   err = ctx->startKeyListing (const_cast<const char **> (cRecps));
224
225   if (err) {
226       log_error ("%s:%s: failed to start recipient keylisting",
227                  SRCNAME, __func__);
228       release_cArray (cRecps);
229       TRETURN -1;
230   }
231
232   do {
233       m_recipients.push_back(ctx->nextKey(err));
234   } while (!err);
235
236   m_recipients.pop_back();
237
238   release_cArray (cRecps);
239
240   TRETURN 0;
241 }
242
243
244 int
245 CryptController::parse_output (GpgME::Data &resolverOutput)
246 {
247   TSTART;
248   // Todo: Use Data::toString
249   std::istringstream ss(resolverOutput.toString());
250   std::string line;
251
252   std::string sigFpr;
253   std::vector<std::string> recpFprs;
254   while (std::getline (ss, line))
255     {
256       rtrim (line);
257       if (line == "cancel")
258         {
259           log_debug ("%s:%s: resolver canceled",
260                      SRCNAME, __func__);
261           TRETURN -2;
262         }
263       if (line == "unencrypted")
264         {
265           log_debug ("%s:%s: FIXME resolver wants unencrypted",
266                      SRCNAME, __func__);
267           TRETURN -1;
268         }
269       std::istringstream lss (line);
270
271       // First is sig or enc
272       std::string what;
273       std::string how;
274       std::string fingerprint;
275
276       std::getline (lss, what, ':');
277       std::getline (lss, how, ':');
278       std::getline (lss, fingerprint, ':');
279
280       if (m_proto == GpgME::UnknownProtocol)
281         {
282           m_proto = (how == "smime") ? GpgME::CMS : GpgME::OpenPGP;
283         }
284
285       if (what == "sig")
286         {
287           if (!sigFpr.empty ())
288             {
289               log_error ("%s:%s: multiple signing keys not supported",
290                          SRCNAME, __func__);
291
292             }
293           sigFpr = fingerprint;
294           continue;
295         }
296       if (what == "enc")
297         {
298           recpFprs.push_back (fingerprint);
299         }
300     }
301
302   if (m_sign && sigFpr.empty())
303     {
304       log_error ("%s:%s: Sign requested but no signing fingerprint - sending unsigned",
305                  SRCNAME, __func__);
306       m_sign = false;
307     }
308   if (m_encrypt && !recpFprs.size())
309     {
310       log_error ("%s:%s: Encrypt requested but no recipient fingerprints",
311                  SRCNAME, __func__);
312       gpgol_message_box (m_mail->getWindow (),
313                          utf8_gettext ("No recipients for encryption selected."),
314                          _("GpgOL"), MB_OK);
315       TRETURN -2;
316     }
317
318   TRETURN lookup_fingerprints (sigFpr, recpFprs);
319 }
320
321 static bool
322 resolve_through_protocol (const GpgME::Protocol &proto, bool sign,
323                           bool encrypt, const std::string &sender,
324                           const std::vector<std::string> &recps,
325                           std::vector<GpgME::Key> &r_keys,
326                           GpgME::Key &r_sig)
327 {
328   TSTART;
329   bool sig_ok = true;
330   bool enc_ok = true;
331
332   const auto cache = KeyCache::instance();
333
334   if (encrypt)
335     {
336       r_keys = cache->getEncryptionKeys(recps, proto);
337       enc_ok = !r_keys.empty();
338     }
339   if (sign && enc_ok)
340     {
341       r_sig = cache->getSigningKey (sender.c_str (), proto);
342       sig_ok = !r_sig.isNull();
343     }
344   TRETURN sig_ok && enc_ok;
345 }
346
347 int
348 CryptController::resolve_keys_cached()
349 {
350   TSTART;
351   // Prepare variables
352   const auto cached_sender = m_mail->getSender ();
353   auto recps = m_recipient_addrs;
354
355   if (m_encrypt)
356     {
357       recps.push_back (cached_sender);
358     }
359
360   bool resolved = false;
361   if (opt.enable_smime && opt.prefer_smime)
362     {
363       resolved = resolve_through_protocol (GpgME::CMS, m_sign, m_encrypt,
364                                            cached_sender, recps, m_recipients,
365                                            m_signer_key);
366       if (resolved)
367         {
368           log_debug ("%s:%s: Resolved with CMS due to preference.",
369                      SRCNAME, __func__);
370           m_proto = GpgME::CMS;
371         }
372     }
373   if (!resolved)
374     {
375       resolved = resolve_through_protocol (GpgME::OpenPGP, m_sign, m_encrypt,
376                                            cached_sender, recps, m_recipients,
377                                            m_signer_key);
378       if (resolved)
379         {
380           log_debug ("%s:%s: Resolved with OpenPGP.",
381                      SRCNAME, __func__);
382           m_proto = GpgME::OpenPGP;
383         }
384     }
385   if (!resolved && (opt.enable_smime && !opt.prefer_smime))
386     {
387       resolved = resolve_through_protocol (GpgME::CMS, m_sign, m_encrypt,
388                                            cached_sender, recps, m_recipients,
389                                            m_signer_key);
390       if (resolved)
391         {
392           log_debug ("%s:%s: Resolved with CMS as fallback.",
393                      SRCNAME, __func__);
394           m_proto = GpgME::CMS;
395         }
396     }
397
398   if (!resolved)
399     {
400       log_debug ("%s:%s: Failed to resolve through cache",
401                  SRCNAME, __func__);
402       m_recipients.clear();
403       m_signer_key = GpgME::Key();
404       m_proto = GpgME::UnknownProtocol;
405       TRETURN 1;
406     }
407
408   if (!m_recipients.empty())
409     {
410       log_debug ("%s:%s: Encrypting with protocol %s to:",
411                  SRCNAME, __func__, to_cstr (m_proto));
412     }
413   for (const auto &key: m_recipients)
414     {
415       log_debug ("%s", anonstr (key.primaryFingerprint ()));
416     }
417   if (!m_signer_key.isNull())
418     {
419       log_debug ("%s:%s: Signing key: %s:%s",
420                  SRCNAME, __func__, anonstr (m_signer_key.primaryFingerprint ()),
421                  to_cstr (m_signer_key.protocol()));
422     }
423   TRETURN 0;
424 }
425
426 int
427 CryptController::resolve_keys ()
428 {
429   TSTART;
430
431   m_recipients.clear();
432
433   if (m_mail->isDraftEncrypt() && opt.draft_key)
434     {
435       const auto key = KeyCache::instance()->getByFpr (opt.draft_key);
436       if (key.isNull())
437         {
438           const char *buf = utf8_gettext ("Failed to encrypt draft.\n\n"
439                                           "The configured encryption key for drafts "
440                                           "could not be found.\n"
441                                           "Please check your configuration or "
442                                           "turn off draft encryption in the settings.");
443           gpgol_message_box (get_active_hwnd (),
444                              buf,
445                              _("GpgOL"), MB_OK);
446           TRETURN -1;
447         }
448       log_debug ("%s:%s: resolved draft encryption key protocol is: %s",
449                  SRCNAME, __func__, to_cstr (key.protocol()));
450       m_recipients.push_back (key);
451       TRETURN 0;
452     }
453
454   if (!m_recipient_addrs.size())
455     {
456       /* Should not happen. But we add it for better bug reports. */
457       const char *bugmsg = utf8_gettext ("Operation failed.\n\n"
458               "This is usually caused by a bug in GpgOL or an error in your setup.\n"
459               "Please see https://www.gpg4win.org/reporting-bugs.html "
460               "or ask your Administrator for support.");
461       char *buf;
462       gpgrt_asprintf (&buf, "Failed to resolve recipients.\n\n%s\n", bugmsg);
463       memdbg_alloc (buf);
464       gpgol_message_box (get_active_hwnd (),
465                          buf,
466                          _("GpgOL"), MB_OK);
467       xfree(buf);
468       TRETURN -1;
469     }
470
471   if (opt.autoresolve && !resolve_keys_cached ())
472     {
473       log_debug ("%s:%s: resolved keys through the cache",
474                  SRCNAME, __func__);
475       start_crypto_overlay();
476       TRETURN 0;
477     }
478
479   std::vector<std::string> args;
480
481   // Collect the arguments
482   char *gpg4win_dir = get_gpg4win_dir ();
483   if (!gpg4win_dir)
484     {
485       TRACEPOINT;
486       TRETURN -1;
487     }
488   const auto resolver = std::string (gpg4win_dir) + "\\bin\\resolver.exe";
489   args.push_back (resolver);
490
491   log_debug ("%s:%s: resolving keys with '%s'",
492              SRCNAME, __func__, resolver.c_str ());
493
494   // We want debug output as OutputDebugString
495   args.push_back (std::string ("--debug"));
496
497   // Yes passing it as int is ok.
498   auto wnd = m_mail->getWindow ();
499   if (wnd)
500     {
501       // Pass the handle of the active window for raise / overlay.
502       args.push_back (std::string ("--hwnd"));
503       args.push_back (std::to_string ((int) (intptr_t) wnd));
504     }
505
506   // Set the overlay caption
507   args.push_back (std::string ("--overlayText"));
508   if (m_encrypt)
509     {
510       args.push_back (std::string (_("Resolving recipients...")));
511     }
512   else if (m_sign)
513     {
514       args.push_back (std::string (_("Resolving signers...")));
515     }
516
517   if (!opt.enable_smime)
518     {
519       args.push_back (std::string ("--protocol"));
520       args.push_back (std::string ("pgp"));
521     }
522
523   if (m_sign)
524     {
525       args.push_back (std::string ("--sign"));
526     }
527   const auto cached_sender = m_mail->getSender ();
528   if (cached_sender.empty())
529     {
530       log_error ("%s:%s: resolve keys without sender.",
531                  SRCNAME, __func__);
532     }
533   else
534     {
535       args.push_back (std::string ("--sender"));
536       args.push_back (cached_sender);
537     }
538
539   if (!opt.autoresolve)
540     {
541       args.push_back (std::string ("--alwaysShow"));
542     }
543
544   if (opt.prefer_smime)
545     {
546       args.push_back (std::string ("--preferred-protocol"));
547       args.push_back (std::string ("cms"));
548     }
549
550   args.push_back (std::string ("--lang"));
551   args.push_back (std::string (gettext_localename ()));
552
553   if (m_encrypt)
554     {
555       args.push_back (std::string ("--encrypt"));
556       // Get the recipients that are cached from OOM
557       for (const auto &addr: m_recipient_addrs)
558         {
559           const auto mbox = GpgME::UserID::addrSpecFromString (addr.c_str());
560           const auto overrides = KeyCache::instance ()->getOverrides (mbox);
561           if (overrides.size())
562             {
563               std::string overrideStr = mbox + ":";
564               for (const auto &key: overrides)
565                 {
566                   if (key.isNull())
567                     {
568                       TRACEPOINT;
569                       continue;
570                     }
571                   overrideStr += key.primaryFingerprint();
572                   overrideStr += ",";
573                 }
574               overrideStr.erase(overrideStr.size() - 1, 1);
575               args.push_back (std::string ("-o"));
576               args.push_back (overrideStr);
577             }
578           args.push_back (mbox);
579         }
580     }
581
582   // Args are prepared. Spawn the resolver.
583   auto ctx = GpgME::Context::createForEngine (GpgME::SpawnEngine);
584   if (!ctx)
585     {
586       // can't happen
587       TRACEPOINT;
588       TRETURN -1;
589     }
590
591
592   // Convert our collected vector to c strings
593   // It's a bit overhead but should be quick for such small
594   // data.
595   char **cargs = vector_to_cArray (args);
596   log_data ("%s:%s: Spawn args:",
597             SRCNAME, __func__);
598   for (size_t i = 0; cargs && cargs[i]; i++)
599     {
600       log_data (SIZE_T_FORMAT ": '%s'", i, cargs[i]);
601     }
602
603   GpgME::Data mystdin (GpgME::Data::null), mystdout, mystderr;
604   GpgME::Error err = ctx->spawn (cargs[0], const_cast <const char**> (cargs),
605                                  mystdin, mystdout, mystderr,
606                                  (GpgME::Context::SpawnFlags) (
607                                   GpgME::Context::SpawnAllowSetFg |
608                                   GpgME::Context::SpawnShowWindow));
609   // Somehow Qt messes up which window to bring back to front.
610   // So we do it manually.
611   bring_to_front (wnd);
612
613   // We need to create an overlay while encrypting as pinentry can take a while
614   start_crypto_overlay();
615
616   log_data ("Resolver stdout:\n'%s'", mystdout.toString ().c_str ());
617   log_data ("Resolver stderr:\n'%s'", mystderr.toString ().c_str ());
618
619   release_cArray (cargs);
620
621   if (err)
622     {
623       log_debug ("%s:%s: Resolver spawn finished Err code: %i asString: %s",
624                  SRCNAME, __func__, err.code(), err.asString());
625     }
626
627   int ret = parse_output (mystdout);
628   if (ret == -1)
629     {
630       log_debug ("%s:%s: Failed to parse / resolve keys.",
631                  SRCNAME, __func__);
632       log_data ("Resolver stdout:\n'%s'", mystdout.toString ().c_str ());
633       log_data ("Resolver stderr:\n'%s'", mystderr.toString ().c_str ());
634       TRETURN -1;
635     }
636
637   TRETURN ret;
638 }
639
640 int
641 CryptController::do_crypto (GpgME::Error &err, std::string &r_diag)
642 {
643   TSTART;
644   log_debug ("%s:%s",
645              SRCNAME, __func__);
646
647   if (m_mail->isDraftEncrypt ())
648     {
649       log_debug ("%s:%s Disabling sign because of draft encrypt",
650                  SRCNAME, __func__);
651       m_sign = false;
652     }
653
654   /* Start a WKS check if necessary. */
655   WKSHelper::instance()->start_check (m_mail->getSender ());
656
657   int ret = resolve_keys ();
658   if (ret == -1)
659     {
660       //error
661       log_debug ("%s:%s: Failure to resolve keys.",
662                  SRCNAME, __func__);
663       TRETURN -1;
664     }
665   if (ret == -2)
666     {
667       // Cancel
668       TRETURN -2;
669     }
670   bool do_inline = m_mail->getDoPGPInline ();
671
672   if (m_proto == GpgME::CMS && do_inline)
673     {
674       log_debug ("%s:%s: Inline for S/MIME not supported. Switching to mime.",
675                  SRCNAME, __func__);
676       do_inline = false;
677       m_mail->setDoPGPInline (false);
678       m_bodyInput = GpgME::Data(GpgME::Data::null);
679     }
680
681   auto ctx = GpgME::Context::create(m_proto);
682
683   if (!ctx)
684     {
685       log_error ("%s:%s: Failure to create context.",
686                  SRCNAME, __func__);
687       gpgol_message_box (m_mail->getWindow (),
688                          "Failure to create context.",
689                          utf8_gettext ("GpgOL"), MB_OK);
690       TRETURN -1;
691     }
692   if (!m_signer_key.isNull())
693     {
694       ctx->addSigningKey (m_signer_key);
695     }
696
697   ctx->setTextMode (m_proto == GpgME::OpenPGP);
698   ctx->setArmor (m_proto == GpgME::OpenPGP);
699
700   if (m_encrypt && m_sign && do_inline)
701     {
702       // Sign encrypt combined
703       const auto result_pair = ctx->signAndEncrypt (m_recipients,
704                                                     do_inline ? m_bodyInput : m_input,
705                                                     m_output,
706                                                     GpgME::Context::AlwaysTrust);
707       const auto err1 = result_pair.first.error();
708       const auto err2 = result_pair.second.error();
709
710       if (err1 || err2)
711         {
712           log_error ("%s:%s: Encrypt / Sign error %s %s.",
713                      SRCNAME, __func__, result_pair.first.error().asString(),
714                      result_pair.second.error().asString());
715           err = err1 ? err1 : err2;
716           GpgME::Data log;
717           const auto err3 = ctx->getAuditLog (log,
718                                               GpgME::Context::DiagnosticAuditLog);
719           if (!err3)
720             {
721               r_diag = log.toString();
722             }
723           TRETURN -1;
724         }
725
726       if (err1.isCanceled() || err2.isCanceled())
727         {
728           err = err1.isCanceled() ? err1 : err2;
729           log_debug ("%s:%s: User cancled",
730                      SRCNAME, __func__);
731           TRETURN -2;
732         }
733     }
734   else if (m_encrypt && m_sign)
735     {
736       // First sign then encrypt
737       const auto sigResult = ctx->sign (m_input, m_output,
738                                         GpgME::Detached);
739       err = sigResult.error();
740       if (err)
741         {
742           log_error ("%s:%s: Signing error %s.",
743                      SRCNAME, __func__, sigResult.error().asString());
744           GpgME::Data log;
745           const auto err3 = ctx->getAuditLog (log,
746                                               GpgME::Context::DiagnosticAuditLog);
747           if (!err3)
748             {
749               r_diag = log.toString();
750             }
751           TRETURN -1;
752         }
753       if (err.isCanceled())
754         {
755           log_debug ("%s:%s: User cancled",
756                      SRCNAME, __func__);
757           TRETURN -2;
758         }
759       parse_micalg (sigResult);
760
761       // We now have plaintext in m_input
762       // The detached signature in m_output
763
764       // Set up the sink object to construct the multipart/signed
765       GpgME::Data multipart;
766       struct sink_s sinkmem;
767       sink_t sink = &sinkmem;
768       memset (sink, 0, sizeof *sink);
769       sink->cb_data = &multipart;
770       sink->writefnc = sink_data_write;
771
772       if (create_sign_attach (sink,
773                               m_proto == GpgME::CMS ?
774                                          PROTOCOL_SMIME : PROTOCOL_OPENPGP,
775                               m_output, m_input, m_micalg.c_str ()))
776         {
777           TRACEPOINT;
778           TRETURN -1;
779         }
780
781       // Now we have the multipart throw away the rest.
782       m_output = GpgME::Data ();
783       m_input = GpgME::Data ();
784       multipart.seek (0, SEEK_SET);
785       const auto encResult = ctx->encrypt (m_recipients, multipart,
786                                            m_output,
787                                            GpgME::Context::AlwaysTrust);
788       err = encResult.error();
789       if (err)
790         {
791           log_error ("%s:%s: Encryption error %s.",
792                      SRCNAME, __func__, err.asString());
793           GpgME::Data log;
794           const auto err3 = ctx->getAuditLog (log,
795                                               GpgME::Context::DiagnosticAuditLog);
796           if (!err3)
797             {
798               r_diag = log.toString();
799             }
800           TRETURN -1;
801         }
802       if (err.isCanceled())
803         {
804           log_debug ("%s:%s: User cancled",
805                      SRCNAME, __func__);
806           TRETURN -2;
807         }
808       // Now we have encrypted output just treat it like encrypted.
809     }
810   else if (m_encrypt)
811     {
812       const auto result = ctx->encrypt (m_recipients, do_inline ? m_bodyInput : m_input,
813                                         m_output,
814                                         GpgME::Context::AlwaysTrust);
815       err = result.error();
816       if (err)
817         {
818           log_error ("%s:%s: Encryption error %s.",
819                      SRCNAME, __func__, err.asString());
820           GpgME::Data log;
821           const auto err3 = ctx->getAuditLog (log,
822                                               GpgME::Context::DiagnosticAuditLog);
823           if (!err3)
824             {
825               r_diag = log.toString();
826             }
827           TRETURN -1;
828         }
829       if (err.isCanceled())
830         {
831           log_debug ("%s:%s: User cancled",
832                      SRCNAME, __func__);
833           TRETURN -2;
834         }
835     }
836   else if (m_sign)
837     {
838       const auto result = ctx->sign (do_inline ? m_bodyInput : m_input, m_output,
839                                      do_inline ? GpgME::Clearsigned :
840                                      GpgME::Detached);
841       err = result.error();
842       if (err)
843         {
844           log_error ("%s:%s: Signing error %s.",
845                      SRCNAME, __func__, err.asString());
846           GpgME::Data log;
847           const auto err3 = ctx->getAuditLog (log,
848                                               GpgME::Context::DiagnosticAuditLog);
849           if (!err3)
850             {
851               r_diag = log.toString();
852             }
853           TRETURN -1;
854         }
855       if (err.isCanceled())
856         {
857           log_debug ("%s:%s: User cancled",
858                      SRCNAME, __func__);
859           TRETURN -2;
860         }
861       parse_micalg (result);
862     }
863   else
864     {
865       // ???
866       log_error ("%s:%s: unreachable code reached.",
867                  SRCNAME, __func__);
868     }
869
870
871   log_debug ("%s:%s: Crypto done sucessfuly.",
872              SRCNAME, __func__);
873   m_crypto_success = true;
874
875   TRETURN 0;
876 }
877
878 static int
879 write_data (sink_t sink, GpgME::Data &data)
880 {
881   TSTART;
882   if (!sink || !sink->writefnc)
883     {
884       TRETURN -1;
885     }
886
887   char buf[4096];
888   size_t nread;
889   data.seek (0, SEEK_SET);
890   while ((nread = data.read (buf, 4096)) > 0)
891     {
892       sink->writefnc (sink, buf, nread);
893     }
894
895   TRETURN 0;
896 }
897
898 int
899 create_sign_attach (sink_t sink, protocol_t protocol,
900                     GpgME::Data &signature,
901                     GpgME::Data &signedData,
902                     const char *micalg)
903 {
904   TSTART;
905   char boundary[BOUNDARYSIZE+1];
906   char top_header[BOUNDARYSIZE+200];
907   int rc = 0;
908
909   /* Write the top header.  */
910   generate_boundary (boundary);
911   create_top_signing_header (top_header, sizeof top_header,
912                              protocol, 1, boundary,
913                              micalg);
914
915   if ((rc = write_string (sink, top_header)))
916     {
917       TRACEPOINT;
918       TRETURN rc;
919     }
920
921   /* Write the boundary so that it is not included in the hashing.  */
922   if ((rc = write_boundary (sink, boundary, 0)))
923     {
924       TRACEPOINT;
925       TRETURN rc;
926     }
927
928   /* Write the signed mime structure */
929   if ((rc = write_data (sink, signedData)))
930     {
931       TRACEPOINT;
932       TRETURN rc;
933     }
934
935   /* Write the signature attachment */
936   if ((rc = write_boundary (sink, boundary, 0)))
937     {
938       TRACEPOINT;
939       TRETURN rc;
940     }
941
942   if (protocol == PROTOCOL_OPENPGP)
943     {
944       rc = write_string (sink,
945                          "Content-Type: application/pgp-signature;\r\n"
946                          "\tname=\"" OPENPGP_SIG_NAME "\"\r\n"
947                          "Content-Transfer-Encoding: 7Bit\r\n");
948     }
949   else
950     {
951       rc = write_string (sink,
952                          "Content-Transfer-Encoding: base64\r\n"
953                          "Content-Type: application/pkcs7-signature\r\n"
954                          "Content-Disposition: inline;\r\n"
955                          "\tfilename=\"" SMIME_SIG_NAME "\"\r\n");
956       /* rc = write_string (sink, */
957       /*                    "Content-Type: application/x-pkcs7-signature\r\n" */
958       /*                    "\tname=\"smime.p7s\"\r\n" */
959       /*                    "Content-Transfer-Encoding: base64\r\n" */
960       /*                    "Content-Disposition: attachment;\r\n" */
961       /*                    "\tfilename=\"smime.p7s\"\r\n"); */
962
963     }
964
965   if (rc)
966     {
967       TRACEPOINT;
968       TRETURN rc;
969     }
970
971   if ((rc = write_string (sink, "\r\n")))
972     {
973       TRACEPOINT;
974       TRETURN rc;
975     }
976
977   // Write the signature data
978   if (protocol == PROTOCOL_SMIME)
979     {
980       const std::string sigStr = signature.toString();
981       if ((rc = write_b64 (sink, (const void *) sigStr.c_str (), sigStr.size())))
982         {
983           TRACEPOINT;
984           TRETURN rc;
985         }
986     }
987   else if ((rc = write_data (sink, signature)))
988     {
989       TRACEPOINT;
990       TRETURN rc;
991     }
992
993   // Add an extra linefeed with should not harm.
994   if ((rc = write_string (sink, "\r\n")))
995     {
996       TRACEPOINT;
997       TRETURN rc;
998     }
999
1000   /* Write the final boundary.  */
1001   if ((rc = write_boundary (sink, boundary, 1)))
1002     {
1003       TRACEPOINT;
1004       TRETURN rc;
1005     }
1006
1007   TRETURN rc;
1008 }
1009
1010 static int
1011 create_encrypt_attach (sink_t sink, protocol_t protocol,
1012                        GpgME::Data &encryptedData,
1013                        int exchange_major_version)
1014 {
1015   TSTART;
1016   char boundary[BOUNDARYSIZE+1];
1017   int rc = create_top_encryption_header (sink, protocol, boundary,
1018                                          false, exchange_major_version);
1019   // From here on use goto failure pattern.
1020   if (rc)
1021     {
1022       log_error ("%s:%s: Failed to create top header.",
1023                  SRCNAME, __func__);
1024       TRETURN rc;
1025     }
1026
1027   if (protocol == PROTOCOL_OPENPGP ||
1028       exchange_major_version >= 15)
1029     {
1030       // With exchange 2016 we have to construct S/MIME
1031       // differently and write the raw data here.
1032       rc = write_data (sink, encryptedData);
1033     }
1034   else
1035     {
1036       const auto encStr = encryptedData.toString();
1037       rc = write_b64 (sink, encStr.c_str(), encStr.size());
1038     }
1039
1040   if (rc)
1041     {
1042       log_error ("%s:%s: Failed to create top header.",
1043                  SRCNAME, __func__);
1044       TRETURN rc;
1045     }
1046
1047   /* Write the final boundary (for OpenPGP) and finish the attachment.  */
1048   if (*boundary && (rc = write_boundary (sink, boundary, 1)))
1049     {
1050       log_error ("%s:%s: Failed to write boundary.",
1051                  SRCNAME, __func__);
1052     }
1053   TRETURN rc;
1054 }
1055
1056 int
1057 CryptController::update_mail_mapi ()
1058 {
1059   TSTART;
1060   log_debug ("%s:%s", SRCNAME, __func__);
1061
1062   LPMESSAGE message = get_oom_base_message (m_mail->item());
1063   if (!message)
1064     {
1065       log_error ("%s:%s: Failed to obtain message.",
1066                  SRCNAME, __func__);
1067       TRETURN -1;
1068     }
1069
1070   if (m_mail->getDoPGPInline ())
1071     {
1072       // Nothing to do for inline.
1073       log_debug ("%s:%s: Inline mail. Setting encoding.",
1074                  SRCNAME, __func__);
1075
1076       SPropValue prop;
1077       prop.ulPropTag = PR_INTERNET_CPID;
1078       prop.Value.l = 65001;
1079       if (HrSetOneProp (message, &prop))
1080         {
1081           log_error ("%s:%s: Failed to set CPID mapiprop.",
1082                      SRCNAME, __func__);
1083         }
1084
1085       TRETURN 0;
1086     }
1087
1088   mapi_attach_item_t *att_table = mapi_create_attach_table (message, 0);
1089
1090   /* When we forward e.g. a crypto mail we have sent the message
1091      has a MOSSTEMPL. We need to remove that. T4321 */
1092   for (ULONG pos=0; att_table && !att_table[pos].end_of_table; pos++)
1093     {
1094       if (att_table[pos].attach_type == ATTACHTYPE_MOSSTEMPL)
1095         {
1096           log_debug ("%s:%s: Found existing moss attachment at "
1097                      "pos %i removing it.", SRCNAME, __func__,
1098                      att_table[pos].mapipos);
1099           if (message->DeleteAttach (att_table[pos].mapipos, 0,
1100                                      nullptr, 0) != S_OK)
1101             {
1102               log_error ("%s:%s: Failed to remove attachment.",
1103                          SRCNAME, __func__);
1104             }
1105
1106         }
1107     }
1108
1109   // Set up the sink object for our MSOXSMIME attachment.
1110   struct sink_s sinkmem;
1111   sink_t sink = &sinkmem;
1112   memset (sink, 0, sizeof *sink);
1113   sink->cb_data = &m_input;
1114   sink->writefnc = sink_data_write;
1115
1116   // For S/MIME encrypted mails we have to use the application/pkcs7-mime
1117   // content type. Otherwise newer (2016) exchange servers will throw
1118   // an M2MCVT.StorageError.Exeption (See GnuPG-Bug-Id: T3853 )
1119
1120   // This means that the conversion / build of the mime structure also
1121   // happens differently.
1122   int exchange_major_version = get_ex_major_version_for_addr (
1123                                         m_mail->getSender ().c_str ());
1124
1125   std::string overrideMimeTag;
1126   if (m_proto == GpgME::CMS && m_encrypt && exchange_major_version >= 15)
1127     {
1128       log_debug ("%s:%s: CMS Encrypt with Exchange %i activating alternative.",
1129                  SRCNAME, __func__, exchange_major_version);
1130       overrideMimeTag = "application/pkcs7-mime";
1131     }
1132
1133   LPATTACH attach = create_mapi_attachment (message, sink,
1134                                             overrideMimeTag.empty() ? nullptr :
1135                                             overrideMimeTag.c_str());
1136   if (!attach)
1137     {
1138       log_error ("%s:%s: Failed to create moss attach.",
1139                  SRCNAME, __func__);
1140       gpgol_release (message);
1141       TRETURN -1;
1142     }
1143
1144   protocol_t protocol = m_proto == GpgME::CMS ?
1145                                    PROTOCOL_SMIME :
1146                                    PROTOCOL_OPENPGP;
1147
1148   int rc = 0;
1149   /* Do we have override MIME ? */
1150   const auto overrideMime = m_mail->get_override_mime_data ();
1151   if (!overrideMime.empty())
1152     {
1153       rc = write_string (sink, overrideMime.c_str ());
1154     }
1155   else if (m_sign && m_encrypt)
1156     {
1157       rc = create_encrypt_attach (sink, protocol, m_output, exchange_major_version);
1158     }
1159   else if (m_encrypt)
1160     {
1161       rc = create_encrypt_attach (sink, protocol, m_output, exchange_major_version);
1162     }
1163   else if (m_sign)
1164     {
1165       rc = create_sign_attach (sink, protocol, m_output, m_input, m_micalg.c_str ());
1166     }
1167
1168   // Close our attachment
1169   if (!rc)
1170     {
1171       rc = close_mapi_attachment (&attach, sink);
1172     }
1173
1174   // Set message class etc.
1175   if (!rc)
1176     {
1177       rc = finalize_message (message, att_table, protocol, m_encrypt ? 1 : 0,
1178                              false, m_mail->isDraftEncrypt ());
1179     }
1180
1181   // only on error.
1182   if (rc)
1183     {
1184       cancel_mapi_attachment (&attach, sink);
1185     }
1186
1187   // cleanup
1188   mapi_release_attach_table (att_table);
1189   gpgol_release (attach);
1190   gpgol_release (message);
1191
1192   TRETURN rc;
1193 }
1194
1195 std::string
1196 CryptController::get_inline_data ()
1197 {
1198   TSTART;
1199   std::string ret;
1200   if (!m_mail->getDoPGPInline ())
1201     {
1202       TRETURN ret;
1203     }
1204   m_output.seek (0, SEEK_SET);
1205   char buf[4096];
1206   size_t nread;
1207   while ((nread = m_output.read (buf, 4096)) > 0)
1208     {
1209       ret += std::string (buf, nread);
1210     }
1211   TRETURN ret;
1212 }
1213
1214 void
1215 CryptController::parse_micalg (const GpgME::SigningResult &result)
1216 {
1217   TSTART;
1218   if (result.isNull())
1219     {
1220       TRACEPOINT;
1221       TRETURN;
1222     }
1223   const auto signature = result.createdSignature(0);
1224   if (signature.isNull())
1225     {
1226       TRACEPOINT;
1227       TRETURN;
1228     }
1229
1230   const char *hashAlg = signature.hashAlgorithmAsString ();
1231   if (!hashAlg)
1232     {
1233       TRACEPOINT;
1234       TRETURN;
1235     }
1236   if (m_proto == GpgME::OpenPGP)
1237     {
1238       m_micalg = std::string("pgp-") + hashAlg;
1239     }
1240   else
1241     {
1242       m_micalg = hashAlg;
1243     }
1244   std::transform(m_micalg.begin(), m_micalg.end(), m_micalg.begin(), ::tolower);
1245
1246   log_debug ("%s:%s: micalg is: '%s'.",
1247              SRCNAME, __func__, m_micalg.c_str ());
1248   TRETURN;
1249 }
1250
1251 void
1252 CryptController::start_crypto_overlay ()
1253 {
1254   TSTART;
1255   auto wid = m_mail->getWindow ();
1256
1257   std::string text;
1258
1259   if (m_encrypt)
1260     {
1261       text = _("Encrypting...");
1262     }
1263   else if (m_sign)
1264     {
1265       text = _("Signing...");
1266     }
1267   m_overlay = std::unique_ptr<Overlay> (new Overlay (wid, text));
1268   TRETURN;
1269 }