2 * Copyright notice from original mutt:
3 * crypt-gpgme.c - GPGME based crypto operations
4 * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
5 * Copyright (C) 1998,1999,2000 Thomas Roessler <roessler@guug.de>
6 * Copyright (C) 2001 Thomas Roessler <roessler@guug.de>
7 * Oliver Ehli <elmy@acm.org>
8 * Copyright (C) 2002, 2003, 2004 g10 Code GmbH
10 * This file is part of mutt-ng, see http://www.muttng.org/.
11 * It's licensed under the GNU General Public License,
12 * please see the file GPL in the top level source directory.
15 #include <lib-lib/lib-lib.h>
20 #ifdef HAVE_LANGINFO_D_T_FMT
21 # include <langinfo.h>
23 #ifdef HAVE_SYS_RESOURCE_H
24 # include <sys/resource.h>
29 #include <lib-mime/mime.h>
30 #include <lib-ui/curses.h>
31 #include <lib-ui/enter.h>
32 #include <lib-ui/menu.h>
41 #include "recvattach.h"
44 /* Values used for comparing addresses. */
45 #define CRYPT_KV_VALID 1
46 #define CRYPT_KV_ADDR 2
47 #define CRYPT_KV_STRING 4
48 #define CRYPT_KV_STRONGID 8
49 #define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
58 struct crypt_cache *next;
66 /* We work based on user IDs, getting from a user ID to the key is
67 check and does not need any memory (gpgme uses reference counting). */
68 typedef struct crypt_keyinfo {
69 struct crypt_keyinfo *next;
71 int idx; /* and the user ID at this index */
72 const char *uid; /* and for convenience point to this user ID */
73 unsigned int flags; /* global and per uid flags (for convenience) */
76 typedef struct crypt_entry {
82 static struct crypt_cache *id_defaults = NULL;
83 static gpgme_key_t signature_key = NULL;
85 /* Show a message that a backend will be invoked. */
86 void crypt_invoke_message (int type)
88 if (type & APPLICATION_PGP) {
89 mutt_message _("Invoking PGP...");
91 else if (type & APPLICATION_SMIME) {
92 mutt_message _("Invoking S/MIME...");
97 * General helper functions.
100 /* return true when S points to a didgit or letter. */
101 static int digit_or_letter (const unsigned char *s)
103 return ((*s >= '0' && *s <= '9')
104 || (*s >= 'A' && *s <= 'Z')
105 || (*s >= 'a' && *s <= 'z'));
109 /* Print the utf-8 encoded string BUF of length LEN bytes to stream
110 FP. Convert the character set. */
111 static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
115 tstr = p_dupstr(buf, len);
116 mutt_convert_string (&tstr, "utf-8", MCharset.charset, M_ICONV_HOOK_FROM);
126 /* Return the keyID for the key K. Note that this string is valid as
127 long as K is valid */
128 static const char *crypt_keyid (crypt_key_t * k)
130 const char *s = "????????";
132 if (k->kobj && k->kobj->subkeys) {
133 s = k->kobj->subkeys->keyid;
134 if ((!option (OPTPGPLONGIDS)) && (m_strlen(s) == 16))
135 /* Return only the short keyID. */
142 /* Return the hexstring fingerprint from the key K. */
143 static const char *crypt_fpr (crypt_key_t * k)
147 if (k->kobj && k->kobj->subkeys)
148 s = k->kobj->subkeys->fpr;
153 /* Parse FLAGS and return a statically allocated(!) string with them. */
154 static char *crypt_key_abilities (int flags)
158 if (!(flags & KEYFLAG_CANENCRYPT))
160 else if (flags & KEYFLAG_PREFER_SIGNING)
165 if (!(flags & KEYFLAG_CANSIGN))
167 else if (flags & KEYFLAG_PREFER_ENCRYPTION)
177 /* Parse FLAGS and return a character describing the most important flag. */
178 static char crypt_flags (int flags)
180 if (flags & KEYFLAG_REVOKED)
182 else if (flags & KEYFLAG_EXPIRED)
184 else if (flags & KEYFLAG_DISABLED)
186 else if (flags & KEYFLAG_CRITICAL)
192 /* Return a copy of KEY. */
193 static crypt_key_t *crypt_copy_key (crypt_key_t *key)
197 k = p_new(crypt_key_t, 1);
199 gpgme_key_ref (key->kobj);
202 k->flags = key->flags;
207 /* Release all the keys at the address of KEYLIST and set the address
209 static void crypt_free_key (crypt_key_t ** keylist)
212 crypt_key_t *k = (*keylist)->next;
219 /* Return trute when key K is valid. */
220 static int crypt_key_is_valid (crypt_key_t * k)
222 if (k->flags & KEYFLAG_CANTUSE)
227 /* Return true whe validity of KEY is sufficient. */
228 static int crypt_id_is_strong (crypt_key_t * key)
230 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
231 gpgme_user_id_t uid = NULL;
235 if ((key->flags & KEYFLAG_ISX509))
238 for (i = 0, uid = key->kobj->uids; (i < key->idx) && uid;
239 i++, uid = uid->next);
244 case GPGME_VALIDITY_UNKNOWN:
245 case GPGME_VALIDITY_UNDEFINED:
246 case GPGME_VALIDITY_NEVER:
247 case GPGME_VALIDITY_MARGINAL:
251 case GPGME_VALIDITY_FULL:
252 case GPGME_VALIDITY_ULTIMATE:
260 /* Return true when the KEY is valid, i.e. not marked as unusable. */
261 static int crypt_id_is_valid (crypt_key_t * key)
263 return !(key->flags & KEYFLAG_CANTUSE);
266 /* Return a bit vector describing how well the addresses ADDR and
267 U_ADDR match and whether KEY is valid. */
268 static int crypt_id_matches_addr (address_t * addr, address_t * u_addr,
273 if (crypt_id_is_valid (key))
274 rv |= CRYPT_KV_VALID;
276 if (crypt_id_is_strong (key))
277 rv |= CRYPT_KV_STRONGID;
279 if (addr->mailbox && u_addr->mailbox
280 && m_strcasecmp(addr->mailbox, u_addr->mailbox) == 0)
283 if (addr->personal && u_addr->personal
284 && m_strcasecmp(addr->personal, u_addr->personal) == 0)
285 rv |= CRYPT_KV_STRING;
292 * GPGME convenient functions.
295 /* Create a new gpgme context and return it. With FOR_SMIME set to
296 true, the protocol of the context is set to CMS. */
297 static gpgme_ctx_t create_gpgme_context (int for_smime)
302 err = gpgme_new (&ctx);
304 mutt_error (_("error creating gpgme context: %s\n"), gpgme_strerror (err));
310 err = gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
312 mutt_error (_("error enabling CMS protocol: %s\n"), gpgme_strerror (err));
321 /* Create a new gpgme data object. This is a wrapper to die on
323 static gpgme_data_t create_gpgme_data (void)
328 err = gpgme_data_new (&data);
330 mutt_error (_("error creating gpgme data object: %s\n"),
331 gpgme_strerror (err));
338 /* Create a new GPGME Data object from the mail body A. With CONVERT
339 passed as true, the lines are converted to CR,LF if required.
340 Return NULL on error or the gpgme_data_t object on success. */
341 static gpgme_data_t body_to_data_object (BODY * a, int convert)
343 char tempfile[_POSIX_PATH_MAX];
348 fptmp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
350 mutt_perror (_("Can't create temporary file"));
354 mutt_write_mime_header (a, fptmp);
356 mutt_write_mime_body (a, fptmp);
360 unsigned char buf[1];
362 data = create_gpgme_data ();
364 while ((c = fgetc (fptmp)) != EOF) {
368 if (c == '\n' && !hadcr) {
370 gpgme_data_write (data, buf, 1);
375 /* FIXME: This is quite suboptimal */
377 gpgme_data_write (data, buf, 1);
379 gpgme_data_seek (data, 0, SEEK_SET);
381 err = gpgme_data_new_from_file (&data, tempfile, 1);
386 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
393 /* Create a GPGME data object from the stream FP but limit the object
394 to LENGTH bytes starting at OFFSET bytes from the beginning of the
396 static gpgme_data_t file_to_data_object (FILE * fp, long offset, long length)
401 err = gpgme_data_new_from_filepart (&data, NULL, fp, offset, length);
403 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
410 /* Write a GPGME data object to the stream FP. */
411 static int data_object_to_stream (gpgme_data_t data, FILE * fp)
417 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
418 ? gpgme_error_from_errno (errno) : 0);
420 mutt_error (_("error rewinding data object: %s\n"), gpgme_strerror (err));
424 while ((nread = gpgme_data_read (data, buf, sizeof (buf)))) {
425 /* fixme: we are not really converting CRLF to LF but just
426 skipping CR. Doing it correctly needs a more complex logic */
427 for (p = buf; nread; p++, nread--) {
433 mutt_perror ("[tempfile]");
438 mutt_error (_("error reading data object: %s\n"), strerror (errno));
444 /* Copy a data object to a newly created temporay file and return that
445 filename. Caller must free. With RET_FP not NULL, don't close the
446 stream but return it there. */
447 static char *data_object_to_tempfile (gpgme_data_t data, FILE ** ret_fp)
450 char tempfile[_POSIX_PATH_MAX];
454 fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
456 mutt_perror (_("Can't create temporary file"));
460 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
461 ? gpgme_error_from_errno (errno) : 0);
465 while ((nread = gpgme_data_read (data, buf, sizeof (buf)))) {
466 if (fwrite (buf, nread, 1, fp) != 1) {
467 mutt_perror (_("Can't create temporary file"));
479 mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
486 return m_strdup(tempfile);
490 /* FIXME: stolen from gpgme to avoid "ambiguous identity" errors */
492 gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
499 if (!ctx || !r_key || !fpr)
500 return gpg_error (GPG_ERR_INV_VALUE);
502 if (strlen (fpr) < 8) /* We have at least a key ID. */
503 return gpg_error (GPG_ERR_INV_VALUE);
505 /* FIXME: We use our own context because we have to avoid the user's
506 I/O callback handlers. */
507 err = gpgme_new (&listctx);
510 gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
511 err = gpgme_op_keylist_start (listctx, fpr, secret);
513 err = gpgme_op_keylist_next (listctx, r_key);
514 gpgme_release (listctx);
518 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
519 The keys must be space delimited. */
520 static gpgme_key_t *create_recipient_set (const char *keylist,
521 gpgme_protocol_t protocol)
527 gpgme_key_t *rset = NULL;
528 unsigned int rset_n = 0;
529 gpgme_key_t key = NULL;
530 gpgme_ctx_t context = NULL;
532 err = gpgme_new (&context);
534 err = gpgme_set_protocol (context, protocol);
541 for (i = 0; *s && *s != ' ' && i < ssizeof(buf) - 1;)
545 if (i > 1 && buf[i - 1] == '!') {
546 /* The user selected to override the valididy of that
550 err = gpgme_get_key2 (context, buf, &key, 0);
552 key->uids->validity = GPGME_VALIDITY_FULL;
556 err = gpgme_get_key2 (context, buf, &key, 0);
559 p_realloc(&rset, rset_n + 1);
560 rset[rset_n++] = key;
563 mutt_error (_("error adding recipient `%s': %s\n"),
564 buf, gpgme_strerror (err));
572 /* NULL terminate. */
573 p_realloc(&rset, rset_n + 1);
574 rset[rset_n++] = NULL;
577 gpgme_release (context);
583 /* Make sure that the correct signer is set. Returns 0 on success. */
584 static int set_signer (gpgme_ctx_t ctx, int for_smime)
586 char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
589 gpgme_key_t key, key2;
591 if (!signid || !*signid)
594 listctx = create_gpgme_context (for_smime);
595 err = gpgme_op_keylist_start (listctx, signid, 1);
597 err = gpgme_op_keylist_next (listctx, &key);
599 gpgme_release (listctx);
600 mutt_error (_("secret key `%s' not found: %s\n"),
601 signid, gpgme_strerror (err));
604 err = gpgme_op_keylist_next (listctx, &key2);
606 gpgme_key_release (key);
607 gpgme_key_release (key2);
608 gpgme_release (listctx);
609 mutt_error (_("ambiguous specification of secret key `%s'\n"), signid);
612 gpgme_op_keylist_end (listctx);
613 gpgme_release (listctx);
615 gpgme_signers_clear (ctx);
616 err = gpgme_signers_add (ctx, key);
617 gpgme_key_release (key);
619 mutt_error (_("error setting secret key `%s': %s\n"),
620 signid, gpgme_strerror (err));
627 /* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
628 and return an allocated filename to a temporary file containing the
629 enciphered text. With USE_SMIME set to true, the smime backend is
630 used. With COMBINED_SIGNED a PGP message is signed and
631 encrypted. Returns NULL in case of error */
632 static char *encrypt_gpgme_object (gpgme_data_t plaintext, gpgme_key_t * rset,
633 int use_smime, int combined_signed)
637 gpgme_data_t ciphertext;
640 ctx = create_gpgme_context (use_smime);
642 gpgme_set_armor (ctx, 1);
644 ciphertext = create_gpgme_data ();
646 if (combined_signed) {
647 if (set_signer (ctx, use_smime)) {
648 gpgme_data_release (ciphertext);
652 err = gpgme_op_encrypt_sign (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
653 plaintext, ciphertext);
656 err = gpgme_op_encrypt (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
657 plaintext, ciphertext);
658 mutt_need_hard_redraw ();
660 mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
661 gpgme_data_release (ciphertext);
668 outfile = data_object_to_tempfile (ciphertext, NULL);
669 gpgme_data_release (ciphertext);
673 /* Find the "micalg" parameter from the last Gpgme operation on
674 context CTX. It is expected that this operation was a sign
675 operation. Return the algorithm name as a C string in buffer BUF
676 which must have been allocated by the caller with size BUFLEN.
677 Returns 0 on success or -1 in case of an error. The return string
678 is truncted to BUFLEN - 1. */
679 static int get_micalg (gpgme_ctx_t ctx, char *buf, ssize_t buflen)
681 gpgme_sign_result_t result = NULL;
682 const char *algorithm_name = NULL;
688 result = gpgme_op_sign_result (ctx);
690 algorithm_name = gpgme_hash_algo_name (result->signatures->hash_algo);
691 if (algorithm_name) {
692 m_strcpy(buf, buflen, algorithm_name);
696 return *buf ? 0 : -1;
699 static void print_time (time_t t, STATE * s)
703 setlocale (LC_TIME, "");
704 #ifdef HAVE_LANGINFO_D_T_FMT
705 strftime (p, sizeof (p), nl_langinfo (D_T_FMT), localtime (&t));
707 strftime (p, sizeof (p), "%c", localtime (&t));
709 setlocale (LC_TIME, "C");
710 state_attach_puts (p, s);
713 /* Implementation of `sign_message'. */
715 /* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
716 USE_SMIME is passed as true. Returns the new body or NULL on
718 static BODY *sign_message (BODY * a, int use_smime)
725 gpgme_data_t message, signature;
727 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
729 message = body_to_data_object (a, 1);
732 signature = create_gpgme_data ();
734 ctx = create_gpgme_context (use_smime);
736 gpgme_set_armor (ctx, 1);
738 if (set_signer (ctx, use_smime)) {
739 gpgme_data_release (signature);
744 err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH);
745 mutt_need_hard_redraw ();
746 gpgme_data_release (message);
748 gpgme_data_release (signature);
750 mutt_error (_("error signing data: %s\n"), gpgme_strerror (err));
754 sigfile = data_object_to_tempfile (signature, NULL);
755 gpgme_data_release (signature);
762 t->type = TYPEMULTIPART;
763 t->subtype = m_strdup("signed");
764 t->encoding = ENC7BIT;
766 t->disposition = DISPINLINE;
768 parameter_set_boundary(&t->parameter);
769 parameter_setval(&t->parameter, "protocol",
770 use_smime ? "application/pkcs7-signature"
771 : "application/pgp-signature");
772 /* Get the micalg from gpgme. Old gpgme versions don't support this
773 for S/MIME so we assume sha-1 in this case. */
774 if (!get_micalg (ctx, buf, sizeof buf))
775 parameter_setval(&t->parameter, "micalg", buf);
777 parameter_setval(&t->parameter, "micalg", "sha1");
783 t->parts->next = body_new();
785 t->type = TYPEAPPLICATION;
787 t->subtype = m_strdup("pkcs7-signature");
788 parameter_setval(&t->parameter, "name", "smime.p7s");
789 t->encoding = ENCBASE64;
791 t->disposition = DISPATTACH;
792 t->d_filename = m_strdup("smime.p7s");
795 t->subtype = m_strdup("pgp-signature");
797 t->disposition = DISPINLINE;
798 t->encoding = ENC7BIT;
800 t->filename = sigfile;
801 t->unlink = 1; /* ok to remove this file after sending. */
807 BODY *crypt_pgp_sign_message (BODY * a)
809 return sign_message (a, 0);
812 BODY *crypt_smime_sign_message (BODY * a)
814 return sign_message (a, 1);
818 * Implementation of `encrypt_message'.
821 /* Encrypt the mail body A to all keys given as space separated keyids
822 or fingerprints in KEYLIST and return the encrypted body. */
823 BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
825 char *outfile = NULL;
827 gpgme_key_t *rset = NULL;
828 gpgme_data_t plaintext;
830 rset = create_recipient_set (keylist, GPGME_PROTOCOL_OpenPGP);
836 plaintext = body_to_data_object (a, 0);
842 outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
843 gpgme_data_release (plaintext);
849 t->type = TYPEMULTIPART;
850 t->subtype = m_strdup("encrypted");
851 t->encoding = ENC7BIT;
853 t->disposition = DISPINLINE;
855 parameter_set_boundary(&t->parameter);
856 parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
858 t->parts = body_new();
859 t->parts->type = TYPEAPPLICATION;
860 t->parts->subtype = m_strdup("pgp-encrypted");
861 t->parts->encoding = ENC7BIT;
863 t->parts->next = body_new();
864 t->parts->next->type = TYPEAPPLICATION;
865 t->parts->next->subtype = m_strdup("octet-stream");
866 t->parts->next->encoding = ENC7BIT;
867 t->parts->next->filename = outfile;
868 t->parts->next->use_disp = 1;
869 t->parts->next->disposition = DISPINLINE;
870 t->parts->next->unlink = 1; /* delete after sending the message */
871 t->parts->next->d_filename = m_strdup("msg.asc"); /* non pgp/mime
878 * Implementation of `smime_build_smime_entity'.
881 /* Encrypt the mail body A to all keys given as space separated
882 fingerprints in KEYLIST and return the S/MIME encrypted body. */
883 BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
885 char *outfile = NULL;
887 gpgme_key_t *rset = NULL;
888 gpgme_data_t plaintext;
890 rset = create_recipient_set (keylist, GPGME_PROTOCOL_CMS);
894 plaintext = body_to_data_object (a, 0);
900 outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
901 gpgme_data_release (plaintext);
907 t->type = TYPEAPPLICATION;
908 t->subtype = m_strdup("pkcs7-mime");
909 parameter_setval(&t->parameter, "name", "smime.p7m");
910 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
911 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
913 t->disposition = DISPATTACH;
914 t->d_filename = m_strdup("smime.p7m");
915 t->filename = outfile;
916 t->unlink = 1; /*delete after sending the message */
924 /* Implementation of `verify_one'. */
926 /* Display the common attributes of the signature summary SUM.
927 Return 1 if there is is a severe warning.
929 static int show_sig_summary (unsigned long sum,
930 gpgme_ctx_t ctx, gpgme_key_t key, int idx,
935 if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
936 state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
940 if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
941 time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
944 state_attach_puts (_("Warning: The key used to create the "
945 "signature expired at: "), s);
947 state_attach_puts ("\n", s);
950 state_attach_puts (_("Warning: At least one certification key "
951 "has expired\n"), s);
954 if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
955 gpgme_verify_result_t result;
956 gpgme_signature_t sig;
959 result = gpgme_op_verify_result (ctx);
961 for (sig = result->signatures, i = 0; sig && (i < idx);
962 sig = sig->next, i++);
964 state_attach_puts (_("Warning: The signature expired at: "), s);
965 print_time (sig ? sig->exp_timestamp : 0, s);
966 state_attach_puts ("\n", s);
969 if ((sum & GPGME_SIGSUM_KEY_MISSING))
970 state_attach_puts (_("Can't verify due to a missing "
971 "key or certificate\n"), s);
973 if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
974 state_attach_puts (_("The CRL is not available\n"), s);
978 if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
979 state_attach_puts (_("Available CRL is too old\n"), s);
983 if ((sum & GPGME_SIGSUM_BAD_POLICY))
984 state_attach_puts (_("A policy requirement was not met\n"), s);
986 if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
987 const char *t0 = NULL, *t1 = NULL;
988 gpgme_verify_result_t result;
989 gpgme_signature_t sig;
992 state_attach_puts (_("A system error occurred"), s);
994 /* Try to figure out some more detailed system error information. */
995 result = gpgme_op_verify_result (ctx);
996 for (sig = result->signatures, i = 0; sig && (i < idx);
997 sig = sig->next, i++);
1000 t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
1004 state_attach_puts (": ", s);
1006 state_attach_puts (t0, s);
1007 if (t1 && !(t0 && !m_strcmp(t0, t1))) {
1009 state_attach_puts (",", s);
1010 state_attach_puts (t1, s);
1013 state_attach_puts ("\n", s);
1020 static void show_fingerprint (gpgme_key_t key, STATE * state)
1025 const char *prefix = _("Fingerprint: ");
1030 s = key->subkeys ? key->subkeys->fpr : NULL;
1033 is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
1035 bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
1036 buf = p_new(char, bufsize);
1037 m_strcpy(buf, bufsize, prefix);
1038 p = buf + m_strlen(buf);
1039 if (is_pgp && m_strlen(s) == 40) { /* PGP v4 style formatted. */
1040 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
1051 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
1054 *p++ = is_pgp ? ' ' : ':';
1055 if (is_pgp && i == 7)
1060 /* just in case print remaining odd digits */
1065 state_attach_puts (buf, state);
1069 /* Show the valididy of a key used for one signature. */
1070 static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
1072 gpgme_verify_result_t result = NULL;
1073 gpgme_signature_t sig = NULL;
1074 const char *txt = NULL;
1076 result = gpgme_op_verify_result (ctx);
1078 for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
1080 switch (sig ? sig->validity : 0) {
1081 case GPGME_VALIDITY_UNKNOWN:
1082 txt = _("WARNING: We have NO indication whether "
1083 "the key belongs to the person named " "as shown above\n");
1085 case GPGME_VALIDITY_UNDEFINED:
1087 case GPGME_VALIDITY_NEVER:
1088 txt = _("WARNING: The key does NOT BELONG to "
1089 "the person named as shown above\n");
1091 case GPGME_VALIDITY_MARGINAL:
1092 txt = _("WARNING: It is NOT certain that the key "
1093 "belongs to the person named as shown above\n");
1095 case GPGME_VALIDITY_FULL:
1096 case GPGME_VALIDITY_ULTIMATE:
1101 state_attach_puts (txt, s);
1104 /* Show information about one signature. This fucntion is called with
1105 the context CTX of a sucessful verification operation and the
1106 enumerator IDX which should start at 0 and incremete for each
1109 Return values are: 0 for normal procession, 1 for a bad signature,
1110 2 for a signature with a warning or -1 for no more signature. */
1111 static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
1114 const char *fpr, *uid;
1115 gpgme_key_t key = NULL;
1116 int i, anybad = 0, anywarn = 0;
1118 gpgme_user_id_t uids = NULL;
1119 gpgme_verify_result_t result;
1120 gpgme_signature_t sig;
1121 gpgme_error_t err = GPG_ERR_NO_ERROR;
1123 result = gpgme_op_verify_result (ctx);
1125 /* FIXME: this code should use a static variable and remember
1126 the current position in the list of signatures, IMHO.
1129 for (i = 0, sig = result->signatures; sig && (i < idx);
1130 i++, sig = sig->next);
1132 return -1; /* Signature not found. */
1134 if (signature_key) {
1135 gpgme_key_release (signature_key);
1136 signature_key = NULL;
1139 created = sig->timestamp;
1143 if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
1146 err = gpgme_get_key2 (ctx, fpr, &key, 0); /* secret key? */
1148 uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
1150 signature_key = key;
1153 key = NULL; /* Old gpgme versions did not set KEY to NULL on
1154 error. Do it here to avoid a double free. */
1158 if (!s || !s->fpout || !(s->flags & M_DISPLAY)); /* No state information so no way to print anything. */
1160 state_attach_puts (_("Error getting key information: "), s);
1161 state_attach_puts (gpg_strerror (err), s);
1162 state_attach_puts ("\n", s);
1165 else if ((sum & GPGME_SIGSUM_GREEN)) {
1166 state_attach_puts (_("Good signature from: "), s);
1167 state_attach_puts (uid, s);
1168 state_attach_puts ("\n", s);
1169 for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
1171 /* Skip primary UID. */
1175 state_attach_puts (_(" aka: "), s);
1176 state_attach_puts (uids->uid, s);
1177 state_attach_puts ("\n", s);
1179 state_attach_puts (_(" created: "), s);
1180 print_time (created, s);
1181 state_attach_puts ("\n", s);
1182 if (show_sig_summary (sum, ctx, key, idx, s))
1184 show_one_sig_validity (ctx, idx, s);
1186 else if ((sum & GPGME_SIGSUM_RED)) {
1187 state_attach_puts (_("*BAD* signature claimed to be from: "), s);
1188 state_attach_puts (uid, s);
1189 state_attach_puts ("\n", s);
1190 show_sig_summary (sum, ctx, key, idx, s);
1192 else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) { /* We can't decide (yellow) but this is a PGP key with a good
1193 signature, so we display what a PGP user expects: The name,
1194 fingerprint and the key validity (which is neither fully or
1196 state_attach_puts (_("Good signature from: "), s);
1197 state_attach_puts (uid, s);
1198 state_attach_puts ("\n", s);
1199 state_attach_puts (_(" created: "), s);
1200 print_time (created, s);
1201 state_attach_puts ("\n", s);
1202 show_one_sig_validity (ctx, idx, s);
1203 show_fingerprint (key, s);
1204 if (show_sig_summary (sum, ctx, key, idx, s))
1207 else { /* can't decide (yellow) */
1209 state_attach_puts (_("Error checking signature"), s);
1210 state_attach_puts ("\n", s);
1211 show_sig_summary (sum, ctx, key, idx, s);
1214 if (key != signature_key)
1215 gpgme_key_release (key);
1218 return anybad ? 1 : anywarn ? 2 : 0;
1221 /* Do the actual verification step. With IS_SMIME set to true we
1222 assume S/MIME (surprise!) */
1223 static int verify_one (BODY * sigbdy, STATE * s,
1224 const char *tempfile, int is_smime)
1230 gpgme_data_t signature, message;
1232 signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
1236 /* We need to tell gpgme about the encoding because the backend can't
1237 auto-detect plain base-64 encoding which is used by S/MIME. */
1239 gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
1241 err = gpgme_data_new_from_file (&message, tempfile, 1);
1243 gpgme_data_release (signature);
1244 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
1247 ctx = create_gpgme_context (is_smime);
1249 /* Note: We don't need a current time output because GPGME avoids
1250 such an attack by separating the meta information from the
1252 state_attach_puts (_("[-- Begin signature information --]\n"), s);
1254 err = gpgme_op_verify (ctx, signature, message, NULL);
1255 mutt_need_hard_redraw ();
1259 snprintf (buf, sizeof (buf) - 1,
1260 _("Error: verification failed: %s\n"), gpgme_strerror (err));
1261 state_attach_puts (buf, s);
1263 else { /* Verification succeeded, see what the result is. */
1267 if (signature_key) {
1268 gpgme_key_release (signature_key);
1269 signature_key = NULL;
1272 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1283 gpgme_verify_result_t result;
1284 gpgme_sig_notation_t notation;
1285 gpgme_signature_t sig;
1287 result = gpgme_op_verify_result (ctx);
1289 for (sig = result->signatures; sig; sig = sig->next) {
1290 if (sig->notations) {
1291 state_attach_puts ("*** Begin Notation (signature by: ", s);
1292 state_attach_puts (sig->fpr, s);
1293 state_attach_puts (") ***\n", s);
1294 for (notation = sig->notations; notation; notation = notation->next)
1296 if (notation->name) {
1297 state_attach_puts (notation->name, s);
1298 state_attach_puts ("=", s);
1300 if (notation->value) {
1301 state_attach_puts (notation->value, s);
1302 if (!(*notation->value
1303 && (notation->value[m_strlen(notation->value) - 1] ==
1305 state_attach_puts ("\n", s);
1308 state_attach_puts ("*** End Notation ***\n", s);
1314 gpgme_release (ctx);
1316 state_attach_puts (_("[-- End signature information --]\n\n"), s);
1318 return badsig ? 1 : anywarn ? 2 : 0;
1321 int crypt_pgp_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1323 return verify_one (sigbdy, s, tempfile, 0);
1326 int crypt_smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1328 return verify_one (sigbdy, s, tempfile, 1);
1332 * Implementation of `decrypt_part'.
1335 /* Decrypt a PGP or SMIME message (depending on the boolean flag
1336 IS_SMIME) with body A described further by state S. Write
1337 plaintext out to file FPOUT and return a new body. For PGP returns
1338 a flag in R_IS_SIGNED to indicate whether this is a combined
1339 encrypted and signed message, for S/MIME it returns true when it is
1340 not a encrypted but a signed message. */
1341 static BODY *decrypt_part (BODY * a, STATE * s, FILE * fpout, int is_smime,
1348 gpgme_data_t ciphertext, plaintext;
1349 int maybe_signed = 0;
1356 ctx = create_gpgme_context (is_smime);
1359 /* Make a data object from the body, create context etc. */
1360 ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
1363 plaintext = create_gpgme_data ();
1365 /* Do the decryption or the verification in case of the S/MIME hack. */
1366 if ((!is_smime) || maybe_signed) {
1368 err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
1369 else if (maybe_signed)
1370 err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
1373 /* Check wether signatures have been verified. */
1374 gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
1376 if (verify_result->signatures)
1381 err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
1382 gpgme_data_release (ciphertext);
1384 if (is_smime && !maybe_signed && gpg_err_code (err) == GPG_ERR_NO_DATA) {
1385 /* Check whether this might be a signed message despite what
1386 the mime header told us. Retry then. gpgsm returns the
1387 error information "unsupported Algorithm '?'" but gpgme
1388 will not store this unknown algorithm, thus we test that
1389 it has not been set. */
1390 gpgme_decrypt_result_t result;
1392 result = gpgme_op_decrypt_result (ctx);
1393 if (!result->unsupported_algorithm) {
1395 gpgme_data_release (plaintext);
1399 mutt_need_hard_redraw ();
1400 if ((s->flags & M_DISPLAY)) {
1403 snprintf (buf, sizeof (buf) - 1,
1404 _("[-- Error: decryption failed: %s --]\n\n"),
1405 gpgme_strerror (err));
1406 state_attach_puts (buf, s);
1408 gpgme_data_release (plaintext);
1409 gpgme_release (ctx);
1412 mutt_need_hard_redraw ();
1414 /* Read the output from GPGME, and make sure to change CRLF to LF,
1415 otherwise read_mime_header has a hard time parsing the message. */
1416 if (data_object_to_stream (plaintext, fpout)) {
1417 gpgme_data_release (plaintext);
1418 gpgme_release (ctx);
1421 gpgme_data_release (plaintext);
1423 a->is_signed_data = 0;
1429 a->is_signed_data = 1;
1431 *r_is_signed = -1; /* A signature exists. */
1433 if ((s->flags & M_DISPLAY))
1434 state_attach_puts (_("[-- Begin signature " "information --]\n"), s);
1435 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1441 if (!anybad && idx && r_is_signed && *r_is_signed)
1442 *r_is_signed = anywarn ? 2 : 1; /* Good signature. */
1444 if ((s->flags & M_DISPLAY))
1445 state_attach_puts (_("[-- End signature " "information --]\n\n"), s);
1447 gpgme_release (ctx);
1452 tattach = mutt_read_mime_header (fpout, 0);
1455 * Need to set the length of this body part.
1457 fstat (fileno (fpout), &info);
1458 tattach->length = info.st_size - tattach->offset;
1460 tattach->warnsig = anywarn;
1462 /* See if we need to recurse on this MIME part. */
1463 mutt_parse_part (fpout, tattach);
1469 /* Decrypt a PGP/MIME message in FPIN and B and return a new body and
1470 the stream in CUR and FPOUT. Returns 0 on success. */
1471 int crypt_pgp_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1473 char tempfile[_POSIX_PATH_MAX];
1475 BODY *first_part = b;
1478 first_part->goodsig = 0;
1479 first_part->warnsig = 0;
1481 if (!mutt_is_multipart_encrypted (b))
1484 if (!b->parts || !b->parts->next)
1491 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1493 mutt_perror (_("Can't create temporary file"));
1498 *cur = decrypt_part (b, &s, *fpout, 0, &is_signed);
1501 first_part->goodsig = 1;
1503 return *cur ? 0 : -1;
1507 /* Decrypt a S/MIME message in FPIN and B and return a new body and
1508 the stream in CUR and FPOUT. Returns 0 on success. */
1509 int crypt_smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b,
1512 char tempfile[_POSIX_PATH_MAX];
1516 long saved_b_offset;
1517 ssize_t saved_b_length;
1520 if (!mutt_is_application_smime (b))
1526 /* Decode the body - we need to pass binary CMS to the
1527 backend. The backend allows for Base64 encoded data but it does
1528 not allow for QP which I have seen in some messages. So better
1530 saved_b_type = b->type;
1531 saved_b_offset = b->offset;
1532 saved_b_length = b->length;
1535 fseeko (s.fpin, b->offset, 0);
1536 tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1538 mutt_perror (_("Can't create temporary file"));
1541 mutt_unlink (tempfile);
1544 mutt_decode_attachment (b, &s);
1546 b->length = ftello (s.fpout);
1553 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1555 mutt_perror (_("Can't create temporary file"));
1558 mutt_unlink (tempfile);
1560 *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
1562 (*cur)->goodsig = is_signed > 0;
1563 b->type = saved_b_type;
1564 b->length = saved_b_length;
1565 b->offset = saved_b_offset;
1568 if (*cur && !is_signed && !(*cur)->parts
1569 && mutt_is_application_smime (*cur)) {
1570 /* Assume that this is a opaque signed s/mime message. This is
1571 an ugly way of doing it but we have anyway a problem with
1572 arbitrary encoded S/MIME messages: Only the outer part may be
1573 encrypted. The entire mime parsing should be revamped,
1574 probably by keeping the temportary files so that we don't
1575 need to decrypt them all the time. Inner parts of an
1576 encrypted part can then pint into this file and tehre won't
1577 never be a need to decrypt again. This needs a partial
1578 rewrite of the MIME engine. */
1582 saved_b_type = bb->type;
1583 saved_b_offset = bb->offset;
1584 saved_b_length = bb->length;
1587 fseeko (s.fpin, bb->offset, 0);
1588 tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1590 mutt_perror (_("Can't create temporary file"));
1593 mutt_unlink (tempfile);
1596 mutt_decode_attachment (bb, &s);
1598 bb->length = ftello (s.fpout);
1606 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1608 mutt_perror (_("Can't create temporary file"));
1611 mutt_unlink (tempfile);
1613 tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
1615 tmp_b->goodsig = is_signed > 0;
1616 bb->type = saved_b_type;
1617 bb->length = saved_b_length;
1618 bb->offset = saved_b_offset;
1621 body_list_wipe(cur);
1624 return *cur ? 0 : -1;
1629 * Implementation of `pgp_check_traditional'.
1632 static int pgp_check_traditional_one_body (FILE * fp, BODY * b,
1635 char tempfile[_POSIX_PATH_MAX];
1636 char buf[HUGE_STRING];
1643 if (b->type != TYPETEXT)
1646 if (tagged_only && !b->tagged)
1649 tempfd = m_tempfd(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1650 if (mutt_decode_save_attachment (fp, b, tempfd, 0) != 0) {
1655 if ((tfp = fopen(tempfile, "r")) == NULL) {
1660 while (fgets (buf, sizeof (buf), tfp)) {
1661 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1662 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1664 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15))
1674 /* fix the content type */
1676 parameter_setval(&b->parameter, "format", "fixed");
1677 parameter_setval(&b->parameter, "x-action",
1678 enc ? "pgp-encrypted" : "pgp-signed");
1682 int crypt_pgp_check_traditional (FILE * fp, BODY * b, int tagged_only)
1687 for (; b; b = b->next) {
1688 if (is_multipart (b))
1689 rv = (crypt_pgp_check_traditional (fp, b->parts, tagged_only) || rv);
1690 else if (b->type == TYPETEXT) {
1691 if ((r = mutt_is_application_pgp (b)))
1694 rv = (pgp_check_traditional_one_body (fp, b, tagged_only) || rv);
1701 /* Implementation of `application_handler'. */
1704 Copy a clearsigned message, and strip the signature and PGP's
1707 XXX - charset handling: We assume that it is safe to do
1708 character set decoding first, dash decoding second here, while
1709 we do it the other way around in the main handler.
1711 (Note that we aren't worse than Outlook & Cie in this, and also
1712 note that we can successfully handle anything produced by any
1713 existing versions of mutt.) */
1715 static void copy_clearsigned (gpgme_data_t data, STATE * s, char *charset)
1717 char buf[HUGE_STRING];
1718 short complete, armor_header;
1723 fname = data_object_to_tempfile (data, &fp);
1729 fc = fgetconv_open (fp, charset, MCharset.charset, M_ICONV_HOOK_FROM);
1731 for (complete = 1, armor_header = 1;
1732 fgetconvs (buf, sizeof (buf), fc) != NULL;
1733 complete = strchr (buf, '\n') != NULL) {
1736 state_puts (buf, s);
1740 if (!m_strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
1750 state_puts (s->prefix, s);
1752 if (buf[0] == '-' && buf[1] == ' ')
1753 state_puts (buf + 2, s);
1755 state_puts (buf, s);
1758 fgetconv_close (&fc);
1763 /* Support for classic_application/pgp */
1764 int crypt_pgp_application_pgp_handler (BODY * m, STATE * s)
1766 int needpass = -1, pgp_keyblock = 0;
1770 off_t last_pos, offset;
1771 char buf[HUGE_STRING];
1772 FILE *pgpout = NULL;
1774 gpgme_error_t err = 0;
1775 gpgme_data_t armored_data = NULL;
1777 short maybe_goodsig = 1;
1778 short have_any_sigs = 0;
1780 char body_charset[STRING]; /* Only used for clearsigned messages. */
1782 /* For clearsigned messages we won't be able to get a character set
1783 but we know that this may only be text thus we assume Latin-1
1785 if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
1786 m_strcpy(body_charset, sizeof(body_charset), "iso-8859-1");
1788 fseeko (s->fpin, m->offset, 0);
1789 last_pos = m->offset;
1791 for (bytes = m->length; bytes > 0;) {
1792 if (fgets (buf, sizeof (buf), s->fpin) == NULL)
1795 offset = ftello (s->fpin);
1796 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1799 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1801 start_pos = last_pos;
1803 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1805 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15)) {
1809 else if (!option (OPTDONTHANDLEPGPKEYS) &&
1810 !m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
1815 /* XXX - we may wish to recode here */
1817 state_puts (s->prefix, s);
1818 state_puts (buf, s);
1822 have_any_sigs = (have_any_sigs || (clearsign && (s->flags & M_VERIFY)));
1824 /* Copy PGP material to an data container */
1825 armored_data = create_gpgme_data ();
1826 gpgme_data_write (armored_data, buf, m_strlen(buf));
1827 while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) {
1828 offset = ftello (s->fpin);
1829 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1832 gpgme_data_write (armored_data, buf, m_strlen(buf));
1834 if ((needpass && !m_strcmp("-----END PGP MESSAGE-----\n", buf))
1836 && (!m_strcmp("-----END PGP SIGNATURE-----\n", buf)
1837 || !m_strcmp("-----END PGP PUBLIC KEY BLOCK-----\n",
1842 /* Invoke PGP if needed */
1843 if (!clearsign || (s->flags & M_VERIFY)) {
1844 unsigned int sig_stat = 0;
1845 gpgme_data_t plaintext;
1848 plaintext = create_gpgme_data ();
1849 ctx = create_gpgme_context (0);
1852 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1854 err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
1855 if (gpg_err_code (err) == GPG_ERR_NO_DATA) {
1856 /* Decrypt verify can't handle signed only messages. */
1857 err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
1858 ? gpgme_error_from_errno (errno) : 0;
1859 /* Must release plaintext so that we supply an
1860 uninitialized object. */
1861 gpgme_data_release (plaintext);
1862 plaintext = create_gpgme_data ();
1863 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1870 snprintf (errbuf, sizeof (errbuf) - 1,
1871 _("Error: decryption/verification failed: %s\n"),
1872 gpgme_strerror (err));
1873 state_attach_puts (errbuf, s);
1875 else { /* Decryption/Verification succeeded */
1879 /* Check wether signatures have been verified. */
1880 gpgme_verify_result_t verify_result;
1882 verify_result = gpgme_op_verify_result (ctx);
1883 if (verify_result->signatures)
1889 if ((s->flags & M_DISPLAY) && sig_stat) {
1894 state_attach_puts (_("[-- Begin signature "
1895 "information --]\n"), s);
1898 (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1907 state_attach_puts (_("[-- End signature "
1908 "information --]\n\n"), s);
1911 tmpfname = data_object_to_tempfile (plaintext, &pgpout);
1914 state_attach_puts (_("Error: copy data failed\n"), s);
1918 p_delete(&tmpfname);
1921 gpgme_release (ctx);
1925 * Now, copy cleartext to the screen. NOTE - we expect that PGP
1926 * outputs utf-8 cleartext. This may not always be true, but it
1927 * seems to be a reasonable guess.
1930 if (s->flags & M_DISPLAY) {
1932 state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
1933 else if (pgp_keyblock)
1934 state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s);
1936 state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s);
1940 copy_clearsigned (armored_data, s, body_charset);
1947 fc = fgetconv_open (pgpout, "utf-8", MCharset.charset, 0);
1948 while ((c = fgetconv (fc)) != EOF) {
1950 if (c == '\n' && s->prefix)
1951 state_puts (s->prefix, s);
1953 fgetconv_close (&fc);
1956 if (s->flags & M_DISPLAY) {
1957 state_putc ('\n', s);
1959 state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
1960 else if (pgp_keyblock)
1961 state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
1963 state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
1971 /* XXX - we may wish to recode here */
1973 state_puts (s->prefix, s);
1974 state_puts (buf, s);
1978 m->goodsig = (maybe_goodsig && have_any_sigs);
1980 if (needpass == -1) {
1981 state_attach_puts (_("[-- Error: could not find beginning"
1982 " of PGP message! --]\n\n"), s);
1988 /* Implementation of `encrypted_handler'. */
1990 /* MIME handler for pgp/mime encrypted messages. */
1991 int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
1993 char tempfile[_POSIX_PATH_MAX];
1996 BODY *orig_body = a;
2001 if (!a || a->type != TYPEAPPLICATION || !a->subtype
2002 || ascii_strcasecmp ("pgp-encrypted", a->subtype)
2003 || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
2004 || ascii_strcasecmp ("octet-stream", a->next->subtype)) {
2005 if (s->flags & M_DISPLAY)
2006 state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
2011 /* Move forward to the application/pgp-encrypted body. */
2014 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2016 if (s->flags & M_DISPLAY)
2017 state_attach_puts (_("[-- Error: could not create temporary file! "
2022 tattach = decrypt_part (a, s, fpout, 0, &is_signed);
2024 tattach->goodsig = is_signed > 0;
2026 if (s->flags & M_DISPLAY)
2027 state_attach_puts (is_signed ?
2029 ("[-- The following data is PGP/MIME signed and encrypted --]\n\n") :
2030 _("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
2033 FILE *savefp = s->fpin;
2036 rc = mutt_body_handler (tattach, s);
2041 * if a multipart/signed is the _only_ sub-part of a
2042 * multipart/encrypted, cache signature verification
2045 if (mutt_is_multipart_signed (tattach) && !tattach->next)
2046 orig_body->goodsig |= tattach->goodsig;
2048 if (s->flags & M_DISPLAY) {
2049 state_puts ("\n", s);
2050 state_attach_puts (is_signed ?
2052 ("[-- End of PGP/MIME signed and encrypted data --]\n")
2053 : _("[-- End of PGP/MIME encrypted data --]\n"), s);
2056 body_list_wipe(&tattach);
2060 mutt_unlink (tempfile);
2064 /* Support for application/smime */
2065 int crypt_smime_application_smime_handler (BODY * a, STATE * s)
2067 char tempfile[_POSIX_PATH_MAX];
2074 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2076 if (s->flags & M_DISPLAY)
2077 state_attach_puts (_("[-- Error: could not create temporary file! "
2082 tattach = decrypt_part (a, s, fpout, 1, &is_signed);
2084 tattach->goodsig = is_signed > 0;
2086 if (s->flags & M_DISPLAY)
2087 state_attach_puts (is_signed ?
2088 _("[-- The following data is S/MIME signed --]\n\n") :
2089 _("[-- The following data is S/MIME encrypted --]\n\n"), s);
2092 FILE *savefp = s->fpin;
2095 rc = mutt_body_handler (tattach, s);
2100 * if a multipart/signed is the _only_ sub-part of a
2101 * multipart/encrypted, cache signature verification
2104 if (mutt_is_multipart_signed (tattach) && !tattach->next) {
2105 if (!(a->goodsig = tattach->goodsig))
2106 a->warnsig = tattach->warnsig;
2108 else if (tattach->goodsig) {
2110 a->warnsig = tattach->warnsig;
2113 if (s->flags & M_DISPLAY) {
2114 state_puts ("\n", s);
2115 state_attach_puts (is_signed ?
2116 _("[-- End of S/MIME signed data --]\n") :
2117 _("[-- End of S/MIME encrypted data --]\n"), s);
2120 body_list_wipe(&tattach);
2124 mutt_unlink (tempfile);
2130 * Format an entry on the CRYPT key selection menu.
2133 * %k key id %K key id of the principal key
2135 * %a algorithm %A algorithm of the princ. key
2136 * %l length %L length of the princ. key
2137 * %f flags %F flags of the princ. key
2138 * %c capabilities %C capabilities of the princ. key
2139 * %t trust/validity of the key-uid association
2141 * %[...] date of key using strftime(3)
2145 crypt_entry_fmt (char *dest, ssize_t destlen, char op,
2146 const char *src, const char *prefix,
2147 const char *ifstr, const char *elstr,
2148 anytype data, format_flag flags)
2151 crypt_entry_t *entry;
2154 int optional = (flags & M_FORMAT_OPTIONAL);
2155 const char *s = NULL;
2161 /* if (isupper ((unsigned char) op)) */
2164 kflags = (key->flags /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
2167 switch (ascii_tolower (op)) {
2171 char buf2[STRING], *p;
2187 while (len > 0 && *cp != ']') {
2196 break; /* not enough space */
2206 if (do_locales && Locale)
2207 setlocale (LC_TIME, Locale);
2212 if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
2213 tt = key->kobj->subkeys->timestamp;
2215 tm = localtime (&tt);
2217 strftime (buf2, sizeof (buf2), dest, tm);
2220 setlocale (LC_TIME, "C");
2222 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2223 snprintf (dest, destlen, fmt, buf2);
2230 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
2231 snprintf (dest, destlen, fmt, entry->num);
2236 /* fixme: we need a way to distinguish between main and subkeys.
2237 Store the idx in entry? */
2238 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2239 snprintf (dest, destlen, fmt, crypt_keyid (key));
2244 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2245 snprintf (dest, destlen, fmt, key->uid);
2250 snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
2251 if (key->kobj->subkeys)
2252 s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
2255 snprintf (dest, destlen, fmt, s);
2260 snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
2261 if (key->kobj->subkeys)
2262 val = key->kobj->subkeys->length;
2265 snprintf (dest, destlen, fmt, val);
2270 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2271 snprintf (dest, destlen, fmt, crypt_flags (kflags));
2273 else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
2278 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2279 snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
2281 else if (!(kflags & (KEYFLAG_ABILITIES)))
2285 if ((kflags & KEYFLAG_ISX509))
2288 gpgme_user_id_t uid = NULL;
2291 for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
2292 i++, uid = uid->next);
2294 switch (uid->validity) {
2295 case GPGME_VALIDITY_UNDEFINED:
2298 case GPGME_VALIDITY_NEVER:
2301 case GPGME_VALIDITY_MARGINAL:
2304 case GPGME_VALIDITY_FULL:
2307 case GPGME_VALIDITY_ULTIMATE:
2310 case GPGME_VALIDITY_UNKNOWN:
2316 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2317 snprintf (dest, destlen, fmt, s ? *s : 'B');
2320 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2321 snprintf (dest, destlen, fmt,
2322 gpgme_get_protocol_name (key->kobj->protocol));
2329 if (flags & M_FORMAT_OPTIONAL)
2330 m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
2331 mutt_attach_fmt, data, 0);
2335 /* Used by the display fucntion to format a line. */
2336 static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
2338 crypt_key_t **key_table = (crypt_key_t **) menu->data;
2339 crypt_entry_t entry;
2341 entry.key = key_table[num];
2342 entry.num = num + 1;
2344 m_strformat(s, l, COLS - SW, PgpEntryFormat, crypt_entry_fmt, &entry,
2345 option(OPTARROWCURSOR) ? M_FORMAT_ARROWCURSOR : 0);
2348 /* Compare two addresses and the keyid to be used for sorting. */
2349 static int _crypt_compare_address (const void *a, const void *b)
2351 crypt_key_t **s = (crypt_key_t **) a;
2352 crypt_key_t **t = (crypt_key_t **) b;
2355 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2358 return m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t)) > 0;
2361 static int crypt_compare_address (const void *a, const void *b)
2363 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_address (a, b)
2364 : _crypt_compare_address (a, b));
2368 /* Compare two key IDs and the addresses to be used for sorting. */
2369 static int _crypt_compare_keyid (const void *a, const void *b)
2371 crypt_key_t **s = (crypt_key_t **) a;
2372 crypt_key_t **t = (crypt_key_t **) b;
2375 if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
2378 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2381 static int crypt_compare_keyid (const void *a, const void *b)
2383 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
2384 : _crypt_compare_keyid (a, b));
2387 /* Compare 2 creation dates and the addresses. For sorting. */
2388 static int _crypt_compare_date (const void *a, const void *b)
2390 crypt_key_t **s = (crypt_key_t **) a;
2391 crypt_key_t **t = (crypt_key_t **) b;
2392 unsigned long ts = 0, tt = 0;
2394 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2395 ts = (*s)->kobj->subkeys->timestamp;
2396 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2397 tt = (*t)->kobj->subkeys->timestamp;
2404 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2407 static int crypt_compare_date (const void *a, const void *b)
2409 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
2410 : _crypt_compare_date (a, b));
2413 /* Compare two trust values, the key length, the creation dates. the
2414 addresses and the key IDs. For sorting. */
2415 static int _crypt_compare_trust (const void *a, const void *b)
2417 crypt_key_t **s = (crypt_key_t **) a;
2418 crypt_key_t **t = (crypt_key_t **) b;
2419 unsigned long ts = 0, tt = 0;
2422 if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
2423 - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
2426 if ((*s)->kobj->uids)
2427 ts = (*s)->kobj->uids->validity;
2428 if ((*t)->kobj->uids)
2429 tt = (*t)->kobj->uids->validity;
2430 if ((r = (tt - ts)))
2433 if ((*s)->kobj->subkeys)
2434 ts = (*s)->kobj->subkeys->length;
2435 if ((*t)->kobj->subkeys)
2436 tt = (*t)->kobj->subkeys->length;
2440 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2441 ts = (*s)->kobj->subkeys->timestamp;
2442 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2443 tt = (*t)->kobj->subkeys->timestamp;
2449 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2451 return (m_strcasecmp(crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
2454 static int crypt_compare_trust (const void *a, const void *b)
2456 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
2457 : _crypt_compare_trust (a, b));
2460 /* Print the X.500 Distinguished Name part KEY from the array of parts
2462 static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
2466 for (; dn->key; dn++) {
2467 if (!m_strcmp(dn->key, key)) {
2470 print_utf8 (fp, dn->value, m_strlen(dn->value));
2477 /* Print all parts of a DN in a standard sequence. */
2478 static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
2480 const char *stdpart[] = {
2481 "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
2483 int any = 0, any2 = 0, i;
2485 for (i = 0; stdpart[i]; i++) {
2488 any = print_dn_part (fp, dn, stdpart[i]);
2490 /* now print the rest without any specific ordering */
2491 for (; dn->key; dn++) {
2492 for (i = 0; stdpart[i]; i++) {
2493 if (!m_strcmp(dn->key, stdpart[i]))
2501 any = print_dn_part (fp, dn, dn->key);
2510 /* Parse an RDN; this is a helper to parse_dn(). */
2511 static const unsigned char *parse_dn_part (struct dn_array_s *array,
2512 const unsigned char *string)
2514 const unsigned char *s, *s1;
2518 /* parse attributeType */
2519 for (s = string + 1; *s && *s != '='; s++);
2521 return NULL; /* error */
2524 return NULL; /* empty key */
2525 array->key = p_dupstr(string, n );
2526 p = (unsigned char *) array->key;
2529 if (*string == '#') { /* hexstring */
2531 for (s = string; hexval(*s) >= 0; s++)
2535 return NULL; /* empty or odd number of digits */
2537 p = p_new(unsigned char, n + 1);
2538 array->value = (char *) p;
2539 for (s1 = string; n; s1 += 2, n--)
2540 *p++ = (hexval(*s1) << 8) | hexval(*s1);
2543 else { /* regular v3 quoted string */
2544 for (n = 0, s = string; *s; s++) {
2545 if (*s == '\\') { /* pair */
2547 if (*s == ',' || *s == '=' || *s == '+'
2548 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
2549 || *s == '\\' || *s == '\"' || *s == ' ')
2551 else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
2556 return NULL; /* invalid escape sequence */
2558 else if (*s == '\"')
2559 return NULL; /* invalid encoding */
2560 else if (*s == ',' || *s == '=' || *s == '+'
2561 || *s == '<' || *s == '>' || *s == '#' || *s == ';')
2567 p = p_new(unsigned char, n + 1);
2568 array->value = (char *) p;
2569 for (s = string; n; s++, n--) {
2572 if (hexval(*s) >= 0) {
2573 *p++ = (hexval(*s) << 8) | hexval(*s + 1);
2588 /* Parse a DN and return an array-ized one. This is not a validating
2589 parser and it does not support any old-stylish syntax; gpgme is
2590 expected to return only rfc2253 compatible strings. */
2591 static struct dn_array_s *parse_dn (const unsigned char *string)
2593 struct dn_array_s *array;
2594 ssize_t arrayidx, arraysize;
2597 arraysize = 7; /* C,ST,L,O,OU,CN,email */
2598 array = p_new(struct dn_array_s, arraysize + 1);
2601 while (*string == ' ')
2605 if (arrayidx >= arraysize) { /* mutt lacks a real safe_realoc - so we need to copy */
2606 struct dn_array_s *a2;
2609 a2 = p_new(struct dn_array_s, arraysize + 1);
2610 for (i = 0; i < arrayidx; i++) {
2611 a2[i].key = array[i].key;
2612 a2[i].value = array[i].value;
2617 array[arrayidx].key = NULL;
2618 array[arrayidx].value = NULL;
2619 string = parse_dn_part (array + arrayidx, string);
2623 while (*string == ' ')
2625 if (*string && *string != ',' && *string != ';' && *string != '+')
2626 goto failure; /* invalid delimiter */
2630 array[arrayidx].key = NULL;
2631 array[arrayidx].value = NULL;
2635 for (i = 0; i < arrayidx; i++) {
2636 p_delete(&array[i].key);
2637 p_delete(&array[i].value);
2644 /* Print a nice representation of the USERID and make sure it is
2645 displayed in a proper way, which does mean to reorder some parts
2646 for S/MIME's DNs. USERID is a string as returned by the gpgme key
2647 functions. It is utf-8 encoded. */
2648 static void parse_and_print_user_id (FILE * fp, const char *userid)
2653 if (*userid == '<') {
2654 s = strchr (userid + 1, '>');
2656 print_utf8 (fp, userid + 1, s - userid - 1);
2658 else if (*userid == '(')
2659 fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
2660 else if (!digit_or_letter ((const unsigned char *) userid))
2661 fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
2663 struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
2666 fputs (_("[Can't display this user ID (invalid DN)]"), fp);
2668 print_dn_parts (fp, dn);
2669 for (i = 0; dn[i].key; i++) {
2670 p_delete(&dn[i].key);
2671 p_delete(&dn[i].value);
2679 KEY_CAP_CAN_ENCRYPT,
2684 static unsigned int key_check_cap (gpgme_key_t key, key_cap_t cap)
2686 gpgme_subkey_t subkey = NULL;
2687 unsigned int ret = 0;
2690 case KEY_CAP_CAN_ENCRYPT:
2691 if (!(ret = key->can_encrypt))
2692 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2693 if ((ret = subkey->can_encrypt))
2696 case KEY_CAP_CAN_SIGN:
2697 if (!(ret = key->can_sign))
2698 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2699 if ((ret = subkey->can_sign))
2702 case KEY_CAP_CAN_CERTIFY:
2703 if (!(ret = key->can_certify))
2704 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2705 if ((ret = subkey->can_certify))
2714 /* Print verbose information about a key or certificate to FP. */
2715 static void print_key_info (gpgme_key_t key, FILE * fp)
2718 const char *s = NULL, *s2 = NULL;
2721 char shortbuf[STRING];
2722 unsigned long aval = 0;
2726 gpgme_user_id_t uid = NULL;
2729 setlocale (LC_TIME, Locale);
2731 is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
2733 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2738 fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
2741 fputs (_("[Invalid]"), fp);
2745 print_utf8 (fp, s, m_strlen(s));
2747 parse_and_print_user_id (fp, s);
2751 if (key->subkeys && (key->subkeys->timestamp > 0)) {
2752 tt = key->subkeys->timestamp;
2754 tm = localtime (&tt);
2755 #ifdef HAVE_LANGINFO_D_T_FMT
2756 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2758 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2760 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2763 if (key->subkeys && (key->subkeys->expires > 0)) {
2764 tt = key->subkeys->expires;
2766 tm = localtime (&tt);
2767 #ifdef HAVE_LANGINFO_D_T_FMT
2768 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2770 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2772 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2776 s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
2780 s2 = is_pgp ? "PGP" : "X.509";
2783 aval = key->subkeys->length;
2785 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
2787 fprintf (fp, _("Key Usage .: "));
2790 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
2791 fprintf (fp, "%s%s", delim, _("encryption"));
2794 if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
2795 fprintf (fp, "%s%s", delim, _("signing"));
2798 if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
2799 fprintf (fp, "%s%s", delim, _("certification"));
2805 s = key->subkeys->fpr;
2806 fputs (_("Fingerprint: "), fp);
2807 if (is_pgp && m_strlen(s) == 40) {
2808 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
2813 putc (is_pgp ? ' ' : ':', fp);
2814 if (is_pgp && i == 4)
2819 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
2822 putc (is_pgp ? ' ' : ':', fp);
2823 if (is_pgp && i == 7)
2827 fprintf (fp, "%s\n", s);
2830 if (key->issuer_serial) {
2831 s = key->issuer_serial;
2833 fprintf (fp, _("Serial-No .: 0x%s\n"), s);
2836 if (key->issuer_name) {
2837 s = key->issuer_name;
2839 fprintf (fp, _("Issued By .: "));
2840 parse_and_print_user_id (fp, s);
2845 /* For PGP we list all subkeys. */
2847 gpgme_subkey_t subkey = NULL;
2849 for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
2853 if (m_strlen(s) == 16)
2854 s += 8; /* display only the short keyID */
2855 fprintf (fp, _("Subkey ....: 0x%s"), s);
2856 if (subkey->revoked) {
2858 fputs (_("[Revoked]"), fp);
2860 if (subkey->invalid) {
2862 fputs (_("[Invalid]"), fp);
2864 if (subkey->expired) {
2866 fputs (_("[Expired]"), fp);
2868 if (subkey->disabled) {
2870 fputs (_("[Disabled]"), fp);
2874 if (subkey->timestamp > 0) {
2875 tt = subkey->timestamp;
2877 tm = localtime (&tt);
2878 #ifdef HAVE_LANGINFO_D_T_FMT
2879 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2881 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2883 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2886 if (subkey->expires > 0) {
2887 tt = subkey->expires;
2889 tm = localtime (&tt);
2890 #ifdef HAVE_LANGINFO_D_T_FMT
2891 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2893 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2895 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2899 s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
2904 aval = subkey->length;
2908 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
2910 fprintf (fp, _("Key Usage .: "));
2913 if (subkey->can_encrypt) {
2914 fprintf (fp, "%s%s", delim, _("encryption"));
2917 if (subkey->can_sign) {
2918 fprintf (fp, "%s%s", delim, _("signing"));
2921 if (subkey->can_certify) {
2922 fprintf (fp, "%s%s", delim, _("certification"));
2930 setlocale (LC_TIME, "C");
2934 /* Show detailed information about the selected key */
2935 static void verify_key (crypt_key_t * key)
2938 char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
2940 gpgme_ctx_t listctx = NULL;
2942 gpgme_key_t k = NULL;
2945 fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2947 mutt_perror (_("Can't create temporary file"));
2950 mutt_message _("Collecting data...");
2952 print_key_info (key->kobj, fp);
2954 err = gpgme_new (&listctx);
2956 fprintf (fp, "Internal error: can't create gpgme context: %s\n",
2957 gpgme_strerror (err));
2960 if ((key->flags & KEYFLAG_ISX509))
2961 gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
2965 while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
2967 err = gpgme_op_keylist_start (listctx, s, 0);
2968 gpgme_key_release (k);
2971 err = gpgme_op_keylist_next (listctx, &k);
2973 fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
2976 gpgme_op_keylist_end (listctx);
2978 print_key_info (k, fp);
2981 fputs (_("Error: certification chain to long - stopping here\n"), fp);
2987 gpgme_key_release (k);
2988 gpgme_release (listctx);
2990 mutt_clear_error ();
2991 snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
2992 mutt_do_pager (cmd, tempfile, 0, NULL);
2995 /* Implementation of `findkeys'. */
2997 /* Convert string_list_t into a pattern string suitable to be passed to GPGME.
2998 We need to convert spaces in an item into a '+' and '%' into
3000 static char *list_to_pattern (string_list_t * list)
3008 for (l = list; l; l = l->next) {
3009 for (s = l->data; *s; s++) {
3014 n++; /* delimiter or end of string */
3016 n++; /* make sure to allocate at least one byte */
3017 pattern = p = p_new(char, n);
3018 for (l = list; l; l = l->next) {
3023 for (s = l->data; *s; s++) {
3029 else if (*s == '+') {
3045 /* Return a list of keys which are candidates for the selection.
3046 Select by looking at the HINTS list. */
3047 static crypt_key_t *get_candidates (string_list_t * hints, unsigned int app,
3050 crypt_key_t *db, *k, **kend;
3056 gpgme_user_id_t uid = NULL;
3058 pattern = list_to_pattern (hints);
3062 err = gpgme_new (&ctx);
3064 mutt_error (_("gpgme_new failed: %s"), gpgme_strerror (err));
3072 if ((app & APPLICATION_PGP)) {
3073 /* Its all a mess. That old GPGME expects different things
3074 depending on the protocol. For gpg we don' t need percent
3075 escaped pappert but simple strings passed in an array to the
3076 keylist_ext_start function. */
3081 for (l = hints, n = 0; l; l = l->next) {
3082 if (l->data && *l->data)
3088 patarr = p_new(char *, n + 1);
3089 for (l = hints, n = 0; l; l = l->next) {
3090 if (l->data && *l->data)
3091 patarr[n++] = m_strdup(l->data);
3094 err = gpgme_op_keylist_ext_start (ctx, (const char **) patarr, secret, 0);
3095 for (n = 0; patarr[n]; n++)
3096 p_delete(&patarr[n]);
3099 mutt_error (_("gpgme_op_keylist_start failed: %s"), gpgme_strerror (err));
3100 gpgme_release (ctx);
3105 while (!(err = gpgme_op_keylist_next (ctx, &key))) {
3106 unsigned int flags = 0;
3108 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
3109 flags |= KEYFLAG_CANENCRYPT;
3110 if (key_check_cap (key, KEY_CAP_CAN_SIGN))
3111 flags |= KEYFLAG_CANSIGN;
3113 #if 0 /* DISABLED code */
3115 /* Bug in gpg. Capabilities are not listed for secret
3116 keys. Try to deduce them from the algorithm. */
3118 switch (key->subkeys[0].pubkey_algo) {
3120 flags |= KEYFLAG_CANENCRYPT;
3121 flags |= KEYFLAG_CANSIGN;
3123 case GPGME_PK_ELG_E:
3124 flags |= KEYFLAG_CANENCRYPT;
3127 flags |= KEYFLAG_CANSIGN;
3131 #endif /* DISABLED code */
3133 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
3134 k = p_new(crypt_key_t, 1);
3143 if (gpg_err_code (err) != GPG_ERR_EOF)
3144 mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err));
3145 gpgme_op_keylist_end (ctx);
3150 if ((app & APPLICATION_SMIME)) {
3151 /* and now look for x509 certificates */
3152 gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
3153 err = gpgme_op_keylist_start (ctx, pattern, 0);
3155 mutt_error (_("gpgme_op_keylist_start failed: %s"), gpgme_strerror (err));
3156 gpgme_release (ctx);
3161 while (!(err = gpgme_op_keylist_next (ctx, &key))) {
3162 unsigned int flags = KEYFLAG_ISX509;
3164 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
3165 flags |= KEYFLAG_CANENCRYPT;
3166 if (key_check_cap (key, KEY_CAP_CAN_SIGN))
3167 flags |= KEYFLAG_CANSIGN;
3169 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
3170 k = p_new(crypt_key_t, 1);
3179 if (gpg_err_code (err) != GPG_ERR_EOF)
3180 mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err));
3181 gpgme_op_keylist_end (ctx);
3184 gpgme_release (ctx);
3189 /* Add the string STR to the list HINTS. This list is later used to
3191 static string_list_t *crypt_add_string_to_hints (string_list_t * hints, const char *str)
3196 if ((scratch = m_strdup(str)) == NULL)
3199 for (t = strtok (scratch, " ,.:\"()<>\n"); t;
3200 t = strtok (NULL, " ,.:\"()<>\n")) {
3201 if (m_strlen(t) > 3)
3202 hints = mutt_add_list(hints, t);
3209 /* Display a menu to select a key from the array KEYS. FORCED_VALID
3210 will be set to true on return if the user did override the the
3212 static crypt_key_t *crypt_select_key (crypt_key_t * keys,
3213 address_t * p, const char *s,
3214 unsigned int app, int *forced_valid)
3217 crypt_key_t **key_table;
3220 char helpstr[STRING], buf[LONG_STRING];
3222 int (*f) (const void *, const void *);
3223 int menu_to_use = 0;
3228 /* build the key table */
3231 for (k = keys; k; k = k->next) {
3232 if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
3239 p_realloc(&key_table, keymax);
3245 if (!i && unusable) {
3246 mutt_error _("All matching keys are marked expired/revoked.");
3252 switch (PgpSortKeys & SORT_MASK) {
3254 f = crypt_compare_date;
3257 f = crypt_compare_keyid;
3260 f = crypt_compare_address;
3264 f = crypt_compare_trust;
3267 qsort (key_table, i, sizeof (crypt_key_t *), f);
3269 if (app & APPLICATION_PGP)
3270 menu_to_use = MENU_KEY_SELECT_PGP;
3271 else if (app & APPLICATION_SMIME)
3272 menu_to_use = MENU_KEY_SELECT_SMIME;
3275 mutt_make_help (buf, sizeof (buf), _("Exit "), menu_to_use, OP_EXIT);
3276 m_strcat(helpstr, sizeof(helpstr), buf);
3277 mutt_make_help (buf, sizeof (buf), _("Select "), menu_to_use,
3278 OP_GENERIC_SELECT_ENTRY);
3279 m_strcat(helpstr, sizeof(helpstr), buf);
3280 mutt_make_help (buf, sizeof (buf), _("Check key "),
3281 menu_to_use, OP_VERIFY_KEY);
3282 m_strcat(helpstr, sizeof(helpstr), buf);
3283 mutt_make_help (buf, sizeof (buf), _("Help"), menu_to_use, OP_HELP);
3284 m_strcat(helpstr, sizeof(helpstr), buf);
3286 menu = mutt_new_menu ();
3288 menu->make_entry = crypt_entry;
3289 menu->menu = menu_to_use;
3290 menu->help = helpstr;
3291 menu->data = key_table;
3296 if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
3297 ts = _("PGP and S/MIME keys matching");
3298 else if ((app & APPLICATION_PGP))
3299 ts = _("PGP keys matching");
3300 else if ((app & APPLICATION_SMIME))
3301 ts = _("S/MIME keys matching");
3303 ts = _("keys matching");
3306 snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
3308 snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
3312 mutt_clear_error ();
3316 switch (mutt_menuLoop (menu)) {
3318 verify_key (key_table[menu->current]);
3319 menu->redraw = REDRAW_FULL;
3323 mutt_message ("%s", key_table[menu->current]->uid);
3326 case OP_GENERIC_SELECT_ENTRY:
3327 /* FIXME make error reporting more verbose - this should be
3328 easy because gpgme provides more information */
3329 if (option (OPTPGPCHECKTRUST)) {
3330 if (!crypt_key_is_valid (key_table[menu->current])) {
3331 mutt_error _("This key can't be used: "
3332 "expired/disabled/revoked.");
3337 if (option (OPTPGPCHECKTRUST) &&
3338 (!crypt_id_is_valid (key_table[menu->current])
3339 || !crypt_id_is_strong (key_table[menu->current]))) {
3341 char buff[LONG_STRING];
3343 if (key_table[menu->current]->flags & KEYFLAG_CANTUSE)
3344 s = N_("ID is expired/disabled/revoked.");
3346 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
3347 gpgme_user_id_t uid = NULL;
3352 uid = key_table[menu->current]->kobj->uids;
3353 for (j = 0; (j < key_table[menu->current]->idx) && uid;
3354 j++, uid = uid->next);
3356 val = uid->validity;
3359 case GPGME_VALIDITY_UNKNOWN:
3360 case GPGME_VALIDITY_UNDEFINED:
3361 warn_s = N_("ID has undefined validity.");
3363 case GPGME_VALIDITY_NEVER:
3364 warn_s = N_("ID is not valid.");
3366 case GPGME_VALIDITY_MARGINAL:
3367 warn_s = N_("ID is only marginally valid.");
3369 case GPGME_VALIDITY_FULL:
3370 case GPGME_VALIDITY_ULTIMATE:
3374 snprintf (buff, sizeof (buff),
3375 _("%s Do you really want to use the key?"), _(warn_s));
3377 if (mutt_yesorno (buff, 0) != 1) {
3378 mutt_clear_error ();
3385 k = crypt_copy_key (key_table[menu->current]);
3396 mutt_menuDestroy (&menu);
3397 p_delete(&key_table);
3399 set_option (OPTNEEDREDRAW);
3404 static crypt_key_t *crypt_getkeybyaddr (address_t * a, short abilities,
3405 unsigned int app, int *forced_valid)
3408 string_list_t *hints = NULL;
3413 int this_key_has_strong;
3414 int this_key_has_weak;
3415 int this_key_has_invalid;
3418 crypt_key_t *keys, *k;
3419 crypt_key_t *the_valid_key = NULL;
3420 crypt_key_t *matches = NULL;
3421 crypt_key_t **matches_endp = &matches;
3425 if (a && a->mailbox)
3426 hints = crypt_add_string_to_hints (hints, a->mailbox);
3427 if (a && a->personal)
3428 hints = crypt_add_string_to_hints (hints, a->personal);
3430 mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
3431 keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
3433 string_list_wipe(&hints);
3438 for (k = keys; k; k = k->next) {
3439 if (abilities && !(k->flags & abilities)) {
3443 this_key_has_weak = 0; /* weak but valid match */
3444 this_key_has_invalid = 0; /* invalid match */
3445 this_key_has_strong = 0; /* strong and valid match */
3446 match = 0; /* any match */
3448 r = rfc822_parse_adrlist (NULL, k->uid);
3449 for (p = r; p; p = p->next) {
3450 int validity = crypt_id_matches_addr (a, p, k);
3452 if (validity & CRYPT_KV_MATCH) /* something matches */
3455 /* is this key a strong candidate? */
3456 if ((validity & CRYPT_KV_VALID)
3457 && (validity & CRYPT_KV_STRONGID)
3458 && (validity & CRYPT_KV_ADDR)) {
3459 if (the_valid_key && the_valid_key != k)
3462 this_key_has_strong = 1;
3464 else if ((validity & CRYPT_KV_MATCH)
3465 && !(validity & CRYPT_KV_VALID))
3466 this_key_has_invalid = 1;
3467 else if ((validity & CRYPT_KV_MATCH)
3468 && (!(validity & CRYPT_KV_STRONGID)
3469 || !(validity & CRYPT_KV_ADDR)))
3470 this_key_has_weak = 1;
3472 address_list_wipe(&r);
3477 if (!this_key_has_strong && this_key_has_invalid)
3479 if (!this_key_has_strong && this_key_has_weak)
3482 *matches_endp = tmp = crypt_copy_key (k);
3483 matches_endp = &tmp->next;
3484 the_valid_key = tmp;
3488 crypt_free_key (&keys);
3491 if (the_valid_key && !multi && !weak
3492 && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
3494 * There was precisely one strong match on a valid ID, there
3495 * were no valid keys with weak matches, and we aren't
3496 * interested in seeing invalid keys.
3498 * Proceed without asking the user.
3500 k = crypt_copy_key (the_valid_key);
3504 * Else: Ask the user.
3506 k = crypt_select_key (matches, a, NULL, app, forced_valid);
3508 crypt_free_key (&matches);
3517 static crypt_key_t *crypt_getkeybystr (const char *p, short abilities,
3518 unsigned int app, int *forced_valid)
3520 string_list_t *hints = NULL;
3522 crypt_key_t *matches = NULL;
3523 crypt_key_t **matches_endp = &matches;
3527 mutt_message (_("Looking for keys matching \"%s\"..."), p);
3531 hints = crypt_add_string_to_hints (hints, p);
3532 keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
3533 string_list_wipe(&hints);
3538 for (k = keys; k; k = k->next) {
3539 if (abilities && !(k->flags & abilities))
3544 if (!*p || !m_strcasecmp(p, crypt_keyid (k))
3545 || (!m_strncasecmp(p, "0x", 2)
3546 && !m_strcasecmp(p + 2, crypt_keyid (k)))
3547 || (option (OPTPGPLONGIDS)
3548 && !m_strncasecmp(p, "0x", 2)
3549 && !m_strcasecmp(p + 2, crypt_keyid (k) + 8))
3550 || m_stristr(k->uid, p)) {
3553 *matches_endp = tmp = crypt_copy_key (k);
3554 matches_endp = &tmp->next;
3558 crypt_free_key (&keys);
3561 k = crypt_select_key (matches, NULL, p, app, forced_valid);
3562 crypt_free_key (&matches);
3569 /* Display TAG as a prompt to ask for a key. If WHATFOR is not null
3570 use it as default and store it under that label as the next
3571 default. ABILITIES describe the required key abilities (sign,
3572 encrypt) and APP the type of the requested key; ether S/MIME or
3573 PGP. Return a copy of the key or NULL if not found. */
3574 static crypt_key_t *crypt_ask_for_key (char *tag,
3577 unsigned int app, int *forced_valid)
3581 struct crypt_cache *l = NULL;
3585 forced_valid = &dummy;
3587 mutt_clear_error ();
3593 for (l = id_defaults; l; l = l->next)
3594 if (!m_strcasecmp(whatfor, l->what)) {
3595 m_strcpy(resp, sizeof(resp), NONULL(l->dflt));
3603 if (mutt_get_field (tag, resp, sizeof (resp), M_CLEAR) != 0)
3608 m_strreplace(&l->dflt, resp);
3610 l = p_new(struct crypt_cache, 1);
3611 l->next = id_defaults;
3613 l->what = m_strdup(whatfor);
3614 l->dflt = m_strdup(resp);
3618 if ((key = crypt_getkeybystr (resp, abilities, app, forced_valid)))
3626 /* This routine attempts to find the keyids of the recipients of a
3627 message. It returns NULL if any of the keys can not be found. */
3628 static char *find_keys (address_t * to, address_t * cc, address_t * bcc,
3631 char *keylist = NULL, *t;
3633 ssize_t keylist_size = 0;
3634 ssize_t keylist_used = 0;
3635 address_t *tmp = NULL, *addr = NULL;
3636 address_t **last = &tmp;
3639 crypt_key_t *k_info, *key;
3640 const char *fqdn = mutt_fqdn (1);
3643 *r_application = APPLICATION_PGP | APPLICATION_SMIME;
3646 for (i = 0; i < 3; i++) {
3661 *last = address_list_dup (p);
3663 last = &((*last)->next);
3666 rfc822_qualify(tmp, fqdn);
3667 address_list_uniq(tmp);
3669 for (p = tmp; p; p = p->next) {
3670 char buf[LONG_STRING];
3671 int forced_valid = 0;
3676 if ((keyID = mutt_crypt_hook (p)) != NULL) {
3679 snprintf (buf, sizeof (buf), _("Use keyID = \"%s\" for %s?"),
3681 if ((r = mutt_yesorno (buf, M_YES)) == M_YES) {
3682 /* check for e-mail address */
3683 if ((t = strchr (keyID, '@')) &&
3684 (addr = rfc822_parse_adrlist (NULL, keyID))) {
3685 rfc822_qualify(addr, fqdn);
3689 k_info = crypt_getkeybystr (keyID, KEYFLAG_CANENCRYPT,
3690 app, &forced_valid);
3695 address_list_wipe(&tmp);
3696 address_list_wipe(&addr);
3702 && (k_info = crypt_getkeybyaddr (q, KEYFLAG_CANENCRYPT,
3703 app, &forced_valid)) == NULL) {
3704 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
3706 if ((key = crypt_ask_for_key (buf, q->mailbox, KEYFLAG_CANENCRYPT,
3708 &forced_valid)) == NULL) {
3710 address_list_wipe(&tmp);
3711 address_list_wipe(&addr);
3719 const char *s = crypt_fpr (key);
3721 keylist_size += m_strlen(s) + 4 + 1;
3722 p_realloc(&keylist, keylist_size);
3723 sprintf (keylist + keylist_used, "%s0x%s%s",
3724 keylist_used ? " " : "", s, forced_valid ? "!" : "");
3726 keylist_used = m_strlen(keylist);
3728 crypt_free_key (&key);
3729 address_list_wipe(&addr);
3731 address_list_wipe(&tmp);
3735 char *crypt_pgp_findkeys (address_t * to, address_t * cc, address_t * bcc)
3737 return find_keys (to, cc, bcc, APPLICATION_PGP);
3740 char *crypt_smime_findkeys (address_t * to, address_t * cc, address_t * bcc)
3742 return find_keys (to, cc, bcc, APPLICATION_SMIME);
3745 static int gpgme_send_menu (HEADER * msg, int *redraw, int is_smime)
3748 char input_signas[STRING];
3751 if (msg->security & APPLICATION_PGP)
3753 else if (msg->security & APPLICATION_SMIME)
3758 mutt_multi_choice (_
3759 ("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
3763 mutt_multi_choice (_
3764 ("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
3768 case 1: /* (e)ncrypt */
3769 msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3770 msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
3773 case 2: /* (s)ign */
3774 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3775 msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3778 case 3: /* sign (a)s */
3779 /* unset_option(OPTCRYPTCHECKTRUST); */
3780 if ((p = crypt_ask_for_key (_("Sign as: "), NULL, KEYFLAG_CANSIGN,
3781 is_smime ? APPLICATION_SMIME :
3782 APPLICATION_PGP, NULL))) {
3783 snprintf (input_signas, sizeof (input_signas), "0x%s", crypt_keyid (p));
3784 m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs,
3786 crypt_free_key (&p);
3788 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3790 *redraw = REDRAW_FULL;
3793 case 4: /* (b)oth */
3795 (is_smime ? (SMIMEENCRYPT | SMIMESIGN) : (PGPENCRYPT | PGPSIGN));
3798 case 5: /* (p)gp or s/(m)ime */
3799 is_smime = !is_smime;
3802 case 6: /* (c)lear */
3807 if (choice == 6 || choice == 7);
3808 else if (is_smime) {
3809 msg->security &= ~APPLICATION_PGP;
3810 msg->security |= APPLICATION_SMIME;
3813 msg->security &= ~APPLICATION_SMIME;
3814 msg->security |= APPLICATION_PGP;
3817 return (msg->security);
3820 int crypt_pgp_send_menu(HEADER * msg, int *redraw)
3822 return gpgme_send_menu(msg, redraw, 0);
3825 int crypt_smime_send_menu(HEADER * msg, int *redraw)
3827 return gpgme_send_menu (msg, redraw, 1);
3830 int crypt_smime_verify_sender (HEADER * h)
3832 address_t *sender = NULL;
3833 unsigned int ret = 1;
3836 h->env->from = mutt_expand_aliases (h->env->from);
3837 sender = h->env->from;
3839 else if (h->env->sender) {
3840 h->env->sender = mutt_expand_aliases (h->env->sender);
3841 sender = h->env->sender;
3845 if (signature_key) {
3846 gpgme_key_t key = signature_key;
3847 gpgme_user_id_t uid = NULL;
3848 int sender_length = 0;
3851 sender_length = m_strlen(sender->mailbox);
3852 for (uid = key->uids; uid && ret; uid = uid->next) {
3853 uid_length = m_strlen(uid->email);
3854 if (1 && (uid->email[0] == '<')
3855 && (uid->email[uid_length - 1] == '>')
3856 && (uid_length == sender_length + 2)
3857 && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
3862 mutt_any_key_to_continue ("Failed to verify sender");
3865 mutt_any_key_to_continue ("Failed to figure out sender");
3867 if (signature_key) {
3868 gpgme_key_release (signature_key);
3869 signature_key = NULL;
3875 static void invoke_import(const char *fname, int smime)
3877 gpgme_ctx_t ctx = create_gpgme_context(smime);
3881 err = gpgme_data_new_from_file(&data, fname, 1);
3883 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
3888 err = gpgme_op_import(ctx, data);
3890 mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
3891 gpgme_data_release(data);
3896 gpgme_data_release(data);
3901 void crypt_pgp_invoke_import(const char *fname)
3903 invoke_import(fname, 0);
3906 void crypt_smime_invoke_import(const char *fname)
3908 invoke_import(fname, 1);
3911 static void pgp_extract_keys_from_attachment (FILE * fp, BODY * top)
3915 char tempfname[_POSIX_PATH_MAX];
3917 tempfp = m_tempfile(tempfname, sizeof(tempfname), NONULL(MCore.tmpdir), NULL);
3918 if (tempfp == NULL) {
3919 mutt_perror (_("Can't create temporary file"));
3928 mutt_body_handler (top, &s);
3931 crypt_pgp_invoke_import(tempfname);
3932 mutt_unlink (tempfname);
3935 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
3938 set_option (OPTDONTHANDLEPGPKEYS);
3940 for (; top; top = top->next) {
3941 if (!tag || top->tagged)
3942 pgp_extract_keys_from_attachment (fp, top);
3948 unset_option (OPTDONTHANDLEPGPKEYS);