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