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