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;
88 /* Make sure that gpg-agent is running. */
89 if (!getenv ("GPG_AGENT_INFO")) {
90 mutt_error ("\nUsing GPGME backend, although no gpg-agent is running");
91 if (mutt_any_key_to_continue (NULL) == -1)
96 /* Show a message that a backend will be invoked. */
97 void crypt_invoke_message (int type)
99 if (type & APPLICATION_PGP) {
100 mutt_message _("Invoking PGP...");
102 else if (type & APPLICATION_SMIME) {
103 mutt_message _("Invoking S/MIME...");
108 * General helper functions.
111 /* return true when S points to a didgit or letter. */
112 static int digit_or_letter (const unsigned char *s)
114 return ((*s >= '0' && *s <= '9')
115 || (*s >= 'A' && *s <= 'Z')
116 || (*s >= 'a' && *s <= 'z'));
120 /* Print the utf-8 encoded string BUF of length LEN bytes to stream
121 FP. Convert the character set. */
122 static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
126 tstr = p_dupstr(buf, len);
127 mutt_convert_string (&tstr, "utf-8", MCharset.charset, M_ICONV_HOOK_FROM);
137 /* Return the keyID for the key K. Note that this string is valid as
138 long as K is valid */
139 static const char *crypt_keyid (crypt_key_t * k)
141 const char *s = "????????";
143 if (k->kobj && k->kobj->subkeys) {
144 s = k->kobj->subkeys->keyid;
145 if ((!option (OPTPGPLONGIDS)) && (m_strlen(s) == 16))
146 /* Return only the short keyID. */
153 /* Return the hexstring fingerprint from the key K. */
154 static const char *crypt_fpr (crypt_key_t * k)
158 if (k->kobj && k->kobj->subkeys)
159 s = k->kobj->subkeys->fpr;
164 /* Parse FLAGS and return a statically allocated(!) string with them. */
165 static char *crypt_key_abilities (int flags)
169 if (!(flags & KEYFLAG_CANENCRYPT))
171 else if (flags & KEYFLAG_PREFER_SIGNING)
176 if (!(flags & KEYFLAG_CANSIGN))
178 else if (flags & KEYFLAG_PREFER_ENCRYPTION)
188 /* Parse FLAGS and return a character describing the most important flag. */
189 static char crypt_flags (int flags)
191 if (flags & KEYFLAG_REVOKED)
193 else if (flags & KEYFLAG_EXPIRED)
195 else if (flags & KEYFLAG_DISABLED)
197 else if (flags & KEYFLAG_CRITICAL)
203 /* Return a copy of KEY. */
204 static crypt_key_t *crypt_copy_key (crypt_key_t *key)
208 k = p_new(crypt_key_t, 1);
210 gpgme_key_ref (key->kobj);
213 k->flags = key->flags;
218 /* Release all the keys at the address of KEYLIST and set the address
220 static void crypt_free_key (crypt_key_t ** keylist)
223 crypt_key_t *k = (*keylist)->next;
230 /* Return trute when key K is valid. */
231 static int crypt_key_is_valid (crypt_key_t * k)
233 if (k->flags & KEYFLAG_CANTUSE)
238 /* Return true whe validity of KEY is sufficient. */
239 static int crypt_id_is_strong (crypt_key_t * key)
241 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
242 gpgme_user_id_t uid = NULL;
246 if ((key->flags & KEYFLAG_ISX509))
249 for (i = 0, uid = key->kobj->uids; (i < key->idx) && uid;
250 i++, uid = uid->next);
255 case GPGME_VALIDITY_UNKNOWN:
256 case GPGME_VALIDITY_UNDEFINED:
257 case GPGME_VALIDITY_NEVER:
258 case GPGME_VALIDITY_MARGINAL:
262 case GPGME_VALIDITY_FULL:
263 case GPGME_VALIDITY_ULTIMATE:
271 /* Return true when the KEY is valid, i.e. not marked as unusable. */
272 static int crypt_id_is_valid (crypt_key_t * key)
274 return !(key->flags & KEYFLAG_CANTUSE);
277 /* Return a bit vector describing how well the addresses ADDR and
278 U_ADDR match and whether KEY is valid. */
279 static int crypt_id_matches_addr (address_t * addr, address_t * u_addr,
284 if (crypt_id_is_valid (key))
285 rv |= CRYPT_KV_VALID;
287 if (crypt_id_is_strong (key))
288 rv |= CRYPT_KV_STRONGID;
290 if (addr->mailbox && u_addr->mailbox
291 && m_strcasecmp(addr->mailbox, u_addr->mailbox) == 0)
294 if (addr->personal && u_addr->personal
295 && m_strcasecmp(addr->personal, u_addr->personal) == 0)
296 rv |= CRYPT_KV_STRING;
303 * GPGME convenient functions.
306 /* Create a new gpgme context and return it. With FOR_SMIME set to
307 true, the protocol of the context is set to CMS. */
308 static gpgme_ctx_t create_gpgme_context (int for_smime)
313 err = gpgme_new (&ctx);
315 mutt_error (_("error creating gpgme context: %s\n"), gpgme_strerror (err));
321 err = gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
323 mutt_error (_("error enabling CMS protocol: %s\n"), gpgme_strerror (err));
332 /* Create a new gpgme data object. This is a wrapper to die on
334 static gpgme_data_t create_gpgme_data (void)
339 err = gpgme_data_new (&data);
341 mutt_error (_("error creating gpgme data object: %s\n"),
342 gpgme_strerror (err));
349 /* Create a new GPGME Data object from the mail body A. With CONVERT
350 passed as true, the lines are converted to CR,LF if required.
351 Return NULL on error or the gpgme_data_t object on success. */
352 static gpgme_data_t body_to_data_object (BODY * a, int convert)
354 char tempfile[_POSIX_PATH_MAX];
359 fptmp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
361 mutt_perror (_("Can't create temporary file"));
365 mutt_write_mime_header (a, fptmp);
367 mutt_write_mime_body (a, fptmp);
371 unsigned char buf[1];
373 data = create_gpgme_data ();
375 while ((c = fgetc (fptmp)) != EOF) {
379 if (c == '\n' && !hadcr) {
381 gpgme_data_write (data, buf, 1);
386 /* FIXME: This is quite suboptimal */
388 gpgme_data_write (data, buf, 1);
390 gpgme_data_seek (data, 0, SEEK_SET);
392 err = gpgme_data_new_from_file (&data, tempfile, 1);
397 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
404 /* Create a GPGME data object from the stream FP but limit the object
405 to LENGTH bytes starting at OFFSET bytes from the beginning of the
407 static gpgme_data_t file_to_data_object (FILE * fp, long offset, long length)
412 err = gpgme_data_new_from_filepart (&data, NULL, fp, offset, length);
414 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
421 /* Write a GPGME data object to the stream FP. */
422 static int data_object_to_stream (gpgme_data_t data, FILE * fp)
428 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
429 ? gpgme_error_from_errno (errno) : 0);
431 mutt_error (_("error rewinding data object: %s\n"), gpgme_strerror (err));
435 while ((nread = gpgme_data_read (data, buf, sizeof (buf)))) {
436 /* fixme: we are not really converting CRLF to LF but just
437 skipping CR. Doing it correctly needs a more complex logic */
438 for (p = buf; nread; p++, nread--) {
444 mutt_perror ("[tempfile]");
449 mutt_error (_("error reading data object: %s\n"), strerror (errno));
455 /* Copy a data object to a newly created temporay file and return that
456 filename. Caller must free. With RET_FP not NULL, don't close the
457 stream but return it there. */
458 static char *data_object_to_tempfile (gpgme_data_t data, FILE ** ret_fp)
461 char tempfile[_POSIX_PATH_MAX];
465 fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
467 mutt_perror (_("Can't create temporary file"));
471 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
472 ? gpgme_error_from_errno (errno) : 0);
476 while ((nread = gpgme_data_read (data, buf, sizeof (buf)))) {
477 if (fwrite (buf, nread, 1, fp) != 1) {
478 mutt_perror (_("Can't create temporary file"));
490 mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
497 return m_strdup(tempfile);
501 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
502 The keys must be space delimited. */
503 static gpgme_key_t *create_recipient_set (const char *keylist,
504 gpgme_protocol_t protocol)
510 gpgme_key_t *rset = NULL;
511 unsigned int rset_n = 0;
512 gpgme_key_t key = NULL;
513 gpgme_ctx_t context = NULL;
515 err = gpgme_new (&context);
517 err = gpgme_set_protocol (context, protocol);
524 for (i = 0; *s && *s != ' ' && i < ssizeof(buf) - 1;)
528 if (i > 1 && buf[i - 1] == '!') {
529 /* The user selected to override the valididy of that
533 err = gpgme_get_key (context, buf, &key, 0);
535 key->uids->validity = GPGME_VALIDITY_FULL;
539 err = gpgme_get_key (context, buf, &key, 0);
542 p_realloc(&rset, rset_n + 1);
543 rset[rset_n++] = key;
546 mutt_error (_("error adding recipient `%s': %s\n"),
547 buf, gpgme_strerror (err));
555 /* NULL terminate. */
556 p_realloc(&rset, rset_n + 1);
557 rset[rset_n++] = NULL;
560 gpgme_release (context);
566 /* Make sure that the correct signer is set. Returns 0 on success. */
567 static int set_signer (gpgme_ctx_t ctx, int for_smime)
569 char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
572 gpgme_key_t key, key2;
574 if (!signid || !*signid)
577 listctx = create_gpgme_context (for_smime);
578 err = gpgme_op_keylist_start (listctx, signid, 1);
580 err = gpgme_op_keylist_next (listctx, &key);
582 gpgme_release (listctx);
583 mutt_error (_("secret key `%s' not found: %s\n"),
584 signid, gpgme_strerror (err));
587 err = gpgme_op_keylist_next (listctx, &key2);
589 gpgme_key_release (key);
590 gpgme_key_release (key2);
591 gpgme_release (listctx);
592 mutt_error (_("ambiguous specification of secret key `%s'\n"), signid);
595 gpgme_op_keylist_end (listctx);
596 gpgme_release (listctx);
598 gpgme_signers_clear (ctx);
599 err = gpgme_signers_add (ctx, key);
600 gpgme_key_release (key);
602 mutt_error (_("error setting secret key `%s': %s\n"),
603 signid, gpgme_strerror (err));
610 /* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
611 and return an allocated filename to a temporary file containing the
612 enciphered text. With USE_SMIME set to true, the smime backend is
613 used. With COMBINED_SIGNED a PGP message is signed and
614 encrypted. Returns NULL in case of error */
615 static char *encrypt_gpgme_object (gpgme_data_t plaintext, gpgme_key_t * rset,
616 int use_smime, int combined_signed)
620 gpgme_data_t ciphertext;
623 ctx = create_gpgme_context (use_smime);
625 gpgme_set_armor (ctx, 1);
627 ciphertext = create_gpgme_data ();
629 if (combined_signed) {
630 if (set_signer (ctx, use_smime)) {
631 gpgme_data_release (ciphertext);
635 err = gpgme_op_encrypt_sign (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
636 plaintext, ciphertext);
639 err = gpgme_op_encrypt (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
640 plaintext, ciphertext);
641 mutt_need_hard_redraw ();
643 mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
644 gpgme_data_release (ciphertext);
651 outfile = data_object_to_tempfile (ciphertext, NULL);
652 gpgme_data_release (ciphertext);
656 /* Find the "micalg" parameter from the last Gpgme operation on
657 context CTX. It is expected that this operation was a sign
658 operation. Return the algorithm name as a C string in buffer BUF
659 which must have been allocated by the caller with size BUFLEN.
660 Returns 0 on success or -1 in case of an error. The return string
661 is truncted to BUFLEN - 1. */
662 static int get_micalg (gpgme_ctx_t ctx, char *buf, ssize_t buflen)
664 gpgme_sign_result_t result = NULL;
665 const char *algorithm_name = NULL;
671 result = gpgme_op_sign_result (ctx);
673 algorithm_name = gpgme_hash_algo_name (result->signatures->hash_algo);
674 if (algorithm_name) {
675 m_strcpy(buf, buflen, algorithm_name);
679 return *buf ? 0 : -1;
682 static void print_time (time_t t, STATE * s)
686 setlocale (LC_TIME, "");
687 #ifdef HAVE_LANGINFO_D_T_FMT
688 strftime (p, sizeof (p), nl_langinfo (D_T_FMT), localtime (&t));
690 strftime (p, sizeof (p), "%c", localtime (&t));
692 setlocale (LC_TIME, "C");
693 state_attach_puts (p, s);
696 /* Implementation of `sign_message'. */
698 /* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
699 USE_SMIME is passed as true. Returns the new body or NULL on
701 static BODY *sign_message (BODY * a, int use_smime)
708 gpgme_data_t message, signature;
710 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
712 message = body_to_data_object (a, 1);
715 signature = create_gpgme_data ();
717 ctx = create_gpgme_context (use_smime);
719 gpgme_set_armor (ctx, 1);
721 if (set_signer (ctx, use_smime)) {
722 gpgme_data_release (signature);
727 err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH);
728 mutt_need_hard_redraw ();
729 gpgme_data_release (message);
731 gpgme_data_release (signature);
733 mutt_error (_("error signing data: %s\n"), gpgme_strerror (err));
737 sigfile = data_object_to_tempfile (signature, NULL);
738 gpgme_data_release (signature);
745 t->type = TYPEMULTIPART;
746 t->subtype = m_strdup("signed");
747 t->encoding = ENC7BIT;
749 t->disposition = DISPINLINE;
751 parameter_set_boundary(&t->parameter);
752 parameter_setval(&t->parameter, "protocol",
753 use_smime ? "application/pkcs7-signature"
754 : "application/pgp-signature");
755 /* Get the micalg from gpgme. Old gpgme versions don't support this
756 for S/MIME so we assume sha-1 in this case. */
757 if (!get_micalg (ctx, buf, sizeof buf))
758 parameter_setval(&t->parameter, "micalg", buf);
760 parameter_setval(&t->parameter, "micalg", "sha1");
766 t->parts->next = body_new();
768 t->type = TYPEAPPLICATION;
770 t->subtype = m_strdup("pkcs7-signature");
771 parameter_setval(&t->parameter, "name", "smime.p7s");
772 t->encoding = ENCBASE64;
774 t->disposition = DISPATTACH;
775 t->d_filename = m_strdup("smime.p7s");
778 t->subtype = m_strdup("pgp-signature");
780 t->disposition = DISPINLINE;
781 t->encoding = ENC7BIT;
783 t->filename = sigfile;
784 t->unlink = 1; /* ok to remove this file after sending. */
790 BODY *crypt_pgp_sign_message (BODY * a)
792 return sign_message (a, 0);
795 BODY *crypt_smime_sign_message (BODY * a)
797 return sign_message (a, 1);
801 * Implementation of `encrypt_message'.
804 /* Encrypt the mail body A to all keys given as space separated keyids
805 or fingerprints in KEYLIST and return the encrypted body. */
806 BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
808 char *outfile = NULL;
810 gpgme_key_t *rset = NULL;
811 gpgme_data_t plaintext;
813 rset = create_recipient_set (keylist, GPGME_PROTOCOL_OpenPGP);
819 plaintext = body_to_data_object (a, 0);
825 outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
826 gpgme_data_release (plaintext);
832 t->type = TYPEMULTIPART;
833 t->subtype = m_strdup("encrypted");
834 t->encoding = ENC7BIT;
836 t->disposition = DISPINLINE;
838 parameter_set_boundary(&t->parameter);
839 parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
841 t->parts = body_new();
842 t->parts->type = TYPEAPPLICATION;
843 t->parts->subtype = m_strdup("pgp-encrypted");
844 t->parts->encoding = ENC7BIT;
846 t->parts->next = body_new();
847 t->parts->next->type = TYPEAPPLICATION;
848 t->parts->next->subtype = m_strdup("octet-stream");
849 t->parts->next->encoding = ENC7BIT;
850 t->parts->next->filename = outfile;
851 t->parts->next->use_disp = 1;
852 t->parts->next->disposition = DISPINLINE;
853 t->parts->next->unlink = 1; /* delete after sending the message */
854 t->parts->next->d_filename = m_strdup("msg.asc"); /* non pgp/mime
861 * Implementation of `smime_build_smime_entity'.
864 /* Encrypt the mail body A to all keys given as space separated
865 fingerprints in KEYLIST and return the S/MIME encrypted body. */
866 BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
868 char *outfile = NULL;
870 gpgme_key_t *rset = NULL;
871 gpgme_data_t plaintext;
873 rset = create_recipient_set (keylist, GPGME_PROTOCOL_CMS);
877 plaintext = body_to_data_object (a, 0);
883 outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
884 gpgme_data_release (plaintext);
890 t->type = TYPEAPPLICATION;
891 t->subtype = m_strdup("pkcs7-mime");
892 parameter_setval(&t->parameter, "name", "smime.p7m");
893 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
894 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
896 t->disposition = DISPATTACH;
897 t->d_filename = m_strdup("smime.p7m");
898 t->filename = outfile;
899 t->unlink = 1; /*delete after sending the message */
907 /* Implementation of `verify_one'. */
909 /* Display the common attributes of the signature summary SUM.
910 Return 1 if there is is a severe warning.
912 static int show_sig_summary (unsigned long sum,
913 gpgme_ctx_t ctx, gpgme_key_t key, int idx,
918 if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
919 state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
923 if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
924 time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
927 state_attach_puts (_("Warning: The key used to create the "
928 "signature expired at: "), s);
930 state_attach_puts ("\n", s);
933 state_attach_puts (_("Warning: At least one certification key "
934 "has expired\n"), s);
937 if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
938 gpgme_verify_result_t result;
939 gpgme_signature_t sig;
942 result = gpgme_op_verify_result (ctx);
944 for (sig = result->signatures, i = 0; sig && (i < idx);
945 sig = sig->next, i++);
947 state_attach_puts (_("Warning: The signature expired at: "), s);
948 print_time (sig ? sig->exp_timestamp : 0, s);
949 state_attach_puts ("\n", s);
952 if ((sum & GPGME_SIGSUM_KEY_MISSING))
953 state_attach_puts (_("Can't verify due to a missing "
954 "key or certificate\n"), s);
956 if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
957 state_attach_puts (_("The CRL is not available\n"), s);
961 if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
962 state_attach_puts (_("Available CRL is too old\n"), s);
966 if ((sum & GPGME_SIGSUM_BAD_POLICY))
967 state_attach_puts (_("A policy requirement was not met\n"), s);
969 if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
970 const char *t0 = NULL, *t1 = NULL;
971 gpgme_verify_result_t result;
972 gpgme_signature_t sig;
975 state_attach_puts (_("A system error occurred"), s);
977 /* Try to figure out some more detailed system error information. */
978 result = gpgme_op_verify_result (ctx);
979 for (sig = result->signatures, i = 0; sig && (i < idx);
980 sig = sig->next, i++);
983 t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
987 state_attach_puts (": ", s);
989 state_attach_puts (t0, s);
990 if (t1 && !(t0 && !m_strcmp(t0, t1))) {
992 state_attach_puts (",", s);
993 state_attach_puts (t1, s);
996 state_attach_puts ("\n", s);
1003 static void show_fingerprint (gpgme_key_t key, STATE * state)
1008 const char *prefix = _("Fingerprint: ");
1013 s = key->subkeys ? key->subkeys->fpr : NULL;
1016 is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
1018 bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
1019 buf = p_new(char, bufsize);
1020 m_strcpy(buf, bufsize, prefix);
1021 p = buf + m_strlen(buf);
1022 if (is_pgp && m_strlen(s) == 40) { /* PGP v4 style formatted. */
1023 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
1034 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
1037 *p++ = is_pgp ? ' ' : ':';
1038 if (is_pgp && i == 7)
1043 /* just in case print remaining odd digits */
1048 state_attach_puts (buf, state);
1052 /* Show the valididy of a key used for one signature. */
1053 static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
1055 gpgme_verify_result_t result = NULL;
1056 gpgme_signature_t sig = NULL;
1057 const char *txt = NULL;
1059 result = gpgme_op_verify_result (ctx);
1061 for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
1063 switch (sig ? sig->validity : 0) {
1064 case GPGME_VALIDITY_UNKNOWN:
1065 txt = _("WARNING: We have NO indication whether "
1066 "the key belongs to the person named " "as shown above\n");
1068 case GPGME_VALIDITY_UNDEFINED:
1070 case GPGME_VALIDITY_NEVER:
1071 txt = _("WARNING: The key does NOT BELONG to "
1072 "the person named as shown above\n");
1074 case GPGME_VALIDITY_MARGINAL:
1075 txt = _("WARNING: It is NOT certain that the key "
1076 "belongs to the person named as shown above\n");
1078 case GPGME_VALIDITY_FULL:
1079 case GPGME_VALIDITY_ULTIMATE:
1084 state_attach_puts (txt, s);
1087 /* Show information about one signature. This fucntion is called with
1088 the context CTX of a sucessful verification operation and the
1089 enumerator IDX which should start at 0 and incremete for each
1092 Return values are: 0 for normal procession, 1 for a bad signature,
1093 2 for a signature with a warning or -1 for no more signature. */
1094 static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
1097 const char *fpr, *uid;
1098 gpgme_key_t key = NULL;
1099 int i, anybad = 0, anywarn = 0;
1101 gpgme_user_id_t uids = NULL;
1102 gpgme_verify_result_t result;
1103 gpgme_signature_t sig;
1104 gpgme_error_t err = GPG_ERR_NO_ERROR;
1106 result = gpgme_op_verify_result (ctx);
1108 /* FIXME: this code should use a static variable and remember
1109 the current position in the list of signatures, IMHO.
1112 for (i = 0, sig = result->signatures; sig && (i < idx);
1113 i++, sig = sig->next);
1115 return -1; /* Signature not found. */
1117 if (signature_key) {
1118 gpgme_key_release (signature_key);
1119 signature_key = NULL;
1122 created = sig->timestamp;
1126 if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
1129 err = gpgme_get_key (ctx, fpr, &key, 0); /* secret key? */
1131 uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
1133 signature_key = key;
1136 key = NULL; /* Old gpgme versions did not set KEY to NULL on
1137 error. Do it here to avoid a double free. */
1141 if (!s || !s->fpout || !(s->flags & M_DISPLAY)); /* No state information so no way to print anything. */
1143 state_attach_puts (_("Error getting key information: "), s);
1144 state_attach_puts (gpg_strerror (err), s);
1145 state_attach_puts ("\n", s);
1148 else if ((sum & GPGME_SIGSUM_GREEN)) {
1149 state_attach_puts (_("Good signature from: "), s);
1150 state_attach_puts (uid, s);
1151 state_attach_puts ("\n", s);
1152 for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
1154 /* Skip primary UID. */
1158 state_attach_puts (_(" aka: "), s);
1159 state_attach_puts (uids->uid, s);
1160 state_attach_puts ("\n", s);
1162 state_attach_puts (_(" created: "), s);
1163 print_time (created, s);
1164 state_attach_puts ("\n", s);
1165 if (show_sig_summary (sum, ctx, key, idx, s))
1167 show_one_sig_validity (ctx, idx, s);
1169 else if ((sum & GPGME_SIGSUM_RED)) {
1170 state_attach_puts (_("*BAD* signature claimed to be from: "), s);
1171 state_attach_puts (uid, s);
1172 state_attach_puts ("\n", s);
1173 show_sig_summary (sum, ctx, key, idx, s);
1175 else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) { /* We can't decide (yellow) but this is a PGP key with a good
1176 signature, so we display what a PGP user expects: The name,
1177 fingerprint and the key validity (which is neither fully or
1179 state_attach_puts (_("Good signature from: "), s);
1180 state_attach_puts (uid, s);
1181 state_attach_puts ("\n", s);
1182 state_attach_puts (_(" created: "), s);
1183 print_time (created, s);
1184 state_attach_puts ("\n", s);
1185 show_one_sig_validity (ctx, idx, s);
1186 show_fingerprint (key, s);
1187 if (show_sig_summary (sum, ctx, key, idx, s))
1190 else { /* can't decide (yellow) */
1192 state_attach_puts (_("Error checking signature"), s);
1193 state_attach_puts ("\n", s);
1194 show_sig_summary (sum, ctx, key, idx, s);
1197 if (key != signature_key)
1198 gpgme_key_release (key);
1201 return anybad ? 1 : anywarn ? 2 : 0;
1204 /* Do the actual verification step. With IS_SMIME set to true we
1205 assume S/MIME (surprise!) */
1206 static int verify_one (BODY * sigbdy, STATE * s,
1207 const char *tempfile, int is_smime)
1213 gpgme_data_t signature, message;
1215 signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
1219 /* We need to tell gpgme about the encoding because the backend can't
1220 auto-detect plain base-64 encoding which is used by S/MIME. */
1222 gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
1224 err = gpgme_data_new_from_file (&message, tempfile, 1);
1226 gpgme_data_release (signature);
1227 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
1230 ctx = create_gpgme_context (is_smime);
1232 /* Note: We don't need a current time output because GPGME avoids
1233 such an attack by separating the meta information from the
1235 state_attach_puts (_("[-- Begin signature information --]\n"), s);
1237 err = gpgme_op_verify (ctx, signature, message, NULL);
1238 mutt_need_hard_redraw ();
1242 snprintf (buf, sizeof (buf) - 1,
1243 _("Error: verification failed: %s\n"), gpgme_strerror (err));
1244 state_attach_puts (buf, s);
1246 else { /* Verification succeeded, see what the result is. */
1250 if (signature_key) {
1251 gpgme_key_release (signature_key);
1252 signature_key = NULL;
1255 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1266 gpgme_verify_result_t result;
1267 gpgme_sig_notation_t notation;
1268 gpgme_signature_t sig;
1270 result = gpgme_op_verify_result (ctx);
1272 for (sig = result->signatures; sig; sig = sig->next) {
1273 if (sig->notations) {
1274 state_attach_puts ("*** Begin Notation (signature by: ", s);
1275 state_attach_puts (sig->fpr, s);
1276 state_attach_puts (") ***\n", s);
1277 for (notation = sig->notations; notation; notation = notation->next)
1279 if (notation->name) {
1280 state_attach_puts (notation->name, s);
1281 state_attach_puts ("=", s);
1283 if (notation->value) {
1284 state_attach_puts (notation->value, s);
1285 if (!(*notation->value
1286 && (notation->value[m_strlen(notation->value) - 1] ==
1288 state_attach_puts ("\n", s);
1291 state_attach_puts ("*** End Notation ***\n", s);
1297 gpgme_release (ctx);
1299 state_attach_puts (_("[-- End signature information --]\n\n"), s);
1301 return badsig ? 1 : anywarn ? 2 : 0;
1304 int crypt_pgp_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1306 return verify_one (sigbdy, s, tempfile, 0);
1309 int crypt_smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1311 return verify_one (sigbdy, s, tempfile, 1);
1315 * Implementation of `decrypt_part'.
1318 /* Decrypt a PGP or SMIME message (depending on the boolean flag
1319 IS_SMIME) with body A described further by state S. Write
1320 plaintext out to file FPOUT and return a new body. For PGP returns
1321 a flag in R_IS_SIGNED to indicate whether this is a combined
1322 encrypted and signed message, for S/MIME it returns true when it is
1323 not a encrypted but a signed message. */
1324 static BODY *decrypt_part (BODY * a, STATE * s, FILE * fpout, int is_smime,
1331 gpgme_data_t ciphertext, plaintext;
1332 int maybe_signed = 0;
1339 ctx = create_gpgme_context (is_smime);
1342 /* Make a data object from the body, create context etc. */
1343 ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
1346 plaintext = create_gpgme_data ();
1348 /* Do the decryption or the verification in case of the S/MIME hack. */
1349 if ((!is_smime) || maybe_signed) {
1351 err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
1352 else if (maybe_signed)
1353 err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
1356 /* Check wether signatures have been verified. */
1357 gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
1359 if (verify_result->signatures)
1364 err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
1365 gpgme_data_release (ciphertext);
1367 if (is_smime && !maybe_signed && gpg_err_code (err) == GPG_ERR_NO_DATA) {
1368 /* Check whether this might be a signed message despite what
1369 the mime header told us. Retry then. gpgsm returns the
1370 error information "unsupported Algorithm '?'" but gpgme
1371 will not store this unknown algorithm, thus we test that
1372 it has not been set. */
1373 gpgme_decrypt_result_t result;
1375 result = gpgme_op_decrypt_result (ctx);
1376 if (!result->unsupported_algorithm) {
1378 gpgme_data_release (plaintext);
1382 mutt_need_hard_redraw ();
1383 if ((s->flags & M_DISPLAY)) {
1386 snprintf (buf, sizeof (buf) - 1,
1387 _("[-- Error: decryption failed: %s --]\n\n"),
1388 gpgme_strerror (err));
1389 state_attach_puts (buf, s);
1391 gpgme_data_release (plaintext);
1392 gpgme_release (ctx);
1395 mutt_need_hard_redraw ();
1397 /* Read the output from GPGME, and make sure to change CRLF to LF,
1398 otherwise read_mime_header has a hard time parsing the message. */
1399 if (data_object_to_stream (plaintext, fpout)) {
1400 gpgme_data_release (plaintext);
1401 gpgme_release (ctx);
1404 gpgme_data_release (plaintext);
1406 a->is_signed_data = 0;
1412 a->is_signed_data = 1;
1414 *r_is_signed = -1; /* A signature exists. */
1416 if ((s->flags & M_DISPLAY))
1417 state_attach_puts (_("[-- Begin signature " "information --]\n"), s);
1418 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1424 if (!anybad && idx && r_is_signed && *r_is_signed)
1425 *r_is_signed = anywarn ? 2 : 1; /* Good signature. */
1427 if ((s->flags & M_DISPLAY))
1428 state_attach_puts (_("[-- End signature " "information --]\n\n"), s);
1430 gpgme_release (ctx);
1435 tattach = mutt_read_mime_header (fpout, 0);
1438 * Need to set the length of this body part.
1440 fstat (fileno (fpout), &info);
1441 tattach->length = info.st_size - tattach->offset;
1443 tattach->warnsig = anywarn;
1445 /* See if we need to recurse on this MIME part. */
1446 mutt_parse_part (fpout, tattach);
1452 /* Decrypt a PGP/MIME message in FPIN and B and return a new body and
1453 the stream in CUR and FPOUT. Returns 0 on success. */
1454 int crypt_pgp_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1456 char tempfile[_POSIX_PATH_MAX];
1458 BODY *first_part = b;
1461 first_part->goodsig = 0;
1462 first_part->warnsig = 0;
1464 if (!mutt_is_multipart_encrypted (b))
1467 if (!b->parts || !b->parts->next)
1474 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1476 mutt_perror (_("Can't create temporary file"));
1481 *cur = decrypt_part (b, &s, *fpout, 0, &is_signed);
1484 first_part->goodsig = 1;
1486 return *cur ? 0 : -1;
1490 /* Decrypt a S/MIME message in FPIN and B and return a new body and
1491 the stream in CUR and FPOUT. Returns 0 on success. */
1492 int crypt_smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b,
1495 char tempfile[_POSIX_PATH_MAX];
1499 long saved_b_offset;
1500 ssize_t saved_b_length;
1503 if (!mutt_is_application_smime (b))
1509 /* Decode the body - we need to pass binary CMS to the
1510 backend. The backend allows for Base64 encoded data but it does
1511 not allow for QP which I have seen in some messages. So better
1513 saved_b_type = b->type;
1514 saved_b_offset = b->offset;
1515 saved_b_length = b->length;
1518 fseeko (s.fpin, b->offset, 0);
1519 tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1521 mutt_perror (_("Can't create temporary file"));
1524 mutt_unlink (tempfile);
1527 mutt_decode_attachment (b, &s);
1529 b->length = ftello (s.fpout);
1536 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1538 mutt_perror (_("Can't create temporary file"));
1541 mutt_unlink (tempfile);
1543 *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
1545 (*cur)->goodsig = is_signed > 0;
1546 b->type = saved_b_type;
1547 b->length = saved_b_length;
1548 b->offset = saved_b_offset;
1551 if (*cur && !is_signed && !(*cur)->parts
1552 && mutt_is_application_smime (*cur)) {
1553 /* Assume that this is a opaque signed s/mime message. This is
1554 an ugly way of doing it but we have anyway a problem with
1555 arbitrary encoded S/MIME messages: Only the outer part may be
1556 encrypted. The entire mime parsing should be revamped,
1557 probably by keeping the temportary files so that we don't
1558 need to decrypt them all the time. Inner parts of an
1559 encrypted part can then pint into this file and tehre won't
1560 never be a need to decrypt again. This needs a partial
1561 rewrite of the MIME engine. */
1565 saved_b_type = bb->type;
1566 saved_b_offset = bb->offset;
1567 saved_b_length = bb->length;
1570 fseeko (s.fpin, bb->offset, 0);
1571 tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1573 mutt_perror (_("Can't create temporary file"));
1576 mutt_unlink (tempfile);
1579 mutt_decode_attachment (bb, &s);
1581 bb->length = ftello (s.fpout);
1589 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1591 mutt_perror (_("Can't create temporary file"));
1594 mutt_unlink (tempfile);
1596 tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
1598 tmp_b->goodsig = is_signed > 0;
1599 bb->type = saved_b_type;
1600 bb->length = saved_b_length;
1601 bb->offset = saved_b_offset;
1604 body_list_wipe(cur);
1607 return *cur ? 0 : -1;
1612 * Implementation of `pgp_check_traditional'.
1615 static int pgp_check_traditional_one_body (FILE * fp, BODY * b,
1618 char tempfile[_POSIX_PATH_MAX];
1619 char buf[HUGE_STRING];
1626 if (b->type != TYPETEXT)
1629 if (tagged_only && !b->tagged)
1632 tempfd = m_tempfd(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1633 if (mutt_decode_save_attachment (fp, b, tempfd, 0) != 0) {
1638 if ((tfp = fopen(tempfile, "r")) == NULL) {
1643 while (fgets (buf, sizeof (buf), tfp)) {
1644 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1645 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1647 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15))
1657 /* fix the content type */
1659 parameter_setval(&b->parameter, "format", "fixed");
1660 parameter_setval(&b->parameter, "x-action",
1661 enc ? "pgp-encrypted" : "pgp-signed");
1665 int crypt_pgp_check_traditional (FILE * fp, BODY * b, int tagged_only)
1670 for (; b; b = b->next) {
1671 if (is_multipart (b))
1672 rv = (crypt_pgp_check_traditional (fp, b->parts, tagged_only) || rv);
1673 else if (b->type == TYPETEXT) {
1674 if ((r = mutt_is_application_pgp (b)))
1677 rv = (pgp_check_traditional_one_body (fp, b, tagged_only) || rv);
1684 /* Implementation of `application_handler'. */
1687 Copy a clearsigned message, and strip the signature and PGP's
1690 XXX - charset handling: We assume that it is safe to do
1691 character set decoding first, dash decoding second here, while
1692 we do it the other way around in the main handler.
1694 (Note that we aren't worse than Outlook & Cie in this, and also
1695 note that we can successfully handle anything produced by any
1696 existing versions of mutt.) */
1698 static void copy_clearsigned (gpgme_data_t data, STATE * s, char *charset)
1700 char buf[HUGE_STRING];
1701 short complete, armor_header;
1706 fname = data_object_to_tempfile (data, &fp);
1712 fc = fgetconv_open (fp, charset, MCharset.charset, M_ICONV_HOOK_FROM);
1714 for (complete = 1, armor_header = 1;
1715 fgetconvs (buf, sizeof (buf), fc) != NULL;
1716 complete = strchr (buf, '\n') != NULL) {
1719 state_puts (buf, s);
1723 if (!m_strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
1733 state_puts (s->prefix, s);
1735 if (buf[0] == '-' && buf[1] == ' ')
1736 state_puts (buf + 2, s);
1738 state_puts (buf, s);
1741 fgetconv_close (&fc);
1746 /* Support for classic_application/pgp */
1747 int crypt_pgp_application_pgp_handler (BODY * m, STATE * s)
1749 int needpass = -1, pgp_keyblock = 0;
1753 off_t last_pos, offset;
1754 char buf[HUGE_STRING];
1755 FILE *pgpout = NULL;
1757 gpgme_error_t err = 0;
1758 gpgme_data_t armored_data = NULL;
1760 short maybe_goodsig = 1;
1761 short have_any_sigs = 0;
1763 char body_charset[STRING]; /* Only used for clearsigned messages. */
1765 /* For clearsigned messages we won't be able to get a character set
1766 but we know that this may only be text thus we assume Latin-1
1768 if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
1769 m_strcpy(body_charset, sizeof(body_charset), "iso-8859-1");
1771 fseeko (s->fpin, m->offset, 0);
1772 last_pos = m->offset;
1774 for (bytes = m->length; bytes > 0;) {
1775 if (fgets (buf, sizeof (buf), s->fpin) == NULL)
1778 offset = ftello (s->fpin);
1779 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1782 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1784 start_pos = last_pos;
1786 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1788 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15)) {
1792 else if (!option (OPTDONTHANDLEPGPKEYS) &&
1793 !m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
1798 /* XXX - we may wish to recode here */
1800 state_puts (s->prefix, s);
1801 state_puts (buf, s);
1805 have_any_sigs = (have_any_sigs || (clearsign && (s->flags & M_VERIFY)));
1807 /* Copy PGP material to an data container */
1808 armored_data = create_gpgme_data ();
1809 gpgme_data_write (armored_data, buf, m_strlen(buf));
1810 while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) {
1811 offset = ftello (s->fpin);
1812 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1815 gpgme_data_write (armored_data, buf, m_strlen(buf));
1817 if ((needpass && !m_strcmp("-----END PGP MESSAGE-----\n", buf))
1819 && (!m_strcmp("-----END PGP SIGNATURE-----\n", buf)
1820 || !m_strcmp("-----END PGP PUBLIC KEY BLOCK-----\n",
1825 /* Invoke PGP if needed */
1826 if (!clearsign || (s->flags & M_VERIFY)) {
1827 unsigned int sig_stat = 0;
1828 gpgme_data_t plaintext;
1831 plaintext = create_gpgme_data ();
1832 ctx = create_gpgme_context (0);
1835 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1837 err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
1838 if (gpg_err_code (err) == GPG_ERR_NO_DATA) {
1839 /* Decrypt verify can't handle signed only messages. */
1840 err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
1841 ? gpgme_error_from_errno (errno) : 0;
1842 /* Must release plaintext so that we supply an
1843 uninitialized object. */
1844 gpgme_data_release (plaintext);
1845 plaintext = create_gpgme_data ();
1846 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1853 snprintf (errbuf, sizeof (errbuf) - 1,
1854 _("Error: decryption/verification failed: %s\n"),
1855 gpgme_strerror (err));
1856 state_attach_puts (errbuf, s);
1858 else { /* Decryption/Verification succeeded */
1862 /* Check wether signatures have been verified. */
1863 gpgme_verify_result_t verify_result;
1865 verify_result = gpgme_op_verify_result (ctx);
1866 if (verify_result->signatures)
1872 if ((s->flags & M_DISPLAY) && sig_stat) {
1877 state_attach_puts (_("[-- Begin signature "
1878 "information --]\n"), s);
1881 (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1890 state_attach_puts (_("[-- End signature "
1891 "information --]\n\n"), s);
1894 tmpfname = data_object_to_tempfile (plaintext, &pgpout);
1897 state_attach_puts (_("Error: copy data failed\n"), s);
1901 p_delete(&tmpfname);
1904 gpgme_release (ctx);
1908 * Now, copy cleartext to the screen. NOTE - we expect that PGP
1909 * outputs utf-8 cleartext. This may not always be true, but it
1910 * seems to be a reasonable guess.
1913 if (s->flags & M_DISPLAY) {
1915 state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
1916 else if (pgp_keyblock)
1917 state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s);
1919 state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s);
1923 copy_clearsigned (armored_data, s, body_charset);
1930 fc = fgetconv_open (pgpout, "utf-8", MCharset.charset, 0);
1931 while ((c = fgetconv (fc)) != EOF) {
1933 if (c == '\n' && s->prefix)
1934 state_puts (s->prefix, s);
1936 fgetconv_close (&fc);
1939 if (s->flags & M_DISPLAY) {
1940 state_putc ('\n', s);
1942 state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
1943 else if (pgp_keyblock)
1944 state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
1946 state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
1954 /* XXX - we may wish to recode here */
1956 state_puts (s->prefix, s);
1957 state_puts (buf, s);
1961 m->goodsig = (maybe_goodsig && have_any_sigs);
1963 if (needpass == -1) {
1964 state_attach_puts (_("[-- Error: could not find beginning"
1965 " of PGP message! --]\n\n"), s);
1971 /* Implementation of `encrypted_handler'. */
1973 /* MIME handler for pgp/mime encrypted messages. */
1974 int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
1976 char tempfile[_POSIX_PATH_MAX];
1979 BODY *orig_body = a;
1984 if (!a || a->type != TYPEAPPLICATION || !a->subtype
1985 || ascii_strcasecmp ("pgp-encrypted", a->subtype)
1986 || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
1987 || ascii_strcasecmp ("octet-stream", a->next->subtype)) {
1988 if (s->flags & M_DISPLAY)
1989 state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
1994 /* Move forward to the application/pgp-encrypted body. */
1997 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1999 if (s->flags & M_DISPLAY)
2000 state_attach_puts (_("[-- Error: could not create temporary file! "
2005 tattach = decrypt_part (a, s, fpout, 0, &is_signed);
2007 tattach->goodsig = is_signed > 0;
2009 if (s->flags & M_DISPLAY)
2010 state_attach_puts (is_signed ?
2012 ("[-- The following data is PGP/MIME signed and encrypted --]\n\n") :
2013 _("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
2016 FILE *savefp = s->fpin;
2019 rc = mutt_body_handler (tattach, s);
2024 * if a multipart/signed is the _only_ sub-part of a
2025 * multipart/encrypted, cache signature verification
2028 if (mutt_is_multipart_signed (tattach) && !tattach->next)
2029 orig_body->goodsig |= tattach->goodsig;
2031 if (s->flags & M_DISPLAY) {
2032 state_puts ("\n", s);
2033 state_attach_puts (is_signed ?
2035 ("[-- End of PGP/MIME signed and encrypted data --]\n")
2036 : _("[-- End of PGP/MIME encrypted data --]\n"), s);
2039 body_list_wipe(&tattach);
2043 mutt_unlink (tempfile);
2047 /* Support for application/smime */
2048 int crypt_smime_application_smime_handler (BODY * a, STATE * s)
2050 char tempfile[_POSIX_PATH_MAX];
2057 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2059 if (s->flags & M_DISPLAY)
2060 state_attach_puts (_("[-- Error: could not create temporary file! "
2065 tattach = decrypt_part (a, s, fpout, 1, &is_signed);
2067 tattach->goodsig = is_signed > 0;
2069 if (s->flags & M_DISPLAY)
2070 state_attach_puts (is_signed ?
2071 _("[-- The following data is S/MIME signed --]\n\n") :
2072 _("[-- The following data is S/MIME encrypted --]\n\n"), s);
2075 FILE *savefp = s->fpin;
2078 rc = mutt_body_handler (tattach, s);
2083 * if a multipart/signed is the _only_ sub-part of a
2084 * multipart/encrypted, cache signature verification
2087 if (mutt_is_multipart_signed (tattach) && !tattach->next) {
2088 if (!(a->goodsig = tattach->goodsig))
2089 a->warnsig = tattach->warnsig;
2091 else if (tattach->goodsig) {
2093 a->warnsig = tattach->warnsig;
2096 if (s->flags & M_DISPLAY) {
2097 state_puts ("\n", s);
2098 state_attach_puts (is_signed ?
2099 _("[-- End of S/MIME signed data --]\n") :
2100 _("[-- End of S/MIME encrypted data --]\n"), s);
2103 body_list_wipe(&tattach);
2107 mutt_unlink (tempfile);
2113 * Format an entry on the CRYPT key selection menu.
2116 * %k key id %K key id of the principal key
2118 * %a algorithm %A algorithm of the princ. key
2119 * %l length %L length of the princ. key
2120 * %f flags %F flags of the princ. key
2121 * %c capabilities %C capabilities of the princ. key
2122 * %t trust/validity of the key-uid association
2124 * %[...] date of key using strftime(3)
2128 crypt_entry_fmt (char *dest, ssize_t destlen, char op,
2129 const char *src, const char *prefix,
2130 const char *ifstr, const char *elstr,
2131 anytype data, format_flag flags)
2134 crypt_entry_t *entry;
2137 int optional = (flags & M_FORMAT_OPTIONAL);
2138 const char *s = NULL;
2144 /* if (isupper ((unsigned char) op)) */
2147 kflags = (key->flags /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
2150 switch (ascii_tolower (op)) {
2154 char buf2[STRING], *p;
2170 while (len > 0 && *cp != ']') {
2179 break; /* not enough space */
2189 if (do_locales && Locale)
2190 setlocale (LC_TIME, Locale);
2195 if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
2196 tt = key->kobj->subkeys->timestamp;
2198 tm = localtime (&tt);
2200 strftime (buf2, sizeof (buf2), dest, tm);
2203 setlocale (LC_TIME, "C");
2205 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2206 snprintf (dest, destlen, fmt, buf2);
2213 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
2214 snprintf (dest, destlen, fmt, entry->num);
2219 /* fixme: we need a way to distinguish between main and subkeys.
2220 Store the idx in entry? */
2221 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2222 snprintf (dest, destlen, fmt, crypt_keyid (key));
2227 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2228 snprintf (dest, destlen, fmt, key->uid);
2233 snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
2234 if (key->kobj->subkeys)
2235 s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
2238 snprintf (dest, destlen, fmt, s);
2243 snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
2244 if (key->kobj->subkeys)
2245 val = key->kobj->subkeys->length;
2248 snprintf (dest, destlen, fmt, val);
2253 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2254 snprintf (dest, destlen, fmt, crypt_flags (kflags));
2256 else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
2261 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2262 snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
2264 else if (!(kflags & (KEYFLAG_ABILITIES)))
2268 if ((kflags & KEYFLAG_ISX509))
2271 gpgme_user_id_t uid = NULL;
2274 for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
2275 i++, uid = uid->next);
2277 switch (uid->validity) {
2278 case GPGME_VALIDITY_UNDEFINED:
2281 case GPGME_VALIDITY_NEVER:
2284 case GPGME_VALIDITY_MARGINAL:
2287 case GPGME_VALIDITY_FULL:
2290 case GPGME_VALIDITY_ULTIMATE:
2293 case GPGME_VALIDITY_UNKNOWN:
2299 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2300 snprintf (dest, destlen, fmt, s ? *s : 'B');
2303 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2304 snprintf (dest, destlen, fmt,
2305 gpgme_get_protocol_name (key->kobj->protocol));
2312 if (flags & M_FORMAT_OPTIONAL)
2313 m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
2314 mutt_attach_fmt, data, 0);
2318 /* Used by the display fucntion to format a line. */
2319 static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
2321 crypt_key_t **key_table = (crypt_key_t **) menu->data;
2322 crypt_entry_t entry;
2324 entry.key = key_table[num];
2325 entry.num = num + 1;
2327 m_strformat(s, l, COLS - SW, PgpEntryFormat, crypt_entry_fmt, &entry,
2328 option(OPTARROWCURSOR) ? M_FORMAT_ARROWCURSOR : 0);
2331 /* Compare two addresses and the keyid to be used for sorting. */
2332 static int _crypt_compare_address (const void *a, const void *b)
2334 crypt_key_t **s = (crypt_key_t **) a;
2335 crypt_key_t **t = (crypt_key_t **) b;
2338 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2341 return m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t)) > 0;
2344 static int crypt_compare_address (const void *a, const void *b)
2346 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_address (a, b)
2347 : _crypt_compare_address (a, b));
2351 /* Compare two key IDs and the addresses to be used for sorting. */
2352 static int _crypt_compare_keyid (const void *a, const void *b)
2354 crypt_key_t **s = (crypt_key_t **) a;
2355 crypt_key_t **t = (crypt_key_t **) b;
2358 if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
2361 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2364 static int crypt_compare_keyid (const void *a, const void *b)
2366 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
2367 : _crypt_compare_keyid (a, b));
2370 /* Compare 2 creation dates and the addresses. For sorting. */
2371 static int _crypt_compare_date (const void *a, const void *b)
2373 crypt_key_t **s = (crypt_key_t **) a;
2374 crypt_key_t **t = (crypt_key_t **) b;
2375 unsigned long ts = 0, tt = 0;
2377 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2378 ts = (*s)->kobj->subkeys->timestamp;
2379 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2380 tt = (*t)->kobj->subkeys->timestamp;
2387 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2390 static int crypt_compare_date (const void *a, const void *b)
2392 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
2393 : _crypt_compare_date (a, b));
2396 /* Compare two trust values, the key length, the creation dates. the
2397 addresses and the key IDs. For sorting. */
2398 static int _crypt_compare_trust (const void *a, const void *b)
2400 crypt_key_t **s = (crypt_key_t **) a;
2401 crypt_key_t **t = (crypt_key_t **) b;
2402 unsigned long ts = 0, tt = 0;
2405 if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
2406 - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
2409 if ((*s)->kobj->uids)
2410 ts = (*s)->kobj->uids->validity;
2411 if ((*t)->kobj->uids)
2412 tt = (*t)->kobj->uids->validity;
2413 if ((r = (tt - ts)))
2416 if ((*s)->kobj->subkeys)
2417 ts = (*s)->kobj->subkeys->length;
2418 if ((*t)->kobj->subkeys)
2419 tt = (*t)->kobj->subkeys->length;
2423 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2424 ts = (*s)->kobj->subkeys->timestamp;
2425 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2426 tt = (*t)->kobj->subkeys->timestamp;
2432 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2434 return (m_strcasecmp(crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
2437 static int crypt_compare_trust (const void *a, const void *b)
2439 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
2440 : _crypt_compare_trust (a, b));
2443 /* Print the X.500 Distinguished Name part KEY from the array of parts
2445 static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
2449 for (; dn->key; dn++) {
2450 if (!m_strcmp(dn->key, key)) {
2453 print_utf8 (fp, dn->value, m_strlen(dn->value));
2460 /* Print all parts of a DN in a standard sequence. */
2461 static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
2463 const char *stdpart[] = {
2464 "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
2466 int any = 0, any2 = 0, i;
2468 for (i = 0; stdpart[i]; i++) {
2471 any = print_dn_part (fp, dn, stdpart[i]);
2473 /* now print the rest without any specific ordering */
2474 for (; dn->key; dn++) {
2475 for (i = 0; stdpart[i]; i++) {
2476 if (!m_strcmp(dn->key, stdpart[i]))
2484 any = print_dn_part (fp, dn, dn->key);
2493 /* Parse an RDN; this is a helper to parse_dn(). */
2494 static const unsigned char *parse_dn_part (struct dn_array_s *array,
2495 const unsigned char *string)
2497 const unsigned char *s, *s1;
2501 /* parse attributeType */
2502 for (s = string + 1; *s && *s != '='; s++);
2504 return NULL; /* error */
2507 return NULL; /* empty key */
2508 array->key = p_dupstr(string, n );
2509 p = (unsigned char *) array->key;
2512 if (*string == '#') { /* hexstring */
2514 for (s = string; hexval(*s) >= 0; s++)
2518 return NULL; /* empty or odd number of digits */
2520 p = p_new(unsigned char, n + 1);
2521 array->value = (char *) p;
2522 for (s1 = string; n; s1 += 2, n--)
2523 *p++ = (hexval(*s1) << 8) | hexval(*s1);
2526 else { /* regular v3 quoted string */
2527 for (n = 0, s = string; *s; s++) {
2528 if (*s == '\\') { /* pair */
2530 if (*s == ',' || *s == '=' || *s == '+'
2531 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
2532 || *s == '\\' || *s == '\"' || *s == ' ')
2534 else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
2539 return NULL; /* invalid escape sequence */
2541 else if (*s == '\"')
2542 return NULL; /* invalid encoding */
2543 else if (*s == ',' || *s == '=' || *s == '+'
2544 || *s == '<' || *s == '>' || *s == '#' || *s == ';')
2550 p = p_new(unsigned char, n + 1);
2551 array->value = (char *) p;
2552 for (s = string; n; s++, n--) {
2555 if (hexval(*s) >= 0) {
2556 *p++ = (hexval(*s) << 8) | hexval(*s + 1);
2571 /* Parse a DN and return an array-ized one. This is not a validating
2572 parser and it does not support any old-stylish syntax; gpgme is
2573 expected to return only rfc2253 compatible strings. */
2574 static struct dn_array_s *parse_dn (const unsigned char *string)
2576 struct dn_array_s *array;
2577 ssize_t arrayidx, arraysize;
2580 arraysize = 7; /* C,ST,L,O,OU,CN,email */
2581 array = p_new(struct dn_array_s, arraysize + 1);
2584 while (*string == ' ')
2588 if (arrayidx >= arraysize) { /* mutt lacks a real safe_realoc - so we need to copy */
2589 struct dn_array_s *a2;
2592 a2 = p_new(struct dn_array_s, arraysize + 1);
2593 for (i = 0; i < arrayidx; i++) {
2594 a2[i].key = array[i].key;
2595 a2[i].value = array[i].value;
2600 array[arrayidx].key = NULL;
2601 array[arrayidx].value = NULL;
2602 string = parse_dn_part (array + arrayidx, string);
2606 while (*string == ' ')
2608 if (*string && *string != ',' && *string != ';' && *string != '+')
2609 goto failure; /* invalid delimiter */
2613 array[arrayidx].key = NULL;
2614 array[arrayidx].value = NULL;
2618 for (i = 0; i < arrayidx; i++) {
2619 p_delete(&array[i].key);
2620 p_delete(&array[i].value);
2627 /* Print a nice representation of the USERID and make sure it is
2628 displayed in a proper way, which does mean to reorder some parts
2629 for S/MIME's DNs. USERID is a string as returned by the gpgme key
2630 functions. It is utf-8 encoded. */
2631 static void parse_and_print_user_id (FILE * fp, const char *userid)
2636 if (*userid == '<') {
2637 s = strchr (userid + 1, '>');
2639 print_utf8 (fp, userid + 1, s - userid - 1);
2641 else if (*userid == '(')
2642 fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
2643 else if (!digit_or_letter ((const unsigned char *) userid))
2644 fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
2646 struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
2649 fputs (_("[Can't display this user ID (invalid DN)]"), fp);
2651 print_dn_parts (fp, dn);
2652 for (i = 0; dn[i].key; i++) {
2653 p_delete(&dn[i].key);
2654 p_delete(&dn[i].value);
2662 KEY_CAP_CAN_ENCRYPT,
2667 static unsigned int key_check_cap (gpgme_key_t key, key_cap_t cap)
2669 gpgme_subkey_t subkey = NULL;
2670 unsigned int ret = 0;
2673 case KEY_CAP_CAN_ENCRYPT:
2674 if (!(ret = key->can_encrypt))
2675 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2676 if ((ret = subkey->can_encrypt))
2679 case KEY_CAP_CAN_SIGN:
2680 if (!(ret = key->can_sign))
2681 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2682 if ((ret = subkey->can_sign))
2685 case KEY_CAP_CAN_CERTIFY:
2686 if (!(ret = key->can_certify))
2687 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2688 if ((ret = subkey->can_certify))
2697 /* Print verbose information about a key or certificate to FP. */
2698 static void print_key_info (gpgme_key_t key, FILE * fp)
2701 const char *s = NULL, *s2 = NULL;
2704 char shortbuf[STRING];
2705 unsigned long aval = 0;
2709 gpgme_user_id_t uid = NULL;
2712 setlocale (LC_TIME, Locale);
2714 is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
2716 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2721 fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
2724 fputs (_("[Invalid]"), fp);
2728 print_utf8 (fp, s, m_strlen(s));
2730 parse_and_print_user_id (fp, s);
2734 if (key->subkeys && (key->subkeys->timestamp > 0)) {
2735 tt = key->subkeys->timestamp;
2737 tm = localtime (&tt);
2738 #ifdef HAVE_LANGINFO_D_T_FMT
2739 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2741 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2743 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2746 if (key->subkeys && (key->subkeys->expires > 0)) {
2747 tt = key->subkeys->expires;
2749 tm = localtime (&tt);
2750 #ifdef HAVE_LANGINFO_D_T_FMT
2751 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2753 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2755 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2759 s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
2763 s2 = is_pgp ? "PGP" : "X.509";
2766 aval = key->subkeys->length;
2768 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
2770 fprintf (fp, _("Key Usage .: "));
2773 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
2774 fprintf (fp, "%s%s", delim, _("encryption"));
2777 if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
2778 fprintf (fp, "%s%s", delim, _("signing"));
2781 if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
2782 fprintf (fp, "%s%s", delim, _("certification"));
2788 s = key->subkeys->fpr;
2789 fputs (_("Fingerprint: "), fp);
2790 if (is_pgp && m_strlen(s) == 40) {
2791 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
2796 putc (is_pgp ? ' ' : ':', fp);
2797 if (is_pgp && i == 4)
2802 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
2805 putc (is_pgp ? ' ' : ':', fp);
2806 if (is_pgp && i == 7)
2810 fprintf (fp, "%s\n", s);
2813 if (key->issuer_serial) {
2814 s = key->issuer_serial;
2816 fprintf (fp, _("Serial-No .: 0x%s\n"), s);
2819 if (key->issuer_name) {
2820 s = key->issuer_name;
2822 fprintf (fp, _("Issued By .: "));
2823 parse_and_print_user_id (fp, s);
2828 /* For PGP we list all subkeys. */
2830 gpgme_subkey_t subkey = NULL;
2832 for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
2836 if (m_strlen(s) == 16)
2837 s += 8; /* display only the short keyID */
2838 fprintf (fp, _("Subkey ....: 0x%s"), s);
2839 if (subkey->revoked) {
2841 fputs (_("[Revoked]"), fp);
2843 if (subkey->invalid) {
2845 fputs (_("[Invalid]"), fp);
2847 if (subkey->expired) {
2849 fputs (_("[Expired]"), fp);
2851 if (subkey->disabled) {
2853 fputs (_("[Disabled]"), fp);
2857 if (subkey->timestamp > 0) {
2858 tt = subkey->timestamp;
2860 tm = localtime (&tt);
2861 #ifdef HAVE_LANGINFO_D_T_FMT
2862 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2864 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2866 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2869 if (subkey->expires > 0) {
2870 tt = subkey->expires;
2872 tm = localtime (&tt);
2873 #ifdef HAVE_LANGINFO_D_T_FMT
2874 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2876 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2878 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2882 s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
2887 aval = subkey->length;
2891 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
2893 fprintf (fp, _("Key Usage .: "));
2896 if (subkey->can_encrypt) {
2897 fprintf (fp, "%s%s", delim, _("encryption"));
2900 if (subkey->can_sign) {
2901 fprintf (fp, "%s%s", delim, _("signing"));
2904 if (subkey->can_certify) {
2905 fprintf (fp, "%s%s", delim, _("certification"));
2913 setlocale (LC_TIME, "C");
2917 /* Show detailed information about the selected key */
2918 static void verify_key (crypt_key_t * key)
2921 char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
2923 gpgme_ctx_t listctx = NULL;
2925 gpgme_key_t k = NULL;
2928 fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2930 mutt_perror (_("Can't create temporary file"));
2933 mutt_message _("Collecting data...");
2935 print_key_info (key->kobj, fp);
2937 err = gpgme_new (&listctx);
2939 fprintf (fp, "Internal error: can't create gpgme context: %s\n",
2940 gpgme_strerror (err));
2943 if ((key->flags & KEYFLAG_ISX509))
2944 gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
2948 while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
2950 err = gpgme_op_keylist_start (listctx, s, 0);
2951 gpgme_key_release (k);
2954 err = gpgme_op_keylist_next (listctx, &k);
2956 fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
2959 gpgme_op_keylist_end (listctx);
2961 print_key_info (k, fp);
2964 fputs (_("Error: certification chain to long - stopping here\n"), fp);
2970 gpgme_key_release (k);
2971 gpgme_release (listctx);
2973 mutt_clear_error ();
2974 snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
2975 mutt_do_pager (cmd, tempfile, 0, NULL);
2978 /* Implementation of `findkeys'. */
2980 /* Convert string_list_t into a pattern string suitable to be passed to GPGME.
2981 We need to convert spaces in an item into a '+' and '%' into
2983 static char *list_to_pattern (string_list_t * list)
2991 for (l = list; l; l = l->next) {
2992 for (s = l->data; *s; s++) {
2997 n++; /* delimiter or end of string */
2999 n++; /* make sure to allocate at least one byte */
3000 pattern = p = p_new(char, n);
3001 for (l = list; l; l = l->next) {
3006 for (s = l->data; *s; s++) {
3012 else if (*s == '+') {
3028 /* Return a list of keys which are candidates for the selection.
3029 Select by looking at the HINTS list. */
3030 static crypt_key_t *get_candidates (string_list_t * hints, unsigned int app,
3033 crypt_key_t *db, *k, **kend;
3039 gpgme_user_id_t uid = NULL;
3041 pattern = list_to_pattern (hints);
3045 err = gpgme_new (&ctx);
3047 mutt_error (_("gpgme_new failed: %s"), gpgme_strerror (err));
3055 if ((app & APPLICATION_PGP)) {
3056 /* Its all a mess. That old GPGME expects different things
3057 depending on the protocol. For gpg we don' t need percent
3058 escaped pappert but simple strings passed in an array to the
3059 keylist_ext_start function. */
3064 for (l = hints, n = 0; l; l = l->next) {
3065 if (l->data && *l->data)
3071 patarr = p_new(char *, n + 1);
3072 for (l = hints, n = 0; l; l = l->next) {
3073 if (l->data && *l->data)
3074 patarr[n++] = m_strdup(l->data);
3077 err = gpgme_op_keylist_ext_start (ctx, (const char **) patarr, secret, 0);
3078 for (n = 0; patarr[n]; n++)
3079 p_delete(&patarr[n]);
3082 mutt_error (_("gpgme_op_keylist_start failed: %s"), gpgme_strerror (err));
3083 gpgme_release (ctx);
3088 while (!(err = gpgme_op_keylist_next (ctx, &key))) {
3089 unsigned int flags = 0;
3091 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
3092 flags |= KEYFLAG_CANENCRYPT;
3093 if (key_check_cap (key, KEY_CAP_CAN_SIGN))
3094 flags |= KEYFLAG_CANSIGN;
3096 #if 0 /* DISABLED code */
3098 /* Bug in gpg. Capabilities are not listed for secret
3099 keys. Try to deduce them from the algorithm. */
3101 switch (key->subkeys[0].pubkey_algo) {
3103 flags |= KEYFLAG_CANENCRYPT;
3104 flags |= KEYFLAG_CANSIGN;
3106 case GPGME_PK_ELG_E:
3107 flags |= KEYFLAG_CANENCRYPT;
3110 flags |= KEYFLAG_CANSIGN;
3114 #endif /* DISABLED code */
3116 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
3117 k = p_new(crypt_key_t, 1);
3126 if (gpg_err_code (err) != GPG_ERR_EOF)
3127 mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err));
3128 gpgme_op_keylist_end (ctx);
3133 if ((app & APPLICATION_SMIME)) {
3134 /* and now look for x509 certificates */
3135 gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
3136 err = gpgme_op_keylist_start (ctx, pattern, 0);
3138 mutt_error (_("gpgme_op_keylist_start failed: %s"), gpgme_strerror (err));
3139 gpgme_release (ctx);
3144 while (!(err = gpgme_op_keylist_next (ctx, &key))) {
3145 unsigned int flags = KEYFLAG_ISX509;
3147 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
3148 flags |= KEYFLAG_CANENCRYPT;
3149 if (key_check_cap (key, KEY_CAP_CAN_SIGN))
3150 flags |= KEYFLAG_CANSIGN;
3152 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
3153 k = p_new(crypt_key_t, 1);
3162 if (gpg_err_code (err) != GPG_ERR_EOF)
3163 mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err));
3164 gpgme_op_keylist_end (ctx);
3167 gpgme_release (ctx);
3172 /* Add the string STR to the list HINTS. This list is later used to
3174 static string_list_t *crypt_add_string_to_hints (string_list_t * hints, const char *str)
3179 if ((scratch = m_strdup(str)) == NULL)
3182 for (t = strtok (scratch, " ,.:\"()<>\n"); t;
3183 t = strtok (NULL, " ,.:\"()<>\n")) {
3184 if (m_strlen(t) > 3)
3185 hints = mutt_add_list(hints, t);
3192 /* Display a menu to select a key from the array KEYS. FORCED_VALID
3193 will be set to true on return if the user did override the the
3195 static crypt_key_t *crypt_select_key (crypt_key_t * keys,
3196 address_t * p, const char *s,
3197 unsigned int app, int *forced_valid)
3200 crypt_key_t **key_table;
3203 char helpstr[STRING], buf[LONG_STRING];
3205 int (*f) (const void *, const void *);
3206 int menu_to_use = 0;
3211 /* build the key table */
3214 for (k = keys; k; k = k->next) {
3215 if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
3222 p_realloc(&key_table, keymax);
3228 if (!i && unusable) {
3229 mutt_error _("All matching keys are marked expired/revoked.");
3235 switch (PgpSortKeys & SORT_MASK) {
3237 f = crypt_compare_date;
3240 f = crypt_compare_keyid;
3243 f = crypt_compare_address;
3247 f = crypt_compare_trust;
3250 qsort (key_table, i, sizeof (crypt_key_t *), f);
3252 if (app & APPLICATION_PGP)
3253 menu_to_use = MENU_KEY_SELECT_PGP;
3254 else if (app & APPLICATION_SMIME)
3255 menu_to_use = MENU_KEY_SELECT_SMIME;
3258 mutt_make_help (buf, sizeof (buf), _("Exit "), menu_to_use, OP_EXIT);
3259 m_strcat(helpstr, sizeof(helpstr), buf);
3260 mutt_make_help (buf, sizeof (buf), _("Select "), menu_to_use,
3261 OP_GENERIC_SELECT_ENTRY);
3262 m_strcat(helpstr, sizeof(helpstr), buf);
3263 mutt_make_help (buf, sizeof (buf), _("Check key "),
3264 menu_to_use, OP_VERIFY_KEY);
3265 m_strcat(helpstr, sizeof(helpstr), buf);
3266 mutt_make_help (buf, sizeof (buf), _("Help"), menu_to_use, OP_HELP);
3267 m_strcat(helpstr, sizeof(helpstr), buf);
3269 menu = mutt_new_menu ();
3271 menu->make_entry = crypt_entry;
3272 menu->menu = menu_to_use;
3273 menu->help = helpstr;
3274 menu->data = key_table;
3279 if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
3280 ts = _("PGP and S/MIME keys matching");
3281 else if ((app & APPLICATION_PGP))
3282 ts = _("PGP keys matching");
3283 else if ((app & APPLICATION_SMIME))
3284 ts = _("S/MIME keys matching");
3286 ts = _("keys matching");
3289 snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
3291 snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
3295 mutt_clear_error ();
3299 switch (mutt_menuLoop (menu)) {
3301 verify_key (key_table[menu->current]);
3302 menu->redraw = REDRAW_FULL;
3306 mutt_message ("%s", key_table[menu->current]->uid);
3309 case OP_GENERIC_SELECT_ENTRY:
3310 /* FIXME make error reporting more verbose - this should be
3311 easy because gpgme provides more information */
3312 if (option (OPTPGPCHECKTRUST)) {
3313 if (!crypt_key_is_valid (key_table[menu->current])) {
3314 mutt_error _("This key can't be used: "
3315 "expired/disabled/revoked.");
3320 if (option (OPTPGPCHECKTRUST) &&
3321 (!crypt_id_is_valid (key_table[menu->current])
3322 || !crypt_id_is_strong (key_table[menu->current]))) {
3324 char buff[LONG_STRING];
3326 if (key_table[menu->current]->flags & KEYFLAG_CANTUSE)
3327 s = N_("ID is expired/disabled/revoked.");
3329 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
3330 gpgme_user_id_t uid = NULL;
3335 uid = key_table[menu->current]->kobj->uids;
3336 for (j = 0; (j < key_table[menu->current]->idx) && uid;
3337 j++, uid = uid->next);
3339 val = uid->validity;
3342 case GPGME_VALIDITY_UNKNOWN:
3343 case GPGME_VALIDITY_UNDEFINED:
3344 warn_s = N_("ID has undefined validity.");
3346 case GPGME_VALIDITY_NEVER:
3347 warn_s = N_("ID is not valid.");
3349 case GPGME_VALIDITY_MARGINAL:
3350 warn_s = N_("ID is only marginally valid.");
3352 case GPGME_VALIDITY_FULL:
3353 case GPGME_VALIDITY_ULTIMATE:
3357 snprintf (buff, sizeof (buff),
3358 _("%s Do you really want to use the key?"), _(warn_s));
3360 if (mutt_yesorno (buff, 0) != 1) {
3361 mutt_clear_error ();
3368 k = crypt_copy_key (key_table[menu->current]);
3379 mutt_menuDestroy (&menu);
3380 p_delete(&key_table);
3382 set_option (OPTNEEDREDRAW);
3387 static crypt_key_t *crypt_getkeybyaddr (address_t * a, short abilities,
3388 unsigned int app, int *forced_valid)
3391 string_list_t *hints = NULL;
3396 int this_key_has_strong;
3397 int this_key_has_weak;
3398 int this_key_has_invalid;
3401 crypt_key_t *keys, *k;
3402 crypt_key_t *the_valid_key = NULL;
3403 crypt_key_t *matches = NULL;
3404 crypt_key_t **matches_endp = &matches;
3408 if (a && a->mailbox)
3409 hints = crypt_add_string_to_hints (hints, a->mailbox);
3410 if (a && a->personal)
3411 hints = crypt_add_string_to_hints (hints, a->personal);
3413 mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
3414 keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
3416 string_list_wipe(&hints);
3421 for (k = keys; k; k = k->next) {
3422 if (abilities && !(k->flags & abilities)) {
3426 this_key_has_weak = 0; /* weak but valid match */
3427 this_key_has_invalid = 0; /* invalid match */
3428 this_key_has_strong = 0; /* strong and valid match */
3429 match = 0; /* any match */
3431 r = rfc822_parse_adrlist (NULL, k->uid);
3432 for (p = r; p; p = p->next) {
3433 int validity = crypt_id_matches_addr (a, p, k);
3435 if (validity & CRYPT_KV_MATCH) /* something matches */
3438 /* is this key a strong candidate? */
3439 if ((validity & CRYPT_KV_VALID)
3440 && (validity & CRYPT_KV_STRONGID)
3441 && (validity & CRYPT_KV_ADDR)) {
3442 if (the_valid_key && the_valid_key != k)
3445 this_key_has_strong = 1;
3447 else if ((validity & CRYPT_KV_MATCH)
3448 && !(validity & CRYPT_KV_VALID))
3449 this_key_has_invalid = 1;
3450 else if ((validity & CRYPT_KV_MATCH)
3451 && (!(validity & CRYPT_KV_STRONGID)
3452 || !(validity & CRYPT_KV_ADDR)))
3453 this_key_has_weak = 1;
3455 address_list_wipe(&r);
3460 if (!this_key_has_strong && this_key_has_invalid)
3462 if (!this_key_has_strong && this_key_has_weak)
3465 *matches_endp = tmp = crypt_copy_key (k);
3466 matches_endp = &tmp->next;
3467 the_valid_key = tmp;
3471 crypt_free_key (&keys);
3474 if (the_valid_key && !multi && !weak
3475 && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
3477 * There was precisely one strong match on a valid ID, there
3478 * were no valid keys with weak matches, and we aren't
3479 * interested in seeing invalid keys.
3481 * Proceed without asking the user.
3483 k = crypt_copy_key (the_valid_key);
3487 * Else: Ask the user.
3489 k = crypt_select_key (matches, a, NULL, app, forced_valid);
3491 crypt_free_key (&matches);
3500 static crypt_key_t *crypt_getkeybystr (const char *p, short abilities,
3501 unsigned int app, int *forced_valid)
3503 string_list_t *hints = NULL;
3505 crypt_key_t *matches = NULL;
3506 crypt_key_t **matches_endp = &matches;
3510 mutt_message (_("Looking for keys matching \"%s\"..."), p);
3514 hints = crypt_add_string_to_hints (hints, p);
3515 keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
3516 string_list_wipe(&hints);
3521 for (k = keys; k; k = k->next) {
3522 if (abilities && !(k->flags & abilities))
3527 if (!*p || !m_strcasecmp(p, crypt_keyid (k))
3528 || (!m_strncasecmp(p, "0x", 2)
3529 && !m_strcasecmp(p + 2, crypt_keyid (k)))
3530 || (option (OPTPGPLONGIDS)
3531 && !m_strncasecmp(p, "0x", 2)
3532 && !m_strcasecmp(p + 2, crypt_keyid (k) + 8))
3533 || m_stristr(k->uid, p)) {
3536 *matches_endp = tmp = crypt_copy_key (k);
3537 matches_endp = &tmp->next;
3541 crypt_free_key (&keys);
3544 k = crypt_select_key (matches, NULL, p, app, forced_valid);
3545 crypt_free_key (&matches);
3552 /* Display TAG as a prompt to ask for a key. If WHATFOR is not null
3553 use it as default and store it under that label as the next
3554 default. ABILITIES describe the required key abilities (sign,
3555 encrypt) and APP the type of the requested key; ether S/MIME or
3556 PGP. Return a copy of the key or NULL if not found. */
3557 static crypt_key_t *crypt_ask_for_key (char *tag,
3560 unsigned int app, int *forced_valid)
3564 struct crypt_cache *l = NULL;
3568 forced_valid = &dummy;
3570 mutt_clear_error ();
3576 for (l = id_defaults; l; l = l->next)
3577 if (!m_strcasecmp(whatfor, l->what)) {
3578 m_strcpy(resp, sizeof(resp), NONULL(l->dflt));
3586 if (mutt_get_field (tag, resp, sizeof (resp), M_CLEAR) != 0)
3591 m_strreplace(&l->dflt, resp);
3593 l = p_new(struct crypt_cache, 1);
3594 l->next = id_defaults;
3596 l->what = m_strdup(whatfor);
3597 l->dflt = m_strdup(resp);
3601 if ((key = crypt_getkeybystr (resp, abilities, app, forced_valid)))
3609 /* This routine attempts to find the keyids of the recipients of a
3610 message. It returns NULL if any of the keys can not be found. */
3611 static char *find_keys (address_t * to, address_t * cc, address_t * bcc,
3614 char *keylist = NULL, *t;
3616 ssize_t keylist_size = 0;
3617 ssize_t keylist_used = 0;
3618 address_t *tmp = NULL, *addr = NULL;
3619 address_t **last = &tmp;
3622 crypt_key_t *k_info, *key;
3623 const char *fqdn = mutt_fqdn (1);
3626 *r_application = APPLICATION_PGP | APPLICATION_SMIME;
3629 for (i = 0; i < 3; i++) {
3644 *last = address_list_dup (p);
3646 last = &((*last)->next);
3649 rfc822_qualify(tmp, fqdn);
3650 address_list_uniq(tmp);
3652 for (p = tmp; p; p = p->next) {
3653 char buf[LONG_STRING];
3654 int forced_valid = 0;
3659 if ((keyID = mutt_crypt_hook (p)) != NULL) {
3662 snprintf (buf, sizeof (buf), _("Use keyID = \"%s\" for %s?"),
3664 if ((r = mutt_yesorno (buf, M_YES)) == M_YES) {
3665 /* check for e-mail address */
3666 if ((t = strchr (keyID, '@')) &&
3667 (addr = rfc822_parse_adrlist (NULL, keyID))) {
3668 rfc822_qualify(addr, fqdn);
3672 k_info = crypt_getkeybystr (keyID, KEYFLAG_CANENCRYPT,
3673 app, &forced_valid);
3678 address_list_wipe(&tmp);
3679 address_list_wipe(&addr);
3685 && (k_info = crypt_getkeybyaddr (q, KEYFLAG_CANENCRYPT,
3686 app, &forced_valid)) == NULL) {
3687 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
3689 if ((key = crypt_ask_for_key (buf, q->mailbox, KEYFLAG_CANENCRYPT,
3691 &forced_valid)) == NULL) {
3693 address_list_wipe(&tmp);
3694 address_list_wipe(&addr);
3702 const char *s = crypt_fpr (key);
3704 keylist_size += m_strlen(s) + 4 + 1;
3705 p_realloc(&keylist, keylist_size);
3706 sprintf (keylist + keylist_used, "%s0x%s%s",
3707 keylist_used ? " " : "", s, forced_valid ? "!" : "");
3709 keylist_used = m_strlen(keylist);
3711 crypt_free_key (&key);
3712 address_list_wipe(&addr);
3714 address_list_wipe(&tmp);
3718 char *crypt_pgp_findkeys (address_t * to, address_t * cc, address_t * bcc)
3720 return find_keys (to, cc, bcc, APPLICATION_PGP);
3723 char *crypt_smime_findkeys (address_t * to, address_t * cc, address_t * bcc)
3725 return find_keys (to, cc, bcc, APPLICATION_SMIME);
3728 static int gpgme_send_menu (HEADER * msg, int *redraw, int is_smime)
3731 char input_signas[STRING];
3734 if (msg->security & APPLICATION_PGP)
3736 else if (msg->security & APPLICATION_SMIME)
3741 mutt_multi_choice (_
3742 ("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
3746 mutt_multi_choice (_
3747 ("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
3751 case 1: /* (e)ncrypt */
3752 msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3753 msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
3756 case 2: /* (s)ign */
3757 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3758 msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3761 case 3: /* sign (a)s */
3762 /* unset_option(OPTCRYPTCHECKTRUST); */
3763 if ((p = crypt_ask_for_key (_("Sign as: "), NULL, KEYFLAG_CANSIGN,
3764 is_smime ? APPLICATION_SMIME :
3765 APPLICATION_PGP, NULL))) {
3766 snprintf (input_signas, sizeof (input_signas), "0x%s", crypt_keyid (p));
3767 m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs,
3769 crypt_free_key (&p);
3771 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3773 *redraw = REDRAW_FULL;
3776 case 4: /* (b)oth */
3778 (is_smime ? (SMIMEENCRYPT | SMIMESIGN) : (PGPENCRYPT | PGPSIGN));
3781 case 5: /* (p)gp or s/(m)ime */
3782 is_smime = !is_smime;
3785 case 6: /* (c)lear */
3790 if (choice == 6 || choice == 7);
3791 else if (is_smime) {
3792 msg->security &= ~APPLICATION_PGP;
3793 msg->security |= APPLICATION_SMIME;
3796 msg->security &= ~APPLICATION_SMIME;
3797 msg->security |= APPLICATION_PGP;
3800 return (msg->security);
3803 int crypt_pgp_send_menu(HEADER * msg, int *redraw)
3805 return gpgme_send_menu(msg, redraw, 0);
3808 int crypt_smime_send_menu(HEADER * msg, int *redraw)
3810 return gpgme_send_menu (msg, redraw, 1);
3813 int crypt_smime_verify_sender (HEADER * h)
3815 address_t *sender = NULL;
3816 unsigned int ret = 1;
3819 h->env->from = mutt_expand_aliases (h->env->from);
3820 sender = h->env->from;
3822 else if (h->env->sender) {
3823 h->env->sender = mutt_expand_aliases (h->env->sender);
3824 sender = h->env->sender;
3828 if (signature_key) {
3829 gpgme_key_t key = signature_key;
3830 gpgme_user_id_t uid = NULL;
3831 int sender_length = 0;
3834 sender_length = m_strlen(sender->mailbox);
3835 for (uid = key->uids; uid && ret; uid = uid->next) {
3836 uid_length = m_strlen(uid->email);
3837 if (1 && (uid->email[0] == '<')
3838 && (uid->email[uid_length - 1] == '>')
3839 && (uid_length == sender_length + 2)
3840 && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
3845 mutt_any_key_to_continue ("Failed to verify sender");
3848 mutt_any_key_to_continue ("Failed to figure out sender");
3850 if (signature_key) {
3851 gpgme_key_release (signature_key);
3852 signature_key = NULL;
3858 static void invoke_import(const char *fname, int smime)
3860 gpgme_ctx_t ctx = create_gpgme_context(smime);
3864 err = gpgme_data_new_from_file(&data, fname, 1);
3866 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
3871 err = gpgme_op_import(ctx, data);
3873 mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
3874 gpgme_data_release(data);
3879 gpgme_data_release(data);
3884 void crypt_pgp_invoke_import(const char *fname)
3886 invoke_import(fname, 0);
3889 void crypt_smime_invoke_import(const char *fname)
3891 invoke_import(fname, 1);
3894 static void pgp_extract_keys_from_attachment (FILE * fp, BODY * top)
3898 char tempfname[_POSIX_PATH_MAX];
3900 tempfp = m_tempfile(tempfname, sizeof(tempfname), NONULL(MCore.tmpdir), NULL);
3901 if (tempfp == NULL) {
3902 mutt_perror (_("Can't create temporary file"));
3911 mutt_body_handler (top, &s);
3914 crypt_pgp_invoke_import(tempfname);
3915 mutt_unlink (tempfname);
3918 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
3921 set_option (OPTDONTHANDLEPGPKEYS);
3923 for (; top; top = top->next) {
3924 if (!tag || top->tagged)
3925 pgp_extract_keys_from_attachment (fp, top);
3931 unset_option (OPTDONTHANDLEPGPKEYS);