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,
498 if (!ctx || !r_key || !fpr)
499 return gpg_error (GPG_ERR_INV_VALUE);
501 if (strlen (fpr) < 8) /* We have at least a key ID. */
502 return gpg_error (GPG_ERR_INV_VALUE);
504 /* FIXME: We use our own context because we have to avoid the user's
505 I/O callback handlers. */
506 err = gpgme_new (&listctx);
509 gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
510 err = gpgme_op_keylist_start (listctx, fpr, secret);
512 err = gpgme_op_keylist_next (listctx, r_key);
513 gpgme_release (listctx);
517 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
518 The keys must be space delimited. */
519 static gpgme_key_t *create_recipient_set (const char *keylist,
520 gpgme_protocol_t protocol)
526 gpgme_key_t *rset = NULL;
527 unsigned int rset_n = 0;
528 gpgme_key_t key = NULL;
529 gpgme_ctx_t context = NULL;
531 err = gpgme_new (&context);
533 err = gpgme_set_protocol (context, protocol);
540 for (i = 0; *s && *s != ' ' && i < ssizeof(buf) - 1;)
544 if (i > 1 && buf[i - 1] == '!') {
545 /* The user selected to override the valididy of that
549 err = gpgme_get_key2 (context, buf, &key, 0);
551 key->uids->validity = GPGME_VALIDITY_FULL;
555 err = gpgme_get_key2 (context, buf, &key, 0);
558 p_realloc(&rset, rset_n + 1);
559 rset[rset_n++] = key;
562 mutt_error (_("error adding recipient `%s': %s\n"),
563 buf, gpgme_strerror (err));
571 /* NULL terminate. */
572 p_realloc(&rset, rset_n + 1);
573 rset[rset_n++] = NULL;
576 gpgme_release (context);
582 /* Make sure that the correct signer is set. Returns 0 on success. */
583 static int set_signer (gpgme_ctx_t ctx, int for_smime)
585 char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
588 gpgme_key_t key, key2;
590 if (!signid || !*signid)
593 listctx = create_gpgme_context (for_smime);
594 err = gpgme_op_keylist_start (listctx, signid, 1);
596 err = gpgme_op_keylist_next (listctx, &key);
598 gpgme_release (listctx);
599 mutt_error (_("secret key `%s' not found: %s\n"),
600 signid, gpgme_strerror (err));
603 err = gpgme_op_keylist_next (listctx, &key2);
605 gpgme_key_release (key);
606 gpgme_key_release (key2);
607 gpgme_release (listctx);
608 mutt_error (_("ambiguous specification of secret key `%s'\n"), signid);
611 gpgme_op_keylist_end (listctx);
612 gpgme_release (listctx);
614 gpgme_signers_clear (ctx);
615 err = gpgme_signers_add (ctx, key);
616 gpgme_key_release (key);
618 mutt_error (_("error setting secret key `%s': %s\n"),
619 signid, gpgme_strerror (err));
626 /* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
627 and return an allocated filename to a temporary file containing the
628 enciphered text. With USE_SMIME set to true, the smime backend is
629 used. With COMBINED_SIGNED a PGP message is signed and
630 encrypted. Returns NULL in case of error */
631 static char *encrypt_gpgme_object (gpgme_data_t plaintext, gpgme_key_t * rset,
632 int use_smime, int combined_signed)
636 gpgme_data_t ciphertext;
639 ctx = create_gpgme_context (use_smime);
641 gpgme_set_armor (ctx, 1);
643 ciphertext = create_gpgme_data ();
645 if (combined_signed) {
646 if (set_signer (ctx, use_smime)) {
647 gpgme_data_release (ciphertext);
651 err = gpgme_op_encrypt_sign (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
652 plaintext, ciphertext);
655 err = gpgme_op_encrypt (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
656 plaintext, ciphertext);
657 mutt_need_hard_redraw ();
659 mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
660 gpgme_data_release (ciphertext);
667 outfile = data_object_to_tempfile (ciphertext, NULL);
668 gpgme_data_release (ciphertext);
672 /* Find the "micalg" parameter from the last Gpgme operation on
673 context CTX. It is expected that this operation was a sign
674 operation. Return the algorithm name as a C string in buffer BUF
675 which must have been allocated by the caller with size BUFLEN.
676 Returns 0 on success or -1 in case of an error. The return string
677 is truncted to BUFLEN - 1. */
678 static int get_micalg (gpgme_ctx_t ctx, char *buf, ssize_t buflen)
680 gpgme_sign_result_t result = NULL;
681 const char *algorithm_name = NULL;
687 result = gpgme_op_sign_result (ctx);
689 algorithm_name = gpgme_hash_algo_name (result->signatures->hash_algo);
690 if (algorithm_name) {
691 m_strcpy(buf, buflen, algorithm_name);
695 return *buf ? 0 : -1;
698 static void print_time (time_t t, STATE * s)
702 setlocale (LC_TIME, "");
703 #ifdef HAVE_LANGINFO_D_T_FMT
704 strftime (p, sizeof (p), nl_langinfo (D_T_FMT), localtime (&t));
706 strftime (p, sizeof (p), "%c", localtime (&t));
708 setlocale (LC_TIME, "C");
709 state_attach_puts (p, s);
712 /* Implementation of `sign_message'. */
714 /* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
715 USE_SMIME is passed as true. Returns the new body or NULL on
717 static BODY *sign_message (BODY * a, int use_smime)
724 gpgme_data_t message, signature;
726 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
728 message = body_to_data_object (a, 1);
731 signature = create_gpgme_data ();
733 ctx = create_gpgme_context (use_smime);
735 gpgme_set_armor (ctx, 1);
737 if (set_signer (ctx, use_smime)) {
738 gpgme_data_release (signature);
743 err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH);
744 mutt_need_hard_redraw ();
745 gpgme_data_release (message);
747 gpgme_data_release (signature);
749 mutt_error (_("error signing data: %s\n"), gpgme_strerror (err));
753 sigfile = data_object_to_tempfile (signature, NULL);
754 gpgme_data_release (signature);
761 t->type = TYPEMULTIPART;
762 t->subtype = m_strdup("signed");
763 t->encoding = ENC7BIT;
765 t->disposition = DISPINLINE;
767 parameter_set_boundary(&t->parameter);
768 parameter_setval(&t->parameter, "protocol",
769 use_smime ? "application/pkcs7-signature"
770 : "application/pgp-signature");
771 /* Get the micalg from gpgme. Old gpgme versions don't support this
772 for S/MIME so we assume sha-1 in this case. */
773 if (!get_micalg (ctx, buf, sizeof buf))
774 parameter_setval(&t->parameter, "micalg", buf);
776 parameter_setval(&t->parameter, "micalg", "sha1");
782 t->parts->next = body_new();
784 t->type = TYPEAPPLICATION;
786 t->subtype = m_strdup("pkcs7-signature");
787 parameter_setval(&t->parameter, "name", "smime.p7s");
788 t->encoding = ENCBASE64;
790 t->disposition = DISPATTACH;
791 t->d_filename = m_strdup("smime.p7s");
794 t->subtype = m_strdup("pgp-signature");
796 t->disposition = DISPINLINE;
797 t->encoding = ENC7BIT;
799 t->filename = sigfile;
800 t->unlink = 1; /* ok to remove this file after sending. */
806 BODY *crypt_pgp_sign_message (BODY * a)
808 return sign_message (a, 0);
811 BODY *crypt_smime_sign_message (BODY * a)
813 return sign_message (a, 1);
817 * Implementation of `encrypt_message'.
820 /* Encrypt the mail body A to all keys given as space separated keyids
821 or fingerprints in KEYLIST and return the encrypted body. */
822 BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
824 char *outfile = NULL;
826 gpgme_key_t *rset = NULL;
827 gpgme_data_t plaintext;
829 rset = create_recipient_set (keylist, GPGME_PROTOCOL_OpenPGP);
835 plaintext = body_to_data_object (a, 0);
841 outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
842 gpgme_data_release (plaintext);
848 t->type = TYPEMULTIPART;
849 t->subtype = m_strdup("encrypted");
850 t->encoding = ENC7BIT;
852 t->disposition = DISPINLINE;
854 parameter_set_boundary(&t->parameter);
855 parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
857 t->parts = body_new();
858 t->parts->type = TYPEAPPLICATION;
859 t->parts->subtype = m_strdup("pgp-encrypted");
860 t->parts->encoding = ENC7BIT;
862 t->parts->next = body_new();
863 t->parts->next->type = TYPEAPPLICATION;
864 t->parts->next->subtype = m_strdup("octet-stream");
865 t->parts->next->encoding = ENC7BIT;
866 t->parts->next->filename = outfile;
867 t->parts->next->use_disp = 1;
868 t->parts->next->disposition = DISPINLINE;
869 t->parts->next->unlink = 1; /* delete after sending the message */
870 t->parts->next->d_filename = m_strdup("msg.asc"); /* non pgp/mime
877 * Implementation of `smime_build_smime_entity'.
880 /* Encrypt the mail body A to all keys given as space separated
881 fingerprints in KEYLIST and return the S/MIME encrypted body. */
882 BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
884 char *outfile = NULL;
886 gpgme_key_t *rset = NULL;
887 gpgme_data_t plaintext;
889 rset = create_recipient_set (keylist, GPGME_PROTOCOL_CMS);
893 plaintext = body_to_data_object (a, 0);
899 outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
900 gpgme_data_release (plaintext);
906 t->type = TYPEAPPLICATION;
907 t->subtype = m_strdup("pkcs7-mime");
908 parameter_setval(&t->parameter, "name", "smime.p7m");
909 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
910 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
912 t->disposition = DISPATTACH;
913 t->d_filename = m_strdup("smime.p7m");
914 t->filename = outfile;
915 t->unlink = 1; /*delete after sending the message */
923 /* Implementation of `verify_one'. */
925 /* Display the common attributes of the signature summary SUM.
926 Return 1 if there is is a severe warning.
928 static int show_sig_summary (unsigned long sum,
929 gpgme_ctx_t ctx, gpgme_key_t key, int idx,
934 if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
935 state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
939 if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
940 time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
943 state_attach_puts (_("Warning: The key used to create the "
944 "signature expired at: "), s);
946 state_attach_puts ("\n", s);
949 state_attach_puts (_("Warning: At least one certification key "
950 "has expired\n"), s);
953 if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
954 gpgme_verify_result_t result;
955 gpgme_signature_t sig;
958 result = gpgme_op_verify_result (ctx);
960 for (sig = result->signatures, i = 0; sig && (i < idx);
961 sig = sig->next, i++);
963 state_attach_puts (_("Warning: The signature expired at: "), s);
964 print_time (sig ? sig->exp_timestamp : 0, s);
965 state_attach_puts ("\n", s);
968 if ((sum & GPGME_SIGSUM_KEY_MISSING))
969 state_attach_puts (_("Can't verify due to a missing "
970 "key or certificate\n"), s);
972 if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
973 state_attach_puts (_("The CRL is not available\n"), s);
977 if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
978 state_attach_puts (_("Available CRL is too old\n"), s);
982 if ((sum & GPGME_SIGSUM_BAD_POLICY))
983 state_attach_puts (_("A policy requirement was not met\n"), s);
985 if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
986 const char *t0 = NULL, *t1 = NULL;
987 gpgme_verify_result_t result;
988 gpgme_signature_t sig;
991 state_attach_puts (_("A system error occurred"), s);
993 /* Try to figure out some more detailed system error information. */
994 result = gpgme_op_verify_result (ctx);
995 for (sig = result->signatures, i = 0; sig && (i < idx);
996 sig = sig->next, i++);
999 t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
1003 state_attach_puts (": ", s);
1005 state_attach_puts (t0, s);
1006 if (t1 && !(t0 && !m_strcmp(t0, t1))) {
1008 state_attach_puts (",", s);
1009 state_attach_puts (t1, s);
1012 state_attach_puts ("\n", s);
1019 static void show_fingerprint (gpgme_key_t key, STATE * state)
1024 const char *prefix = _("Fingerprint: ");
1029 s = key->subkeys ? key->subkeys->fpr : NULL;
1032 is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
1034 bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
1035 buf = p_new(char, bufsize);
1036 m_strcpy(buf, bufsize, prefix);
1037 p = buf + m_strlen(buf);
1038 if (is_pgp && m_strlen(s) == 40) { /* PGP v4 style formatted. */
1039 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
1050 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
1053 *p++ = is_pgp ? ' ' : ':';
1054 if (is_pgp && i == 7)
1059 /* just in case print remaining odd digits */
1064 state_attach_puts (buf, state);
1068 /* Show the valididy of a key used for one signature. */
1069 static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
1071 gpgme_verify_result_t result = NULL;
1072 gpgme_signature_t sig = NULL;
1073 const char *txt = NULL;
1075 result = gpgme_op_verify_result (ctx);
1077 for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
1079 switch (sig ? sig->validity : 0) {
1080 case GPGME_VALIDITY_UNKNOWN:
1081 txt = _("WARNING: We have NO indication whether "
1082 "the key belongs to the person named " "as shown above\n");
1084 case GPGME_VALIDITY_UNDEFINED:
1086 case GPGME_VALIDITY_NEVER:
1087 txt = _("WARNING: The key does NOT BELONG to "
1088 "the person named as shown above\n");
1090 case GPGME_VALIDITY_MARGINAL:
1091 txt = _("WARNING: It is NOT certain that the key "
1092 "belongs to the person named as shown above\n");
1094 case GPGME_VALIDITY_FULL:
1095 case GPGME_VALIDITY_ULTIMATE:
1100 state_attach_puts (txt, s);
1103 /* Show information about one signature. This fucntion is called with
1104 the context CTX of a sucessful verification operation and the
1105 enumerator IDX which should start at 0 and incremete for each
1108 Return values are: 0 for normal procession, 1 for a bad signature,
1109 2 for a signature with a warning or -1 for no more signature. */
1110 static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
1113 const char *fpr, *uid;
1114 gpgme_key_t key = NULL;
1115 int i, anybad = 0, anywarn = 0;
1117 gpgme_user_id_t uids = NULL;
1118 gpgme_verify_result_t result;
1119 gpgme_signature_t sig;
1120 gpgme_error_t err = GPG_ERR_NO_ERROR;
1122 result = gpgme_op_verify_result (ctx);
1124 /* FIXME: this code should use a static variable and remember
1125 the current position in the list of signatures, IMHO.
1128 for (i = 0, sig = result->signatures; sig && (i < idx);
1129 i++, sig = sig->next);
1131 return -1; /* Signature not found. */
1133 if (signature_key) {
1134 gpgme_key_release (signature_key);
1135 signature_key = NULL;
1138 created = sig->timestamp;
1142 if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
1145 err = gpgme_get_key2 (ctx, fpr, &key, 0); /* secret key? */
1147 uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
1149 signature_key = key;
1152 key = NULL; /* Old gpgme versions did not set KEY to NULL on
1153 error. Do it here to avoid a double free. */
1157 if (!s || !s->fpout || !(s->flags & M_DISPLAY)); /* No state information so no way to print anything. */
1159 state_attach_puts (_("Error getting key information: "), s);
1160 state_attach_puts (gpg_strerror (err), s);
1161 state_attach_puts ("\n", s);
1164 else if ((sum & GPGME_SIGSUM_GREEN)) {
1165 state_attach_puts (_("Good signature from: "), s);
1166 state_attach_puts (uid, s);
1167 state_attach_puts ("\n", s);
1168 for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
1170 /* Skip primary UID. */
1174 state_attach_puts (_(" aka: "), s);
1175 state_attach_puts (uids->uid, s);
1176 state_attach_puts ("\n", s);
1178 state_attach_puts (_(" created: "), s);
1179 print_time (created, s);
1180 state_attach_puts ("\n", s);
1181 if (show_sig_summary (sum, ctx, key, idx, s))
1183 show_one_sig_validity (ctx, idx, s);
1185 else if ((sum & GPGME_SIGSUM_RED)) {
1186 state_attach_puts (_("*BAD* signature claimed to be from: "), s);
1187 state_attach_puts (uid, s);
1188 state_attach_puts ("\n", s);
1189 show_sig_summary (sum, ctx, key, idx, s);
1191 else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) { /* We can't decide (yellow) but this is a PGP key with a good
1192 signature, so we display what a PGP user expects: The name,
1193 fingerprint and the key validity (which is neither fully or
1195 state_attach_puts (_("Good signature from: "), s);
1196 state_attach_puts (uid, s);
1197 state_attach_puts ("\n", s);
1198 state_attach_puts (_(" created: "), s);
1199 print_time (created, s);
1200 state_attach_puts ("\n", s);
1201 show_one_sig_validity (ctx, idx, s);
1202 show_fingerprint (key, s);
1203 if (show_sig_summary (sum, ctx, key, idx, s))
1206 else { /* can't decide (yellow) */
1208 state_attach_puts (_("Error checking signature"), s);
1209 state_attach_puts ("\n", s);
1210 show_sig_summary (sum, ctx, key, idx, s);
1213 if (key != signature_key)
1214 gpgme_key_release (key);
1217 return anybad ? 1 : anywarn ? 2 : 0;
1220 /* Do the actual verification step. With IS_SMIME set to true we
1221 assume S/MIME (surprise!) */
1222 static int verify_one (BODY * sigbdy, STATE * s,
1223 const char *tempfile, int is_smime)
1229 gpgme_data_t signature, message;
1231 signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
1235 /* We need to tell gpgme about the encoding because the backend can't
1236 auto-detect plain base-64 encoding which is used by S/MIME. */
1238 gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
1240 err = gpgme_data_new_from_file (&message, tempfile, 1);
1242 gpgme_data_release (signature);
1243 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
1246 ctx = create_gpgme_context (is_smime);
1248 /* Note: We don't need a current time output because GPGME avoids
1249 such an attack by separating the meta information from the
1251 state_attach_puts (_("[-- Begin signature information --]\n"), s);
1253 err = gpgme_op_verify (ctx, signature, message, NULL);
1254 mutt_need_hard_redraw ();
1258 snprintf (buf, sizeof (buf) - 1,
1259 _("Error: verification failed: %s\n"), gpgme_strerror (err));
1260 state_attach_puts (buf, s);
1262 else { /* Verification succeeded, see what the result is. */
1266 if (signature_key) {
1267 gpgme_key_release (signature_key);
1268 signature_key = NULL;
1271 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1282 gpgme_verify_result_t result;
1283 gpgme_sig_notation_t notation;
1284 gpgme_signature_t sig;
1286 result = gpgme_op_verify_result (ctx);
1288 for (sig = result->signatures; sig; sig = sig->next) {
1289 if (sig->notations) {
1290 state_attach_puts ("*** Begin Notation (signature by: ", s);
1291 state_attach_puts (sig->fpr, s);
1292 state_attach_puts (") ***\n", s);
1293 for (notation = sig->notations; notation; notation = notation->next)
1295 if (notation->name) {
1296 state_attach_puts (notation->name, s);
1297 state_attach_puts ("=", s);
1299 if (notation->value) {
1300 state_attach_puts (notation->value, s);
1301 if (!(*notation->value
1302 && (notation->value[m_strlen(notation->value) - 1] ==
1304 state_attach_puts ("\n", s);
1307 state_attach_puts ("*** End Notation ***\n", s);
1313 gpgme_release (ctx);
1315 state_attach_puts (_("[-- End signature information --]\n\n"), s);
1317 return badsig ? 1 : anywarn ? 2 : 0;
1320 int crypt_pgp_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1322 return verify_one (sigbdy, s, tempfile, 0);
1325 int crypt_smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1327 return verify_one (sigbdy, s, tempfile, 1);
1331 * Implementation of `decrypt_part'.
1334 /* Decrypt a PGP or SMIME message (depending on the boolean flag
1335 IS_SMIME) with body A described further by state S. Write
1336 plaintext out to file FPOUT and return a new body. For PGP returns
1337 a flag in R_IS_SIGNED to indicate whether this is a combined
1338 encrypted and signed message, for S/MIME it returns true when it is
1339 not a encrypted but a signed message. */
1340 static BODY *decrypt_part (BODY * a, STATE * s, FILE * fpout, int is_smime,
1347 gpgme_data_t ciphertext, plaintext;
1348 int maybe_signed = 0;
1355 ctx = create_gpgme_context (is_smime);
1358 /* Make a data object from the body, create context etc. */
1359 ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
1362 plaintext = create_gpgme_data ();
1364 /* Do the decryption or the verification in case of the S/MIME hack. */
1365 if ((!is_smime) || maybe_signed) {
1367 err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
1368 else if (maybe_signed)
1369 err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
1372 /* Check wether signatures have been verified. */
1373 gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
1375 if (verify_result->signatures)
1380 err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
1381 gpgme_data_release (ciphertext);
1383 if (is_smime && !maybe_signed && gpg_err_code (err) == GPG_ERR_NO_DATA) {
1384 /* Check whether this might be a signed message despite what
1385 the mime header told us. Retry then. gpgsm returns the
1386 error information "unsupported Algorithm '?'" but gpgme
1387 will not store this unknown algorithm, thus we test that
1388 it has not been set. */
1389 gpgme_decrypt_result_t result;
1391 result = gpgme_op_decrypt_result (ctx);
1392 if (!result->unsupported_algorithm) {
1394 gpgme_data_release (plaintext);
1398 mutt_need_hard_redraw ();
1399 if ((s->flags & M_DISPLAY)) {
1402 snprintf (buf, sizeof (buf) - 1,
1403 _("[-- Error: decryption failed: %s --]\n\n"),
1404 gpgme_strerror (err));
1405 state_attach_puts (buf, s);
1407 gpgme_data_release (plaintext);
1408 gpgme_release (ctx);
1411 mutt_need_hard_redraw ();
1413 /* Read the output from GPGME, and make sure to change CRLF to LF,
1414 otherwise read_mime_header has a hard time parsing the message. */
1415 if (data_object_to_stream (plaintext, fpout)) {
1416 gpgme_data_release (plaintext);
1417 gpgme_release (ctx);
1420 gpgme_data_release (plaintext);
1422 a->is_signed_data = 0;
1428 a->is_signed_data = 1;
1430 *r_is_signed = -1; /* A signature exists. */
1432 if ((s->flags & M_DISPLAY))
1433 state_attach_puts (_("[-- Begin signature " "information --]\n"), s);
1434 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1440 if (!anybad && idx && r_is_signed && *r_is_signed)
1441 *r_is_signed = anywarn ? 2 : 1; /* Good signature. */
1443 if ((s->flags & M_DISPLAY))
1444 state_attach_puts (_("[-- End signature " "information --]\n\n"), s);
1446 gpgme_release (ctx);
1451 tattach = mutt_read_mime_header (fpout, 0);
1454 * Need to set the length of this body part.
1456 fstat (fileno (fpout), &info);
1457 tattach->length = info.st_size - tattach->offset;
1459 tattach->warnsig = anywarn;
1461 /* See if we need to recurse on this MIME part. */
1462 mutt_parse_part (fpout, tattach);
1468 /* Decrypt a PGP/MIME message in FPIN and B and return a new body and
1469 the stream in CUR and FPOUT. Returns 0 on success. */
1470 int crypt_pgp_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1472 char tempfile[_POSIX_PATH_MAX];
1474 BODY *first_part = b;
1477 first_part->goodsig = 0;
1478 first_part->warnsig = 0;
1480 if (!mutt_is_multipart_encrypted (b))
1483 if (!b->parts || !b->parts->next)
1490 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1492 mutt_perror (_("Can't create temporary file"));
1497 *cur = decrypt_part (b, &s, *fpout, 0, &is_signed);
1500 first_part->goodsig = 1;
1502 return *cur ? 0 : -1;
1506 /* Decrypt a S/MIME message in FPIN and B and return a new body and
1507 the stream in CUR and FPOUT. Returns 0 on success. */
1508 int crypt_smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b,
1511 char tempfile[_POSIX_PATH_MAX];
1515 long saved_b_offset;
1516 ssize_t saved_b_length;
1519 if (!mutt_is_application_smime (b))
1525 /* Decode the body - we need to pass binary CMS to the
1526 backend. The backend allows for Base64 encoded data but it does
1527 not allow for QP which I have seen in some messages. So better
1529 saved_b_type = b->type;
1530 saved_b_offset = b->offset;
1531 saved_b_length = b->length;
1534 fseeko (s.fpin, b->offset, 0);
1535 tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1537 mutt_perror (_("Can't create temporary file"));
1540 mutt_unlink (tempfile);
1543 mutt_decode_attachment (b, &s);
1545 b->length = ftello (s.fpout);
1552 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1554 mutt_perror (_("Can't create temporary file"));
1557 mutt_unlink (tempfile);
1559 *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
1561 (*cur)->goodsig = is_signed > 0;
1562 b->type = saved_b_type;
1563 b->length = saved_b_length;
1564 b->offset = saved_b_offset;
1567 if (*cur && !is_signed && !(*cur)->parts
1568 && mutt_is_application_smime (*cur)) {
1569 /* Assume that this is a opaque signed s/mime message. This is
1570 an ugly way of doing it but we have anyway a problem with
1571 arbitrary encoded S/MIME messages: Only the outer part may be
1572 encrypted. The entire mime parsing should be revamped,
1573 probably by keeping the temportary files so that we don't
1574 need to decrypt them all the time. Inner parts of an
1575 encrypted part can then pint into this file and tehre won't
1576 never be a need to decrypt again. This needs a partial
1577 rewrite of the MIME engine. */
1581 saved_b_type = bb->type;
1582 saved_b_offset = bb->offset;
1583 saved_b_length = bb->length;
1586 fseeko (s.fpin, bb->offset, 0);
1587 tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1589 mutt_perror (_("Can't create temporary file"));
1592 mutt_unlink (tempfile);
1595 mutt_decode_attachment (bb, &s);
1597 bb->length = ftello (s.fpout);
1605 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1607 mutt_perror (_("Can't create temporary file"));
1610 mutt_unlink (tempfile);
1612 tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
1614 tmp_b->goodsig = is_signed > 0;
1615 bb->type = saved_b_type;
1616 bb->length = saved_b_length;
1617 bb->offset = saved_b_offset;
1620 body_list_wipe(cur);
1623 return *cur ? 0 : -1;
1628 * Implementation of `pgp_check_traditional'.
1631 static int pgp_check_traditional_one_body (FILE * fp, BODY * b,
1634 char tempfile[_POSIX_PATH_MAX];
1635 char buf[HUGE_STRING];
1642 if (b->type != TYPETEXT)
1645 if (tagged_only && !b->tagged)
1648 tempfd = m_tempfd(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1649 if (mutt_decode_save_attachment (fp, b, tempfd, 0) != 0) {
1654 if ((tfp = fopen(tempfile, "r")) == NULL) {
1659 while (fgets (buf, sizeof (buf), tfp)) {
1660 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1661 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1663 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15))
1673 /* fix the content type */
1675 parameter_setval(&b->parameter, "format", "fixed");
1676 parameter_setval(&b->parameter, "x-action",
1677 enc ? "pgp-encrypted" : "pgp-signed");
1681 int crypt_pgp_check_traditional (FILE * fp, BODY * b, int tagged_only)
1686 for (; b; b = b->next) {
1687 if (is_multipart (b))
1688 rv = (crypt_pgp_check_traditional (fp, b->parts, tagged_only) || rv);
1689 else if (b->type == TYPETEXT) {
1690 if ((r = mutt_is_application_pgp (b)))
1693 rv = (pgp_check_traditional_one_body (fp, b, tagged_only) || rv);
1700 /* Implementation of `application_handler'. */
1703 Copy a clearsigned message, and strip the signature and PGP's
1706 XXX - charset handling: We assume that it is safe to do
1707 character set decoding first, dash decoding second here, while
1708 we do it the other way around in the main handler.
1710 (Note that we aren't worse than Outlook & Cie in this, and also
1711 note that we can successfully handle anything produced by any
1712 existing versions of mutt.) */
1714 static void copy_clearsigned (gpgme_data_t data, STATE * s, char *charset)
1716 char buf[HUGE_STRING];
1717 short complete, armor_header;
1722 fname = data_object_to_tempfile (data, &fp);
1728 fc = fgetconv_open (fp, charset, MCharset.charset, M_ICONV_HOOK_FROM);
1730 for (complete = 1, armor_header = 1;
1731 fgetconvs (buf, sizeof (buf), fc) != NULL;
1732 complete = strchr (buf, '\n') != NULL) {
1735 state_puts (buf, s);
1739 if (!m_strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
1749 state_puts (s->prefix, s);
1751 if (buf[0] == '-' && buf[1] == ' ')
1752 state_puts (buf + 2, s);
1754 state_puts (buf, s);
1757 fgetconv_close (&fc);
1762 /* Support for classic_application/pgp */
1763 int crypt_pgp_application_pgp_handler (BODY * m, STATE * s)
1765 int needpass = -1, pgp_keyblock = 0;
1769 off_t last_pos, offset;
1770 char buf[HUGE_STRING];
1771 FILE *pgpout = NULL;
1773 gpgme_error_t err = 0;
1774 gpgme_data_t armored_data = NULL;
1776 short maybe_goodsig = 1;
1777 short have_any_sigs = 0;
1779 char body_charset[STRING]; /* Only used for clearsigned messages. */
1781 /* For clearsigned messages we won't be able to get a character set
1782 but we know that this may only be text thus we assume Latin-1
1784 if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
1785 m_strcpy(body_charset, sizeof(body_charset), "iso-8859-1");
1787 fseeko (s->fpin, m->offset, 0);
1788 last_pos = m->offset;
1790 for (bytes = m->length; bytes > 0;) {
1791 if (fgets (buf, sizeof (buf), s->fpin) == NULL)
1794 offset = ftello (s->fpin);
1795 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1798 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1800 start_pos = last_pos;
1802 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1804 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15)) {
1808 else if (!option (OPTDONTHANDLEPGPKEYS) &&
1809 !m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
1814 /* XXX - we may wish to recode here */
1816 state_puts (s->prefix, s);
1817 state_puts (buf, s);
1821 have_any_sigs = (have_any_sigs || (clearsign && (s->flags & M_VERIFY)));
1823 /* Copy PGP material to an data container */
1824 armored_data = create_gpgme_data ();
1825 gpgme_data_write (armored_data, buf, m_strlen(buf));
1826 while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) {
1827 offset = ftello (s->fpin);
1828 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1831 gpgme_data_write (armored_data, buf, m_strlen(buf));
1833 if ((needpass && !m_strcmp("-----END PGP MESSAGE-----\n", buf))
1835 && (!m_strcmp("-----END PGP SIGNATURE-----\n", buf)
1836 || !m_strcmp("-----END PGP PUBLIC KEY BLOCK-----\n",
1841 /* Invoke PGP if needed */
1842 if (!clearsign || (s->flags & M_VERIFY)) {
1843 unsigned int sig_stat = 0;
1844 gpgme_data_t plaintext;
1847 plaintext = create_gpgme_data ();
1848 ctx = create_gpgme_context (0);
1851 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1853 err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
1854 if (gpg_err_code (err) == GPG_ERR_NO_DATA) {
1855 /* Decrypt verify can't handle signed only messages. */
1856 err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
1857 ? gpgme_error_from_errno (errno) : 0;
1858 /* Must release plaintext so that we supply an
1859 uninitialized object. */
1860 gpgme_data_release (plaintext);
1861 plaintext = create_gpgme_data ();
1862 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1869 snprintf (errbuf, sizeof (errbuf) - 1,
1870 _("Error: decryption/verification failed: %s\n"),
1871 gpgme_strerror (err));
1872 state_attach_puts (errbuf, s);
1874 else { /* Decryption/Verification succeeded */
1878 /* Check wether signatures have been verified. */
1879 gpgme_verify_result_t verify_result;
1881 verify_result = gpgme_op_verify_result (ctx);
1882 if (verify_result->signatures)
1888 if ((s->flags & M_DISPLAY) && sig_stat) {
1893 state_attach_puts (_("[-- Begin signature "
1894 "information --]\n"), s);
1897 (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1906 state_attach_puts (_("[-- End signature "
1907 "information --]\n\n"), s);
1910 tmpfname = data_object_to_tempfile (plaintext, &pgpout);
1913 state_attach_puts (_("Error: copy data failed\n"), s);
1917 p_delete(&tmpfname);
1920 gpgme_release (ctx);
1924 * Now, copy cleartext to the screen. NOTE - we expect that PGP
1925 * outputs utf-8 cleartext. This may not always be true, but it
1926 * seems to be a reasonable guess.
1929 if (s->flags & M_DISPLAY) {
1931 state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
1932 else if (pgp_keyblock)
1933 state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s);
1935 state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s);
1939 copy_clearsigned (armored_data, s, body_charset);
1946 fc = fgetconv_open (pgpout, "utf-8", MCharset.charset, 0);
1947 while ((c = fgetconv (fc)) != EOF) {
1949 if (c == '\n' && s->prefix)
1950 state_puts (s->prefix, s);
1952 fgetconv_close (&fc);
1955 if (s->flags & M_DISPLAY) {
1956 state_putc ('\n', s);
1958 state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
1959 else if (pgp_keyblock)
1960 state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
1962 state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
1970 /* XXX - we may wish to recode here */
1972 state_puts (s->prefix, s);
1973 state_puts (buf, s);
1977 m->goodsig = (maybe_goodsig && have_any_sigs);
1979 if (needpass == -1) {
1980 state_attach_puts (_("[-- Error: could not find beginning"
1981 " of PGP message! --]\n\n"), s);
1987 /* Implementation of `encrypted_handler'. */
1989 /* MIME handler for pgp/mime encrypted messages. */
1990 int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
1992 char tempfile[_POSIX_PATH_MAX];
1995 BODY *orig_body = a;
2000 if (!a || a->type != TYPEAPPLICATION || !a->subtype
2001 || ascii_strcasecmp ("pgp-encrypted", a->subtype)
2002 || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
2003 || ascii_strcasecmp ("octet-stream", a->next->subtype)) {
2004 if (s->flags & M_DISPLAY)
2005 state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
2010 /* Move forward to the application/pgp-encrypted body. */
2013 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2015 if (s->flags & M_DISPLAY)
2016 state_attach_puts (_("[-- Error: could not create temporary file! "
2021 tattach = decrypt_part (a, s, fpout, 0, &is_signed);
2023 tattach->goodsig = is_signed > 0;
2025 if (s->flags & M_DISPLAY)
2026 state_attach_puts (is_signed ?
2028 ("[-- The following data is PGP/MIME signed and encrypted --]\n\n") :
2029 _("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
2032 FILE *savefp = s->fpin;
2035 rc = mutt_body_handler (tattach, s);
2040 * if a multipart/signed is the _only_ sub-part of a
2041 * multipart/encrypted, cache signature verification
2044 if (mutt_is_multipart_signed (tattach) && !tattach->next)
2045 orig_body->goodsig |= tattach->goodsig;
2047 if (s->flags & M_DISPLAY) {
2048 state_puts ("\n", s);
2049 state_attach_puts (is_signed ?
2051 ("[-- End of PGP/MIME signed and encrypted data --]\n")
2052 : _("[-- End of PGP/MIME encrypted data --]\n"), s);
2055 body_list_wipe(&tattach);
2059 mutt_unlink (tempfile);
2063 /* Support for application/smime */
2064 int crypt_smime_application_smime_handler (BODY * a, STATE * s)
2066 char tempfile[_POSIX_PATH_MAX];
2073 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2075 if (s->flags & M_DISPLAY)
2076 state_attach_puts (_("[-- Error: could not create temporary file! "
2081 tattach = decrypt_part (a, s, fpout, 1, &is_signed);
2083 tattach->goodsig = is_signed > 0;
2085 if (s->flags & M_DISPLAY)
2086 state_attach_puts (is_signed ?
2087 _("[-- The following data is S/MIME signed --]\n\n") :
2088 _("[-- The following data is S/MIME encrypted --]\n\n"), s);
2091 FILE *savefp = s->fpin;
2094 rc = mutt_body_handler (tattach, s);
2099 * if a multipart/signed is the _only_ sub-part of a
2100 * multipart/encrypted, cache signature verification
2103 if (mutt_is_multipart_signed (tattach) && !tattach->next) {
2104 if (!(a->goodsig = tattach->goodsig))
2105 a->warnsig = tattach->warnsig;
2107 else if (tattach->goodsig) {
2109 a->warnsig = tattach->warnsig;
2112 if (s->flags & M_DISPLAY) {
2113 state_puts ("\n", s);
2114 state_attach_puts (is_signed ?
2115 _("[-- End of S/MIME signed data --]\n") :
2116 _("[-- End of S/MIME encrypted data --]\n"), s);
2119 body_list_wipe(&tattach);
2123 mutt_unlink (tempfile);
2129 * Format an entry on the CRYPT key selection menu.
2132 * %k key id %K key id of the principal key
2134 * %a algorithm %A algorithm of the princ. key
2135 * %l length %L length of the princ. key
2136 * %f flags %F flags of the princ. key
2137 * %c capabilities %C capabilities of the princ. key
2138 * %t trust/validity of the key-uid association
2140 * %[...] date of key using strftime(3)
2144 crypt_entry_fmt (char *dest, ssize_t destlen, char op,
2145 const char *src, const char *prefix,
2146 const char *ifstr, const char *elstr,
2147 anytype data, format_flag flags)
2150 crypt_entry_t *entry;
2153 int optional = (flags & M_FORMAT_OPTIONAL);
2154 const char *s = NULL;
2160 /* if (isupper ((unsigned char) op)) */
2163 kflags = (key->flags /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
2166 switch (ascii_tolower (op)) {
2170 char buf2[STRING], *p;
2186 while (len > 0 && *cp != ']') {
2195 break; /* not enough space */
2205 if (do_locales && Locale)
2206 setlocale (LC_TIME, Locale);
2211 if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
2212 tt = key->kobj->subkeys->timestamp;
2214 tm = localtime (&tt);
2216 strftime (buf2, sizeof (buf2), dest, tm);
2219 setlocale (LC_TIME, "C");
2221 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2222 snprintf (dest, destlen, fmt, buf2);
2229 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
2230 snprintf (dest, destlen, fmt, entry->num);
2235 /* fixme: we need a way to distinguish between main and subkeys.
2236 Store the idx in entry? */
2237 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2238 snprintf (dest, destlen, fmt, crypt_keyid (key));
2243 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2244 snprintf (dest, destlen, fmt, key->uid);
2249 snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
2250 if (key->kobj->subkeys)
2251 s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
2254 snprintf (dest, destlen, fmt, s);
2259 snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
2260 if (key->kobj->subkeys)
2261 val = key->kobj->subkeys->length;
2264 snprintf (dest, destlen, fmt, val);
2269 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2270 snprintf (dest, destlen, fmt, crypt_flags (kflags));
2272 else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
2277 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2278 snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
2280 else if (!(kflags & (KEYFLAG_ABILITIES)))
2284 if ((kflags & KEYFLAG_ISX509))
2287 gpgme_user_id_t uid = NULL;
2290 for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
2291 i++, uid = uid->next);
2293 switch (uid->validity) {
2294 case GPGME_VALIDITY_UNDEFINED:
2297 case GPGME_VALIDITY_NEVER:
2300 case GPGME_VALIDITY_MARGINAL:
2303 case GPGME_VALIDITY_FULL:
2306 case GPGME_VALIDITY_ULTIMATE:
2309 case GPGME_VALIDITY_UNKNOWN:
2315 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2316 snprintf (dest, destlen, fmt, s ? *s : 'B');
2319 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2320 snprintf (dest, destlen, fmt,
2321 gpgme_get_protocol_name (key->kobj->protocol));
2328 if (flags & M_FORMAT_OPTIONAL)
2329 m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
2330 mutt_attach_fmt, data, 0);
2334 /* Used by the display fucntion to format a line. */
2335 static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
2337 crypt_key_t **key_table = (crypt_key_t **) menu->data;
2338 crypt_entry_t entry;
2340 entry.key = key_table[num];
2341 entry.num = num + 1;
2343 m_strformat(s, l, COLS - SW, PgpEntryFormat, crypt_entry_fmt, &entry,
2344 option(OPTARROWCURSOR) ? M_FORMAT_ARROWCURSOR : 0);
2347 /* Compare two addresses and the keyid to be used for sorting. */
2348 static int _crypt_compare_address (const void *a, const void *b)
2350 crypt_key_t **s = (crypt_key_t **) a;
2351 crypt_key_t **t = (crypt_key_t **) b;
2354 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2357 return m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t)) > 0;
2360 static int crypt_compare_address (const void *a, const void *b)
2362 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_address (a, b)
2363 : _crypt_compare_address (a, b));
2367 /* Compare two key IDs and the addresses to be used for sorting. */
2368 static int _crypt_compare_keyid (const void *a, const void *b)
2370 crypt_key_t **s = (crypt_key_t **) a;
2371 crypt_key_t **t = (crypt_key_t **) b;
2374 if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
2377 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2380 static int crypt_compare_keyid (const void *a, const void *b)
2382 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
2383 : _crypt_compare_keyid (a, b));
2386 /* Compare 2 creation dates and the addresses. For sorting. */
2387 static int _crypt_compare_date (const void *a, const void *b)
2389 crypt_key_t **s = (crypt_key_t **) a;
2390 crypt_key_t **t = (crypt_key_t **) b;
2391 unsigned long ts = 0, tt = 0;
2393 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2394 ts = (*s)->kobj->subkeys->timestamp;
2395 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2396 tt = (*t)->kobj->subkeys->timestamp;
2403 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2406 static int crypt_compare_date (const void *a, const void *b)
2408 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
2409 : _crypt_compare_date (a, b));
2412 /* Compare two trust values, the key length, the creation dates. the
2413 addresses and the key IDs. For sorting. */
2414 static int _crypt_compare_trust (const void *a, const void *b)
2416 crypt_key_t **s = (crypt_key_t **) a;
2417 crypt_key_t **t = (crypt_key_t **) b;
2418 unsigned long ts = 0, tt = 0;
2421 if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
2422 - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
2425 if ((*s)->kobj->uids)
2426 ts = (*s)->kobj->uids->validity;
2427 if ((*t)->kobj->uids)
2428 tt = (*t)->kobj->uids->validity;
2429 if ((r = (tt - ts)))
2432 if ((*s)->kobj->subkeys)
2433 ts = (*s)->kobj->subkeys->length;
2434 if ((*t)->kobj->subkeys)
2435 tt = (*t)->kobj->subkeys->length;
2439 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2440 ts = (*s)->kobj->subkeys->timestamp;
2441 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2442 tt = (*t)->kobj->subkeys->timestamp;
2448 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2450 return (m_strcasecmp(crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
2453 static int crypt_compare_trust (const void *a, const void *b)
2455 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
2456 : _crypt_compare_trust (a, b));
2459 /* Print the X.500 Distinguished Name part KEY from the array of parts
2461 static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
2465 for (; dn->key; dn++) {
2466 if (!m_strcmp(dn->key, key)) {
2469 print_utf8 (fp, dn->value, m_strlen(dn->value));
2476 /* Print all parts of a DN in a standard sequence. */
2477 static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
2479 const char *stdpart[] = {
2480 "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
2482 int any = 0, any2 = 0, i;
2484 for (i = 0; stdpart[i]; i++) {
2487 any = print_dn_part (fp, dn, stdpart[i]);
2489 /* now print the rest without any specific ordering */
2490 for (; dn->key; dn++) {
2491 for (i = 0; stdpart[i]; i++) {
2492 if (!m_strcmp(dn->key, stdpart[i]))
2500 any = print_dn_part (fp, dn, dn->key);
2509 /* Parse an RDN; this is a helper to parse_dn(). */
2510 static const unsigned char *parse_dn_part (struct dn_array_s *array,
2511 const unsigned char *string)
2513 const unsigned char *s, *s1;
2517 /* parse attributeType */
2518 for (s = string + 1; *s && *s != '='; s++);
2520 return NULL; /* error */
2523 return NULL; /* empty key */
2524 array->key = p_dupstr(string, n );
2525 p = (unsigned char *) array->key;
2528 if (*string == '#') { /* hexstring */
2530 for (s = string; hexval(*s) >= 0; s++)
2534 return NULL; /* empty or odd number of digits */
2536 p = p_new(unsigned char, n + 1);
2537 array->value = (char *) p;
2538 for (s1 = string; n; s1 += 2, n--)
2539 *p++ = (hexval(*s1) << 8) | hexval(*s1);
2542 else { /* regular v3 quoted string */
2543 for (n = 0, s = string; *s; s++) {
2544 if (*s == '\\') { /* pair */
2546 if (*s == ',' || *s == '=' || *s == '+'
2547 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
2548 || *s == '\\' || *s == '\"' || *s == ' ')
2550 else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
2555 return NULL; /* invalid escape sequence */
2557 else if (*s == '\"')
2558 return NULL; /* invalid encoding */
2559 else if (*s == ',' || *s == '=' || *s == '+'
2560 || *s == '<' || *s == '>' || *s == '#' || *s == ';')
2566 p = p_new(unsigned char, n + 1);
2567 array->value = (char *) p;
2568 for (s = string; n; s++, n--) {
2571 if (hexval(*s) >= 0) {
2572 *p++ = (hexval(*s) << 8) | hexval(*s + 1);
2587 /* Parse a DN and return an array-ized one. This is not a validating
2588 parser and it does not support any old-stylish syntax; gpgme is
2589 expected to return only rfc2253 compatible strings. */
2590 static struct dn_array_s *parse_dn (const unsigned char *string)
2592 struct dn_array_s *array;
2593 ssize_t arrayidx, arraysize;
2596 arraysize = 7; /* C,ST,L,O,OU,CN,email */
2597 array = p_new(struct dn_array_s, arraysize + 1);
2600 while (*string == ' ')
2604 if (arrayidx >= arraysize) { /* mutt lacks a real safe_realoc - so we need to copy */
2605 struct dn_array_s *a2;
2608 a2 = p_new(struct dn_array_s, arraysize + 1);
2609 for (i = 0; i < arrayidx; i++) {
2610 a2[i].key = array[i].key;
2611 a2[i].value = array[i].value;
2616 array[arrayidx].key = NULL;
2617 array[arrayidx].value = NULL;
2618 string = parse_dn_part (array + arrayidx, string);
2622 while (*string == ' ')
2624 if (*string && *string != ',' && *string != ';' && *string != '+')
2625 goto failure; /* invalid delimiter */
2629 array[arrayidx].key = NULL;
2630 array[arrayidx].value = NULL;
2634 for (i = 0; i < arrayidx; i++) {
2635 p_delete(&array[i].key);
2636 p_delete(&array[i].value);
2643 /* Print a nice representation of the USERID and make sure it is
2644 displayed in a proper way, which does mean to reorder some parts
2645 for S/MIME's DNs. USERID is a string as returned by the gpgme key
2646 functions. It is utf-8 encoded. */
2647 static void parse_and_print_user_id (FILE * fp, const char *userid)
2652 if (*userid == '<') {
2653 s = strchr (userid + 1, '>');
2655 print_utf8 (fp, userid + 1, s - userid - 1);
2657 else if (*userid == '(')
2658 fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
2659 else if (!digit_or_letter ((const unsigned char *) userid))
2660 fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
2662 struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
2665 fputs (_("[Can't display this user ID (invalid DN)]"), fp);
2667 print_dn_parts (fp, dn);
2668 for (i = 0; dn[i].key; i++) {
2669 p_delete(&dn[i].key);
2670 p_delete(&dn[i].value);
2678 KEY_CAP_CAN_ENCRYPT,
2683 static unsigned int key_check_cap (gpgme_key_t key, key_cap_t cap)
2685 gpgme_subkey_t subkey = NULL;
2686 unsigned int ret = 0;
2689 case KEY_CAP_CAN_ENCRYPT:
2690 if (!(ret = key->can_encrypt))
2691 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2692 if ((ret = subkey->can_encrypt))
2695 case KEY_CAP_CAN_SIGN:
2696 if (!(ret = key->can_sign))
2697 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2698 if ((ret = subkey->can_sign))
2701 case KEY_CAP_CAN_CERTIFY:
2702 if (!(ret = key->can_certify))
2703 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2704 if ((ret = subkey->can_certify))
2713 /* Print verbose information about a key or certificate to FP. */
2714 static void print_key_info (gpgme_key_t key, FILE * fp)
2717 const char *s = NULL, *s2 = NULL;
2720 char shortbuf[STRING];
2721 unsigned long aval = 0;
2725 gpgme_user_id_t uid = NULL;
2728 setlocale (LC_TIME, Locale);
2730 is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
2732 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2737 fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
2740 fputs (_("[Invalid]"), fp);
2744 print_utf8 (fp, s, m_strlen(s));
2746 parse_and_print_user_id (fp, s);
2750 if (key->subkeys && (key->subkeys->timestamp > 0)) {
2751 tt = key->subkeys->timestamp;
2753 tm = localtime (&tt);
2754 #ifdef HAVE_LANGINFO_D_T_FMT
2755 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2757 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2759 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2762 if (key->subkeys && (key->subkeys->expires > 0)) {
2763 tt = key->subkeys->expires;
2765 tm = localtime (&tt);
2766 #ifdef HAVE_LANGINFO_D_T_FMT
2767 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2769 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2771 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2775 s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
2779 s2 = is_pgp ? "PGP" : "X.509";
2782 aval = key->subkeys->length;
2784 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
2786 fprintf (fp, _("Key Usage .: "));
2789 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
2790 fprintf (fp, "%s%s", delim, _("encryption"));
2793 if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
2794 fprintf (fp, "%s%s", delim, _("signing"));
2797 if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
2798 fprintf (fp, "%s%s", delim, _("certification"));
2804 s = key->subkeys->fpr;
2805 fputs (_("Fingerprint: "), fp);
2806 if (is_pgp && m_strlen(s) == 40) {
2807 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
2812 putc (is_pgp ? ' ' : ':', fp);
2813 if (is_pgp && i == 4)
2818 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
2821 putc (is_pgp ? ' ' : ':', fp);
2822 if (is_pgp && i == 7)
2826 fprintf (fp, "%s\n", s);
2829 if (key->issuer_serial) {
2830 s = key->issuer_serial;
2832 fprintf (fp, _("Serial-No .: 0x%s\n"), s);
2835 if (key->issuer_name) {
2836 s = key->issuer_name;
2838 fprintf (fp, _("Issued By .: "));
2839 parse_and_print_user_id (fp, s);
2844 /* For PGP we list all subkeys. */
2846 gpgme_subkey_t subkey = NULL;
2848 for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
2852 if (m_strlen(s) == 16)
2853 s += 8; /* display only the short keyID */
2854 fprintf (fp, _("Subkey ....: 0x%s"), s);
2855 if (subkey->revoked) {
2857 fputs (_("[Revoked]"), fp);
2859 if (subkey->invalid) {
2861 fputs (_("[Invalid]"), fp);
2863 if (subkey->expired) {
2865 fputs (_("[Expired]"), fp);
2867 if (subkey->disabled) {
2869 fputs (_("[Disabled]"), fp);
2873 if (subkey->timestamp > 0) {
2874 tt = subkey->timestamp;
2876 tm = localtime (&tt);
2877 #ifdef HAVE_LANGINFO_D_T_FMT
2878 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2880 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2882 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2885 if (subkey->expires > 0) {
2886 tt = subkey->expires;
2888 tm = localtime (&tt);
2889 #ifdef HAVE_LANGINFO_D_T_FMT
2890 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2892 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2894 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2898 s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
2903 aval = subkey->length;
2907 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
2909 fprintf (fp, _("Key Usage .: "));
2912 if (subkey->can_encrypt) {
2913 fprintf (fp, "%s%s", delim, _("encryption"));
2916 if (subkey->can_sign) {
2917 fprintf (fp, "%s%s", delim, _("signing"));
2920 if (subkey->can_certify) {
2921 fprintf (fp, "%s%s", delim, _("certification"));
2929 setlocale (LC_TIME, "C");
2933 /* Show detailed information about the selected key */
2934 static void verify_key (crypt_key_t * key)
2937 char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
2939 gpgme_ctx_t listctx = NULL;
2941 gpgme_key_t k = NULL;
2944 fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2946 mutt_perror (_("Can't create temporary file"));
2949 mutt_message _("Collecting data...");
2951 print_key_info (key->kobj, fp);
2953 err = gpgme_new (&listctx);
2955 fprintf (fp, "Internal error: can't create gpgme context: %s\n",
2956 gpgme_strerror (err));
2959 if ((key->flags & KEYFLAG_ISX509))
2960 gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
2964 while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
2966 err = gpgme_op_keylist_start (listctx, s, 0);
2967 gpgme_key_release (k);
2970 err = gpgme_op_keylist_next (listctx, &k);
2972 fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
2975 gpgme_op_keylist_end (listctx);
2977 print_key_info (k, fp);
2980 fputs (_("Error: certification chain to long - stopping here\n"), fp);
2986 gpgme_key_release (k);
2987 gpgme_release (listctx);
2989 mutt_clear_error ();
2990 snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
2991 mutt_do_pager (cmd, tempfile, 0, NULL);
2994 /* Implementation of `findkeys'. */
2996 /* Convert string_list_t into a pattern string suitable to be passed to GPGME.
2997 We need to convert spaces in an item into a '+' and '%' into
2999 static char *list_to_pattern (string_list_t * list)
3007 for (l = list; l; l = l->next) {
3008 for (s = l->data; *s; s++) {
3013 n++; /* delimiter or end of string */
3015 n++; /* make sure to allocate at least one byte */
3016 pattern = p = p_new(char, n);
3017 for (l = list; l; l = l->next) {
3022 for (s = l->data; *s; s++) {
3028 else if (*s == '+') {
3044 /* Return a list of keys which are candidates for the selection.
3045 Select by looking at the HINTS list. */
3046 static crypt_key_t *get_candidates (string_list_t * hints, unsigned int app,
3049 crypt_key_t *db, *k, **kend;
3055 gpgme_user_id_t uid = NULL;
3057 pattern = list_to_pattern (hints);
3061 err = gpgme_new (&ctx);
3063 mutt_error (_("gpgme_new failed: %s"), gpgme_strerror (err));
3071 if ((app & APPLICATION_PGP)) {
3072 /* Its all a mess. That old GPGME expects different things
3073 depending on the protocol. For gpg we don' t need percent
3074 escaped pappert but simple strings passed in an array to the
3075 keylist_ext_start function. */
3080 for (l = hints, n = 0; l; l = l->next) {
3081 if (l->data && *l->data)
3087 patarr = p_new(char *, n + 1);
3088 for (l = hints, n = 0; l; l = l->next) {
3089 if (l->data && *l->data)
3090 patarr[n++] = m_strdup(l->data);
3093 err = gpgme_op_keylist_ext_start (ctx, (const char **) patarr, secret, 0);
3094 for (n = 0; patarr[n]; n++)
3095 p_delete(&patarr[n]);
3098 mutt_error (_("gpgme_op_keylist_start failed: %s"), gpgme_strerror (err));
3099 gpgme_release (ctx);
3104 while (!(err = gpgme_op_keylist_next (ctx, &key))) {
3105 unsigned int flags = 0;
3107 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
3108 flags |= KEYFLAG_CANENCRYPT;
3109 if (key_check_cap (key, KEY_CAP_CAN_SIGN))
3110 flags |= KEYFLAG_CANSIGN;
3112 #if 0 /* DISABLED code */
3114 /* Bug in gpg. Capabilities are not listed for secret
3115 keys. Try to deduce them from the algorithm. */
3117 switch (key->subkeys[0].pubkey_algo) {
3119 flags |= KEYFLAG_CANENCRYPT;
3120 flags |= KEYFLAG_CANSIGN;
3122 case GPGME_PK_ELG_E:
3123 flags |= KEYFLAG_CANENCRYPT;
3126 flags |= KEYFLAG_CANSIGN;
3130 #endif /* DISABLED code */
3132 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
3133 k = p_new(crypt_key_t, 1);
3142 if (gpg_err_code (err) != GPG_ERR_EOF)
3143 mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err));
3144 gpgme_op_keylist_end (ctx);
3149 if ((app & APPLICATION_SMIME)) {
3150 /* and now look for x509 certificates */
3151 gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
3152 err = gpgme_op_keylist_start (ctx, pattern, 0);
3154 mutt_error (_("gpgme_op_keylist_start failed: %s"), gpgme_strerror (err));
3155 gpgme_release (ctx);
3160 while (!(err = gpgme_op_keylist_next (ctx, &key))) {
3161 unsigned int flags = KEYFLAG_ISX509;
3163 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
3164 flags |= KEYFLAG_CANENCRYPT;
3165 if (key_check_cap (key, KEY_CAP_CAN_SIGN))
3166 flags |= KEYFLAG_CANSIGN;
3168 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
3169 k = p_new(crypt_key_t, 1);
3178 if (gpg_err_code (err) != GPG_ERR_EOF)
3179 mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err));
3180 gpgme_op_keylist_end (ctx);
3183 gpgme_release (ctx);
3188 /* Add the string STR to the list HINTS. This list is later used to
3190 static string_list_t *crypt_add_string_to_hints (string_list_t * hints, const char *str)
3195 if ((scratch = m_strdup(str)) == NULL)
3198 for (t = strtok (scratch, " ,.:\"()<>\n"); t;
3199 t = strtok (NULL, " ,.:\"()<>\n")) {
3200 if (m_strlen(t) > 3)
3201 hints = mutt_add_list(hints, t);
3208 /* Display a menu to select a key from the array KEYS. FORCED_VALID
3209 will be set to true on return if the user did override the the
3211 static crypt_key_t *crypt_select_key (crypt_key_t * keys,
3212 address_t * p, const char *s,
3213 unsigned int app, int *forced_valid)
3216 crypt_key_t **key_table;
3219 char helpstr[STRING], buf[LONG_STRING];
3221 int (*f) (const void *, const void *);
3222 int menu_to_use = 0;
3227 /* build the key table */
3230 for (k = keys; k; k = k->next) {
3231 if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
3238 p_realloc(&key_table, keymax);
3244 if (!i && unusable) {
3245 mutt_error _("All matching keys are marked expired/revoked.");
3251 switch (PgpSortKeys & SORT_MASK) {
3253 f = crypt_compare_date;
3256 f = crypt_compare_keyid;
3259 f = crypt_compare_address;
3263 f = crypt_compare_trust;
3266 qsort (key_table, i, sizeof (crypt_key_t *), f);
3268 if (app & APPLICATION_PGP)
3269 menu_to_use = MENU_KEY_SELECT_PGP;
3270 else if (app & APPLICATION_SMIME)
3271 menu_to_use = MENU_KEY_SELECT_SMIME;
3274 mutt_make_help (buf, sizeof (buf), _("Exit "), menu_to_use, OP_EXIT);
3275 m_strcat(helpstr, sizeof(helpstr), buf);
3276 mutt_make_help (buf, sizeof (buf), _("Select "), menu_to_use,
3277 OP_GENERIC_SELECT_ENTRY);
3278 m_strcat(helpstr, sizeof(helpstr), buf);
3279 mutt_make_help (buf, sizeof (buf), _("Check key "),
3280 menu_to_use, OP_VERIFY_KEY);
3281 m_strcat(helpstr, sizeof(helpstr), buf);
3282 mutt_make_help (buf, sizeof (buf), _("Help"), menu_to_use, OP_HELP);
3283 m_strcat(helpstr, sizeof(helpstr), buf);
3285 menu = mutt_new_menu ();
3287 menu->make_entry = crypt_entry;
3288 menu->menu = menu_to_use;
3289 menu->help = helpstr;
3290 menu->data = key_table;
3295 if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
3296 ts = _("PGP and S/MIME keys matching");
3297 else if ((app & APPLICATION_PGP))
3298 ts = _("PGP keys matching");
3299 else if ((app & APPLICATION_SMIME))
3300 ts = _("S/MIME keys matching");
3302 ts = _("keys matching");
3305 snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
3307 snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
3311 mutt_clear_error ();
3315 switch (mutt_menuLoop (menu)) {
3317 verify_key (key_table[menu->current]);
3318 menu->redraw = REDRAW_FULL;
3322 mutt_message ("%s", key_table[menu->current]->uid);
3325 case OP_GENERIC_SELECT_ENTRY:
3326 /* FIXME make error reporting more verbose - this should be
3327 easy because gpgme provides more information */
3328 if (option (OPTPGPCHECKTRUST)) {
3329 if (!crypt_key_is_valid (key_table[menu->current])) {
3330 mutt_error _("This key can't be used: "
3331 "expired/disabled/revoked.");
3336 if (option (OPTPGPCHECKTRUST) &&
3337 (!crypt_id_is_valid (key_table[menu->current])
3338 || !crypt_id_is_strong (key_table[menu->current]))) {
3340 char buff[LONG_STRING];
3342 if (key_table[menu->current]->flags & KEYFLAG_CANTUSE)
3343 s = N_("ID is expired/disabled/revoked.");
3345 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
3346 gpgme_user_id_t uid = NULL;
3351 uid = key_table[menu->current]->kobj->uids;
3352 for (j = 0; (j < key_table[menu->current]->idx) && uid;
3353 j++, uid = uid->next);
3355 val = uid->validity;
3358 case GPGME_VALIDITY_UNKNOWN:
3359 case GPGME_VALIDITY_UNDEFINED:
3360 warn_s = N_("ID has undefined validity.");
3362 case GPGME_VALIDITY_NEVER:
3363 warn_s = N_("ID is not valid.");
3365 case GPGME_VALIDITY_MARGINAL:
3366 warn_s = N_("ID is only marginally valid.");
3368 case GPGME_VALIDITY_FULL:
3369 case GPGME_VALIDITY_ULTIMATE:
3373 snprintf (buff, sizeof (buff),
3374 _("%s Do you really want to use the key?"), _(warn_s));
3376 if (mutt_yesorno (buff, 0) != 1) {
3377 mutt_clear_error ();
3384 k = crypt_copy_key (key_table[menu->current]);
3395 mutt_menuDestroy (&menu);
3396 p_delete(&key_table);
3398 set_option (OPTNEEDREDRAW);
3403 static crypt_key_t *crypt_getkeybyaddr (address_t * a, short abilities,
3404 unsigned int app, int *forced_valid)
3407 string_list_t *hints = NULL;
3412 int this_key_has_strong;
3413 int this_key_has_weak;
3414 int this_key_has_invalid;
3417 crypt_key_t *keys, *k;
3418 crypt_key_t *the_valid_key = NULL;
3419 crypt_key_t *matches = NULL;
3420 crypt_key_t **matches_endp = &matches;
3424 if (a && a->mailbox)
3425 hints = crypt_add_string_to_hints (hints, a->mailbox);
3426 if (a && a->personal)
3427 hints = crypt_add_string_to_hints (hints, a->personal);
3429 mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
3430 keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
3432 string_list_wipe(&hints);
3437 for (k = keys; k; k = k->next) {
3438 if (abilities && !(k->flags & abilities)) {
3442 this_key_has_weak = 0; /* weak but valid match */
3443 this_key_has_invalid = 0; /* invalid match */
3444 this_key_has_strong = 0; /* strong and valid match */
3445 match = 0; /* any match */
3447 r = rfc822_parse_adrlist (NULL, k->uid);
3448 for (p = r; p; p = p->next) {
3449 int validity = crypt_id_matches_addr (a, p, k);
3451 if (validity & CRYPT_KV_MATCH) /* something matches */
3454 /* is this key a strong candidate? */
3455 if ((validity & CRYPT_KV_VALID)
3456 && (validity & CRYPT_KV_STRONGID)
3457 && (validity & CRYPT_KV_ADDR)) {
3458 if (the_valid_key && the_valid_key != k)
3461 this_key_has_strong = 1;
3463 else if ((validity & CRYPT_KV_MATCH)
3464 && !(validity & CRYPT_KV_VALID))
3465 this_key_has_invalid = 1;
3466 else if ((validity & CRYPT_KV_MATCH)
3467 && (!(validity & CRYPT_KV_STRONGID)
3468 || !(validity & CRYPT_KV_ADDR)))
3469 this_key_has_weak = 1;
3471 address_list_wipe(&r);
3476 if (!this_key_has_strong && this_key_has_invalid)
3478 if (!this_key_has_strong && this_key_has_weak)
3481 *matches_endp = tmp = crypt_copy_key (k);
3482 matches_endp = &tmp->next;
3483 the_valid_key = tmp;
3487 crypt_free_key (&keys);
3490 if (the_valid_key && !multi && !weak
3491 && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
3493 * There was precisely one strong match on a valid ID, there
3494 * were no valid keys with weak matches, and we aren't
3495 * interested in seeing invalid keys.
3497 * Proceed without asking the user.
3499 k = crypt_copy_key (the_valid_key);
3503 * Else: Ask the user.
3505 k = crypt_select_key (matches, a, NULL, app, forced_valid);
3507 crypt_free_key (&matches);
3516 static crypt_key_t *crypt_getkeybystr (const char *p, short abilities,
3517 unsigned int app, int *forced_valid)
3519 string_list_t *hints = NULL;
3521 crypt_key_t *matches = NULL;
3522 crypt_key_t **matches_endp = &matches;
3526 mutt_message (_("Looking for keys matching \"%s\"..."), p);
3530 hints = crypt_add_string_to_hints (hints, p);
3531 keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
3532 string_list_wipe(&hints);
3537 for (k = keys; k; k = k->next) {
3538 if (abilities && !(k->flags & abilities))
3543 if (!*p || !m_strcasecmp(p, crypt_keyid (k))
3544 || (!m_strncasecmp(p, "0x", 2)
3545 && !m_strcasecmp(p + 2, crypt_keyid (k)))
3546 || (option (OPTPGPLONGIDS)
3547 && !m_strncasecmp(p, "0x", 2)
3548 && !m_strcasecmp(p + 2, crypt_keyid (k) + 8))
3549 || m_stristr(k->uid, p)) {
3552 *matches_endp = tmp = crypt_copy_key (k);
3553 matches_endp = &tmp->next;
3557 crypt_free_key (&keys);
3560 k = crypt_select_key (matches, NULL, p, app, forced_valid);
3561 crypt_free_key (&matches);
3568 /* Display TAG as a prompt to ask for a key. If WHATFOR is not null
3569 use it as default and store it under that label as the next
3570 default. ABILITIES describe the required key abilities (sign,
3571 encrypt) and APP the type of the requested key; ether S/MIME or
3572 PGP. Return a copy of the key or NULL if not found. */
3573 static crypt_key_t *crypt_ask_for_key (char *tag,
3576 unsigned int app, int *forced_valid)
3580 struct crypt_cache *l = NULL;
3584 forced_valid = &dummy;
3586 mutt_clear_error ();
3592 for (l = id_defaults; l; l = l->next)
3593 if (!m_strcasecmp(whatfor, l->what)) {
3594 m_strcpy(resp, sizeof(resp), NONULL(l->dflt));
3602 if (mutt_get_field (tag, resp, sizeof (resp), M_CLEAR) != 0)
3607 m_strreplace(&l->dflt, resp);
3609 l = p_new(struct crypt_cache, 1);
3610 l->next = id_defaults;
3612 l->what = m_strdup(whatfor);
3613 l->dflt = m_strdup(resp);
3617 if ((key = crypt_getkeybystr (resp, abilities, app, forced_valid)))
3625 /* This routine attempts to find the keyids of the recipients of a
3626 message. It returns NULL if any of the keys can not be found. */
3627 static char *find_keys (address_t * to, address_t * cc, address_t * bcc,
3630 char *keylist = NULL, *t;
3632 ssize_t keylist_size = 0;
3633 ssize_t keylist_used = 0;
3634 address_t *tmp = NULL, *addr = NULL;
3635 address_t **last = &tmp;
3638 crypt_key_t *k_info, *key;
3639 const char *fqdn = mutt_fqdn (1);
3642 *r_application = APPLICATION_PGP | APPLICATION_SMIME;
3645 for (i = 0; i < 3; i++) {
3660 *last = address_list_dup (p);
3662 last = &((*last)->next);
3665 rfc822_qualify(tmp, fqdn);
3666 address_list_uniq(tmp);
3668 for (p = tmp; p; p = p->next) {
3669 char buf[LONG_STRING];
3670 int forced_valid = 0;
3675 if ((keyID = mutt_crypt_hook (p)) != NULL) {
3678 snprintf (buf, sizeof (buf), _("Use keyID = \"%s\" for %s?"),
3680 if ((r = mutt_yesorno (buf, M_YES)) == M_YES) {
3681 /* check for e-mail address */
3682 if ((t = strchr (keyID, '@')) &&
3683 (addr = rfc822_parse_adrlist (NULL, keyID))) {
3684 rfc822_qualify(addr, fqdn);
3688 k_info = crypt_getkeybystr (keyID, KEYFLAG_CANENCRYPT,
3689 app, &forced_valid);
3694 address_list_wipe(&tmp);
3695 address_list_wipe(&addr);
3701 && (k_info = crypt_getkeybyaddr (q, KEYFLAG_CANENCRYPT,
3702 app, &forced_valid)) == NULL) {
3703 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
3705 if ((key = crypt_ask_for_key (buf, q->mailbox, KEYFLAG_CANENCRYPT,
3707 &forced_valid)) == NULL) {
3709 address_list_wipe(&tmp);
3710 address_list_wipe(&addr);
3718 const char *s = crypt_fpr (key);
3720 keylist_size += m_strlen(s) + 4 + 1;
3721 p_realloc(&keylist, keylist_size);
3722 sprintf (keylist + keylist_used, "%s0x%s%s",
3723 keylist_used ? " " : "", s, forced_valid ? "!" : "");
3725 keylist_used = m_strlen(keylist);
3727 crypt_free_key (&key);
3728 address_list_wipe(&addr);
3730 address_list_wipe(&tmp);
3734 char *crypt_pgp_findkeys (address_t * to, address_t * cc, address_t * bcc)
3736 return find_keys (to, cc, bcc, APPLICATION_PGP);
3739 char *crypt_smime_findkeys (address_t * to, address_t * cc, address_t * bcc)
3741 return find_keys (to, cc, bcc, APPLICATION_SMIME);
3744 static int gpgme_send_menu (HEADER * msg, int *redraw, int is_smime)
3747 char input_signas[STRING];
3750 if (msg->security & APPLICATION_PGP)
3752 else if (msg->security & APPLICATION_SMIME)
3757 mutt_multi_choice (_
3758 ("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
3762 mutt_multi_choice (_
3763 ("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
3767 case 1: /* (e)ncrypt */
3768 msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3769 msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
3772 case 2: /* (s)ign */
3773 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3774 msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3777 case 3: /* sign (a)s */
3778 /* unset_option(OPTCRYPTCHECKTRUST); */
3779 if ((p = crypt_ask_for_key (_("Sign as: "), NULL, KEYFLAG_CANSIGN,
3780 is_smime ? APPLICATION_SMIME :
3781 APPLICATION_PGP, NULL))) {
3782 snprintf (input_signas, sizeof (input_signas), "0x%s", crypt_keyid (p));
3783 m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs,
3785 crypt_free_key (&p);
3787 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3789 *redraw = REDRAW_FULL;
3792 case 4: /* (b)oth */
3794 (is_smime ? (SMIMEENCRYPT | SMIMESIGN) : (PGPENCRYPT | PGPSIGN));
3797 case 5: /* (p)gp or s/(m)ime */
3798 is_smime = !is_smime;
3801 case 6: /* (c)lear */
3806 if (choice == 6 || choice == 7);
3807 else if (is_smime) {
3808 msg->security &= ~APPLICATION_PGP;
3809 msg->security |= APPLICATION_SMIME;
3812 msg->security &= ~APPLICATION_SMIME;
3813 msg->security |= APPLICATION_PGP;
3816 return (msg->security);
3819 int crypt_pgp_send_menu(HEADER * msg, int *redraw)
3821 return gpgme_send_menu(msg, redraw, 0);
3824 int crypt_smime_send_menu(HEADER * msg, int *redraw)
3826 return gpgme_send_menu (msg, redraw, 1);
3829 int crypt_smime_verify_sender (HEADER * h)
3831 address_t *sender = NULL;
3832 unsigned int ret = 1;
3835 h->env->from = mutt_expand_aliases (h->env->from);
3836 sender = h->env->from;
3838 else if (h->env->sender) {
3839 h->env->sender = mutt_expand_aliases (h->env->sender);
3840 sender = h->env->sender;
3844 if (signature_key) {
3845 gpgme_key_t key = signature_key;
3846 gpgme_user_id_t uid = NULL;
3847 int sender_length = 0;
3850 sender_length = m_strlen(sender->mailbox);
3851 for (uid = key->uids; uid && ret; uid = uid->next) {
3852 uid_length = m_strlen(uid->email);
3853 if (1 && (uid->email[0] == '<')
3854 && (uid->email[uid_length - 1] == '>')
3855 && (uid_length == sender_length + 2)
3856 && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
3861 mutt_any_key_to_continue ("Failed to verify sender");
3864 mutt_any_key_to_continue ("Failed to figure out sender");
3866 if (signature_key) {
3867 gpgme_key_release (signature_key);
3868 signature_key = NULL;
3874 static void invoke_import(const char *fname, int smime)
3876 gpgme_ctx_t ctx = create_gpgme_context(smime);
3880 err = gpgme_data_new_from_file(&data, fname, 1);
3882 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
3887 err = gpgme_op_import(ctx, data);
3889 mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
3890 gpgme_data_release(data);
3895 gpgme_data_release(data);
3900 void crypt_pgp_invoke_import(const char *fname)
3902 invoke_import(fname, 0);
3905 void crypt_smime_invoke_import(const char *fname)
3907 invoke_import(fname, 1);
3910 static void pgp_extract_keys_from_attachment (FILE * fp, BODY * top)
3914 char tempfname[_POSIX_PATH_MAX];
3916 tempfp = m_tempfile(tempfname, sizeof(tempfname), NONULL(MCore.tmpdir), NULL);
3917 if (tempfp == NULL) {
3918 mutt_perror (_("Can't create temporary file"));
3927 mutt_body_handler (top, &s);
3930 crypt_pgp_invoke_import(tempfname);
3931 mutt_unlink (tempfname);
3934 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
3937 set_option (OPTDONTHANDLEPGPKEYS);
3939 for (; top; top = top->next) {
3940 if (!tag || top->tagged)
3941 pgp_extract_keys_from_attachment (fp, top);
3947 unset_option (OPTDONTHANDLEPGPKEYS);
3953 /* fixme: needs documentation. */
3954 void crypt_pgp_invoke_getkeys (address_t * addr)
3958 /* Generate a PGP public key attachment. */
3959 BODY *crypt_pgp_make_key_attachment (char *tempf)
3966 /* fixme: Needs documentation. */
3967 void crypt_smime_getkeys (ENVELOPE * env)