Rocco Rutte:
[apps/madmutt.git] / pgpkey.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996,1997 Michael R. Elkins <me@mutt.org>
4  * Copyright (c) 1998,1999 Thomas Roessler <roessler@does-not-exist.org>
5  *
6  * This file is part of mutt-ng, see http://www.muttng.org/.
7  * It's licensed under the GNU General Public License,
8  * please see the file GPL in the top level source directory.
9  */
10
11 #if HAVE_CONFIG_H
12 # include "config.h"
13 #endif
14
15 #include "mutt.h"
16 #include "mutt_curses.h"
17 #include "mutt_menu.h"
18 #include "mime.h"
19 #include "pgp.h"
20 #include "pager.h"
21 #include "sort.h"
22
23 #include <string.h>
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <sys/wait.h>
29
30 #include <locale.h>
31
32 #ifdef CRYPT_BACKEND_CLASSIC_PGP
33
34 struct pgp_cache {
35   char *what;
36   char *dflt;
37   struct pgp_cache *next;
38 };
39
40 static struct pgp_cache *id_defaults = NULL;
41
42 static char trust_flags[] = "?- +";
43
44 static char *pgp_key_abilities (int flags)
45 {
46   static char buff[3];
47
48   if (!(flags & KEYFLAG_CANENCRYPT))
49     buff[0] = '-';
50   else if (flags & KEYFLAG_PREFER_SIGNING)
51     buff[0] = '.';
52   else
53     buff[0] = 'e';
54
55   if (!(flags & KEYFLAG_CANSIGN))
56     buff[1] = '-';
57   else if (flags & KEYFLAG_PREFER_ENCRYPTION)
58     buff[1] = '.';
59   else
60     buff[1] = 's';
61
62   buff[2] = '\0';
63
64   return buff;
65 }
66
67 static char pgp_flags (int flags)
68 {
69   if (flags & KEYFLAG_REVOKED)
70     return 'R';
71   else if (flags & KEYFLAG_EXPIRED)
72     return 'X';
73   else if (flags & KEYFLAG_DISABLED)
74     return 'd';
75   else if (flags & KEYFLAG_CRITICAL)
76     return 'c';
77   else
78     return ' ';
79 }
80
81 static pgp_key_t pgp_principal_key (pgp_key_t key)
82 {
83   if (key->flags & KEYFLAG_SUBKEY && key->parent)
84     return key->parent;
85   else
86     return key;
87 }
88
89 /*
90  * Format an entry on the PGP key selection menu.
91  * 
92  * %n   number
93  * %k   key id          %K      key id of the principal key
94  * %u   user id
95  * %a   algorithm       %A      algorithm of the princ. key
96  * %l   length          %L      length of the princ. key
97  * %f   flags           %F      flags of the princ. key
98  * %c   capabilities    %C      capabilities of the princ. key
99  * %t   trust/validity of the key-uid association
100  * %[...] date of key using strftime(3)
101  */
102
103 typedef struct pgp_entry {
104   size_t num;
105   pgp_uid_t *uid;
106 } pgp_entry_t;
107
108 static const char *pgp_entry_fmt (char *dest,
109                                   size_t destlen,
110                                   char op,
111                                   const char *src,
112                                   const char *prefix,
113                                   const char *ifstring,
114                                   const char *elsestring,
115                                   unsigned long data, format_flag flags)
116 {
117   char fmt[16];
118   pgp_entry_t *entry;
119   pgp_uid_t *uid;
120   pgp_key_t key, pkey;
121   int kflags = 0;
122   int optional = (flags & M_FORMAT_OPTIONAL);
123
124   entry = (pgp_entry_t *) data;
125   uid = entry->uid;
126   key = uid->parent;
127   pkey = pgp_principal_key (key);
128
129   if (isupper ((unsigned char) op))
130     key = pkey;
131
132   kflags = key->flags | (pkey->flags & KEYFLAG_RESTRICTIONS)
133     | uid->flags;
134
135   switch (ascii_tolower (op)) {
136   case '[':
137
138     {
139       const char *cp;
140       char buf2[SHORT_STRING], *p;
141       int do_locales;
142       struct tm *tm;
143       size_t len;
144
145       p = dest;
146
147       cp = src;
148       if (*cp == '!') {
149         do_locales = 0;
150         cp++;
151       }
152       else
153         do_locales = 1;
154
155       len = destlen - 1;
156       while (len > 0 && *cp != ']') {
157         if (*cp == '%') {
158           cp++;
159           if (len >= 2) {
160             *p++ = '%';
161             *p++ = *cp;
162             len -= 2;
163           }
164           else
165             break;              /* not enough space */
166           cp++;
167         }
168         else {
169           *p++ = *cp++;
170           len--;
171         }
172       }
173       *p = 0;
174
175       if (do_locales && Locale)
176         setlocale (LC_TIME, Locale);
177
178       tm = localtime (&key->gen_time);
179
180       strftime (buf2, sizeof (buf2), dest, tm);
181
182       if (do_locales)
183         setlocale (LC_TIME, "C");
184
185       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
186       snprintf (dest, destlen, fmt, buf2);
187       if (len > 0)
188         src = cp + 1;
189     }
190     break;
191   case 'n':
192     if (!optional) {
193       snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
194       snprintf (dest, destlen, fmt, entry->num);
195     }
196     break;
197   case 'k':
198     if (!optional) {
199       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
200       snprintf (dest, destlen, fmt, _pgp_keyid (key));
201     }
202     break;
203   case 'u':
204     if (!optional) {
205       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
206       snprintf (dest, destlen, fmt, uid->addr);
207     }
208     break;
209   case 'a':
210     if (!optional) {
211       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
212       snprintf (dest, destlen, fmt, key->algorithm);
213     }
214     break;
215   case 'l':
216     if (!optional) {
217       snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
218       snprintf (dest, destlen, fmt, key->keylen);
219     }
220     break;
221   case 'f':
222     if (!optional) {
223       snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
224       snprintf (dest, destlen, fmt, pgp_flags (kflags));
225     }
226     else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
227       optional = 0;
228     break;
229   case 'c':
230     if (!optional) {
231       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
232       snprintf (dest, destlen, fmt, pgp_key_abilities (kflags));
233     }
234     else if (!(kflags & (KEYFLAG_ABILITIES)))
235       optional = 0;
236     break;
237   case 't':
238     if (!optional) {
239       snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
240       snprintf (dest, destlen, fmt, trust_flags[uid->trust & 0x03]);
241     }
242     else if (!(uid->trust & 0x03))
243       /* undefined trust */
244       optional = 0;
245     break;
246   default:
247     *dest = '\0';
248   }
249
250   if (optional)
251     mutt_FormatString (dest, destlen, ifstring, mutt_attach_fmt, data, 0);
252   else if (flags & M_FORMAT_OPTIONAL)
253     mutt_FormatString (dest, destlen, elsestring, mutt_attach_fmt, data, 0);
254   return (src);
255 }
256
257 static void pgp_entry (char *s, size_t l, MUTTMENU * menu, int num)
258 {
259   pgp_uid_t **KeyTable = (pgp_uid_t **) menu->data;
260   pgp_entry_t entry;
261
262   entry.uid = KeyTable[num];
263   entry.num = num + 1;
264
265   mutt_FormatString (s, l, NONULL (PgpEntryFormat), pgp_entry_fmt,
266                      (unsigned long) &entry, M_FORMAT_ARROWCURSOR);
267 }
268
269 static int _pgp_compare_address (const void *a, const void *b)
270 {
271   int r;
272
273   pgp_uid_t **s = (pgp_uid_t **) a;
274   pgp_uid_t **t = (pgp_uid_t **) b;
275
276   if ((r = mutt_strcasecmp ((*s)->addr, (*t)->addr)))
277     return r > 0;
278   else
279     return (mutt_strcasecmp (_pgp_keyid ((*s)->parent),
280                              _pgp_keyid ((*t)->parent)) > 0);
281 }
282
283 static int pgp_compare_address (const void *a, const void *b)
284 {
285   return ((PgpSortKeys & SORT_REVERSE) ? !_pgp_compare_address (a, b)
286           : _pgp_compare_address (a, b));
287 }
288
289
290
291 static int _pgp_compare_keyid (const void *a, const void *b)
292 {
293   int r;
294
295   pgp_uid_t **s = (pgp_uid_t **) a;
296   pgp_uid_t **t = (pgp_uid_t **) b;
297
298   if ((r = mutt_strcasecmp (_pgp_keyid ((*s)->parent),
299                             _pgp_keyid ((*t)->parent))))
300     return r > 0;
301   else
302     return (mutt_strcasecmp ((*s)->addr, (*t)->addr)) > 0;
303 }
304
305 static int pgp_compare_keyid (const void *a, const void *b)
306 {
307   return ((PgpSortKeys & SORT_REVERSE) ? !_pgp_compare_keyid (a, b)
308           : _pgp_compare_keyid (a, b));
309 }
310
311 static int _pgp_compare_date (const void *a, const void *b)
312 {
313   int r;
314   pgp_uid_t **s = (pgp_uid_t **) a;
315   pgp_uid_t **t = (pgp_uid_t **) b;
316
317   if ((r = ((*s)->parent->gen_time - (*t)->parent->gen_time)))
318     return r > 0;
319   return (mutt_strcasecmp ((*s)->addr, (*t)->addr)) > 0;
320 }
321
322 static int pgp_compare_date (const void *a, const void *b)
323 {
324   return ((PgpSortKeys & SORT_REVERSE) ? !_pgp_compare_date (a, b)
325           : _pgp_compare_date (a, b));
326 }
327
328 static int _pgp_compare_trust (const void *a, const void *b)
329 {
330   int r;
331
332   pgp_uid_t **s = (pgp_uid_t **) a;
333   pgp_uid_t **t = (pgp_uid_t **) b;
334
335   if ((r = (((*s)->parent->flags & (KEYFLAG_RESTRICTIONS))
336             - ((*t)->parent->flags & (KEYFLAG_RESTRICTIONS)))))
337     return r > 0;
338   if ((r = ((*s)->trust - (*t)->trust)))
339     return r < 0;
340   if ((r = ((*s)->parent->keylen - (*t)->parent->keylen)))
341     return r < 0;
342   if ((r = ((*s)->parent->gen_time - (*t)->parent->gen_time)))
343     return r < 0;
344   if ((r = mutt_strcasecmp ((*s)->addr, (*t)->addr)))
345     return r > 0;
346   return (mutt_strcasecmp (_pgp_keyid ((*s)->parent),
347                            _pgp_keyid ((*t)->parent))) > 0;
348 }
349
350 static int pgp_compare_trust (const void *a, const void *b)
351 {
352   return ((PgpSortKeys & SORT_REVERSE) ? !_pgp_compare_trust (a, b)
353           : _pgp_compare_trust (a, b));
354 }
355
356 static int pgp_key_is_valid (pgp_key_t k)
357 {
358   pgp_key_t pk = pgp_principal_key (k);
359
360   if (k->flags & KEYFLAG_CANTUSE)
361     return 0;
362   if (pk->flags & KEYFLAG_CANTUSE)
363     return 0;
364
365   return 1;
366 }
367
368 static int pgp_id_is_strong (pgp_uid_t * uid)
369 {
370   if ((uid->trust & 3) < 3)
371     return 0;
372   /* else */
373   return 1;
374 }
375
376 static int pgp_id_is_valid (pgp_uid_t * uid)
377 {
378   if (!pgp_key_is_valid (uid->parent))
379     return 0;
380   if (uid->flags & KEYFLAG_CANTUSE)
381     return 0;
382   /* else */
383   return 1;
384 }
385
386 #define PGP_KV_VALID    1
387 #define PGP_KV_ADDR     2
388 #define PGP_KV_STRING   4
389 #define PGP_KV_STRONGID 8
390
391 #define PGP_KV_MATCH (PGP_KV_ADDR|PGP_KV_STRING)
392
393 static int pgp_id_matches_addr (ADDRESS * addr, ADDRESS * u_addr,
394                                 pgp_uid_t * uid)
395 {
396   int rv = 0;
397
398   if (pgp_id_is_valid (uid))
399     rv |= PGP_KV_VALID;
400
401   if (pgp_id_is_strong (uid))
402     rv |= PGP_KV_STRONGID;
403
404   if (addr->mailbox && u_addr->mailbox
405       && mutt_strcasecmp (addr->mailbox, u_addr->mailbox) == 0)
406     rv |= PGP_KV_ADDR;
407
408   if (addr->personal && u_addr->personal
409       && mutt_strcasecmp (addr->personal, u_addr->personal) == 0)
410     rv |= PGP_KV_STRING;
411
412   return rv;
413 }
414
415 static pgp_key_t pgp_select_key (pgp_key_t keys, ADDRESS * p, const char *s)
416 {
417   int keymax;
418   pgp_uid_t **KeyTable;
419   MUTTMENU *menu;
420   int i, done = 0;
421   char helpstr[SHORT_STRING], buf[LONG_STRING], tmpbuf[STRING];
422   char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
423   FILE *fp, *devnull;
424   pid_t thepid;
425   pgp_key_t kp;
426   pgp_uid_t *a;
427   int (*f) (const void *, const void *);
428
429   int unusable = 0;
430
431   keymax = 0;
432   KeyTable = NULL;
433
434   for (i = 0, kp = keys; kp; kp = kp->next) {
435     if (!option (OPTPGPSHOWUNUSABLE) && (kp->flags & KEYFLAG_CANTUSE)) {
436       unusable = 1;
437       continue;
438     }
439
440     for (a = kp->address; a; a = a->next) {
441       if (!option (OPTPGPSHOWUNUSABLE) && (a->flags & KEYFLAG_CANTUSE)) {
442         unusable = 1;
443         continue;
444       }
445
446       if (i == keymax) {
447         keymax += 5;
448         safe_realloc (&KeyTable, sizeof (pgp_uid_t *) * keymax);
449       }
450
451       KeyTable[i++] = a;
452     }
453   }
454
455   if (!i && unusable) {
456     mutt_error _("All matching keys are expired, revoked, or disabled.");
457
458     mutt_sleep (1);
459     return NULL;
460   }
461
462   switch (PgpSortKeys & SORT_MASK) {
463   case SORT_DATE:
464     f = pgp_compare_date;
465     break;
466   case SORT_KEYID:
467     f = pgp_compare_keyid;
468     break;
469   case SORT_ADDRESS:
470     f = pgp_compare_address;
471     break;
472   case SORT_TRUST:
473   default:
474     f = pgp_compare_trust;
475     break;
476   }
477   qsort (KeyTable, i, sizeof (pgp_uid_t *), f);
478
479   helpstr[0] = 0;
480   mutt_make_help (buf, sizeof (buf), _("Exit  "), MENU_PGP, OP_EXIT);
481   strcat (helpstr, buf);        /* __STRCAT_CHECKED__ */
482   mutt_make_help (buf, sizeof (buf), _("Select  "), MENU_PGP,
483                   OP_GENERIC_SELECT_ENTRY);
484   strcat (helpstr, buf);        /* __STRCAT_CHECKED__ */
485   mutt_make_help (buf, sizeof (buf), _("Check key  "), MENU_PGP,
486                   OP_VERIFY_KEY);
487   strcat (helpstr, buf);        /* __STRCAT_CHECKED__ */
488   mutt_make_help (buf, sizeof (buf), _("Help"), MENU_PGP, OP_HELP);
489   strcat (helpstr, buf);        /* __STRCAT_CHECKED__ */
490
491   menu = mutt_new_menu ();
492   menu->max = i;
493   menu->make_entry = pgp_entry;
494   menu->menu = MENU_PGP;
495   menu->help = helpstr;
496   menu->data = KeyTable;
497
498   if (p)
499     snprintf (buf, sizeof (buf), _("PGP keys matching <%s>."), p->mailbox);
500   else
501     snprintf (buf, sizeof (buf), _("PGP keys matching \"%s\"."), s);
502
503
504   menu->title = buf;
505
506   kp = NULL;
507
508   mutt_clear_error ();
509
510   while (!done) {
511     switch (mutt_menuLoop (menu)) {
512
513     case OP_VERIFY_KEY:
514
515       mutt_mktemp (tempfile);
516       if ((devnull = fopen ("/dev/null", "w")) == NULL) {       /* __FOPEN_CHECKED__ */
517         mutt_perror _("Can't open /dev/null");
518
519         break;
520       }
521       if ((fp = safe_fopen (tempfile, "w")) == NULL) {
522         fclose (devnull);
523         mutt_perror _("Can't create temporary file");
524
525         break;
526       }
527
528       mutt_message _("Invoking PGP...");
529
530       snprintf (tmpbuf, sizeof (tmpbuf), "0x%s",
531                 pgp_keyid (pgp_principal_key
532                            (KeyTable[menu->current]->parent)));
533
534       if ((thepid = pgp_invoke_verify_key (NULL, NULL, NULL, -1,
535                                            fileno (fp), fileno (devnull),
536                                            tmpbuf)) == -1) {
537         mutt_perror _("Can't create filter");
538
539         unlink (tempfile);
540         fclose (fp);
541         fclose (devnull);
542       }
543
544       mutt_wait_filter (thepid);
545       fclose (fp);
546       fclose (devnull);
547       mutt_clear_error ();
548       snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"),
549                 pgp_keyid (pgp_principal_key
550                            (KeyTable[menu->current]->parent)));
551       mutt_do_pager (cmd, tempfile, 0, NULL);
552       menu->redraw = REDRAW_FULL;
553
554       break;
555
556     case OP_VIEW_ID:
557
558       mutt_message ("%s", KeyTable[menu->current]->addr);
559       break;
560
561     case OP_GENERIC_SELECT_ENTRY:
562
563
564       /* XXX make error reporting more verbose */
565
566       if (option (OPTPGPCHECKTRUST))
567         if (!pgp_key_is_valid (KeyTable[menu->current]->parent)) {
568           mutt_error _("This key can't be used: expired/disabled/revoked.");
569
570           break;
571         }
572
573       if (option (OPTPGPCHECKTRUST) &&
574           (!pgp_id_is_valid (KeyTable[menu->current])
575            || !pgp_id_is_strong (KeyTable[menu->current]))) {
576         char *s = "";
577         char buff[LONG_STRING];
578
579         if (KeyTable[menu->current]->flags & KEYFLAG_CANTUSE)
580           s = N_("ID is expired/disabled/revoked.");
581         else
582           switch (KeyTable[menu->current]->trust & 0x03) {
583           case 0:
584             s = N_("ID has undefined validity.");
585             break;
586           case 1:
587             s = N_("ID is not valid.");
588             break;
589           case 2:
590             s = N_("ID is only marginally valid.");
591             break;
592           }
593
594         snprintf (buff, sizeof (buff),
595                   _("%s Do you really want to use the key?"), _(s));
596
597         if (mutt_yesorno (buff, M_NO) != M_YES) {
598           mutt_clear_error ();
599           break;
600         }
601       }
602
603 # if 0
604       kp = pgp_principal_key (KeyTable[menu->current]->parent);
605 # else
606       kp = KeyTable[menu->current]->parent;
607 # endif
608       done = 1;
609       break;
610
611     case OP_EXIT:
612
613       kp = NULL;
614       done = 1;
615       break;
616     }
617   }
618
619   mutt_menuDestroy (&menu);
620   FREE (&KeyTable);
621
622   set_option (OPTNEEDREDRAW);
623
624   return (kp);
625 }
626
627 pgp_key_t pgp_ask_for_key (char *tag, char *whatfor,
628                            short abilities, pgp_ring_t keyring)
629 {
630   pgp_key_t key;
631   char resp[SHORT_STRING];
632   struct pgp_cache *l = NULL;
633
634   mutt_clear_error ();
635
636   resp[0] = 0;
637   if (whatfor) {
638
639     for (l = id_defaults; l; l = l->next)
640       if (!mutt_strcasecmp (whatfor, l->what)) {
641         strfcpy (resp, NONULL (l->dflt), sizeof (resp));
642         break;
643       }
644   }
645
646
647   FOREVER {
648     resp[0] = 0;
649     if (mutt_get_field (tag, resp, sizeof (resp), M_CLEAR) != 0)
650       return NULL;
651
652     if (whatfor) {
653       if (l)
654         mutt_str_replace (&l->dflt, resp);
655       else {
656         l = safe_malloc (sizeof (struct pgp_cache));
657         l->next = id_defaults;
658         id_defaults = l;
659         l->what = safe_strdup (whatfor);
660         l->dflt = safe_strdup (resp);
661       }
662     }
663
664     if ((key = pgp_getkeybystr (resp, abilities, keyring)))
665       return key;
666
667     BEEP ();
668   }
669   /* not reached */
670 }
671
672 /* generate a public key attachment */
673
674 BODY *pgp_make_key_attachment (char *tempf)
675 {
676   BODY *att;
677   char buff[LONG_STRING];
678   char tempfb[_POSIX_PATH_MAX], tmp[STRING];
679   FILE *tempfp;
680   FILE *devnull;
681   struct stat sb;
682   pid_t thepid;
683   pgp_key_t key;
684
685   unset_option (OPTPGPCHECKTRUST);
686
687   key =
688     pgp_ask_for_key (_("Please enter the key ID: "), NULL, 0, PGP_PUBRING);
689
690   if (!key)
691     return NULL;
692
693   snprintf (tmp, sizeof (tmp), "0x%s", pgp_keyid (pgp_principal_key (key)));
694   pgp_free_key (&key);
695
696   if (!tempf) {
697     mutt_mktemp (tempfb);
698     tempf = tempfb;
699   }
700
701   if ((tempfp = safe_fopen (tempf, tempf == tempfb ? "w" : "a")) == NULL) {
702     mutt_perror _("Can't create temporary file");
703
704     return NULL;
705   }
706
707   if ((devnull = fopen ("/dev/null", "w")) == NULL) {   /* __FOPEN_CHECKED__ */
708     mutt_perror _("Can't open /dev/null");
709
710     fclose (tempfp);
711     if (tempf == tempfb)
712       unlink (tempf);
713     return NULL;
714   }
715
716   mutt_message _("Invoking pgp...");
717
718
719   if ((thepid =
720        pgp_invoke_export (NULL, NULL, NULL, -1,
721                           fileno (tempfp), fileno (devnull), tmp)) == -1) {
722     mutt_perror _("Can't create filter");
723
724     unlink (tempf);
725     fclose (tempfp);
726     fclose (devnull);
727     return NULL;
728   }
729
730   mutt_wait_filter (thepid);
731
732   fclose (tempfp);
733   fclose (devnull);
734
735   att = mutt_new_body ();
736   att->filename = safe_strdup (tempf);
737   att->unlink = 1;
738   att->use_disp = 0;
739   att->type = TYPEAPPLICATION;
740   att->subtype = safe_strdup ("pgp-keys");
741   snprintf (buff, sizeof (buff), _("PGP Key %s."), tmp);
742   att->description = safe_strdup (buff);
743   mutt_update_encoding (att);
744
745   stat (tempf, &sb);
746   att->length = sb.st_size;
747
748   return att;
749 }
750
751 static LIST *pgp_add_string_to_hints (LIST * hints, const char *str)
752 {
753   char *scratch;
754   char *t;
755
756   if ((scratch = safe_strdup (str)) == NULL)
757     return hints;
758
759   for (t = strtok (scratch, " ,.:\"()<>\n"); t;
760        t = strtok (NULL, " ,.:\"()<>\n")) {
761     if (mutt_strlen (t) > 3)
762       hints = mutt_add_list (hints, t);
763   }
764
765   FREE (&scratch);
766   return hints;
767 }
768
769 static pgp_key_t *pgp_get_lastp (pgp_key_t p)
770 {
771   for (; p; p = p->next)
772     if (!p->next)
773       return &p->next;
774
775   return NULL;
776 }
777
778 pgp_key_t pgp_getkeybyaddr (ADDRESS * a, short abilities, pgp_ring_t keyring)
779 {
780   ADDRESS *r, *p;
781   LIST *hints = NULL;
782
783   int weak = 0;
784   int invalid = 0;
785   int multi = 0;
786   int this_key_has_strong;
787   int this_key_has_weak;
788   int this_key_has_invalid;
789   int match;
790
791   pgp_key_t keys, k, kn;
792   pgp_key_t the_valid_key = NULL;
793   pgp_key_t matches = NULL;
794   pgp_key_t *last = &matches;
795   pgp_uid_t *q;
796
797   if (a && a->mailbox)
798     hints = pgp_add_string_to_hints (hints, a->mailbox);
799   if (a && a->personal)
800     hints = pgp_add_string_to_hints (hints, a->personal);
801
802   mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
803   keys = pgp_get_candidates (keyring, hints);
804
805   mutt_free_list (&hints);
806
807   if (!keys)
808     return NULL;
809
810   dprint (5, (debugfile, "pgp_getkeybyaddr: looking for %s <%s>.",
811               a->personal, a->mailbox));
812
813
814   for (k = keys; k; k = kn) {
815     kn = k->next;
816
817     dprint (5, (debugfile, "  looking at key: %s\n", pgp_keyid (k)));
818
819     if (abilities && !(k->flags & abilities)) {
820       dprint (5, (debugfile, "  insufficient abilities: Has %x, want %x\n",
821                   k->flags, abilities));
822       continue;
823     }
824
825     this_key_has_weak = 0;      /* weak but valid match   */
826     this_key_has_invalid = 0;   /* invalid match          */
827     this_key_has_strong = 0;    /* strong and valid match */
828     match = 0;                  /* any match              */
829
830     for (q = k->address; q; q = q->next) {
831       r = rfc822_parse_adrlist (NULL, q->addr);
832
833       for (p = r; p; p = p->next) {
834         int validity = pgp_id_matches_addr (a, p, q);
835
836         if (validity & PGP_KV_MATCH)    /* something matches */
837           match = 1;
838
839         /* is this key a strong candidate? */
840         if ((validity & PGP_KV_VALID) && (validity & PGP_KV_STRONGID)
841             && (validity & PGP_KV_ADDR)) {
842           if (the_valid_key && the_valid_key != k)
843             multi = 1;
844           the_valid_key = k;
845           this_key_has_strong = 1;
846         }
847         else if ((validity & PGP_KV_MATCH) && !(validity & PGP_KV_VALID))
848           this_key_has_invalid = 1;
849         else if ((validity & PGP_KV_MATCH)
850                  && (!(validity & PGP_KV_STRONGID)
851                      || !(validity & PGP_KV_ADDR)))
852           this_key_has_weak = 1;
853       }
854
855       rfc822_free_address (&r);
856     }
857
858     if (match && !this_key_has_strong && this_key_has_invalid)
859       invalid = 1;
860     if (match && !this_key_has_strong && this_key_has_weak)
861       weak = 1;
862
863     if (match) {
864       *last = pgp_principal_key (k);
865       kn = pgp_remove_key (&keys, *last);
866       last = pgp_get_lastp (k);
867     }
868   }
869
870   pgp_free_key (&keys);
871
872   if (matches) {
873     if (the_valid_key && !multi /* && !weak 
874                                    && !(invalid && option (OPTPGPSHOWUNUSABLE)) */ ) {
875       /*
876        * There was precisely one strong match on a valid ID.
877        * 
878        * Proceed without asking the user.
879        */
880       pgp_remove_key (&matches, the_valid_key);
881       pgp_free_key (&matches);
882       k = the_valid_key;
883     }
884     else {
885       /* 
886        * Else: Ask the user.
887        */
888       if ((k = pgp_select_key (matches, a, NULL)))
889         pgp_remove_key (&matches, k);
890       pgp_free_key (&matches);
891     }
892
893     return k;
894   }
895
896   return NULL;
897 }
898
899 pgp_key_t pgp_getkeybystr (char *p, short abilities, pgp_ring_t keyring)
900 {
901   LIST *hints = NULL;
902   pgp_key_t keys;
903   pgp_key_t matches = NULL;
904   pgp_key_t *last = &matches;
905   pgp_key_t k, kn;
906   pgp_uid_t *a;
907   short match;
908
909   mutt_message (_("Looking for keys matching \"%s\"..."), p);
910
911   hints = pgp_add_string_to_hints (hints, p);
912   keys = pgp_get_candidates (keyring, hints);
913   mutt_free_list (&hints);
914
915   if (!keys)
916     return NULL;
917
918
919   for (k = keys; k; k = kn) {
920     kn = k->next;
921     if (abilities && !(k->flags & abilities))
922       continue;
923
924     match = 0;
925
926     for (a = k->address; a; a = a->next) {
927       dprint (5,
928               (debugfile,
929                "pgp_getkeybystr: matching \"%s\" against key %s, \"%s\": ", p,
930                pgp_keyid (k), a->addr));
931       if (!*p || mutt_strcasecmp (p, pgp_keyid (k)) == 0
932           || (!mutt_strncasecmp (p, "0x", 2)
933               && !mutt_strcasecmp (p + 2, pgp_keyid (k)))
934           || (option (OPTPGPLONGIDS) && !mutt_strncasecmp (p, "0x", 2)
935               && !mutt_strcasecmp (p + 2, k->keyid + 8))
936           || mutt_stristr (a->addr, p)) {
937         dprint (5, (debugfile, "match.\n"));
938         match = 1;
939         break;
940       }
941     }
942
943     if (match) {
944       *last = pgp_principal_key (k);
945       kn = pgp_remove_key (&keys, *last);
946       last = pgp_get_lastp (k);
947     }
948   }
949
950   pgp_free_key (&keys);
951
952   if (matches) {
953     if ((k = pgp_select_key (matches, NULL, p)))
954       pgp_remove_key (&matches, k);
955
956     pgp_free_key (&matches);
957     return k;
958   }
959
960   return NULL;
961 }
962
963
964
965 #endif /* CRYPT_BACKEND_CLASSIC_PGP */