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