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