/* * Copyright (C) 1996-1997,2007 Michael R. Elkins * Copyright (c) 1998-2003 Thomas Roessler * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #if HAVE_CONFIG_H # include "config.h" #endif #include "mutt.h" #include "mutt_curses.h" #include "mutt_menu.h" #include "mime.h" #include "pgp.h" #include "pager.h" #include "sort.h" #include #include #include #include #include #include #include #ifdef CRYPT_BACKEND_CLASSIC_PGP struct pgp_cache { char *what; char *dflt; struct pgp_cache *next; }; static struct pgp_cache *id_defaults = NULL; static const char trust_flags[] = "?- +"; static char *pgp_key_abilities (int flags) { static char buff[3]; if (!(flags & KEYFLAG_CANENCRYPT)) buff[0] = '-'; else if (flags & KEYFLAG_PREFER_SIGNING) buff[0] = '.'; else buff[0] = 'e'; if (!(flags & KEYFLAG_CANSIGN)) buff[1] = '-'; else if (flags & KEYFLAG_PREFER_ENCRYPTION) buff[1] = '.'; else buff[1] = 's'; buff[2] = '\0'; return buff; } static char pgp_flags (int flags) { if (flags & KEYFLAG_REVOKED) return 'R'; else if (flags & KEYFLAG_EXPIRED) return 'X'; else if (flags & KEYFLAG_DISABLED) return 'd'; else if (flags & KEYFLAG_CRITICAL) return 'c'; else return ' '; } static pgp_key_t pgp_principal_key (pgp_key_t key) { if (key->flags & KEYFLAG_SUBKEY && key->parent) return key->parent; else return key; } /* * Format an entry on the PGP key selection menu. * * %n number * %k key id %K key id of the principal key * %u user id * %a algorithm %A algorithm of the princ. key * %l length %L length of the princ. key * %f flags %F flags of the princ. key * %c capabilities %C capabilities of the princ. key * %t trust/validity of the key-uid association * %[...] date of key using strftime(3) */ typedef struct pgp_entry { size_t num; pgp_uid_t *uid; } pgp_entry_t; static const char *pgp_entry_fmt (char *dest, size_t destlen, size_t col, int cols, char op, const char *src, const char *prefix, const char *ifstring, const char *elsestring, void *data, format_flag flags) { char fmt[16]; pgp_entry_t *entry; pgp_uid_t *uid; pgp_key_t key, pkey; int kflags = 0; int optional = (flags & MUTT_FORMAT_OPTIONAL); entry = (pgp_entry_t *) data; uid = entry->uid; key = uid->parent; pkey = pgp_principal_key (key); if (isupper ((unsigned char) op)) key = pkey; kflags = key->flags | (pkey->flags & KEYFLAG_RESTRICTIONS) | uid->flags; switch (ascii_tolower (op)) { case '[': { const char *cp; char buf2[SHORT_STRING], *p; int do_locales; struct tm *tm; size_t len; p = dest; cp = src; if (*cp == '!') { do_locales = 0; cp++; } else do_locales = 1; len = destlen - 1; while (len > 0 && *cp != ']') { if (*cp == '%') { cp++; if (len >= 2) { *p++ = '%'; *p++ = *cp; len -= 2; } else break; /* not enough space */ cp++; } else { *p++ = *cp++; len--; } } *p = 0; tm = localtime (&key->gen_time); if (!do_locales) setlocale (LC_TIME, "C"); strftime (buf2, sizeof (buf2), dest, tm); if (!do_locales) setlocale (LC_TIME, ""); snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, buf2); if (len > 0) src = cp + 1; } break; case 'n': if (!optional) { snprintf (fmt, sizeof (fmt), "%%%sd", prefix); snprintf (dest, destlen, fmt, entry->num); } break; case 'k': if (!optional) { snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, _pgp_keyid (key)); } break; case 'u': if (!optional) { snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, NONULL (uid->addr)); } break; case 'a': if (!optional) { snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, key->algorithm); } break; case 'l': if (!optional) { snprintf (fmt, sizeof (fmt), "%%%sd", prefix); snprintf (dest, destlen, fmt, key->keylen); } break; case 'f': if (!optional) { snprintf (fmt, sizeof (fmt), "%%%sc", prefix); snprintf (dest, destlen, fmt, pgp_flags (kflags)); } else if (!(kflags & (KEYFLAG_RESTRICTIONS))) optional = 0; break; case 'c': if (!optional) { snprintf (fmt, sizeof (fmt), "%%%ss", prefix); snprintf (dest, destlen, fmt, pgp_key_abilities (kflags)); } else if (!(kflags & (KEYFLAG_ABILITIES))) optional = 0; break; case 't': if (!optional) { snprintf (fmt, sizeof (fmt), "%%%sc", prefix); snprintf (dest, destlen, fmt, trust_flags[uid->trust & 0x03]); } else if (!(uid->trust & 0x03)) /* undefined trust */ optional = 0; break; default: *dest = '\0'; } if (optional) mutt_FormatString (dest, destlen, col, cols, ifstring, pgp_entry_fmt, data, 0); else if (flags & MUTT_FORMAT_OPTIONAL) mutt_FormatString (dest, destlen, col, cols, elsestring, pgp_entry_fmt, data, 0); return (src); } static void pgp_entry (char *s, size_t l, MUTTMENU * menu, int num) { pgp_uid_t **KeyTable = (pgp_uid_t **) menu->data; pgp_entry_t entry; entry.uid = KeyTable[num]; entry.num = num + 1; mutt_FormatString (s, l, 0, MuttIndexWindow->cols, NONULL (PgpEntryFormat), pgp_entry_fmt, &entry, MUTT_FORMAT_ARROWCURSOR); } static int _pgp_compare_address (const void *a, const void *b) { int r; pgp_uid_t **s = (pgp_uid_t **) a; pgp_uid_t **t = (pgp_uid_t **) b; if ((r = mutt_strcasecmp ((*s)->addr, (*t)->addr))) return r; else return mutt_strcasecmp (pgp_fpr_or_lkeyid ((*s)->parent), pgp_fpr_or_lkeyid ((*t)->parent)); } static int pgp_compare_address (const void *a, const void *b) { return ((PgpSortKeys & SORT_REVERSE) ? !_pgp_compare_address (a, b) : _pgp_compare_address (a, b)); } static int _pgp_compare_keyid (const void *a, const void *b) { int r; pgp_uid_t **s = (pgp_uid_t **) a; pgp_uid_t **t = (pgp_uid_t **) b; if ((r = mutt_strcasecmp (pgp_fpr_or_lkeyid ((*s)->parent), pgp_fpr_or_lkeyid ((*t)->parent)))) return r; else return (mutt_strcasecmp ((*s)->addr, (*t)->addr)); } static int pgp_compare_keyid (const void *a, const void *b) { return ((PgpSortKeys & SORT_REVERSE) ? !_pgp_compare_keyid (a, b) : _pgp_compare_keyid (a, b)); } static int _pgp_compare_date (const void *a, const void *b) { int r; pgp_uid_t **s = (pgp_uid_t **) a; pgp_uid_t **t = (pgp_uid_t **) b; if ((r = mutt_numeric_cmp ((*s)->parent->gen_time, (*t)->parent->gen_time))) return r; return (mutt_strcasecmp ((*s)->addr, (*t)->addr)); } static int pgp_compare_date (const void *a, const void *b) { return ((PgpSortKeys & SORT_REVERSE) ? !_pgp_compare_date (a, b) : _pgp_compare_date (a, b)); } static int _pgp_compare_trust (const void *a, const void *b) { int r; pgp_uid_t **s = (pgp_uid_t **) a; pgp_uid_t **t = (pgp_uid_t **) b; if ((r = mutt_numeric_cmp (((*s)->parent->flags & (KEYFLAG_RESTRICTIONS)), ((*t)->parent->flags & (KEYFLAG_RESTRICTIONS))))) return r; /* Note: reversed */ if ((r = mutt_numeric_cmp ((*t)->trust, (*s)->trust))) return r; /* Note: reversed */ if ((r = mutt_numeric_cmp ((*t)->parent->keylen, (*s)->parent->keylen))) return r; /* Note: reversed */ if ((r = mutt_numeric_cmp ((*t)->parent->gen_time, (*s)->parent->gen_time))) return r; if ((r = mutt_strcasecmp ((*s)->addr, (*t)->addr))) return r; return (mutt_strcasecmp (pgp_fpr_or_lkeyid ((*s)->parent), pgp_fpr_or_lkeyid ((*t)->parent))); } static int pgp_compare_trust (const void *a, const void *b) { return ((PgpSortKeys & SORT_REVERSE) ? !_pgp_compare_trust (a, b) : _pgp_compare_trust (a, b)); } static int pgp_key_is_valid (pgp_key_t k) { pgp_key_t pk = pgp_principal_key (k); if (k->flags & KEYFLAG_CANTUSE) return 0; if (pk->flags & KEYFLAG_CANTUSE) return 0; return 1; } static int pgp_id_is_strong (pgp_uid_t *uid) { if ((uid->trust & 3) < 3) return 0; /* else */ return 1; } static int pgp_id_is_valid (pgp_uid_t *uid) { if (!pgp_key_is_valid (uid->parent)) return 0; if (uid->flags & KEYFLAG_CANTUSE) return 0; /* else */ return 1; } #define PGP_KV_VALID 1 #define PGP_KV_ADDR 2 #define PGP_KV_STRING 4 #define PGP_KV_STRONGID 8 #define PGP_KV_MATCH (PGP_KV_ADDR|PGP_KV_STRING) static int pgp_id_matches_addr (ADDRESS *addr, ADDRESS *u_addr, pgp_uid_t *uid) { int rv = 0; if (pgp_id_is_valid (uid)) rv |= PGP_KV_VALID; if (pgp_id_is_strong (uid)) rv |= PGP_KV_STRONGID; if (addr->mailbox && u_addr->mailbox && mutt_strcasecmp (addr->mailbox, u_addr->mailbox) == 0) rv |= PGP_KV_ADDR; if (addr->personal && u_addr->personal && mutt_strcasecmp (addr->personal, u_addr->personal) == 0) rv |= PGP_KV_STRING; return rv; } static pgp_key_t pgp_select_key (pgp_key_t keys, ADDRESS * p, const char *s) { int keymax; pgp_uid_t **KeyTable; MUTTMENU *menu; int i, done = 0; char helpstr[LONG_STRING], buf[LONG_STRING], tmpbuf[STRING]; char cmd[LONG_STRING]; BUFFER *tempfile; FILE *fp, *devnull; pid_t thepid; pgp_key_t kp; pgp_uid_t *a; int (*f) (const void *, const void *); int unusable = 0; keymax = 0; KeyTable = NULL; for (i = 0, kp = keys; kp; kp = kp->next) { if (!option (OPTPGPSHOWUNUSABLE) && (kp->flags & KEYFLAG_CANTUSE)) { unusable = 1; continue; } for (a = kp->address; a; a = a->next) { if (!option (OPTPGPSHOWUNUSABLE) && (a->flags & KEYFLAG_CANTUSE)) { unusable = 1; continue; } if (i == keymax) { keymax += 5; safe_realloc (&KeyTable, sizeof (pgp_uid_t *) * keymax); } KeyTable[i++] = a; } } if (!i && unusable) { mutt_error _("All matching keys are expired, revoked, or disabled."); mutt_sleep (1); return NULL; } switch (PgpSortKeys & SORT_MASK) { case SORT_DATE: f = pgp_compare_date; break; case SORT_KEYID: f = pgp_compare_keyid; break; case SORT_ADDRESS: f = pgp_compare_address; break; case SORT_TRUST: default: f = pgp_compare_trust; break; } qsort (KeyTable, i, sizeof (pgp_uid_t *), f); helpstr[0] = 0; mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_PGP, OP_EXIT); strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ mutt_make_help (buf, sizeof (buf), _("Select "), MENU_PGP, OP_GENERIC_SELECT_ENTRY); strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ mutt_make_help (buf, sizeof (buf), _("Check key "), MENU_PGP, OP_VERIFY_KEY); strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ mutt_make_help (buf, sizeof (buf), _("Help"), MENU_PGP, OP_HELP); strcat (helpstr, buf); /* __STRCAT_CHECKED__ */ menu = mutt_new_menu (MENU_PGP); menu->max = i; menu->make_entry = pgp_entry; menu->help = helpstr; menu->data = KeyTable; mutt_push_current_menu (menu); if (p) snprintf (buf, sizeof (buf), _("PGP keys matching <%s>."), p->mailbox); else snprintf (buf, sizeof (buf), _("PGP keys matching \"%s\"."), s); menu->title = buf; kp = NULL; mutt_clear_error (); while (!done) { switch (mutt_menuLoop (menu)) { case OP_VERIFY_KEY: if ((devnull = fopen ("/dev/null", "w")) == NULL) /* __FOPEN_CHECKED__ */ { mutt_perror _("Can't open /dev/null"); break; } tempfile = mutt_buffer_pool_get (); mutt_buffer_mktemp (tempfile); if ((fp = safe_fopen (mutt_b2s (tempfile), "w")) == NULL) { mutt_perror _("Can't create temporary file"); safe_fclose (&devnull); mutt_buffer_pool_release (&tempfile); break; } mutt_message _("Invoking PGP..."); snprintf (tmpbuf, sizeof (tmpbuf), "0x%s", pgp_fpr_or_lkeyid (pgp_principal_key (KeyTable[menu->current]->parent))); if ((thepid = pgp_invoke_verify_key (NULL, NULL, NULL, -1, fileno (fp), fileno (devnull), tmpbuf)) == -1) { mutt_perror _("Can't create filter"); unlink (mutt_b2s (tempfile)); mutt_buffer_pool_release (&tempfile); safe_fclose (&fp); safe_fclose (&devnull); break; } mutt_wait_filter (thepid); safe_fclose (&fp); safe_fclose (&devnull); mutt_clear_error (); snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), pgp_keyid (pgp_principal_key (KeyTable[menu->current]->parent))); mutt_do_pager (cmd, mutt_b2s (tempfile), 0, NULL); mutt_buffer_pool_release (&tempfile); menu->redraw = REDRAW_FULL; break; case OP_VIEW_ID: mutt_message ("%s", NONULL (KeyTable[menu->current]->addr)); break; case OP_GENERIC_SELECT_ENTRY: /* XXX make error reporting more verbose */ if (option (OPTPGPCHECKTRUST)) if (!pgp_key_is_valid (KeyTable[menu->current]->parent)) { mutt_error _("This key can't be used: expired/disabled/revoked."); break; } if (option (OPTPGPCHECKTRUST) && (!pgp_id_is_valid (KeyTable[menu->current]) || !pgp_id_is_strong (KeyTable[menu->current]))) { char *s = ""; char buff[LONG_STRING]; if (KeyTable[menu->current]->flags & KEYFLAG_CANTUSE) s = N_("ID is expired/disabled/revoked."); else switch (KeyTable[menu->current]->trust & 0x03) { case 0: s = N_("ID has undefined validity."); break; case 1: s = N_("ID is not valid."); break; case 2: s = N_("ID is only marginally valid."); break; } snprintf (buff, sizeof (buff), _("%s Do you really want to use the key?"), _(s)); if (mutt_yesorno (buff, MUTT_NO) != MUTT_YES) { mutt_clear_error (); break; } } # if 0 kp = pgp_principal_key (KeyTable[menu->current]->parent); # else kp = KeyTable[menu->current]->parent; # endif done = 1; break; case OP_EXIT: kp = NULL; done = 1; break; } } mutt_pop_current_menu (menu); mutt_menuDestroy (&menu); FREE (&KeyTable); return (kp); } pgp_key_t pgp_ask_for_key (char *tag, char *whatfor, short abilities, pgp_ring_t keyring) { pgp_key_t key; char resp[SHORT_STRING]; struct pgp_cache *l = NULL; mutt_clear_error (); resp[0] = 0; if (whatfor) { for (l = id_defaults; l; l = l->next) if (!mutt_strcasecmp (whatfor, l->what)) { strfcpy (resp, NONULL (l->dflt), sizeof (resp)); break; } } FOREVER { resp[0] = 0; if (mutt_get_field (tag, resp, sizeof (resp), MUTT_CLEAR) != 0) return NULL; if (whatfor) { if (l) mutt_str_replace (&l->dflt, resp); else { l = safe_malloc (sizeof (struct pgp_cache)); l->next = id_defaults; id_defaults = l; l->what = safe_strdup (whatfor); l->dflt = safe_strdup (resp); } } if ((key = pgp_getkeybystr (resp, abilities, keyring))) return key; BEEP (); } /* not reached */ } /* generate a public key attachment */ BODY *pgp_make_key_attachment (void) { BODY *att = NULL; char buff[LONG_STRING], tmp[STRING]; BUFFER *tempf = NULL; FILE *tempfp; FILE *devnull; struct stat sb; pid_t thepid; pgp_key_t key; unset_option (OPTPGPCHECKTRUST); key = pgp_ask_for_key (_("Please enter the key ID: "), NULL, 0, PGP_PUBRING); if (!key) return NULL; snprintf (tmp, sizeof (tmp), "0x%s", pgp_fpr_or_lkeyid (pgp_principal_key (key))); pgp_free_key (&key); tempf = mutt_buffer_pool_get (); mutt_buffer_mktemp (tempf); if ((tempfp = safe_fopen (mutt_b2s (tempf), "w")) == NULL) { mutt_perror _("Can't create temporary file"); goto cleanup; } if ((devnull = fopen ("/dev/null", "w")) == NULL) /* __FOPEN_CHECKED__ */ { mutt_perror _("Can't open /dev/null"); safe_fclose (&tempfp); unlink (mutt_b2s (tempf)); goto cleanup; } mutt_message _("Invoking PGP..."); if ((thepid = pgp_invoke_export (NULL, NULL, NULL, -1, fileno (tempfp), fileno (devnull), tmp)) == -1) { mutt_perror _("Can't create filter"); safe_fclose (&tempfp); unlink (mutt_b2s (tempf)); safe_fclose (&devnull); goto cleanup; } mutt_wait_filter (thepid); safe_fclose (&tempfp); safe_fclose (&devnull); att = mutt_new_body (); att->filename = safe_strdup (mutt_b2s (tempf)); att->unlink = 1; att->use_disp = 0; att->type = TYPEAPPLICATION; att->subtype = safe_strdup ("pgp-keys"); snprintf (buff, sizeof (buff), _("PGP Key %s."), tmp); att->description = safe_strdup (buff); mutt_update_encoding (att); stat (mutt_b2s (tempf), &sb); att->length = sb.st_size; cleanup: mutt_buffer_pool_release (&tempf); return att; } static LIST *pgp_add_string_to_hints (LIST *hints, const char *str) { char *scratch; char *t; if ((scratch = safe_strdup (str)) == NULL) return hints; for (t = strtok (scratch, " ,.:\"()<>\n"); t; t = strtok (NULL, " ,.:\"()<>\n")) { if (strlen (t) > 3) hints = mutt_add_list (hints, t); } FREE (&scratch); return hints; } static pgp_key_t *pgp_get_lastp (pgp_key_t p) { for (; p; p = p->next) if (!p->next) return &p->next; return NULL; } pgp_key_t pgp_getkeybyaddr (ADDRESS * a, short abilities, pgp_ring_t keyring, int oppenc_mode) { ADDRESS *r, *p; LIST *hints = NULL; int multi = 0; int match; pgp_key_t keys, k, kn; pgp_key_t the_strong_valid_key = NULL; pgp_key_t a_valid_addrmatch_key = NULL; pgp_key_t matches = NULL; pgp_key_t *last = &matches; pgp_uid_t *q; if (a && a->mailbox) hints = mutt_add_list (hints, a->mailbox); if (a && a->personal) hints = pgp_add_string_to_hints (hints, a->personal); if (! oppenc_mode ) mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox); keys = pgp_get_candidates (keyring, hints); mutt_free_list (&hints); if (!keys) return NULL; dprint (5, (debugfile, "pgp_getkeybyaddr: looking for %s <%s>.\n", NONULL (a->personal), NONULL (a->mailbox))); for (k = keys; k; k = kn) { kn = k->next; dprint (5, (debugfile, " looking at key: %s\n", pgp_keyid (k))); if (abilities && !(k->flags & abilities)) { dprint (5, (debugfile, " insufficient abilities: Has %x, want %x\n", k->flags, abilities)); continue; } match = 0; /* any match */ for (q = k->address; q; q = q->next) { r = rfc822_parse_adrlist (NULL, NONULL (q->addr)); for (p = r; p; p = p->next) { int validity = pgp_id_matches_addr (a, p, q); if (validity & PGP_KV_MATCH) /* something matches */ match = 1; if ((validity & PGP_KV_VALID) && (validity & PGP_KV_ADDR)) { if (validity & PGP_KV_STRONGID) { if (the_strong_valid_key && the_strong_valid_key != k) multi = 1; the_strong_valid_key = k; } else { a_valid_addrmatch_key = k; } } } rfc822_free_address (&r); } if (match) { *last = pgp_principal_key (k); kn = pgp_remove_key (&keys, *last); last = pgp_get_lastp (k); } } pgp_free_key (&keys); if (matches) { if (oppenc_mode) { if (the_strong_valid_key) { pgp_remove_key (&matches, the_strong_valid_key); k = the_strong_valid_key; } else if (a_valid_addrmatch_key && !option (OPTCRYPTOPPENCSTRONGKEYS)) { pgp_remove_key (&matches, a_valid_addrmatch_key); k = a_valid_addrmatch_key; } else k = NULL; } else if (the_strong_valid_key && !multi) { /* * There was precisely one strong match on a valid ID. * * Proceed without asking the user. */ pgp_remove_key (&matches, the_strong_valid_key); k = the_strong_valid_key; } else { /* * Else: Ask the user. */ if ((k = pgp_select_key (matches, a, NULL))) pgp_remove_key (&matches, k); } pgp_free_key (&matches); return k; } return NULL; } pgp_key_t pgp_getkeybystr (char *p, short abilities, pgp_ring_t keyring) { LIST *hints = NULL; pgp_key_t keys; pgp_key_t matches = NULL; pgp_key_t *last = &matches; pgp_key_t k, kn; pgp_uid_t *a; short match; size_t l; const char *ps, *pl, *pfcopy, *phint; if ((l = mutt_strlen (p)) && p[l-1] == '!') p[l-1] = 0; mutt_message (_("Looking for keys matching \"%s\"..."), p); pfcopy = crypt_get_fingerprint_or_id (p, &phint, &pl, &ps); hints = pgp_add_string_to_hints (hints, phint); keys = pgp_get_candidates (keyring, hints); mutt_free_list (&hints); if (!keys) goto out; for (k = keys; k; k = kn) { kn = k->next; if (abilities && !(k->flags & abilities)) continue; /* This shouldn't happen, but keys without any addresses aren't selectable * in pgp_select_key(). */ if (!k->address) continue; match = 0; dprint (5, (debugfile, "pgp_getkeybystr: matching \"%s\" against key %s:\n", p, pgp_long_keyid (k))); if (!*p || (pfcopy && mutt_strcasecmp (pfcopy, k->fingerprint) == 0) || (pl && mutt_strcasecmp (pl, pgp_long_keyid (k)) == 0) || (ps && mutt_strcasecmp (ps, pgp_short_keyid (k)) == 0)) { dprint (5, (debugfile, "\t\tmatch.\n")); match = 1; } else { for (a = k->address; a; a = a->next) { dprint (5, (debugfile, "pgp_getkeybystr: matching \"%s\" against key %s, \"%s\":\n", p, pgp_long_keyid (k), NONULL (a->addr))); if (mutt_stristr (a->addr, p)) { dprint (5, (debugfile, "\t\tmatch.\n")); match = 1; break; } } } if (match) { *last = pgp_principal_key (k); kn = pgp_remove_key (&keys, *last); last = pgp_get_lastp (k); } } pgp_free_key (&keys); if (matches) { if ((k = pgp_select_key (matches, NULL, p))) pgp_remove_key (&matches, k); pgp_free_key (&matches); FREE (&pfcopy); if (l && !p[l-1]) p[l-1] = '!'; return k; } out: FREE (&pfcopy); if (l && !p[l-1]) p[l-1] = '!'; return NULL; } #endif /* CRYPT_BACKEND_CLASSIC_PGP */