Pass address book overrides to keyresolver
[gpgol.git] / src / keycache.cpp
1 /* @file keycache.cpp
2  * @brief Internal keycache
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
22 #include "keycache.h"
23
24 #include "common.h"
25 #include "cpphelp.h"
26 #include "mail.h"
27
28 #include <gpg-error.h>
29 #include <gpgme++/context.h>
30 #include <gpgme++/key.h>
31 #include <gpgme++/data.h>
32 #include <gpgme++/importresult.h>
33
34 #include <windows.h>
35
36 #include <set>
37 #include <unordered_map>
38 #include <sstream>
39
40 GPGRT_LOCK_DEFINE (keycache_lock);
41 GPGRT_LOCK_DEFINE (fpr_map_lock);
42 GPGRT_LOCK_DEFINE (update_lock);
43 GPGRT_LOCK_DEFINE (import_lock);
44 static KeyCache* singleton = nullptr;
45
46 /** At some point we need to set a limit. There
47   seems to be no limit on how many recipients a mail
48   can have in outlook.
49
50   We would run out of resources or block.
51
52   50 Threads already seems a bit excessive but
53   it should really cover most legit use cases.
54 */
55
56 #define MAX_LOCATOR_THREADS 50
57 static int s_thread_cnt;
58
59 namespace
60 {
61   class LocateArgs
62     {
63       public:
64         LocateArgs (const std::string& mbox, Mail *mail = nullptr):
65           m_mbox (mbox),
66           m_mail (mail)
67         {
68           TSTART;
69           s_thread_cnt++;
70           Mail::lockDelete ();
71           if (Mail::isValidPtr (m_mail))
72             {
73               m_mail->incrementLocateCount ();
74             }
75           Mail::unlockDelete ();
76           TRETURN;
77         };
78
79         ~LocateArgs()
80         {
81           TSTART;
82           s_thread_cnt--;
83           Mail::lockDelete ();
84           if (Mail::isValidPtr (m_mail))
85             {
86               m_mail->decrementLocateCount ();
87             }
88           Mail::unlockDelete ();
89           TRETURN;
90         }
91
92         std::string m_mbox;
93         Mail *m_mail;
94     };
95 } // namespace
96
97 typedef std::pair<std::string, GpgME::Protocol> update_arg_t;
98
99 typedef std::pair<std::unique_ptr<LocateArgs>, std::string> import_arg_t;
100
101 static DWORD WINAPI
102 do_update (LPVOID arg)
103 {
104   TSTART;
105   auto args = std::unique_ptr<update_arg_t> ((update_arg_t*) arg);
106
107   log_debug ("%s:%s updating: \"%s\" with protocol %s",
108              SRCNAME, __func__, anonstr (args->first.c_str ()),
109              to_cstr (args->second));
110
111   auto ctx = std::unique_ptr<GpgME::Context> (GpgME::Context::createForProtocol
112                                               (args->second));
113
114   if (!ctx)
115     {
116       TRACEPOINT;
117       KeyCache::instance ()->onUpdateJobDone (args->first.c_str(),
118                                               GpgME::Key ());
119       TRETURN 0;
120     }
121
122   ctx->setKeyListMode (GpgME::KeyListMode::Local |
123                        GpgME::KeyListMode::Signatures |
124                        GpgME::KeyListMode::Validate |
125                        GpgME::KeyListMode::WithTofu);
126   GpgME::Error err;
127   const auto newKey = ctx->key (args->first.c_str (), err, false);
128   TRACEPOINT;
129
130   if (newKey.isNull())
131     {
132       log_debug ("%s:%s Failed to find key for %s",
133                  SRCNAME, __func__, anonstr (args->first.c_str ()));
134     }
135   if (err)
136     {
137       log_debug ("%s:%s Failed to find key for %s err: %s",
138                  SRCNAME, __func__, anonstr (args->first.c_str()),
139                  err.asString ());
140     }
141   KeyCache::instance ()->onUpdateJobDone (args->first.c_str(),
142                                           newKey);
143   log_debug ("%s:%s Update job done",
144              SRCNAME, __func__);
145   TRETURN 0;
146 }
147
148 static DWORD WINAPI
149 do_import (LPVOID arg)
150 {
151   TSTART;
152   auto args = std::unique_ptr<import_arg_t> ((import_arg_t*) arg);
153
154   const std::string mbox = args->first->m_mbox;
155
156   log_debug ("%s:%s importing for: \"%s\" with data \n%s",
157              SRCNAME, __func__, anonstr (mbox.c_str ()),
158              anonstr (args->second.c_str ()));
159   auto ctx = std::unique_ptr<GpgME::Context> (GpgME::Context::createForProtocol
160                                               (GpgME::OpenPGP));
161
162   if (!ctx)
163     {
164       TRACEPOINT;
165       TRETURN 0;
166     }
167   // We want to avoid unneccessary copies. The c_str will be valid
168   // until args goes out of scope.
169   const char *keyStr = args->second.c_str ();
170   GpgME::Data data (keyStr, strlen (keyStr), /* copy */ false);
171
172   if (data.type () != GpgME::Data::PGPKey)
173     {
174       log_debug ("%s:%s Data for: %s is not a PGP Key",
175                  SRCNAME, __func__, anonstr (mbox.c_str ()));
176       TRETURN 0;
177     }
178   data.rewind ();
179
180   const auto result = ctx->importKeys (data);
181
182   std::vector<std::string> fingerprints;
183   for (const auto import: result.imports())
184     {
185       if (import.error())
186         {
187           log_debug ("%s:%s Error importing: %s",
188                      SRCNAME, __func__, import.error().asString());
189           continue;
190         }
191       const char *fpr = import.fingerprint ();
192       if (!fpr)
193         {
194           TRACEPOINT;
195           continue;
196         }
197
198       update_arg_t * update_args = new update_arg_t;
199       update_args->first = std::string (fpr);
200       update_args->second = GpgME::OpenPGP;
201
202       // We do it blocking to be sure that when all imports
203       // are done they are also part of the keycache.
204       do_update ((LPVOID) update_args);
205
206       fingerprints.push_back (fpr);
207       log_debug ("%s:%s Imported: %s from addressbook.",
208                  SRCNAME, __func__, anonstr (fpr));
209     }
210
211   KeyCache::instance ()->onAddrBookImportJobDone (mbox, fingerprints);
212
213   log_debug ("%s:%s Import job done for: %s",
214              SRCNAME, __func__, anonstr (mbox.c_str ()));
215   TRETURN 0;
216 }
217
218
219 class KeyCache::Private
220 {
221 public:
222   Private()
223   {
224
225   }
226
227   void setPgpKey(const std::string &mbox, const GpgME::Key &key)
228   {
229     TSTART;
230     gpgol_lock (&keycache_lock);
231     auto it = m_pgp_key_map.find (mbox);
232
233     if (it == m_pgp_key_map.end ())
234       {
235         m_pgp_key_map.insert (std::pair<std::string, GpgME::Key> (mbox, key));
236       }
237     else
238       {
239         it->second = key;
240       }
241     insertOrUpdateInFprMap (key);
242     gpgol_unlock (&keycache_lock);
243     TRETURN;
244   }
245
246   void setSmimeKey(const std::string &mbox, const GpgME::Key &key)
247   {
248     TSTART;
249     gpgol_lock (&keycache_lock);
250     auto it = m_smime_key_map.find (mbox);
251
252     if (it == m_smime_key_map.end ())
253       {
254         m_smime_key_map.insert (std::pair<std::string, GpgME::Key> (mbox, key));
255       }
256     else
257       {
258         it->second = key;
259       }
260     insertOrUpdateInFprMap (key);
261     gpgol_unlock (&keycache_lock);
262     TRETURN;
263   }
264
265   void setPgpKeySecret(const std::string &mbox, const GpgME::Key &key)
266   {
267     TSTART;
268     gpgol_lock (&keycache_lock);
269     auto it = m_pgp_skey_map.find (mbox);
270
271     if (it == m_pgp_skey_map.end ())
272       {
273         m_pgp_skey_map.insert (std::pair<std::string, GpgME::Key> (mbox, key));
274       }
275     else
276       {
277         it->second = key;
278       }
279     insertOrUpdateInFprMap (key);
280     gpgol_unlock (&keycache_lock);
281     TRETURN;
282   }
283
284   void setSmimeKeySecret(const std::string &mbox, const GpgME::Key &key)
285   {
286     TSTART;
287     gpgol_lock (&keycache_lock);
288     auto it = m_smime_skey_map.find (mbox);
289
290     if (it == m_smime_skey_map.end ())
291       {
292         m_smime_skey_map.insert (std::pair<std::string, GpgME::Key> (mbox, key));
293       }
294     else
295       {
296         it->second = key;
297       }
298     insertOrUpdateInFprMap (key);
299     gpgol_unlock (&keycache_lock);
300     TRETURN;
301   }
302
303   std::vector<GpgME::Key> getPGPOverrides (const char *addr)
304   {
305     TSTART;
306     std::vector<GpgME::Key> ret;
307
308     if (!addr)
309       {
310         TRETURN ret;
311       }
312     auto mbox = GpgME::UserID::addrSpecFromString (addr);
313
314     gpgol_lock (&keycache_lock);
315     const auto it = m_addr_book_overrides.find (mbox);
316     if (it == m_addr_book_overrides.end ())
317       {
318         gpgol_unlock (&keycache_lock);
319         TRETURN ret;
320       }
321     for (const auto fpr: it->second)
322       {
323         const auto key = getByFpr (fpr.c_str (), false);
324         if (key.isNull())
325           {
326             log_debug ("%s:%s: No key for %s in the cache?!",
327                        SRCNAME, __func__, anonstr (fpr.c_str()));
328             continue;
329           }
330         ret.push_back (key);
331       }
332
333     gpgol_unlock (&keycache_lock);
334     TRETURN ret;
335   }
336
337   GpgME::Key getKey (const char *addr, GpgME::Protocol proto)
338   {
339     TSTART;
340     if (!addr)
341       {
342         TRETURN GpgME::Key();
343       }
344     auto mbox = GpgME::UserID::addrSpecFromString (addr);
345
346     if (proto == GpgME::OpenPGP)
347       {
348         gpgol_lock (&keycache_lock);
349         const auto it = m_pgp_key_map.find (mbox);
350
351         if (it == m_pgp_key_map.end ())
352           {
353             gpgol_unlock (&keycache_lock);
354             TRETURN GpgME::Key();
355           }
356         const auto ret = it->second;
357         gpgol_unlock (&keycache_lock);
358
359         TRETURN ret;
360       }
361     gpgol_lock (&keycache_lock);
362     const auto it = m_smime_key_map.find (mbox);
363
364     if (it == m_smime_key_map.end ())
365       {
366         gpgol_unlock (&keycache_lock);
367         TRETURN GpgME::Key();
368       }
369     const auto ret = it->second;
370     gpgol_unlock (&keycache_lock);
371
372     TRETURN ret;
373   }
374
375   GpgME::Key getSKey (const char *addr, GpgME::Protocol proto)
376   {
377     TSTART;
378     if (!addr)
379       {
380         TRETURN GpgME::Key();
381       }
382     auto mbox = GpgME::UserID::addrSpecFromString (addr);
383
384     if (proto == GpgME::OpenPGP)
385       {
386         gpgol_lock (&keycache_lock);
387         const auto it = m_pgp_skey_map.find (mbox);
388
389         if (it == m_pgp_skey_map.end ())
390           {
391             gpgol_unlock (&keycache_lock);
392             TRETURN GpgME::Key();
393           }
394         const auto ret = it->second;
395         gpgol_unlock (&keycache_lock);
396
397         TRETURN ret;
398       }
399     gpgol_lock (&keycache_lock);
400     const auto it = m_smime_skey_map.find (mbox);
401
402     if (it == m_smime_skey_map.end ())
403       {
404         gpgol_unlock (&keycache_lock);
405         TRETURN GpgME::Key();
406       }
407     const auto ret = it->second;
408     gpgol_unlock (&keycache_lock);
409
410     TRETURN ret;
411   }
412
413   GpgME::Key getSigningKey (const char *addr, GpgME::Protocol proto)
414   {
415     TSTART;
416     const auto key = getSKey (addr, proto);
417     if (key.isNull())
418       {
419         log_debug ("%s:%s: secret key for %s is null",
420                    SRCNAME, __func__, anonstr (addr));
421         TRETURN key;
422       }
423     if (!key.canReallySign())
424       {
425         log_debug ("%s:%s: Discarding key for %s because it can't sign",
426                    SRCNAME, __func__, anonstr (addr));
427         TRETURN GpgME::Key();
428       }
429     if (!key.hasSecret())
430       {
431         log_debug ("%s:%s: Discarding key for %s because it has no secret",
432                    SRCNAME, __func__, anonstr (addr));
433         TRETURN GpgME::Key();
434       }
435     if (in_de_vs_mode () && !key.isDeVs())
436       {
437         log_debug ("%s:%s: signing key for %s is not deVS",
438                    SRCNAME, __func__, anonstr (addr));
439         TRETURN GpgME::Key();
440       }
441     TRETURN key;
442   }
443
444   std::vector<GpgME::Key> getEncryptionKeys (const std::vector<std::string>
445                                              &recipients,
446                                              GpgME::Protocol proto)
447   {
448     TSTART;
449     std::vector<GpgME::Key> ret;
450     if (recipients.empty ())
451       {
452         TRACEPOINT;
453         TRETURN ret;
454       }
455     for (const auto &recip: recipients)
456       {
457         if (proto == GpgME::OpenPGP)
458           {
459             const auto overrides = getPGPOverrides (recip.c_str ());
460
461             if (!overrides.empty())
462               {
463                 ret.insert (ret.end (), overrides.begin (), overrides.end ());
464                 log_debug ("%s:%s: Using overides for %s",
465                            SRCNAME, __func__, anonstr (recip.c_str ()));
466                 continue;
467               }
468           }
469         const auto key = getKey (recip.c_str (), proto);
470         if (key.isNull())
471           {
472             log_debug ("%s:%s: No key for %s. no internal encryption",
473                        SRCNAME, __func__, anonstr (recip.c_str ()));
474             TRETURN std::vector<GpgME::Key>();
475           }
476
477         if (!key.canEncrypt() || key.isRevoked() ||
478             key.isExpired() || key.isDisabled() || key.isInvalid())
479           {
480             log_data ("%s:%s: Invalid key for %s. no internal encryption",
481                        SRCNAME, __func__, anonstr (recip.c_str ()));
482             TRETURN std::vector<GpgME::Key>();
483           }
484
485         if (in_de_vs_mode () && !key.isDeVs ())
486           {
487             log_data ("%s:%s: key for %s is not deVS",
488                       SRCNAME, __func__, anonstr (recip.c_str ()));
489             TRETURN std::vector<GpgME::Key>();
490           }
491
492         bool validEnough = false;
493         /* Here we do the check if the key is valid for this recipient */
494         const auto addrSpec = GpgME::UserID::addrSpecFromString (recip.c_str ());
495         for (const auto &uid: key.userIDs ())
496           {
497             if (addrSpec != uid.addrSpec())
498               {
499                 // Ignore unmatching addr specs
500                 continue;
501               }
502             if (uid.validity() >= GpgME::UserID::Marginal ||
503                 uid.origin() == GpgME::Key::OriginWKD)
504               {
505                 validEnough = true;
506                 break;
507               }
508           }
509         if (!validEnough)
510           {
511             log_debug ("%s:%s: UID for %s does not have at least marginal trust",
512                        SRCNAME, __func__, anonstr (recip.c_str ()));
513             TRETURN std::vector<GpgME::Key>();
514           }
515         // Accepting key
516         ret.push_back (key);
517       }
518     TRETURN ret;
519   }
520
521   void insertOrUpdateInFprMap (const GpgME::Key &key)
522     {
523       TSTART;
524       if (key.isNull() || !key.primaryFingerprint())
525         {
526           TRACEPOINT;
527           TRETURN;
528         }
529       gpgol_lock (&fpr_map_lock);
530
531       /* First ensure that we have the subkeys mapped to the primary
532          fpr */
533       const char *primaryFpr = key.primaryFingerprint ();
534
535       for (const auto &sub: key.subkeys())
536         {
537           const char *subFpr = sub.fingerprint();
538           auto it = m_sub_fpr_map.find (subFpr);
539           if (it == m_sub_fpr_map.end ())
540             {
541               m_sub_fpr_map.insert (std::make_pair(
542                                      std::string (subFpr),
543                                      std::string (primaryFpr)));
544             }
545         }
546
547       auto it = m_fpr_map.find (primaryFpr);
548
549       log_debug ("%s:%s \"%s\" updated.",
550                  SRCNAME, __func__, anonstr (primaryFpr));
551       if (it == m_fpr_map.end ())
552         {
553           m_fpr_map.insert (std::make_pair (primaryFpr, key));
554
555           gpgol_unlock (&fpr_map_lock);
556           TRETURN;
557         }
558
559       if (it->second.hasSecret () && !key.hasSecret())
560         {
561           log_debug ("%s:%s Lost secret info on update. Merging.",
562                      SRCNAME, __func__);
563           auto merged = key;
564           merged.mergeWith (it->second);
565           it->second = merged;
566         }
567       else
568         {
569           it->second = key;
570         }
571       gpgol_unlock (&fpr_map_lock);
572       TRETURN;
573     }
574
575   GpgME::Key getFromMap (const char *fpr) const
576   {
577     TSTART;
578     if (!fpr)
579       {
580         TRACEPOINT;
581         TRETURN GpgME::Key();
582       }
583
584     gpgol_lock (&fpr_map_lock);
585     std::string primaryFpr;
586     const auto it = m_sub_fpr_map.find (fpr);
587     if (it != m_sub_fpr_map.end ())
588       {
589         log_debug ("%s:%s using \"%s\" for \"%s\"",
590                    SRCNAME, __func__, anonstr (it->second.c_str()),
591                    anonstr (fpr));
592         primaryFpr = it->second;
593       }
594     else
595       {
596         primaryFpr = fpr;
597       }
598
599     const auto keyIt = m_fpr_map.find (primaryFpr);
600     if (keyIt != m_fpr_map.end ())
601       {
602         const auto ret = keyIt->second;
603         gpgol_unlock (&fpr_map_lock);
604         TRETURN ret;
605       }
606     gpgol_unlock (&fpr_map_lock);
607     TRETURN GpgME::Key();
608   }
609
610   GpgME::Key getByFpr (const char *fpr, bool block) const
611     {
612       TSTART;
613       if (!fpr)
614         {
615           TRACEPOINT;
616           TRETURN GpgME::Key ();
617         }
618
619       TRACEPOINT;
620       const auto ret = getFromMap (fpr);
621       if (ret.isNull())
622         {
623           // If the key was not found we need to check if there is
624           // an update running.
625           if (block)
626             {
627               const std::string sFpr (fpr);
628               int i = 0;
629
630               gpgol_lock (&update_lock);
631               while (m_update_jobs.find(sFpr) != m_update_jobs.end ())
632                 {
633                   i++;
634                   if (i % 100 == 0)
635                     {
636                       log_debug ("%s:%s Waiting on update for \"%s\"",
637                                  SRCNAME, __func__, anonstr (fpr));
638                     }
639                   gpgol_unlock (&update_lock);
640                   Sleep (10);
641                   gpgol_lock (&update_lock);
642                   if (i == 3000)
643                     {
644                       /* Just to be on the save side */
645                       log_error ("%s:%s Waiting on update for \"%s\" "
646                                  "failed! Bug!",
647                                  SRCNAME, __func__, anonstr (fpr));
648                       break;
649                     }
650                 }
651               gpgol_unlock (&update_lock);
652
653               TRACEPOINT;
654               const auto ret2 = getFromMap (fpr);
655               if (ret2.isNull ())
656                 {
657                   log_debug ("%s:%s Cache miss after blocking check %s.",
658                              SRCNAME, __func__, anonstr (fpr));
659                 }
660               else
661                 {
662                   log_debug ("%s:%s Cache hit after wait for %s.",
663                              SRCNAME, __func__, anonstr (fpr));
664                   TRETURN ret2;
665                 }
666             }
667           log_debug ("%s:%s Cache miss for %s.",
668                      SRCNAME, __func__, anonstr (fpr));
669           TRETURN GpgME::Key();
670         }
671
672       log_debug ("%s:%s Cache hit for %s.",
673                  SRCNAME, __func__, anonstr (fpr));
674       TRETURN ret;
675     }
676
677   void update (const char *fpr, GpgME::Protocol proto)
678      {
679        TSTART;
680        if (!fpr)
681          {
682            TRETURN;
683          }
684        const std::string sFpr (fpr);
685        gpgol_lock (&update_lock);
686        if (m_update_jobs.find(sFpr) != m_update_jobs.end ())
687          {
688            log_debug ("%s:%s Update for \"%s\" already in progress.",
689                       SRCNAME, __func__, anonstr (fpr));
690            gpgol_unlock (&update_lock);
691          }
692
693        m_update_jobs.insert (sFpr);
694        gpgol_unlock (&update_lock);
695        update_arg_t * args = new update_arg_t;
696        args->first = sFpr;
697        args->second = proto;
698        CloseHandle (CreateThread (NULL, 0, do_update,
699                                   (LPVOID) args, 0,
700                                   NULL));
701        TRETURN;
702      }
703
704   void onUpdateJobDone (const char *fpr, const GpgME::Key &key)
705     {
706       TSTART;
707       if (!fpr)
708         {
709           TRETURN;
710         }
711       TRACEPOINT;
712       insertOrUpdateInFprMap (key);
713       gpgol_lock (&update_lock);
714       const auto it = m_update_jobs.find(fpr);
715
716       if (it == m_update_jobs.end())
717         {
718           log_debug ("%s:%s Update for \"%s\" already finished.",
719                      SRCNAME, __func__, anonstr (fpr));
720           gpgol_unlock (&update_lock);
721           TRETURN;
722         }
723       m_update_jobs.erase (it);
724       gpgol_unlock (&update_lock);
725       TRACEPOINT;
726       TRETURN;
727     }
728
729   void importFromAddrBook (const std::string &mbox, const char *data,
730                            Mail *mail)
731     {
732       TSTART;
733       if (!data || mbox.empty() || !mail)
734         {
735           TRACEPOINT;
736           TRETURN;
737         }
738        gpgol_lock (&import_lock);
739        if (m_import_jobs.find (mbox) != m_import_jobs.end ())
740          {
741            log_debug ("%s:%s import for \"%s\" already in progress.",
742                       SRCNAME, __func__, anonstr (mbox.c_str ()));
743            gpgol_unlock (&import_lock);
744          }
745        m_import_jobs.insert (mbox);
746        gpgol_unlock (&import_lock);
747
748        import_arg_t * args = new import_arg_t;
749        args->first = std::unique_ptr<LocateArgs> (new LocateArgs (mbox, mail));
750        args->second = std::string (data);
751        CloseHandle (CreateThread (NULL, 0, do_import,
752                                   (LPVOID) args, 0,
753                                   NULL));
754
755       TRETURN;
756     }
757
758   void onAddrBookImportJobDone (const std::string &mbox,
759                                 const std::vector<std::string> &result_fprs)
760     {
761       TSTART;
762       gpgol_lock (&keycache_lock);
763       auto it = m_addr_book_overrides.find (mbox);
764       if (it != m_addr_book_overrides.end ())
765         {
766           it->second = result_fprs;
767         }
768       else
769         {
770           m_addr_book_overrides.insert (
771                 std::make_pair (mbox, result_fprs));
772         }
773       gpgol_unlock (&keycache_lock);
774       gpgol_lock (&import_lock);
775       const auto job_it = m_import_jobs.find(mbox);
776
777       if (job_it == m_import_jobs.end())
778         {
779           log_error ("%s:%s import for \"%s\" already finished.",
780                      SRCNAME, __func__, anonstr (mbox.c_str ()));
781           gpgol_unlock (&import_lock);
782           TRETURN;
783         }
784       m_import_jobs.erase (job_it);
785       gpgol_unlock (&import_lock);
786       TRETURN;
787     }
788
789   std::unordered_map<std::string, GpgME::Key> m_pgp_key_map;
790   std::unordered_map<std::string, GpgME::Key> m_smime_key_map;
791   std::unordered_map<std::string, GpgME::Key> m_pgp_skey_map;
792   std::unordered_map<std::string, GpgME::Key> m_smime_skey_map;
793   std::unordered_map<std::string, GpgME::Key> m_fpr_map;
794   std::unordered_map<std::string, std::string> m_sub_fpr_map;
795   std::unordered_map<std::string, std::vector<std::string> >
796     m_addr_book_overrides;
797   std::set<std::string> m_update_jobs;
798   std::set<std::string> m_import_jobs;
799 };
800
801 KeyCache::KeyCache():
802   d(new Private)
803 {
804 }
805
806 KeyCache *
807 KeyCache::instance ()
808 {
809   if (!singleton)
810     {
811       singleton = new KeyCache();
812     }
813   return singleton;
814 }
815
816 GpgME::Key
817 KeyCache::getSigningKey (const char *addr, GpgME::Protocol proto) const
818 {
819   return d->getSigningKey (addr, proto);
820 }
821
822 std::vector<GpgME::Key>
823 KeyCache::getEncryptionKeys (const std::vector<std::string> &recipients, GpgME::Protocol proto) const
824 {
825   return d->getEncryptionKeys (recipients, proto);
826 }
827
828 static GpgME::Key
829 get_most_valid_key_simple (const std::vector<GpgME::Key> &keys)
830 {
831   GpgME::Key candidate;
832   for (const auto &key: keys)
833     {
834       if (key.isRevoked() || key.isExpired() ||
835           key.isDisabled() || key.isInvalid())
836         {
837           log_debug ("%s:%s: Skipping invalid S/MIME key",
838                      SRCNAME, __func__);
839           continue;
840         }
841       if (candidate.isNull() || !candidate.numUserIDs())
842         {
843           if (key.numUserIDs() &&
844               candidate.userID(0).validity() <= key.userID(0).validity())
845             {
846               candidate = key;
847             }
848         }
849     }
850   return candidate;
851 }
852
853 static std::vector<GpgME::Key>
854 get_local_smime_keys (const std::string &addr)
855 {
856   TSTART;
857   std::vector<GpgME::Key> keys;
858   auto ctx = std::unique_ptr<GpgME::Context> (
859                                               GpgME::Context::createForProtocol (GpgME::CMS));
860   if (!ctx)
861     {
862       TRACEPOINT;
863       TRETURN keys;
864     }
865   // We need to validate here to fetch CRL's
866   ctx->setKeyListMode (GpgME::KeyListMode::Local |
867                        GpgME::KeyListMode::Validate |
868                        GpgME::KeyListMode::Signatures);
869   GpgME::Error e = ctx->startKeyListing (addr.c_str());
870   if (e)
871     {
872       TRACEPOINT;
873       TRETURN keys;
874     }
875
876   GpgME::Error err;
877   do {
878       keys.push_back(ctx->nextKey(err));
879   } while (!err);
880   keys.pop_back();
881
882   TRETURN keys;
883 }
884
885 static std::vector<GpgME::Key>
886 get_extern_smime_keys (const std::string &addr, bool import)
887 {
888   TSTART;
889   std::vector<GpgME::Key> keys;
890   auto ctx = std::unique_ptr<GpgME::Context> (
891                                               GpgME::Context::createForProtocol (GpgME::CMS));
892   if (!ctx)
893     {
894       TRACEPOINT;
895       TRETURN keys;
896     }
897   // We need to validate here to fetch CRL's
898   ctx->setKeyListMode (GpgME::KeyListMode::Extern);
899   GpgME::Error e = ctx->startKeyListing (addr.c_str());
900   if (e)
901     {
902       TRACEPOINT;
903       TRETURN keys;
904     }
905
906   GpgME::Error err;
907   do
908     {
909       const auto key = ctx->nextKey (err);
910       if (!err && !key.isNull())
911         {
912           keys.push_back (key);
913           log_debug ("%s:%s: Found extern S/MIME key for %s with fpr: %s",
914                      SRCNAME, __func__, anonstr (addr.c_str()),
915                      anonstr (key.primaryFingerprint()));
916         }
917     } while (!err);
918
919   if (import && keys.size ())
920     {
921       const GpgME::ImportResult res = ctx->importKeys(keys);
922       log_debug ("%s:%s: Import result for %s: err: %s",
923                  SRCNAME, __func__, anonstr (addr.c_str()),
924                  res.error ().asString ());
925
926     }
927
928   TRETURN keys;
929 }
930
931 static DWORD WINAPI
932 do_locate (LPVOID arg)
933 {
934   TSTART;
935   if (!arg)
936     {
937       TRETURN 0;
938     }
939
940   auto args = std::unique_ptr<LocateArgs> ((LocateArgs *) arg);
941
942   const auto addr = args->m_mbox;
943
944   log_debug ("%s:%s searching key for addr: \"%s\"",
945              SRCNAME, __func__, anonstr (addr.c_str()));
946
947   const auto k = GpgME::Key::locate (addr.c_str());
948
949   if (!k.isNull ())
950     {
951       log_debug ("%s:%s found key for addr: \"%s\":%s",
952                  SRCNAME, __func__, anonstr (addr.c_str()),
953                  anonstr (k.primaryFingerprint()));
954       KeyCache::instance ()->setPgpKey (addr, k);
955     }
956   log_debug ("%s:%s pgp locate done",
957              SRCNAME, __func__);
958
959   if (opt.enable_smime)
960     {
961       GpgME::Key candidate = get_most_valid_key_simple (
962                                     get_local_smime_keys (addr));
963       if (!candidate.isNull())
964         {
965           log_debug ("%s:%s found SMIME key for addr: \"%s\":%s",
966                      SRCNAME, __func__, anonstr (addr.c_str()),
967                      anonstr (candidate.primaryFingerprint()));
968           KeyCache::instance()->setSmimeKey (addr, candidate);
969           TRETURN 0;
970         }
971       if (!opt.search_smime_servers || (!k.isNull() && !opt.prefer_smime))
972         {
973           log_debug ("%s:%s Found no S/MIME key locally and external "
974                      "search is disabled.", SRCNAME, __func__);
975           TRETURN 0;
976         }
977       /* Search for extern keys and import them */
978       const auto externs = get_extern_smime_keys (addr, true);
979       if (externs.empty())
980         {
981           TRETURN 0;
982         }
983       /* We found and imported external keys. We need to get them
984          locally now to ensure that they are valid etc. */
985       candidate = get_most_valid_key_simple (
986                                     get_local_smime_keys (addr));
987       if (!candidate.isNull())
988         {
989           log_debug ("%s:%s found ext. SMIME key for addr: \"%s\":%s",
990                      SRCNAME, __func__, anonstr (addr.c_str()),
991                      anonstr (candidate.primaryFingerprint()));
992           KeyCache::instance()->setSmimeKey (addr, candidate);
993           TRETURN 0;
994         }
995       else
996         {
997           log_debug ("%s:%s: Found no valid key in extern S/MIME certs",
998                      SRCNAME, __func__);
999         }
1000     }
1001   TRETURN 0;
1002 }
1003
1004 static void
1005 locate_secret (const char *addr, GpgME::Protocol proto)
1006 {
1007   TSTART;
1008   auto ctx = std::unique_ptr<GpgME::Context> (
1009                       GpgME::Context::createForProtocol (proto));
1010   if (!ctx)
1011     {
1012       TRACEPOINT;
1013       TRETURN;
1014     }
1015   if (!addr)
1016     {
1017       TRACEPOINT;
1018       TRETURN;
1019     }
1020   const auto mbox = GpgME::UserID::addrSpecFromString (addr);
1021
1022   if (mbox.empty())
1023     {
1024       log_debug ("%s:%s: Empty mbox for addr %s",
1025                  SRCNAME, __func__, anonstr (addr));
1026       TRETURN;
1027     }
1028
1029   // We need to validate here to fetch CRL's
1030   ctx->setKeyListMode (GpgME::KeyListMode::Local |
1031                        GpgME::KeyListMode::Validate);
1032   GpgME::Error e = ctx->startKeyListing (mbox.c_str(), true);
1033   if (e)
1034     {
1035       TRACEPOINT;
1036       TRETURN;
1037     }
1038
1039   std::vector<GpgME::Key> keys;
1040   GpgME::Error err;
1041   do
1042     {
1043       const auto key = ctx->nextKey(err);
1044       if (key.isNull())
1045         {
1046           continue;
1047         }
1048       if (key.isRevoked() || key.isExpired() ||
1049           key.isDisabled() || key.isInvalid())
1050         {
1051           if ((opt.enable_debug & DBG_DATA))
1052             {
1053               std::stringstream ss;
1054               ss << key;
1055               log_data ("%s:%s: Skipping invalid secret key %s",
1056                         SRCNAME, __func__, ss.str().c_str());
1057             }
1058           continue;
1059         }
1060       if (proto == GpgME::OpenPGP)
1061         {
1062           log_debug ("%s:%s found pgp skey for addr: \"%s\":%s",
1063                      SRCNAME, __func__, anonstr (mbox.c_str()),
1064                      anonstr (key.primaryFingerprint()));
1065           KeyCache::instance()->setPgpKeySecret (mbox, key);
1066           TRETURN;
1067         }
1068       if (proto == GpgME::CMS)
1069         {
1070           log_debug ("%s:%s found cms skey for addr: \"%s\":%s",
1071                      SRCNAME, __func__, anonstr (mbox.c_str ()),
1072                      anonstr (key.primaryFingerprint()));
1073           KeyCache::instance()->setSmimeKeySecret (mbox, key);
1074           TRETURN;
1075         }
1076     } while (!err);
1077   TRETURN;
1078 }
1079
1080 static DWORD WINAPI
1081 do_locate_secret (LPVOID arg)
1082 {
1083   TSTART;
1084   auto args = std::unique_ptr<LocateArgs> ((LocateArgs *) arg);
1085
1086   log_debug ("%s:%s searching secret key for addr: \"%s\"",
1087              SRCNAME, __func__, anonstr (args->m_mbox.c_str ()));
1088
1089   locate_secret (args->m_mbox.c_str(), GpgME::OpenPGP);
1090   if (opt.enable_smime)
1091     {
1092       locate_secret (args->m_mbox.c_str(), GpgME::CMS);
1093     }
1094   log_debug ("%s:%s locator sthread thread done",
1095              SRCNAME, __func__);
1096   TRETURN 0;
1097 }
1098
1099 void
1100 KeyCache::startLocate (const std::vector<std::string> &addrs, Mail *mail) const
1101 {
1102   for (const auto &addr: addrs)
1103     {
1104       startLocate (addr.c_str(), mail);
1105     }
1106 }
1107
1108 void
1109 KeyCache::startLocate (const char *addr, Mail *mail) const
1110 {
1111   TSTART;
1112   if (!addr)
1113     {
1114       TRACEPOINT;
1115       TRETURN;
1116     }
1117   std::string recp = GpgME::UserID::addrSpecFromString (addr);
1118   if (recp.empty ())
1119     {
1120       TRETURN;
1121     }
1122   gpgol_lock (&keycache_lock);
1123   if (d->m_pgp_key_map.find (recp) == d->m_pgp_key_map.end ())
1124     {
1125       // It's enough to look at the PGP Key map. We marked
1126       // searched keys there.
1127       d->m_pgp_key_map.insert (std::pair<std::string, GpgME::Key> (recp, GpgME::Key()));
1128       log_debug ("%s:%s Creating a locator thread",
1129                  SRCNAME, __func__);
1130       const auto args = new LocateArgs(recp, mail);
1131       HANDLE thread = CreateThread (NULL, 0, do_locate,
1132                                     args, 0,
1133                                     NULL);
1134       CloseHandle (thread);
1135     }
1136   gpgol_unlock (&keycache_lock);
1137   TRETURN;
1138 }
1139
1140 void
1141 KeyCache::startLocateSecret (const char *addr, Mail *mail) const
1142 {
1143   TSTART;
1144   if (!addr)
1145     {
1146       TRACEPOINT;
1147       TRETURN;
1148     }
1149   std::string recp = GpgME::UserID::addrSpecFromString (addr);
1150   if (recp.empty ())
1151     {
1152       TRETURN;
1153     }
1154   gpgol_lock (&keycache_lock);
1155   if (d->m_pgp_skey_map.find (recp) == d->m_pgp_skey_map.end ())
1156     {
1157       // It's enough to look at the PGP Key map. We marked
1158       // searched keys there.
1159       d->m_pgp_skey_map.insert (std::pair<std::string, GpgME::Key> (recp, GpgME::Key()));
1160       log_debug ("%s:%s Creating a locator thread",
1161                  SRCNAME, __func__);
1162       const auto args = new LocateArgs(recp, mail);
1163
1164       HANDLE thread = CreateThread (NULL, 0, do_locate_secret,
1165                                     (LPVOID) args, 0,
1166                                     NULL);
1167       CloseHandle (thread);
1168     }
1169   gpgol_unlock (&keycache_lock);
1170   TRETURN;
1171 }
1172
1173
1174 void
1175 KeyCache::setSmimeKey(const std::string &mbox, const GpgME::Key &key)
1176 {
1177   d->setSmimeKey(mbox, key);
1178 }
1179
1180 void
1181 KeyCache::setPgpKey(const std::string &mbox, const GpgME::Key &key)
1182 {
1183   d->setPgpKey(mbox, key);
1184 }
1185
1186 void
1187 KeyCache::setSmimeKeySecret(const std::string &mbox, const GpgME::Key &key)
1188 {
1189   d->setSmimeKeySecret(mbox, key);
1190 }
1191
1192 void
1193 KeyCache::setPgpKeySecret(const std::string &mbox, const GpgME::Key &key)
1194 {
1195   d->setPgpKeySecret(mbox, key);
1196 }
1197
1198 bool
1199 KeyCache::isMailResolvable(Mail *mail)
1200 {
1201   TSTART;
1202   /* Get the data from the mail. */
1203   const auto sender = mail->getSender ();
1204   auto recps = mail->getCachedRecipients ();
1205
1206   if (sender.empty() || recps.empty())
1207     {
1208       log_debug ("%s:%s: Mail has no sender or no recipients.",
1209                  SRCNAME, __func__);
1210       TRETURN false;
1211     }
1212
1213
1214   std::vector<GpgME::Key> encKeys = getEncryptionKeys (recps,
1215                                                        GpgME::OpenPGP);
1216
1217   if (!encKeys.empty())
1218     {
1219       TRETURN true;
1220     }
1221
1222   if (!opt.enable_smime)
1223     {
1224       TRETURN false;
1225     }
1226
1227   /* Check S/MIME instead here we need to include the sender
1228      as we can't just generate a key. */
1229   recps.push_back (sender);
1230   GpgME::Key sigKey= getSigningKey (sender.c_str(), GpgME::CMS);
1231   encKeys = getEncryptionKeys (recps, GpgME::CMS);
1232
1233   TRETURN !encKeys.empty() && !sigKey.isNull();
1234 }
1235
1236 void
1237 KeyCache::update (const char *fpr, GpgME::Protocol proto)
1238 {
1239   d->update (fpr, proto);
1240 }
1241
1242 GpgME::Key
1243 KeyCache::getByFpr (const char *fpr, bool block) const
1244 {
1245   return d->getByFpr (fpr, block);
1246 }
1247
1248 void
1249 KeyCache::onUpdateJobDone (const char *fpr, const GpgME::Key &key)
1250 {
1251   return d->onUpdateJobDone (fpr, key);
1252 }
1253
1254 void
1255 KeyCache::importFromAddrBook (const std::string &mbox, const char *key_data,
1256                               Mail *mail) const
1257 {
1258   return d->importFromAddrBook (mbox, key_data, mail);
1259 }
1260
1261 void
1262 KeyCache::onAddrBookImportJobDone (const std::string &mbox,
1263                                    const std::vector<std::string> &result_fprs)
1264 {
1265   return d->onAddrBookImportJobDone (mbox, result_fprs);
1266 }
1267
1268 std::vector<GpgME::Key>
1269 KeyCache::getOverrides (const std::string &mbox)
1270 {
1271   return d->getPGPOverrides (mbox.c_str ());
1272 }