Add lock tracing
[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_error ("%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 DWORD WINAPI
829 do_locate (LPVOID arg)
830 {
831   TSTART;
832   if (!arg)
833     {
834       TRETURN 0;
835     }
836
837   auto args = std::unique_ptr<LocateArgs> ((LocateArgs *) arg);
838
839   const auto addr = args->m_mbox;
840
841   log_debug ("%s:%s searching key for addr: \"%s\"",
842              SRCNAME, __func__, anonstr (addr.c_str()));
843
844   const auto k = GpgME::Key::locate (addr.c_str());
845
846   if (!k.isNull ())
847     {
848       log_debug ("%s:%s found key for addr: \"%s\":%s",
849                  SRCNAME, __func__, anonstr (addr.c_str()),
850                  anonstr (k.primaryFingerprint()));
851       KeyCache::instance ()->setPgpKey (addr, k);
852     }
853
854   if (opt.enable_smime)
855     {
856       auto ctx = std::unique_ptr<GpgME::Context> (
857                           GpgME::Context::createForProtocol (GpgME::CMS));
858       if (!ctx)
859         {
860           TRACEPOINT;
861           TRETURN 0;
862         }
863       // We need to validate here to fetch CRL's
864       ctx->setKeyListMode (GpgME::KeyListMode::Local |
865                            GpgME::KeyListMode::Validate |
866                            GpgME::KeyListMode::Signatures);
867       GpgME::Error e = ctx->startKeyListing (addr.c_str());
868       if (e)
869         {
870           TRACEPOINT;
871           TRETURN 0;
872         }
873
874       std::vector<GpgME::Key> keys;
875       GpgME::Error err;
876       do {
877           keys.push_back(ctx->nextKey(err));
878       } while (!err);
879       keys.pop_back();
880
881       GpgME::Key candidate;
882       for (const auto &key: keys)
883         {
884           if (key.isRevoked() || key.isExpired() ||
885               key.isDisabled() || key.isInvalid())
886             {
887               log_debug ("%s:%s: Skipping invalid S/MIME key",
888                          SRCNAME, __func__);
889               continue;
890             }
891           if (candidate.isNull() || !candidate.numUserIDs())
892             {
893               if (key.numUserIDs() &&
894                   candidate.userID(0).validity() <= key.userID(0).validity())
895                 {
896                   candidate = key;
897                 }
898             }
899         }
900       if (!candidate.isNull())
901         {
902           log_debug ("%s:%s found SMIME key for addr: \"%s\":%s",
903                      SRCNAME, __func__, anonstr (addr.c_str()),
904                      anonstr (candidate.primaryFingerprint()));
905           KeyCache::instance()->setSmimeKey (addr, candidate);
906         }
907     }
908   log_debug ("%s:%s locator thread done",
909              SRCNAME, __func__);
910   TRETURN 0;
911 }
912
913 static void
914 locate_secret (const char *addr, GpgME::Protocol proto)
915 {
916   TSTART;
917   auto ctx = std::unique_ptr<GpgME::Context> (
918                       GpgME::Context::createForProtocol (proto));
919   if (!ctx)
920     {
921       TRACEPOINT;
922       TRETURN;
923     }
924   if (!addr)
925     {
926       TRACEPOINT;
927       TRETURN;
928     }
929   const auto mbox = GpgME::UserID::addrSpecFromString (addr);
930
931   if (mbox.empty())
932     {
933       log_debug ("%s:%s: Empty mbox for addr %s",
934                  SRCNAME, __func__, anonstr (addr));
935       TRETURN;
936     }
937
938   // We need to validate here to fetch CRL's
939   ctx->setKeyListMode (GpgME::KeyListMode::Local |
940                        GpgME::KeyListMode::Validate);
941   GpgME::Error e = ctx->startKeyListing (mbox.c_str(), true);
942   if (e)
943     {
944       TRACEPOINT;
945       TRETURN;
946     }
947
948   std::vector<GpgME::Key> keys;
949   GpgME::Error err;
950   do
951     {
952       const auto key = ctx->nextKey(err);
953       if (key.isNull())
954         {
955           continue;
956         }
957       if (key.isRevoked() || key.isExpired() ||
958           key.isDisabled() || key.isInvalid())
959         {
960           if ((opt.enable_debug & DBG_DATA))
961             {
962               std::stringstream ss;
963               ss << key;
964               log_data ("%s:%s: Skipping invalid secret key %s",
965                         SRCNAME, __func__, ss.str().c_str());
966             }
967           continue;
968         }
969       if (proto == GpgME::OpenPGP)
970         {
971           log_debug ("%s:%s found pgp skey for addr: \"%s\":%s",
972                      SRCNAME, __func__, anonstr (mbox.c_str()),
973                      anonstr (key.primaryFingerprint()));
974           KeyCache::instance()->setPgpKeySecret (mbox, key);
975           TRETURN;
976         }
977       if (proto == GpgME::CMS)
978         {
979           log_debug ("%s:%s found cms skey for addr: \"%s\":%s",
980                      SRCNAME, __func__, anonstr (mbox.c_str ()),
981                      anonstr (key.primaryFingerprint()));
982           KeyCache::instance()->setSmimeKeySecret (mbox, key);
983           TRETURN;
984         }
985     } while (!err);
986   TRETURN;
987 }
988
989 static DWORD WINAPI
990 do_locate_secret (LPVOID arg)
991 {
992   TSTART;
993   auto args = std::unique_ptr<LocateArgs> ((LocateArgs *) arg);
994
995   log_debug ("%s:%s searching secret key for addr: \"%s\"",
996              SRCNAME, __func__, anonstr (args->m_mbox.c_str ()));
997
998   locate_secret (args->m_mbox.c_str(), GpgME::OpenPGP);
999   if (opt.enable_smime)
1000     {
1001       locate_secret (args->m_mbox.c_str(), GpgME::CMS);
1002     }
1003   log_debug ("%s:%s locator sthread thread done",
1004              SRCNAME, __func__);
1005   TRETURN 0;
1006 }
1007
1008 void
1009 KeyCache::startLocate (const std::vector<std::string> &addrs, Mail *mail) const
1010 {
1011   for (const auto &addr: addrs)
1012     {
1013       startLocate (addr.c_str(), mail);
1014     }
1015 }
1016
1017 void
1018 KeyCache::startLocate (const char *addr, Mail *mail) const
1019 {
1020   TSTART;
1021   if (!addr)
1022     {
1023       TRACEPOINT;
1024       TRETURN;
1025     }
1026   std::string recp = GpgME::UserID::addrSpecFromString (addr);
1027   if (recp.empty ())
1028     {
1029       TRETURN;
1030     }
1031   gpgol_lock (&keycache_lock);
1032   if (d->m_pgp_key_map.find (recp) == d->m_pgp_key_map.end ())
1033     {
1034       // It's enough to look at the PGP Key map. We marked
1035       // searched keys there.
1036       d->m_pgp_key_map.insert (std::pair<std::string, GpgME::Key> (recp, GpgME::Key()));
1037       log_debug ("%s:%s Creating a locator thread",
1038                  SRCNAME, __func__);
1039       const auto args = new LocateArgs(recp, mail);
1040       HANDLE thread = CreateThread (NULL, 0, do_locate,
1041                                     args, 0,
1042                                     NULL);
1043       CloseHandle (thread);
1044     }
1045   gpgol_unlock (&keycache_lock);
1046   TRETURN;
1047 }
1048
1049 void
1050 KeyCache::startLocateSecret (const char *addr, Mail *mail) const
1051 {
1052   TSTART;
1053   if (!addr)
1054     {
1055       TRACEPOINT;
1056       TRETURN;
1057     }
1058   std::string recp = GpgME::UserID::addrSpecFromString (addr);
1059   if (recp.empty ())
1060     {
1061       TRETURN;
1062     }
1063   gpgol_lock (&keycache_lock);
1064   if (d->m_pgp_skey_map.find (recp) == d->m_pgp_skey_map.end ())
1065     {
1066       // It's enough to look at the PGP Key map. We marked
1067       // searched keys there.
1068       d->m_pgp_skey_map.insert (std::pair<std::string, GpgME::Key> (recp, GpgME::Key()));
1069       log_debug ("%s:%s Creating a locator thread",
1070                  SRCNAME, __func__);
1071       const auto args = new LocateArgs(recp, mail);
1072
1073       HANDLE thread = CreateThread (NULL, 0, do_locate_secret,
1074                                     (LPVOID) args, 0,
1075                                     NULL);
1076       CloseHandle (thread);
1077     }
1078   gpgol_unlock (&keycache_lock);
1079   TRETURN;
1080 }
1081
1082
1083 void
1084 KeyCache::setSmimeKey(const std::string &mbox, const GpgME::Key &key)
1085 {
1086   d->setSmimeKey(mbox, key);
1087 }
1088
1089 void
1090 KeyCache::setPgpKey(const std::string &mbox, const GpgME::Key &key)
1091 {
1092   d->setPgpKey(mbox, key);
1093 }
1094
1095 void
1096 KeyCache::setSmimeKeySecret(const std::string &mbox, const GpgME::Key &key)
1097 {
1098   d->setSmimeKeySecret(mbox, key);
1099 }
1100
1101 void
1102 KeyCache::setPgpKeySecret(const std::string &mbox, const GpgME::Key &key)
1103 {
1104   d->setPgpKeySecret(mbox, key);
1105 }
1106
1107 bool
1108 KeyCache::isMailResolvable(Mail *mail)
1109 {
1110   TSTART;
1111   /* Get the data from the mail. */
1112   const auto sender = mail->getSender ();
1113   auto recps = mail->getCachedRecipients ();
1114
1115   if (sender.empty() || recps.empty())
1116     {
1117       log_debug ("%s:%s: Mail has no sender or no recipients.",
1118                  SRCNAME, __func__);
1119       TRETURN false;
1120     }
1121
1122
1123   std::vector<GpgME::Key> encKeys = getEncryptionKeys (recps,
1124                                                        GpgME::OpenPGP);
1125
1126   if (!encKeys.empty())
1127     {
1128       TRETURN true;
1129     }
1130
1131   if (!opt.enable_smime)
1132     {
1133       TRETURN false;
1134     }
1135
1136   /* Check S/MIME instead here we need to include the sender
1137      as we can't just generate a key. */
1138   recps.push_back (sender);
1139   GpgME::Key sigKey= getSigningKey (sender.c_str(), GpgME::CMS);
1140   encKeys = getEncryptionKeys (recps, GpgME::CMS);
1141
1142   TRETURN !encKeys.empty() && !sigKey.isNull();
1143 }
1144
1145 void
1146 KeyCache::update (const char *fpr, GpgME::Protocol proto)
1147 {
1148   d->update (fpr, proto);
1149 }
1150
1151 GpgME::Key
1152 KeyCache::getByFpr (const char *fpr, bool block) const
1153 {
1154   return d->getByFpr (fpr, block);
1155 }
1156
1157 void
1158 KeyCache::onUpdateJobDone (const char *fpr, const GpgME::Key &key)
1159 {
1160   return d->onUpdateJobDone (fpr, key);
1161 }
1162
1163 void
1164 KeyCache::importFromAddrBook (const std::string &mbox, const char *key_data,
1165                               Mail *mail) const
1166 {
1167   return d->importFromAddrBook (mbox, key_data, mail);
1168 }
1169
1170 void
1171 KeyCache::onAddrBookImportJobDone (const std::string &mbox,
1172                                    const std::vector<std::string> &result_fprs)
1173 {
1174   return d->onAddrBookImportJobDone (mbox, result_fprs);
1175 }