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>
19 #include <lib-mime/mime.h>
20 #include <lib-ui/curses.h>
21 #include <lib-ui/enter.h>
22 #include <lib-ui/menu.h>
31 #include "recvattach.h"
34 /* Values used for comparing addresses. */
35 #define CRYPT_KV_VALID 1
36 #define CRYPT_KV_ADDR 2
37 #define CRYPT_KV_STRING 4
38 #define CRYPT_KV_STRONGID 8
39 #define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
48 struct crypt_cache *next;
56 /* We work based on user IDs, getting from a user ID to the key is
57 check and does not need any memory (gpgme uses reference counting). */
58 typedef struct crypt_keyinfo {
59 struct crypt_keyinfo *next;
61 int idx; /* and the user ID at this index */
62 const char *uid; /* and for convenience point to this user ID */
63 unsigned int flags; /* global and per uid flags (for convenience) */
66 typedef struct crypt_entry {
72 static struct crypt_cache *id_defaults = NULL;
73 static gpgme_key_t signature_key = NULL;
75 /* Show a message that a backend will be invoked. */
76 void crypt_invoke_message (int type)
78 if (type & APPLICATION_PGP) {
79 mutt_message _("Invoking PGP...");
81 else if (type & APPLICATION_SMIME) {
82 mutt_message _("Invoking S/MIME...");
87 * General helper functions.
90 /* return true when S points to a didgit or letter. */
91 static int digit_or_letter (const unsigned char *s)
93 return ((*s >= '0' && *s <= '9')
94 || (*s >= 'A' && *s <= 'Z')
95 || (*s >= 'a' && *s <= 'z'));
99 /* Print the utf-8 encoded string BUF of length LEN bytes to stream
100 FP. Convert the character set. */
101 static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
105 tstr = p_dupstr(buf, len);
106 mutt_convert_string (&tstr, "utf-8", MCharset.charset, M_ICONV_HOOK_FROM);
116 /* Return the keyID for the key K. Note that this string is valid as
117 long as K is valid */
118 static const char *crypt_keyid (crypt_key_t * k)
120 const char *s = "????????";
122 if (k->kobj && k->kobj->subkeys) {
123 s = k->kobj->subkeys->keyid;
124 if ((!option (OPTPGPLONGIDS)) && (m_strlen(s) == 16))
125 /* Return only the short keyID. */
132 /* Return the hexstring fingerprint from the key K. */
133 static const char *crypt_fpr (crypt_key_t * k)
137 if (k->kobj && k->kobj->subkeys)
138 s = k->kobj->subkeys->fpr;
143 /* Parse FLAGS and return a statically allocated(!) string with them. */
144 static char *crypt_key_abilities (int flags)
148 if (!(flags & KEYFLAG_CANENCRYPT))
150 else if (flags & KEYFLAG_PREFER_SIGNING)
155 if (!(flags & KEYFLAG_CANSIGN))
157 else if (flags & KEYFLAG_PREFER_ENCRYPTION)
167 /* Parse FLAGS and return a character describing the most important flag. */
168 static char crypt_flags (int flags)
170 if (flags & KEYFLAG_REVOKED)
172 else if (flags & KEYFLAG_EXPIRED)
174 else if (flags & KEYFLAG_DISABLED)
176 else if (flags & KEYFLAG_CRITICAL)
182 /* Return a copy of KEY. */
183 static crypt_key_t *crypt_copy_key (crypt_key_t *key)
187 k = p_new(crypt_key_t, 1);
189 gpgme_key_ref (key->kobj);
192 k->flags = key->flags;
197 /* Release all the keys at the address of KEYLIST and set the address
199 static void crypt_free_key (crypt_key_t ** keylist)
202 crypt_key_t *k = (*keylist)->next;
209 /* Return trute when key K is valid. */
210 static int crypt_key_is_valid (crypt_key_t * k)
212 if (k->flags & KEYFLAG_CANTUSE)
217 /* Return true whe validity of KEY is sufficient. */
218 static int crypt_id_is_strong (crypt_key_t * key)
220 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
221 gpgme_user_id_t uid = NULL;
225 if ((key->flags & KEYFLAG_ISX509))
228 for (i = 0, uid = key->kobj->uids; (i < key->idx) && uid;
229 i++, uid = uid->next);
234 case GPGME_VALIDITY_UNKNOWN:
235 case GPGME_VALIDITY_UNDEFINED:
236 case GPGME_VALIDITY_NEVER:
237 case GPGME_VALIDITY_MARGINAL:
241 case GPGME_VALIDITY_FULL:
242 case GPGME_VALIDITY_ULTIMATE:
250 /* Return true when the KEY is valid, i.e. not marked as unusable. */
251 static int crypt_id_is_valid (crypt_key_t * key)
253 return !(key->flags & KEYFLAG_CANTUSE);
256 /* Return a bit vector describing how well the addresses ADDR and
257 U_ADDR match and whether KEY is valid. */
258 static int crypt_id_matches_addr (address_t * addr, address_t * u_addr,
263 if (crypt_id_is_valid (key))
264 rv |= CRYPT_KV_VALID;
266 if (crypt_id_is_strong (key))
267 rv |= CRYPT_KV_STRONGID;
269 if (addr->mailbox && u_addr->mailbox
270 && m_strcasecmp(addr->mailbox, u_addr->mailbox) == 0)
273 if (addr->personal && u_addr->personal
274 && m_strcasecmp(addr->personal, u_addr->personal) == 0)
275 rv |= CRYPT_KV_STRING;
282 * GPGME convenient functions.
285 /* Create a new gpgme context and return it. With FOR_SMIME set to
286 true, the protocol of the context is set to CMS. */
287 static gpgme_ctx_t create_gpgme_context (int for_smime)
292 err = gpgme_new (&ctx);
294 mutt_error (_("error creating gpgme context: %s\n"), gpgme_strerror (err));
300 err = gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
302 mutt_error (_("error enabling CMS protocol: %s\n"), gpgme_strerror (err));
311 /* Create a new gpgme data object. This is a wrapper to die on
313 static gpgme_data_t create_gpgme_data (void)
318 err = gpgme_data_new (&data);
320 mutt_error (_("error creating gpgme data object: %s\n"),
321 gpgme_strerror (err));
328 /* Create a new GPGME Data object from the mail body A. With CONVERT
329 passed as true, the lines are converted to CR,LF if required.
330 Return NULL on error or the gpgme_data_t object on success. */
331 static gpgme_data_t body_to_data_object (BODY * a, int convert)
333 char tempfile[_POSIX_PATH_MAX];
338 fptmp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
340 mutt_perror (_("Can't create temporary file"));
344 mutt_write_mime_header (a, fptmp);
346 mutt_write_mime_body (a, fptmp);
350 unsigned char buf[1];
352 data = create_gpgme_data ();
354 while ((c = fgetc (fptmp)) != EOF) {
358 if (c == '\n' && !hadcr) {
360 gpgme_data_write (data, buf, 1);
365 /* FIXME: This is quite suboptimal */
367 gpgme_data_write (data, buf, 1);
369 gpgme_data_seek (data, 0, SEEK_SET);
371 err = gpgme_data_new_from_file (&data, tempfile, 1);
376 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
383 /* Create a GPGME data object from the stream FP but limit the object
384 to LENGTH bytes starting at OFFSET bytes from the beginning of the
386 static gpgme_data_t file_to_data_object (FILE * fp, long offset, long length)
391 err = gpgme_data_new_from_filepart (&data, NULL, fp, offset, length);
393 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
400 /* Write a GPGME data object to the stream FP. */
401 static int data_object_to_stream (gpgme_data_t data, FILE * fp)
407 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
408 ? gpgme_error_from_errno (errno) : 0);
410 mutt_error (_("error rewinding data object: %s\n"), gpgme_strerror (err));
414 while ((nread = gpgme_data_read (data, buf, sizeof (buf)))) {
415 /* fixme: we are not really converting CRLF to LF but just
416 skipping CR. Doing it correctly needs a more complex logic */
417 for (p = buf; nread; p++, nread--) {
423 mutt_perror ("[tempfile]");
428 mutt_error (_("error reading data object: %s\n"), strerror (errno));
434 /* Copy a data object to a newly created temporay file and return that
435 filename. Caller must free. With RET_FP not NULL, don't close the
436 stream but return it there. */
437 static char *data_object_to_tempfile (gpgme_data_t data, FILE ** ret_fp)
440 char tempfile[_POSIX_PATH_MAX];
444 fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
446 mutt_perror (_("Can't create temporary file"));
450 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
451 ? gpgme_error_from_errno (errno) : 0);
455 while ((nread = gpgme_data_read (data, buf, sizeof (buf)))) {
456 if (fwrite (buf, nread, 1, fp) != 1) {
457 mutt_perror (_("Can't create temporary file"));
469 mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
476 return m_strdup(tempfile);
480 /* FIXME: stolen from gpgme to avoid "ambiguous identity" errors */
482 gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
488 if (!ctx || !r_key || !fpr)
489 return gpg_error (GPG_ERR_INV_VALUE);
491 if (strlen (fpr) < 8) /* We have at least a key ID. */
492 return gpg_error (GPG_ERR_INV_VALUE);
494 /* FIXME: We use our own context because we have to avoid the user's
495 I/O callback handlers. */
496 err = gpgme_new (&listctx);
499 gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
500 err = gpgme_op_keylist_start (listctx, fpr, secret);
502 err = gpgme_op_keylist_next (listctx, r_key);
503 gpgme_release (listctx);
507 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
508 The keys must be space delimited. */
509 static gpgme_key_t *create_recipient_set (const char *keylist,
510 gpgme_protocol_t protocol)
516 gpgme_key_t *rset = NULL;
517 unsigned int rset_n = 0;
518 gpgme_key_t key = NULL;
519 gpgme_ctx_t context = NULL;
521 err = gpgme_new (&context);
523 err = gpgme_set_protocol (context, protocol);
530 for (i = 0; *s && *s != ' ' && i < ssizeof(buf) - 1;)
534 if (i > 1 && buf[i - 1] == '!') {
535 /* The user selected to override the valididy of that
539 err = gpgme_get_key2 (context, buf, &key, 0);
541 key->uids->validity = GPGME_VALIDITY_FULL;
545 err = gpgme_get_key2 (context, buf, &key, 0);
548 p_realloc(&rset, rset_n + 1);
549 rset[rset_n++] = key;
552 mutt_error (_("error adding recipient `%s': %s\n"),
553 buf, gpgme_strerror (err));
561 /* NULL terminate. */
562 p_realloc(&rset, rset_n + 1);
563 rset[rset_n++] = NULL;
566 gpgme_release (context);
572 /* Make sure that the correct signer is set. Returns 0 on success. */
573 static int set_signer (gpgme_ctx_t ctx, int for_smime)
575 char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
578 gpgme_key_t key, key2;
580 if (!signid || !*signid)
583 listctx = create_gpgme_context (for_smime);
584 err = gpgme_op_keylist_start (listctx, signid, 1);
586 err = gpgme_op_keylist_next (listctx, &key);
588 gpgme_release (listctx);
589 mutt_error (_("secret key `%s' not found: %s\n"),
590 signid, gpgme_strerror (err));
593 err = gpgme_op_keylist_next (listctx, &key2);
595 gpgme_key_release (key);
596 gpgme_key_release (key2);
597 gpgme_release (listctx);
598 mutt_error (_("ambiguous specification of secret key `%s'\n"), signid);
601 gpgme_op_keylist_end (listctx);
602 gpgme_release (listctx);
604 gpgme_signers_clear (ctx);
605 err = gpgme_signers_add (ctx, key);
606 gpgme_key_release (key);
608 mutt_error (_("error setting secret key `%s': %s\n"),
609 signid, gpgme_strerror (err));
616 /* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
617 and return an allocated filename to a temporary file containing the
618 enciphered text. With USE_SMIME set to true, the smime backend is
619 used. With COMBINED_SIGNED a PGP message is signed and
620 encrypted. Returns NULL in case of error */
621 static char *encrypt_gpgme_object (gpgme_data_t plaintext, gpgme_key_t * rset,
622 int use_smime, int combined_signed)
626 gpgme_data_t ciphertext;
629 ctx = create_gpgme_context (use_smime);
631 gpgme_set_armor (ctx, 1);
633 ciphertext = create_gpgme_data ();
635 if (combined_signed) {
636 if (set_signer (ctx, use_smime)) {
637 gpgme_data_release (ciphertext);
641 err = gpgme_op_encrypt_sign (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
642 plaintext, ciphertext);
645 err = gpgme_op_encrypt (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
646 plaintext, ciphertext);
647 mutt_need_hard_redraw ();
649 mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
650 gpgme_data_release (ciphertext);
657 outfile = data_object_to_tempfile (ciphertext, NULL);
658 gpgme_data_release (ciphertext);
662 /* Find the "micalg" parameter from the last Gpgme operation on
663 context CTX. It is expected that this operation was a sign
664 operation. Return the algorithm name as a C string in buffer BUF
665 which must have been allocated by the caller with size BUFLEN.
666 Returns 0 on success or -1 in case of an error. The return string
667 is truncted to BUFLEN - 1. */
668 static int get_micalg (gpgme_ctx_t ctx, char *buf, ssize_t buflen)
670 gpgme_sign_result_t result = NULL;
671 const char *algorithm_name = NULL;
677 result = gpgme_op_sign_result (ctx);
679 algorithm_name = gpgme_hash_algo_name (result->signatures->hash_algo);
680 if (algorithm_name) {
681 m_strcpy(buf, buflen, algorithm_name);
685 return *buf ? 0 : -1;
688 static void print_time (time_t t, STATE * s)
692 setlocale (LC_TIME, "");
693 #ifdef HAVE_LANGINFO_D_T_FMT
694 strftime (p, sizeof (p), nl_langinfo (D_T_FMT), localtime (&t));
696 strftime (p, sizeof (p), "%c", localtime (&t));
698 setlocale (LC_TIME, "C");
699 state_attach_puts (p, s);
702 /* Implementation of `sign_message'. */
704 /* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
705 USE_SMIME is passed as true. Returns the new body or NULL on
707 static BODY *sign_message (BODY * a, int use_smime)
714 gpgme_data_t message, signature;
716 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
718 message = body_to_data_object (a, 1);
721 signature = create_gpgme_data ();
723 ctx = create_gpgme_context (use_smime);
725 gpgme_set_armor (ctx, 1);
727 if (set_signer (ctx, use_smime)) {
728 gpgme_data_release (signature);
733 err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH);
734 mutt_need_hard_redraw ();
735 gpgme_data_release (message);
737 gpgme_data_release (signature);
739 mutt_error (_("error signing data: %s\n"), gpgme_strerror (err));
743 sigfile = data_object_to_tempfile (signature, NULL);
744 gpgme_data_release (signature);
751 t->type = TYPEMULTIPART;
752 t->subtype = m_strdup("signed");
753 t->encoding = ENC7BIT;
755 t->disposition = DISPINLINE;
757 parameter_set_boundary(&t->parameter);
758 parameter_setval(&t->parameter, "protocol",
759 use_smime ? "application/pkcs7-signature"
760 : "application/pgp-signature");
761 /* Get the micalg from gpgme. Old gpgme versions don't support this
762 for S/MIME so we assume sha-1 in this case. */
763 if (!get_micalg (ctx, buf, sizeof buf))
764 parameter_setval(&t->parameter, "micalg", buf);
766 parameter_setval(&t->parameter, "micalg", "sha1");
772 t->parts->next = body_new();
774 t->type = TYPEAPPLICATION;
776 t->subtype = m_strdup("pkcs7-signature");
777 parameter_setval(&t->parameter, "name", "smime.p7s");
778 t->encoding = ENCBASE64;
780 t->disposition = DISPATTACH;
781 t->d_filename = m_strdup("smime.p7s");
784 t->subtype = m_strdup("pgp-signature");
786 t->disposition = DISPINLINE;
787 t->encoding = ENC7BIT;
789 t->filename = sigfile;
790 t->unlink = 1; /* ok to remove this file after sending. */
796 BODY *crypt_pgp_sign_message (BODY * a)
798 return sign_message (a, 0);
801 BODY *crypt_smime_sign_message (BODY * a)
803 return sign_message (a, 1);
807 * Implementation of `encrypt_message'.
810 /* Encrypt the mail body A to all keys given as space separated keyids
811 or fingerprints in KEYLIST and return the encrypted body. */
812 BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
814 char *outfile = NULL;
816 gpgme_key_t *rset = NULL;
817 gpgme_data_t plaintext;
819 rset = create_recipient_set (keylist, GPGME_PROTOCOL_OpenPGP);
825 plaintext = body_to_data_object (a, 0);
831 outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
832 gpgme_data_release (plaintext);
838 t->type = TYPEMULTIPART;
839 t->subtype = m_strdup("encrypted");
840 t->encoding = ENC7BIT;
842 t->disposition = DISPINLINE;
844 parameter_set_boundary(&t->parameter);
845 parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
847 t->parts = body_new();
848 t->parts->type = TYPEAPPLICATION;
849 t->parts->subtype = m_strdup("pgp-encrypted");
850 t->parts->encoding = ENC7BIT;
852 t->parts->next = body_new();
853 t->parts->next->type = TYPEAPPLICATION;
854 t->parts->next->subtype = m_strdup("octet-stream");
855 t->parts->next->encoding = ENC7BIT;
856 t->parts->next->filename = outfile;
857 t->parts->next->use_disp = 1;
858 t->parts->next->disposition = DISPINLINE;
859 t->parts->next->unlink = 1; /* delete after sending the message */
860 t->parts->next->d_filename = m_strdup("msg.asc"); /* non pgp/mime
867 * Implementation of `smime_build_smime_entity'.
870 /* Encrypt the mail body A to all keys given as space separated
871 fingerprints in KEYLIST and return the S/MIME encrypted body. */
872 BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
874 char *outfile = NULL;
876 gpgme_key_t *rset = NULL;
877 gpgme_data_t plaintext;
879 rset = create_recipient_set (keylist, GPGME_PROTOCOL_CMS);
883 plaintext = body_to_data_object (a, 0);
889 outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
890 gpgme_data_release (plaintext);
896 t->type = TYPEAPPLICATION;
897 t->subtype = m_strdup("pkcs7-mime");
898 parameter_setval(&t->parameter, "name", "smime.p7m");
899 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
900 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
902 t->disposition = DISPATTACH;
903 t->d_filename = m_strdup("smime.p7m");
904 t->filename = outfile;
905 t->unlink = 1; /*delete after sending the message */
913 /* Implementation of `verify_one'. */
915 /* Display the common attributes of the signature summary SUM.
916 Return 1 if there is is a severe warning.
918 static int show_sig_summary (unsigned long sum,
919 gpgme_ctx_t ctx, gpgme_key_t key, int idx,
924 if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
925 state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
929 if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
930 time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
933 state_attach_puts (_("Warning: The key used to create the "
934 "signature expired at: "), s);
936 state_attach_puts ("\n", s);
939 state_attach_puts (_("Warning: At least one certification key "
940 "has expired\n"), s);
943 if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
944 gpgme_verify_result_t result;
945 gpgme_signature_t sig;
948 result = gpgme_op_verify_result (ctx);
950 for (sig = result->signatures, i = 0; sig && (i < idx);
951 sig = sig->next, i++);
953 state_attach_puts (_("Warning: The signature expired at: "), s);
954 print_time (sig ? sig->exp_timestamp : 0, s);
955 state_attach_puts ("\n", s);
958 if ((sum & GPGME_SIGSUM_KEY_MISSING))
959 state_attach_puts (_("Can't verify due to a missing "
960 "key or certificate\n"), s);
962 if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
963 state_attach_puts (_("The CRL is not available\n"), s);
967 if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
968 state_attach_puts (_("Available CRL is too old\n"), s);
972 if ((sum & GPGME_SIGSUM_BAD_POLICY))
973 state_attach_puts (_("A policy requirement was not met\n"), s);
975 if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
976 const char *t0 = NULL, *t1 = NULL;
977 gpgme_verify_result_t result;
978 gpgme_signature_t sig;
981 state_attach_puts (_("A system error occurred"), s);
983 /* Try to figure out some more detailed system error information. */
984 result = gpgme_op_verify_result (ctx);
985 for (sig = result->signatures, i = 0; sig && (i < idx);
986 sig = sig->next, i++);
989 t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
993 state_attach_puts (": ", s);
995 state_attach_puts (t0, s);
996 if (t1 && !(t0 && !m_strcmp(t0, t1))) {
998 state_attach_puts (",", s);
999 state_attach_puts (t1, s);
1002 state_attach_puts ("\n", s);
1009 static void show_fingerprint (gpgme_key_t key, STATE * state)
1014 const char *prefix = _("Fingerprint: ");
1019 s = key->subkeys ? key->subkeys->fpr : NULL;
1022 is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
1024 bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
1025 buf = p_new(char, bufsize);
1026 m_strcpy(buf, bufsize, prefix);
1027 p = buf + m_strlen(buf);
1028 if (is_pgp && m_strlen(s) == 40) { /* PGP v4 style formatted. */
1029 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
1040 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
1043 *p++ = is_pgp ? ' ' : ':';
1044 if (is_pgp && i == 7)
1049 /* just in case print remaining odd digits */
1054 state_attach_puts (buf, state);
1058 /* Show the valididy of a key used for one signature. */
1059 static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
1061 gpgme_verify_result_t result = NULL;
1062 gpgme_signature_t sig = NULL;
1063 const char *txt = NULL;
1065 result = gpgme_op_verify_result (ctx);
1067 for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
1069 switch (sig ? sig->validity : 0) {
1070 case GPGME_VALIDITY_UNKNOWN:
1071 txt = _("WARNING: We have NO indication whether "
1072 "the key belongs to the person named " "as shown above\n");
1074 case GPGME_VALIDITY_UNDEFINED:
1076 case GPGME_VALIDITY_NEVER:
1077 txt = _("WARNING: The key does NOT BELONG to "
1078 "the person named as shown above\n");
1080 case GPGME_VALIDITY_MARGINAL:
1081 txt = _("WARNING: It is NOT certain that the key "
1082 "belongs to the person named as shown above\n");
1084 case GPGME_VALIDITY_FULL:
1085 case GPGME_VALIDITY_ULTIMATE:
1090 state_attach_puts (txt, s);
1093 /* Show information about one signature. This fucntion is called with
1094 the context CTX of a sucessful verification operation and the
1095 enumerator IDX which should start at 0 and incremete for each
1098 Return values are: 0 for normal procession, 1 for a bad signature,
1099 2 for a signature with a warning or -1 for no more signature. */
1100 static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
1103 const char *fpr, *uid;
1104 gpgme_key_t key = NULL;
1105 int i, anybad = 0, anywarn = 0;
1107 gpgme_user_id_t uids = NULL;
1108 gpgme_verify_result_t result;
1109 gpgme_signature_t sig;
1110 gpgme_error_t err = GPG_ERR_NO_ERROR;
1112 result = gpgme_op_verify_result (ctx);
1114 /* FIXME: this code should use a static variable and remember
1115 the current position in the list of signatures, IMHO.
1118 for (i = 0, sig = result->signatures; sig && (i < idx);
1119 i++, sig = sig->next);
1121 return -1; /* Signature not found. */
1123 if (signature_key) {
1124 gpgme_key_release (signature_key);
1125 signature_key = NULL;
1128 created = sig->timestamp;
1132 if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
1135 err = gpgme_get_key2 (ctx, fpr, &key, 0); /* secret key? */
1137 uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
1139 signature_key = key;
1142 key = NULL; /* Old gpgme versions did not set KEY to NULL on
1143 error. Do it here to avoid a double free. */
1147 if (!s || !s->fpout || !(s->flags & M_DISPLAY)); /* No state information so no way to print anything. */
1149 state_attach_puts (_("Error getting key information: "), s);
1150 state_attach_puts (gpg_strerror (err), s);
1151 state_attach_puts ("\n", s);
1154 else if ((sum & GPGME_SIGSUM_GREEN)) {
1155 state_attach_puts (_("Good signature from: "), s);
1156 state_attach_puts (uid, s);
1157 state_attach_puts ("\n", s);
1158 for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
1160 /* Skip primary UID. */
1164 state_attach_puts (_(" aka: "), s);
1165 state_attach_puts (uids->uid, s);
1166 state_attach_puts ("\n", s);
1168 state_attach_puts (_(" created: "), s);
1169 print_time (created, s);
1170 state_attach_puts ("\n", s);
1171 if (show_sig_summary (sum, ctx, key, idx, s))
1173 show_one_sig_validity (ctx, idx, s);
1175 else if ((sum & GPGME_SIGSUM_RED)) {
1176 state_attach_puts (_("*BAD* signature claimed to be from: "), s);
1177 state_attach_puts (uid, s);
1178 state_attach_puts ("\n", s);
1179 show_sig_summary (sum, ctx, key, idx, s);
1181 else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) { /* We can't decide (yellow) but this is a PGP key with a good
1182 signature, so we display what a PGP user expects: The name,
1183 fingerprint and the key validity (which is neither fully or
1185 state_attach_puts (_("Good signature from: "), s);
1186 state_attach_puts (uid, s);
1187 state_attach_puts ("\n", s);
1188 state_attach_puts (_(" created: "), s);
1189 print_time (created, s);
1190 state_attach_puts ("\n", s);
1191 show_one_sig_validity (ctx, idx, s);
1192 show_fingerprint (key, s);
1193 if (show_sig_summary (sum, ctx, key, idx, s))
1196 else { /* can't decide (yellow) */
1198 state_attach_puts (_("Error checking signature"), s);
1199 state_attach_puts ("\n", s);
1200 show_sig_summary (sum, ctx, key, idx, s);
1203 if (key != signature_key)
1204 gpgme_key_release (key);
1207 return anybad ? 1 : anywarn ? 2 : 0;
1210 /* Do the actual verification step. With IS_SMIME set to true we
1211 assume S/MIME (surprise!) */
1212 static int verify_one (BODY * sigbdy, STATE * s,
1213 const char *tempfile, int is_smime)
1219 gpgme_data_t signature, message;
1221 signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
1225 /* We need to tell gpgme about the encoding because the backend can't
1226 auto-detect plain base-64 encoding which is used by S/MIME. */
1228 gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
1230 err = gpgme_data_new_from_file (&message, tempfile, 1);
1232 gpgme_data_release (signature);
1233 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
1236 ctx = create_gpgme_context (is_smime);
1238 /* Note: We don't need a current time output because GPGME avoids
1239 such an attack by separating the meta information from the
1241 state_attach_puts (_("[-- Begin signature information --]\n"), s);
1243 err = gpgme_op_verify (ctx, signature, message, NULL);
1244 mutt_need_hard_redraw ();
1248 snprintf (buf, sizeof (buf) - 1,
1249 _("Error: verification failed: %s\n"), gpgme_strerror (err));
1250 state_attach_puts (buf, s);
1252 else { /* Verification succeeded, see what the result is. */
1256 if (signature_key) {
1257 gpgme_key_release (signature_key);
1258 signature_key = NULL;
1261 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1272 gpgme_verify_result_t result;
1273 gpgme_sig_notation_t notation;
1274 gpgme_signature_t sig;
1276 result = gpgme_op_verify_result (ctx);
1278 for (sig = result->signatures; sig; sig = sig->next) {
1279 if (sig->notations) {
1280 state_attach_puts ("*** Begin Notation (signature by: ", s);
1281 state_attach_puts (sig->fpr, s);
1282 state_attach_puts (") ***\n", s);
1283 for (notation = sig->notations; notation; notation = notation->next)
1285 if (notation->name) {
1286 state_attach_puts (notation->name, s);
1287 state_attach_puts ("=", s);
1289 if (notation->value) {
1290 state_attach_puts (notation->value, s);
1291 if (!(*notation->value
1292 && (notation->value[m_strlen(notation->value) - 1] ==
1294 state_attach_puts ("\n", s);
1297 state_attach_puts ("*** End Notation ***\n", s);
1303 gpgme_release (ctx);
1305 state_attach_puts (_("[-- End signature information --]\n\n"), s);
1307 return badsig ? 1 : anywarn ? 2 : 0;
1310 int crypt_pgp_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1312 return verify_one (sigbdy, s, tempfile, 0);
1315 int crypt_smime_verify_one (BODY * sigbdy, STATE * s, const char *tempfile)
1317 return verify_one (sigbdy, s, tempfile, 1);
1321 * Implementation of `decrypt_part'.
1324 /* Decrypt a PGP or SMIME message (depending on the boolean flag
1325 IS_SMIME) with body A described further by state S. Write
1326 plaintext out to file FPOUT and return a new body. For PGP returns
1327 a flag in R_IS_SIGNED to indicate whether this is a combined
1328 encrypted and signed message, for S/MIME it returns true when it is
1329 not a encrypted but a signed message. */
1330 static BODY *decrypt_part (BODY * a, STATE * s, FILE * fpout, int is_smime,
1337 gpgme_data_t ciphertext, plaintext;
1338 int maybe_signed = 0;
1345 ctx = create_gpgme_context (is_smime);
1348 /* Make a data object from the body, create context etc. */
1349 ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
1352 plaintext = create_gpgme_data ();
1354 /* Do the decryption or the verification in case of the S/MIME hack. */
1355 if ((!is_smime) || maybe_signed) {
1357 err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
1358 else if (maybe_signed)
1359 err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
1362 /* Check wether signatures have been verified. */
1363 gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
1365 if (verify_result->signatures)
1370 err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
1371 gpgme_data_release (ciphertext);
1373 if (is_smime && !maybe_signed && gpg_err_code (err) == GPG_ERR_NO_DATA) {
1374 /* Check whether this might be a signed message despite what
1375 the mime header told us. Retry then. gpgsm returns the
1376 error information "unsupported Algorithm '?'" but gpgme
1377 will not store this unknown algorithm, thus we test that
1378 it has not been set. */
1379 gpgme_decrypt_result_t result;
1381 result = gpgme_op_decrypt_result (ctx);
1382 if (!result->unsupported_algorithm) {
1384 gpgme_data_release (plaintext);
1388 mutt_need_hard_redraw ();
1389 if ((s->flags & M_DISPLAY)) {
1392 snprintf (buf, sizeof (buf) - 1,
1393 _("[-- Error: decryption failed: %s --]\n\n"),
1394 gpgme_strerror (err));
1395 state_attach_puts (buf, s);
1397 gpgme_data_release (plaintext);
1398 gpgme_release (ctx);
1401 mutt_need_hard_redraw ();
1403 /* Read the output from GPGME, and make sure to change CRLF to LF,
1404 otherwise read_mime_header has a hard time parsing the message. */
1405 if (data_object_to_stream (plaintext, fpout)) {
1406 gpgme_data_release (plaintext);
1407 gpgme_release (ctx);
1410 gpgme_data_release (plaintext);
1412 a->is_signed_data = 0;
1418 a->is_signed_data = 1;
1420 *r_is_signed = -1; /* A signature exists. */
1422 if ((s->flags & M_DISPLAY))
1423 state_attach_puts (_("[-- Begin signature " "information --]\n"), s);
1424 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1430 if (!anybad && idx && r_is_signed && *r_is_signed)
1431 *r_is_signed = anywarn ? 2 : 1; /* Good signature. */
1433 if ((s->flags & M_DISPLAY))
1434 state_attach_puts (_("[-- End signature " "information --]\n\n"), s);
1436 gpgme_release (ctx);
1441 tattach = mutt_read_mime_header (fpout, 0);
1444 * Need to set the length of this body part.
1446 fstat (fileno (fpout), &info);
1447 tattach->length = info.st_size - tattach->offset;
1449 tattach->warnsig = anywarn;
1451 /* See if we need to recurse on this MIME part. */
1452 mutt_parse_part (fpout, tattach);
1458 /* Decrypt a PGP/MIME message in FPIN and B and return a new body and
1459 the stream in CUR and FPOUT. Returns 0 on success. */
1460 int crypt_pgp_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1462 char tempfile[_POSIX_PATH_MAX];
1464 BODY *first_part = b;
1467 first_part->goodsig = 0;
1468 first_part->warnsig = 0;
1470 if (!mutt_is_multipart_encrypted (b))
1473 if (!b->parts || !b->parts->next)
1480 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1482 mutt_perror (_("Can't create temporary file"));
1487 *cur = decrypt_part (b, &s, *fpout, 0, &is_signed);
1490 first_part->goodsig = 1;
1492 return *cur ? 0 : -1;
1496 /* Decrypt a S/MIME message in FPIN and B and return a new body and
1497 the stream in CUR and FPOUT. Returns 0 on success. */
1498 int crypt_smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b,
1501 char tempfile[_POSIX_PATH_MAX];
1505 long saved_b_offset;
1506 ssize_t saved_b_length;
1509 if (!mutt_is_application_smime (b))
1515 /* Decode the body - we need to pass binary CMS to the
1516 backend. The backend allows for Base64 encoded data but it does
1517 not allow for QP which I have seen in some messages. So better
1519 saved_b_type = b->type;
1520 saved_b_offset = b->offset;
1521 saved_b_length = b->length;
1524 fseeko (s.fpin, b->offset, 0);
1525 tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1527 mutt_perror (_("Can't create temporary file"));
1530 mutt_unlink (tempfile);
1533 mutt_decode_attachment (b, &s);
1535 b->length = ftello (s.fpout);
1542 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1544 mutt_perror (_("Can't create temporary file"));
1547 mutt_unlink (tempfile);
1549 *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
1551 (*cur)->goodsig = is_signed > 0;
1552 b->type = saved_b_type;
1553 b->length = saved_b_length;
1554 b->offset = saved_b_offset;
1557 if (*cur && !is_signed && !(*cur)->parts
1558 && mutt_is_application_smime (*cur)) {
1559 /* Assume that this is a opaque signed s/mime message. This is
1560 an ugly way of doing it but we have anyway a problem with
1561 arbitrary encoded S/MIME messages: Only the outer part may be
1562 encrypted. The entire mime parsing should be revamped,
1563 probably by keeping the temportary files so that we don't
1564 need to decrypt them all the time. Inner parts of an
1565 encrypted part can then pint into this file and tehre won't
1566 never be a need to decrypt again. This needs a partial
1567 rewrite of the MIME engine. */
1571 saved_b_type = bb->type;
1572 saved_b_offset = bb->offset;
1573 saved_b_length = bb->length;
1576 fseeko (s.fpin, bb->offset, 0);
1577 tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1579 mutt_perror (_("Can't create temporary file"));
1582 mutt_unlink (tempfile);
1585 mutt_decode_attachment (bb, &s);
1587 bb->length = ftello (s.fpout);
1595 *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1597 mutt_perror (_("Can't create temporary file"));
1600 mutt_unlink (tempfile);
1602 tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
1604 tmp_b->goodsig = is_signed > 0;
1605 bb->type = saved_b_type;
1606 bb->length = saved_b_length;
1607 bb->offset = saved_b_offset;
1610 body_list_wipe(cur);
1613 return *cur ? 0 : -1;
1618 * Implementation of `pgp_check_traditional'.
1621 static int pgp_check_traditional_one_body (FILE * fp, BODY * b,
1624 char tempfile[_POSIX_PATH_MAX];
1625 char buf[HUGE_STRING];
1632 if (b->type != TYPETEXT)
1635 if (tagged_only && !b->tagged)
1638 tempfd = m_tempfd(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1639 if (mutt_decode_save_attachment (fp, b, tempfd, 0) != 0) {
1644 if ((tfp = fopen(tempfile, "r")) == NULL) {
1649 while (fgets (buf, sizeof (buf), tfp)) {
1650 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1651 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1653 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15))
1663 /* fix the content type */
1665 parameter_setval(&b->parameter, "format", "fixed");
1666 parameter_setval(&b->parameter, "x-action",
1667 enc ? "pgp-encrypted" : "pgp-signed");
1671 int crypt_pgp_check_traditional (FILE * fp, BODY * b, int tagged_only)
1676 for (; b; b = b->next) {
1677 if (is_multipart (b))
1678 rv = (crypt_pgp_check_traditional (fp, b->parts, tagged_only) || rv);
1679 else if (b->type == TYPETEXT) {
1680 if ((r = mutt_is_application_pgp (b)))
1683 rv = (pgp_check_traditional_one_body (fp, b, tagged_only) || rv);
1690 /* Implementation of `application_handler'. */
1693 Copy a clearsigned message, and strip the signature and PGP's
1696 XXX - charset handling: We assume that it is safe to do
1697 character set decoding first, dash decoding second here, while
1698 we do it the other way around in the main handler.
1700 (Note that we aren't worse than Outlook & Cie in this, and also
1701 note that we can successfully handle anything produced by any
1702 existing versions of mutt.) */
1704 static void copy_clearsigned (gpgme_data_t data, STATE * s, char *charset)
1706 char buf[HUGE_STRING];
1707 short complete, armor_header;
1712 fname = data_object_to_tempfile (data, &fp);
1718 fc = fgetconv_open (fp, charset, MCharset.charset, M_ICONV_HOOK_FROM);
1720 for (complete = 1, armor_header = 1;
1721 fgetconvs (buf, sizeof (buf), fc) != NULL;
1722 complete = strchr (buf, '\n') != NULL) {
1725 state_puts (buf, s);
1729 if (!m_strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
1739 state_puts (s->prefix, s);
1741 if (buf[0] == '-' && buf[1] == ' ')
1742 state_puts (buf + 2, s);
1744 state_puts (buf, s);
1747 fgetconv_close (&fc);
1752 /* Support for classic_application/pgp */
1753 int crypt_pgp_application_pgp_handler (BODY * m, STATE * s)
1755 int needpass = -1, pgp_keyblock = 0;
1759 off_t last_pos, offset;
1760 char buf[HUGE_STRING];
1761 FILE *pgpout = NULL;
1763 gpgme_error_t err = 0;
1764 gpgme_data_t armored_data = NULL;
1766 short maybe_goodsig = 1;
1767 short have_any_sigs = 0;
1769 char body_charset[STRING]; /* Only used for clearsigned messages. */
1771 /* For clearsigned messages we won't be able to get a character set
1772 but we know that this may only be text thus we assume Latin-1
1774 if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
1775 m_strcpy(body_charset, sizeof(body_charset), "iso-8859-1");
1777 fseeko (s->fpin, m->offset, 0);
1778 last_pos = m->offset;
1780 for (bytes = m->length; bytes > 0;) {
1781 if (fgets (buf, sizeof (buf), s->fpin) == NULL)
1784 offset = ftello (s->fpin);
1785 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1788 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1790 start_pos = last_pos;
1792 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1794 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15)) {
1798 else if (!option (OPTDONTHANDLEPGPKEYS) &&
1799 !m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
1804 /* XXX - we may wish to recode here */
1806 state_puts (s->prefix, s);
1807 state_puts (buf, s);
1811 have_any_sigs = (have_any_sigs || (clearsign && (s->flags & M_VERIFY)));
1813 /* Copy PGP material to an data container */
1814 armored_data = create_gpgme_data ();
1815 gpgme_data_write (armored_data, buf, m_strlen(buf));
1816 while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) {
1817 offset = ftello (s->fpin);
1818 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1821 gpgme_data_write (armored_data, buf, m_strlen(buf));
1823 if ((needpass && !m_strcmp("-----END PGP MESSAGE-----\n", buf))
1825 && (!m_strcmp("-----END PGP SIGNATURE-----\n", buf)
1826 || !m_strcmp("-----END PGP PUBLIC KEY BLOCK-----\n",
1831 /* Invoke PGP if needed */
1832 if (!clearsign || (s->flags & M_VERIFY)) {
1833 unsigned int sig_stat = 0;
1834 gpgme_data_t plaintext;
1837 plaintext = create_gpgme_data ();
1838 ctx = create_gpgme_context (0);
1841 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1843 err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
1844 if (gpg_err_code (err) == GPG_ERR_NO_DATA) {
1845 /* Decrypt verify can't handle signed only messages. */
1846 err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
1847 ? gpgme_error_from_errno (errno) : 0;
1848 /* Must release plaintext so that we supply an
1849 uninitialized object. */
1850 gpgme_data_release (plaintext);
1851 plaintext = create_gpgme_data ();
1852 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1859 snprintf (errbuf, sizeof (errbuf) - 1,
1860 _("Error: decryption/verification failed: %s\n"),
1861 gpgme_strerror (err));
1862 state_attach_puts (errbuf, s);
1864 else { /* Decryption/Verification succeeded */
1868 /* Check wether signatures have been verified. */
1869 gpgme_verify_result_t verify_result;
1871 verify_result = gpgme_op_verify_result (ctx);
1872 if (verify_result->signatures)
1878 if ((s->flags & M_DISPLAY) && sig_stat) {
1883 state_attach_puts (_("[-- Begin signature "
1884 "information --]\n"), s);
1887 (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1896 state_attach_puts (_("[-- End signature "
1897 "information --]\n\n"), s);
1900 tmpfname = data_object_to_tempfile (plaintext, &pgpout);
1903 state_attach_puts (_("Error: copy data failed\n"), s);
1907 p_delete(&tmpfname);
1910 gpgme_release (ctx);
1914 * Now, copy cleartext to the screen. NOTE - we expect that PGP
1915 * outputs utf-8 cleartext. This may not always be true, but it
1916 * seems to be a reasonable guess.
1919 if (s->flags & M_DISPLAY) {
1921 state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
1922 else if (pgp_keyblock)
1923 state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s);
1925 state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s);
1929 copy_clearsigned (armored_data, s, body_charset);
1936 fc = fgetconv_open (pgpout, "utf-8", MCharset.charset, 0);
1937 while ((c = fgetconv (fc)) != EOF) {
1939 if (c == '\n' && s->prefix)
1940 state_puts (s->prefix, s);
1942 fgetconv_close (&fc);
1945 if (s->flags & M_DISPLAY) {
1946 state_putc ('\n', s);
1948 state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
1949 else if (pgp_keyblock)
1950 state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
1952 state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
1960 /* XXX - we may wish to recode here */
1962 state_puts (s->prefix, s);
1963 state_puts (buf, s);
1967 m->goodsig = (maybe_goodsig && have_any_sigs);
1969 if (needpass == -1) {
1970 state_attach_puts (_("[-- Error: could not find beginning"
1971 " of PGP message! --]\n\n"), s);
1977 /* Implementation of `encrypted_handler'. */
1979 /* MIME handler for pgp/mime encrypted messages. */
1980 int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
1982 char tempfile[_POSIX_PATH_MAX];
1985 BODY *orig_body = a;
1990 if (!a || a->type != TYPEAPPLICATION || !a->subtype
1991 || ascii_strcasecmp ("pgp-encrypted", a->subtype)
1992 || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
1993 || ascii_strcasecmp ("octet-stream", a->next->subtype)) {
1994 if (s->flags & M_DISPLAY)
1995 state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
2000 /* Move forward to the application/pgp-encrypted body. */
2003 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2005 if (s->flags & M_DISPLAY)
2006 state_attach_puts (_("[-- Error: could not create temporary file! "
2011 tattach = decrypt_part (a, s, fpout, 0, &is_signed);
2013 tattach->goodsig = is_signed > 0;
2015 if (s->flags & M_DISPLAY)
2016 state_attach_puts (is_signed ?
2018 ("[-- The following data is PGP/MIME signed and encrypted --]\n\n") :
2019 _("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
2022 FILE *savefp = s->fpin;
2025 rc = mutt_body_handler (tattach, s);
2030 * if a multipart/signed is the _only_ sub-part of a
2031 * multipart/encrypted, cache signature verification
2034 if (mutt_is_multipart_signed (tattach) && !tattach->next)
2035 orig_body->goodsig |= tattach->goodsig;
2037 if (s->flags & M_DISPLAY) {
2038 state_puts ("\n", s);
2039 state_attach_puts (is_signed ?
2041 ("[-- End of PGP/MIME signed and encrypted data --]\n")
2042 : _("[-- End of PGP/MIME encrypted data --]\n"), s);
2045 body_list_wipe(&tattach);
2049 mutt_unlink (tempfile);
2053 /* Support for application/smime */
2054 int crypt_smime_application_smime_handler (BODY * a, STATE * s)
2056 char tempfile[_POSIX_PATH_MAX];
2063 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2065 if (s->flags & M_DISPLAY)
2066 state_attach_puts (_("[-- Error: could not create temporary file! "
2071 tattach = decrypt_part (a, s, fpout, 1, &is_signed);
2073 tattach->goodsig = is_signed > 0;
2075 if (s->flags & M_DISPLAY)
2076 state_attach_puts (is_signed ?
2077 _("[-- The following data is S/MIME signed --]\n\n") :
2078 _("[-- The following data is S/MIME encrypted --]\n\n"), s);
2081 FILE *savefp = s->fpin;
2084 rc = mutt_body_handler (tattach, s);
2089 * if a multipart/signed is the _only_ sub-part of a
2090 * multipart/encrypted, cache signature verification
2093 if (mutt_is_multipart_signed (tattach) && !tattach->next) {
2094 if (!(a->goodsig = tattach->goodsig))
2095 a->warnsig = tattach->warnsig;
2097 else if (tattach->goodsig) {
2099 a->warnsig = tattach->warnsig;
2102 if (s->flags & M_DISPLAY) {
2103 state_puts ("\n", s);
2104 state_attach_puts (is_signed ?
2105 _("[-- End of S/MIME signed data --]\n") :
2106 _("[-- End of S/MIME encrypted data --]\n"), s);
2109 body_list_wipe(&tattach);
2113 mutt_unlink (tempfile);
2119 * Format an entry on the CRYPT key selection menu.
2122 * %k key id %K key id of the principal key
2124 * %a algorithm %A algorithm of the princ. key
2125 * %l length %L length of the princ. key
2126 * %f flags %F flags of the princ. key
2127 * %c capabilities %C capabilities of the princ. key
2128 * %t trust/validity of the key-uid association
2130 * %[...] date of key using strftime(3)
2134 crypt_entry_fmt (char *dest, ssize_t destlen, char op,
2135 const char *src, const char *prefix,
2136 const char *ifstr, const char *elstr,
2137 anytype data, format_flag flags)
2140 crypt_entry_t *entry;
2143 int optional = (flags & M_FORMAT_OPTIONAL);
2144 const char *s = NULL;
2150 /* if (isupper ((unsigned char) op)) */
2153 kflags = (key->flags /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
2156 switch (ascii_tolower (op)) {
2160 char buf2[STRING], *p;
2176 while (len > 0 && *cp != ']') {
2185 break; /* not enough space */
2195 if (do_locales && Locale)
2196 setlocale (LC_TIME, Locale);
2201 if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
2202 tt = key->kobj->subkeys->timestamp;
2204 tm = localtime (&tt);
2206 strftime (buf2, sizeof (buf2), dest, tm);
2209 setlocale (LC_TIME, "C");
2211 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2212 snprintf (dest, destlen, fmt, buf2);
2219 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
2220 snprintf (dest, destlen, fmt, entry->num);
2225 /* fixme: we need a way to distinguish between main and subkeys.
2226 Store the idx in entry? */
2227 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2228 snprintf (dest, destlen, fmt, crypt_keyid (key));
2233 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2234 snprintf (dest, destlen, fmt, key->uid);
2239 snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
2240 if (key->kobj->subkeys)
2241 s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
2244 snprintf (dest, destlen, fmt, s);
2249 snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
2250 if (key->kobj->subkeys)
2251 val = key->kobj->subkeys->length;
2254 snprintf (dest, destlen, fmt, val);
2259 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2260 snprintf (dest, destlen, fmt, crypt_flags (kflags));
2262 else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
2267 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2268 snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
2270 else if (!(kflags & (KEYFLAG_ABILITIES)))
2274 if ((kflags & KEYFLAG_ISX509))
2277 gpgme_user_id_t uid = NULL;
2280 for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
2281 i++, uid = uid->next);
2283 switch (uid->validity) {
2284 case GPGME_VALIDITY_UNDEFINED:
2287 case GPGME_VALIDITY_NEVER:
2290 case GPGME_VALIDITY_MARGINAL:
2293 case GPGME_VALIDITY_FULL:
2296 case GPGME_VALIDITY_ULTIMATE:
2299 case GPGME_VALIDITY_UNKNOWN:
2305 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2306 snprintf (dest, destlen, fmt, s ? *s : 'B');
2309 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2310 snprintf (dest, destlen, fmt,
2311 gpgme_get_protocol_name (key->kobj->protocol));
2318 if (flags & M_FORMAT_OPTIONAL)
2319 m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
2320 mutt_attach_fmt, data, 0);
2324 /* Used by the display fucntion to format a line. */
2325 static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
2327 crypt_key_t **key_table = (crypt_key_t **) menu->data;
2328 crypt_entry_t entry;
2330 entry.key = key_table[num];
2331 entry.num = num + 1;
2333 m_strformat(s, l, COLS - SW, PgpEntryFormat, crypt_entry_fmt, &entry,
2334 option(OPTARROWCURSOR) ? M_FORMAT_ARROWCURSOR : 0);
2337 /* Compare two addresses and the keyid to be used for sorting. */
2338 static int _crypt_compare_address (const void *a, const void *b)
2340 crypt_key_t **s = (crypt_key_t **) a;
2341 crypt_key_t **t = (crypt_key_t **) b;
2344 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2347 return m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t)) > 0;
2350 static int crypt_compare_address (const void *a, const void *b)
2352 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_address (a, b)
2353 : _crypt_compare_address (a, b));
2357 /* Compare two key IDs and the addresses to be used for sorting. */
2358 static int _crypt_compare_keyid (const void *a, const void *b)
2360 crypt_key_t **s = (crypt_key_t **) a;
2361 crypt_key_t **t = (crypt_key_t **) b;
2364 if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
2367 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2370 static int crypt_compare_keyid (const void *a, const void *b)
2372 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
2373 : _crypt_compare_keyid (a, b));
2376 /* Compare 2 creation dates and the addresses. For sorting. */
2377 static int _crypt_compare_date (const void *a, const void *b)
2379 crypt_key_t **s = (crypt_key_t **) a;
2380 crypt_key_t **t = (crypt_key_t **) b;
2381 unsigned long ts = 0, tt = 0;
2383 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2384 ts = (*s)->kobj->subkeys->timestamp;
2385 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2386 tt = (*t)->kobj->subkeys->timestamp;
2393 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2396 static int crypt_compare_date (const void *a, const void *b)
2398 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
2399 : _crypt_compare_date (a, b));
2402 /* Compare two trust values, the key length, the creation dates. the
2403 addresses and the key IDs. For sorting. */
2404 static int _crypt_compare_trust (const void *a, const void *b)
2406 crypt_key_t **s = (crypt_key_t **) a;
2407 crypt_key_t **t = (crypt_key_t **) b;
2408 unsigned long ts = 0, tt = 0;
2411 if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
2412 - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
2415 if ((*s)->kobj->uids)
2416 ts = (*s)->kobj->uids->validity;
2417 if ((*t)->kobj->uids)
2418 tt = (*t)->kobj->uids->validity;
2419 if ((r = (tt - ts)))
2422 if ((*s)->kobj->subkeys)
2423 ts = (*s)->kobj->subkeys->length;
2424 if ((*t)->kobj->subkeys)
2425 tt = (*t)->kobj->subkeys->length;
2429 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2430 ts = (*s)->kobj->subkeys->timestamp;
2431 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2432 tt = (*t)->kobj->subkeys->timestamp;
2438 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2440 return (m_strcasecmp(crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
2443 static int crypt_compare_trust (const void *a, const void *b)
2445 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
2446 : _crypt_compare_trust (a, b));
2449 /* Print the X.500 Distinguished Name part KEY from the array of parts
2451 static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
2455 for (; dn->key; dn++) {
2456 if (!m_strcmp(dn->key, key)) {
2459 print_utf8 (fp, dn->value, m_strlen(dn->value));
2466 /* Print all parts of a DN in a standard sequence. */
2467 static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
2469 const char *stdpart[] = {
2470 "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
2472 int any = 0, any2 = 0, i;
2474 for (i = 0; stdpart[i]; i++) {
2477 any = print_dn_part (fp, dn, stdpart[i]);
2479 /* now print the rest without any specific ordering */
2480 for (; dn->key; dn++) {
2481 for (i = 0; stdpart[i]; i++) {
2482 if (!m_strcmp(dn->key, stdpart[i]))
2490 any = print_dn_part (fp, dn, dn->key);
2499 /* Parse an RDN; this is a helper to parse_dn(). */
2500 static const unsigned char *parse_dn_part (struct dn_array_s *array,
2501 const unsigned char *string)
2503 const unsigned char *s, *s1;
2507 /* parse attributeType */
2508 for (s = string + 1; *s && *s != '='; s++);
2510 return NULL; /* error */
2513 return NULL; /* empty key */
2514 array->key = p_dupstr(string, n );
2515 p = (unsigned char *) array->key;
2518 if (*string == '#') { /* hexstring */
2520 for (s = string; hexval(*s) >= 0; s++)
2524 return NULL; /* empty or odd number of digits */
2526 p = p_new(unsigned char, n + 1);
2527 array->value = (char *) p;
2528 for (s1 = string; n; s1 += 2, n--)
2529 *p++ = (hexval(*s1) << 8) | hexval(*s1);
2532 else { /* regular v3 quoted string */
2533 for (n = 0, s = string; *s; s++) {
2534 if (*s == '\\') { /* pair */
2536 if (*s == ',' || *s == '=' || *s == '+'
2537 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
2538 || *s == '\\' || *s == '\"' || *s == ' ')
2540 else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
2545 return NULL; /* invalid escape sequence */
2547 else if (*s == '\"')
2548 return NULL; /* invalid encoding */
2549 else if (*s == ',' || *s == '=' || *s == '+'
2550 || *s == '<' || *s == '>' || *s == '#' || *s == ';')
2556 p = p_new(unsigned char, n + 1);
2557 array->value = (char *) p;
2558 for (s = string; n; s++, n--) {
2561 if (hexval(*s) >= 0) {
2562 *p++ = (hexval(*s) << 8) | hexval(*s + 1);
2577 /* Parse a DN and return an array-ized one. This is not a validating
2578 parser and it does not support any old-stylish syntax; gpgme is
2579 expected to return only rfc2253 compatible strings. */
2580 static struct dn_array_s *parse_dn (const unsigned char *string)
2582 struct dn_array_s *array;
2583 ssize_t arrayidx, arraysize;
2586 arraysize = 7; /* C,ST,L,O,OU,CN,email */
2587 array = p_new(struct dn_array_s, arraysize + 1);
2590 while (*string == ' ')
2594 if (arrayidx >= arraysize) { /* mutt lacks a real safe_realoc - so we need to copy */
2595 struct dn_array_s *a2;
2598 a2 = p_new(struct dn_array_s, arraysize + 1);
2599 for (i = 0; i < arrayidx; i++) {
2600 a2[i].key = array[i].key;
2601 a2[i].value = array[i].value;
2606 array[arrayidx].key = NULL;
2607 array[arrayidx].value = NULL;
2608 string = parse_dn_part (array + arrayidx, string);
2612 while (*string == ' ')
2614 if (*string && *string != ',' && *string != ';' && *string != '+')
2615 goto failure; /* invalid delimiter */
2619 array[arrayidx].key = NULL;
2620 array[arrayidx].value = NULL;
2624 for (i = 0; i < arrayidx; i++) {
2625 p_delete(&array[i].key);
2626 p_delete(&array[i].value);
2633 /* Print a nice representation of the USERID and make sure it is
2634 displayed in a proper way, which does mean to reorder some parts
2635 for S/MIME's DNs. USERID is a string as returned by the gpgme key
2636 functions. It is utf-8 encoded. */
2637 static void parse_and_print_user_id (FILE * fp, const char *userid)
2642 if (*userid == '<') {
2643 s = strchr (userid + 1, '>');
2645 print_utf8 (fp, userid + 1, s - userid - 1);
2647 else if (*userid == '(')
2648 fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
2649 else if (!digit_or_letter ((const unsigned char *) userid))
2650 fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
2652 struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
2655 fputs (_("[Can't display this user ID (invalid DN)]"), fp);
2657 print_dn_parts (fp, dn);
2658 for (i = 0; dn[i].key; i++) {
2659 p_delete(&dn[i].key);
2660 p_delete(&dn[i].value);
2668 KEY_CAP_CAN_ENCRYPT,
2673 static unsigned int key_check_cap (gpgme_key_t key, key_cap_t cap)
2675 gpgme_subkey_t subkey = NULL;
2676 unsigned int ret = 0;
2679 case KEY_CAP_CAN_ENCRYPT:
2680 if (!(ret = key->can_encrypt))
2681 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2682 if ((ret = subkey->can_encrypt))
2685 case KEY_CAP_CAN_SIGN:
2686 if (!(ret = key->can_sign))
2687 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2688 if ((ret = subkey->can_sign))
2691 case KEY_CAP_CAN_CERTIFY:
2692 if (!(ret = key->can_certify))
2693 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2694 if ((ret = subkey->can_certify))
2703 /* Print verbose information about a key or certificate to FP. */
2704 static void print_key_info (gpgme_key_t key, FILE * fp)
2707 const char *s = NULL, *s2 = NULL;
2710 char shortbuf[STRING];
2711 unsigned long aval = 0;
2715 gpgme_user_id_t uid = NULL;
2718 setlocale (LC_TIME, Locale);
2720 is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
2722 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2727 fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
2730 fputs (_("[Invalid]"), fp);
2734 print_utf8 (fp, s, m_strlen(s));
2736 parse_and_print_user_id (fp, s);
2740 if (key->subkeys && (key->subkeys->timestamp > 0)) {
2741 tt = key->subkeys->timestamp;
2743 tm = localtime (&tt);
2744 #ifdef HAVE_LANGINFO_D_T_FMT
2745 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2747 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2749 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2752 if (key->subkeys && (key->subkeys->expires > 0)) {
2753 tt = key->subkeys->expires;
2755 tm = localtime (&tt);
2756 #ifdef HAVE_LANGINFO_D_T_FMT
2757 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2759 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2761 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2765 s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
2769 s2 = is_pgp ? "PGP" : "X.509";
2772 aval = key->subkeys->length;
2774 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
2776 fprintf (fp, _("Key Usage .: "));
2779 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
2780 fprintf (fp, "%s%s", delim, _("encryption"));
2783 if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
2784 fprintf (fp, "%s%s", delim, _("signing"));
2787 if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
2788 fprintf (fp, "%s%s", delim, _("certification"));
2794 s = key->subkeys->fpr;
2795 fputs (_("Fingerprint: "), fp);
2796 if (is_pgp && m_strlen(s) == 40) {
2797 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
2802 putc (is_pgp ? ' ' : ':', fp);
2803 if (is_pgp && i == 4)
2808 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
2811 putc (is_pgp ? ' ' : ':', fp);
2812 if (is_pgp && i == 7)
2816 fprintf (fp, "%s\n", s);
2819 if (key->issuer_serial) {
2820 s = key->issuer_serial;
2822 fprintf (fp, _("Serial-No .: 0x%s\n"), s);
2825 if (key->issuer_name) {
2826 s = key->issuer_name;
2828 fprintf (fp, _("Issued By .: "));
2829 parse_and_print_user_id (fp, s);
2834 /* For PGP we list all subkeys. */
2836 gpgme_subkey_t subkey = NULL;
2838 for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
2842 if (m_strlen(s) == 16)
2843 s += 8; /* display only the short keyID */
2844 fprintf (fp, _("Subkey ....: 0x%s"), s);
2845 if (subkey->revoked) {
2847 fputs (_("[Revoked]"), fp);
2849 if (subkey->invalid) {
2851 fputs (_("[Invalid]"), fp);
2853 if (subkey->expired) {
2855 fputs (_("[Expired]"), fp);
2857 if (subkey->disabled) {
2859 fputs (_("[Disabled]"), fp);
2863 if (subkey->timestamp > 0) {
2864 tt = subkey->timestamp;
2866 tm = localtime (&tt);
2867 #ifdef HAVE_LANGINFO_D_T_FMT
2868 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2870 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2872 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2875 if (subkey->expires > 0) {
2876 tt = subkey->expires;
2878 tm = localtime (&tt);
2879 #ifdef HAVE_LANGINFO_D_T_FMT
2880 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2882 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2884 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2888 s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
2893 aval = subkey->length;
2897 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
2899 fprintf (fp, _("Key Usage .: "));
2902 if (subkey->can_encrypt) {
2903 fprintf (fp, "%s%s", delim, _("encryption"));
2906 if (subkey->can_sign) {
2907 fprintf (fp, "%s%s", delim, _("signing"));
2910 if (subkey->can_certify) {
2911 fprintf (fp, "%s%s", delim, _("certification"));
2919 setlocale (LC_TIME, "C");
2923 /* Show detailed information about the selected key */
2924 static void verify_key (crypt_key_t * key)
2927 char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
2929 gpgme_ctx_t listctx = NULL;
2931 gpgme_key_t k = NULL;
2934 fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2936 mutt_perror (_("Can't create temporary file"));
2939 mutt_message _("Collecting data...");
2941 print_key_info (key->kobj, fp);
2943 err = gpgme_new (&listctx);
2945 fprintf (fp, "Internal error: can't create gpgme context: %s\n",
2946 gpgme_strerror (err));
2949 if ((key->flags & KEYFLAG_ISX509))
2950 gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
2954 while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
2956 err = gpgme_op_keylist_start (listctx, s, 0);
2957 gpgme_key_release (k);
2960 err = gpgme_op_keylist_next (listctx, &k);
2962 fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
2965 gpgme_op_keylist_end (listctx);
2967 print_key_info (k, fp);
2970 fputs (_("Error: certification chain to long - stopping here\n"), fp);
2976 gpgme_key_release (k);
2977 gpgme_release (listctx);
2979 mutt_clear_error ();
2980 snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
2981 mutt_do_pager (cmd, tempfile, 0, NULL);
2984 /* Implementation of `findkeys'. */
2986 /* Convert string_list_t into a pattern string suitable to be passed to GPGME.
2987 We need to convert spaces in an item into a '+' and '%' into
2989 static char *list_to_pattern (string_list_t * list)
2997 for (l = list; l; l = l->next) {
2998 for (s = l->data; *s; s++) {
3003 n++; /* delimiter or end of string */
3005 n++; /* make sure to allocate at least one byte */
3006 pattern = p = p_new(char, n);
3007 for (l = list; l; l = l->next) {
3012 for (s = l->data; *s; s++) {
3018 else if (*s == '+') {
3034 /* Return a list of keys which are candidates for the selection.
3035 Select by looking at the HINTS list. */
3036 static crypt_key_t *get_candidates (string_list_t * hints, unsigned int app,
3039 crypt_key_t *db, *k, **kend;
3045 gpgme_user_id_t uid = NULL;
3047 pattern = list_to_pattern (hints);
3051 err = gpgme_new (&ctx);
3053 mutt_error (_("gpgme_new failed: %s"), gpgme_strerror (err));
3061 if ((app & APPLICATION_PGP)) {
3062 /* Its all a mess. That old GPGME expects different things
3063 depending on the protocol. For gpg we don' t need percent
3064 escaped pappert but simple strings passed in an array to the
3065 keylist_ext_start function. */
3070 for (l = hints, n = 0; l; l = l->next) {
3071 if (l->data && *l->data)
3077 patarr = p_new(char *, n + 1);
3078 for (l = hints, n = 0; l; l = l->next) {
3079 if (l->data && *l->data)
3080 patarr[n++] = m_strdup(l->data);
3083 err = gpgme_op_keylist_ext_start (ctx, (const char **) patarr, secret, 0);
3084 for (n = 0; patarr[n]; n++)
3085 p_delete(&patarr[n]);
3088 mutt_error (_("gpgme_op_keylist_start failed: %s"), gpgme_strerror (err));
3089 gpgme_release (ctx);
3094 while (!(err = gpgme_op_keylist_next (ctx, &key))) {
3095 unsigned int flags = 0;
3097 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
3098 flags |= KEYFLAG_CANENCRYPT;
3099 if (key_check_cap (key, KEY_CAP_CAN_SIGN))
3100 flags |= KEYFLAG_CANSIGN;
3102 #if 0 /* DISABLED code */
3104 /* Bug in gpg. Capabilities are not listed for secret
3105 keys. Try to deduce them from the algorithm. */
3107 switch (key->subkeys[0].pubkey_algo) {
3109 flags |= KEYFLAG_CANENCRYPT;
3110 flags |= KEYFLAG_CANSIGN;
3112 case GPGME_PK_ELG_E:
3113 flags |= KEYFLAG_CANENCRYPT;
3116 flags |= KEYFLAG_CANSIGN;
3120 #endif /* DISABLED code */
3122 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
3123 k = p_new(crypt_key_t, 1);
3132 if (gpg_err_code (err) != GPG_ERR_EOF)
3133 mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err));
3134 gpgme_op_keylist_end (ctx);
3139 if ((app & APPLICATION_SMIME)) {
3140 /* and now look for x509 certificates */
3141 gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
3142 err = gpgme_op_keylist_start (ctx, pattern, 0);
3144 mutt_error (_("gpgme_op_keylist_start failed: %s"), gpgme_strerror (err));
3145 gpgme_release (ctx);
3150 while (!(err = gpgme_op_keylist_next (ctx, &key))) {
3151 unsigned int flags = KEYFLAG_ISX509;
3153 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
3154 flags |= KEYFLAG_CANENCRYPT;
3155 if (key_check_cap (key, KEY_CAP_CAN_SIGN))
3156 flags |= KEYFLAG_CANSIGN;
3158 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
3159 k = p_new(crypt_key_t, 1);
3168 if (gpg_err_code (err) != GPG_ERR_EOF)
3169 mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err));
3170 gpgme_op_keylist_end (ctx);
3173 gpgme_release (ctx);
3178 /* Add the string STR to the list HINTS. This list is later used to
3180 static string_list_t *crypt_add_string_to_hints (string_list_t * hints, const char *str)
3185 if ((scratch = m_strdup(str)) == NULL)
3188 for (t = strtok (scratch, " ,.:\"()<>\n"); t;
3189 t = strtok (NULL, " ,.:\"()<>\n")) {
3190 if (m_strlen(t) > 3)
3191 hints = mutt_add_list(hints, t);
3198 /* Display a menu to select a key from the array KEYS. FORCED_VALID
3199 will be set to true on return if the user did override the the
3201 static crypt_key_t *crypt_select_key (crypt_key_t * keys,
3202 address_t * p, const char *s,
3203 unsigned int app, int *forced_valid)
3206 crypt_key_t **key_table;
3209 char helpstr[STRING], buf[LONG_STRING];
3211 int (*f) (const void *, const void *);
3212 int menu_to_use = 0;
3217 /* build the key table */
3220 for (k = keys; k; k = k->next) {
3221 if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
3228 p_realloc(&key_table, keymax);
3234 if (!i && unusable) {
3235 mutt_error _("All matching keys are marked expired/revoked.");
3241 switch (PgpSortKeys & SORT_MASK) {
3243 f = crypt_compare_date;
3246 f = crypt_compare_keyid;
3249 f = crypt_compare_address;
3253 f = crypt_compare_trust;
3256 qsort (key_table, i, sizeof (crypt_key_t *), f);
3258 if (app & APPLICATION_PGP)
3259 menu_to_use = MENU_KEY_SELECT_PGP;
3260 else if (app & APPLICATION_SMIME)
3261 menu_to_use = MENU_KEY_SELECT_SMIME;
3264 mutt_make_help (buf, sizeof (buf), _("Exit "), menu_to_use, OP_EXIT);
3265 m_strcat(helpstr, sizeof(helpstr), buf);
3266 mutt_make_help (buf, sizeof (buf), _("Select "), menu_to_use,
3267 OP_GENERIC_SELECT_ENTRY);
3268 m_strcat(helpstr, sizeof(helpstr), buf);
3269 mutt_make_help (buf, sizeof (buf), _("Check key "),
3270 menu_to_use, OP_VERIFY_KEY);
3271 m_strcat(helpstr, sizeof(helpstr), buf);
3272 mutt_make_help (buf, sizeof (buf), _("Help"), menu_to_use, OP_HELP);
3273 m_strcat(helpstr, sizeof(helpstr), buf);
3275 menu = mutt_new_menu ();
3277 menu->make_entry = crypt_entry;
3278 menu->menu = menu_to_use;
3279 menu->help = helpstr;
3280 menu->data = key_table;
3285 if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
3286 ts = _("PGP and S/MIME keys matching");
3287 else if ((app & APPLICATION_PGP))
3288 ts = _("PGP keys matching");
3289 else if ((app & APPLICATION_SMIME))
3290 ts = _("S/MIME keys matching");
3292 ts = _("keys matching");
3295 snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
3297 snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
3301 mutt_clear_error ();
3305 switch (mutt_menuLoop (menu)) {
3307 verify_key (key_table[menu->current]);
3308 menu->redraw = REDRAW_FULL;
3312 mutt_message ("%s", key_table[menu->current]->uid);
3315 case OP_GENERIC_SELECT_ENTRY:
3316 /* FIXME make error reporting more verbose - this should be
3317 easy because gpgme provides more information */
3318 if (option (OPTPGPCHECKTRUST)) {
3319 if (!crypt_key_is_valid (key_table[menu->current])) {
3320 mutt_error _("This key can't be used: "
3321 "expired/disabled/revoked.");
3326 if (option (OPTPGPCHECKTRUST) &&
3327 (!crypt_id_is_valid (key_table[menu->current])
3328 || !crypt_id_is_strong (key_table[menu->current]))) {
3330 char buff[LONG_STRING];
3332 if (key_table[menu->current]->flags & KEYFLAG_CANTUSE)
3333 s = N_("ID is expired/disabled/revoked.");
3335 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
3336 gpgme_user_id_t uid = NULL;
3341 uid = key_table[menu->current]->kobj->uids;
3342 for (j = 0; (j < key_table[menu->current]->idx) && uid;
3343 j++, uid = uid->next);
3345 val = uid->validity;
3348 case GPGME_VALIDITY_UNKNOWN:
3349 case GPGME_VALIDITY_UNDEFINED:
3350 warn_s = N_("ID has undefined validity.");
3352 case GPGME_VALIDITY_NEVER:
3353 warn_s = N_("ID is not valid.");
3355 case GPGME_VALIDITY_MARGINAL:
3356 warn_s = N_("ID is only marginally valid.");
3358 case GPGME_VALIDITY_FULL:
3359 case GPGME_VALIDITY_ULTIMATE:
3363 snprintf (buff, sizeof (buff),
3364 _("%s Do you really want to use the key?"), _(warn_s));
3366 if (mutt_yesorno (buff, 0) != 1) {
3367 mutt_clear_error ();
3374 k = crypt_copy_key (key_table[menu->current]);
3385 mutt_menuDestroy (&menu);
3386 p_delete(&key_table);
3388 set_option (OPTNEEDREDRAW);
3393 static crypt_key_t *crypt_getkeybyaddr (address_t * a, short abilities,
3394 unsigned int app, int *forced_valid)
3397 string_list_t *hints = NULL;
3402 int this_key_has_strong;
3403 int this_key_has_weak;
3404 int this_key_has_invalid;
3407 crypt_key_t *keys, *k;
3408 crypt_key_t *the_valid_key = NULL;
3409 crypt_key_t *matches = NULL;
3410 crypt_key_t **matches_endp = &matches;
3414 if (a && a->mailbox)
3415 hints = crypt_add_string_to_hints (hints, a->mailbox);
3416 if (a && a->personal)
3417 hints = crypt_add_string_to_hints (hints, a->personal);
3419 mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
3420 keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
3422 string_list_wipe(&hints);
3427 for (k = keys; k; k = k->next) {
3428 if (abilities && !(k->flags & abilities)) {
3432 this_key_has_weak = 0; /* weak but valid match */
3433 this_key_has_invalid = 0; /* invalid match */
3434 this_key_has_strong = 0; /* strong and valid match */
3435 match = 0; /* any match */
3437 r = rfc822_parse_adrlist (NULL, k->uid);
3438 for (p = r; p; p = p->next) {
3439 int validity = crypt_id_matches_addr (a, p, k);
3441 if (validity & CRYPT_KV_MATCH) /* something matches */
3444 /* is this key a strong candidate? */
3445 if ((validity & CRYPT_KV_VALID)
3446 && (validity & CRYPT_KV_STRONGID)
3447 && (validity & CRYPT_KV_ADDR)) {
3448 if (the_valid_key && the_valid_key != k)
3451 this_key_has_strong = 1;
3453 else if ((validity & CRYPT_KV_MATCH)
3454 && !(validity & CRYPT_KV_VALID))
3455 this_key_has_invalid = 1;
3456 else if ((validity & CRYPT_KV_MATCH)
3457 && (!(validity & CRYPT_KV_STRONGID)
3458 || !(validity & CRYPT_KV_ADDR)))
3459 this_key_has_weak = 1;
3461 address_list_wipe(&r);
3466 if (!this_key_has_strong && this_key_has_invalid)
3468 if (!this_key_has_strong && this_key_has_weak)
3471 *matches_endp = tmp = crypt_copy_key (k);
3472 matches_endp = &tmp->next;
3473 the_valid_key = tmp;
3477 crypt_free_key (&keys);
3480 if (the_valid_key && !multi && !weak
3481 && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
3483 * There was precisely one strong match on a valid ID, there
3484 * were no valid keys with weak matches, and we aren't
3485 * interested in seeing invalid keys.
3487 * Proceed without asking the user.
3489 k = crypt_copy_key (the_valid_key);
3493 * Else: Ask the user.
3495 k = crypt_select_key (matches, a, NULL, app, forced_valid);
3497 crypt_free_key (&matches);
3506 static crypt_key_t *crypt_getkeybystr (const char *p, short abilities,
3507 unsigned int app, int *forced_valid)
3509 string_list_t *hints = NULL;
3511 crypt_key_t *matches = NULL;
3512 crypt_key_t **matches_endp = &matches;
3516 mutt_message (_("Looking for keys matching \"%s\"..."), p);
3520 hints = crypt_add_string_to_hints (hints, p);
3521 keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
3522 string_list_wipe(&hints);
3527 for (k = keys; k; k = k->next) {
3528 if (abilities && !(k->flags & abilities))
3533 if (!*p || !m_strcasecmp(p, crypt_keyid (k))
3534 || (!m_strncasecmp(p, "0x", 2)
3535 && !m_strcasecmp(p + 2, crypt_keyid (k)))
3536 || (option (OPTPGPLONGIDS)
3537 && !m_strncasecmp(p, "0x", 2)
3538 && !m_strcasecmp(p + 2, crypt_keyid (k) + 8))
3539 || m_stristr(k->uid, p)) {
3542 *matches_endp = tmp = crypt_copy_key (k);
3543 matches_endp = &tmp->next;
3547 crypt_free_key (&keys);
3550 k = crypt_select_key (matches, NULL, p, app, forced_valid);
3551 crypt_free_key (&matches);
3558 /* Display TAG as a prompt to ask for a key. If WHATFOR is not null
3559 use it as default and store it under that label as the next
3560 default. ABILITIES describe the required key abilities (sign,
3561 encrypt) and APP the type of the requested key; ether S/MIME or
3562 PGP. Return a copy of the key or NULL if not found. */
3563 static crypt_key_t *crypt_ask_for_key (char *tag,
3566 unsigned int app, int *forced_valid)
3570 struct crypt_cache *l = NULL;
3574 forced_valid = &dummy;
3576 mutt_clear_error ();
3582 for (l = id_defaults; l; l = l->next)
3583 if (!m_strcasecmp(whatfor, l->what)) {
3584 m_strcpy(resp, sizeof(resp), NONULL(l->dflt));
3592 if (mutt_get_field (tag, resp, sizeof (resp), M_CLEAR) != 0)
3597 m_strreplace(&l->dflt, resp);
3599 l = p_new(struct crypt_cache, 1);
3600 l->next = id_defaults;
3602 l->what = m_strdup(whatfor);
3603 l->dflt = m_strdup(resp);
3607 if ((key = crypt_getkeybystr (resp, abilities, app, forced_valid)))
3615 /* This routine attempts to find the keyids of the recipients of a
3616 message. It returns NULL if any of the keys can not be found. */
3617 static char *find_keys (address_t * to, address_t * cc, address_t * bcc,
3620 char *keylist = NULL, *t;
3622 ssize_t keylist_size = 0;
3623 ssize_t keylist_used = 0;
3624 address_t *tmp = NULL, *addr = NULL;
3625 address_t **last = &tmp;
3628 crypt_key_t *k_info, *key;
3629 const char *fqdn = mutt_fqdn (1);
3632 *r_application = APPLICATION_PGP | APPLICATION_SMIME;
3635 for (i = 0; i < 3; i++) {
3650 *last = address_list_dup (p);
3652 last = &((*last)->next);
3655 rfc822_qualify(tmp, fqdn);
3656 address_list_uniq(tmp);
3658 for (p = tmp; p; p = p->next) {
3659 char buf[LONG_STRING];
3660 int forced_valid = 0;
3665 if ((keyID = mutt_crypt_hook (p)) != NULL) {
3668 snprintf (buf, sizeof (buf), _("Use keyID = \"%s\" for %s?"),
3670 if ((r = mutt_yesorno (buf, M_YES)) == M_YES) {
3671 /* check for e-mail address */
3672 if ((t = strchr (keyID, '@')) &&
3673 (addr = rfc822_parse_adrlist (NULL, keyID))) {
3674 rfc822_qualify(addr, fqdn);
3678 k_info = crypt_getkeybystr (keyID, KEYFLAG_CANENCRYPT,
3679 app, &forced_valid);
3684 address_list_wipe(&tmp);
3685 address_list_wipe(&addr);
3691 && (k_info = crypt_getkeybyaddr (q, KEYFLAG_CANENCRYPT,
3692 app, &forced_valid)) == NULL) {
3693 snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
3695 if ((key = crypt_ask_for_key (buf, q->mailbox, KEYFLAG_CANENCRYPT,
3697 &forced_valid)) == NULL) {
3699 address_list_wipe(&tmp);
3700 address_list_wipe(&addr);
3708 const char *s = crypt_fpr (key);
3710 keylist_size += m_strlen(s) + 4 + 1;
3711 p_realloc(&keylist, keylist_size);
3712 sprintf (keylist + keylist_used, "%s0x%s%s",
3713 keylist_used ? " " : "", s, forced_valid ? "!" : "");
3715 keylist_used = m_strlen(keylist);
3717 crypt_free_key (&key);
3718 address_list_wipe(&addr);
3720 address_list_wipe(&tmp);
3724 char *crypt_pgp_findkeys (address_t * to, address_t * cc, address_t * bcc)
3726 return find_keys (to, cc, bcc, APPLICATION_PGP);
3729 char *crypt_smime_findkeys (address_t * to, address_t * cc, address_t * bcc)
3731 return find_keys (to, cc, bcc, APPLICATION_SMIME);
3734 static int gpgme_send_menu (HEADER * msg, int *redraw, int is_smime)
3737 char input_signas[STRING];
3740 if (msg->security & APPLICATION_PGP)
3742 else if (msg->security & APPLICATION_SMIME)
3747 mutt_multi_choice (_
3748 ("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
3752 mutt_multi_choice (_
3753 ("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
3757 case 1: /* (e)ncrypt */
3758 msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3759 msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
3762 case 2: /* (s)ign */
3763 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3764 msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3767 case 3: /* sign (a)s */
3768 /* unset_option(OPTCRYPTCHECKTRUST); */
3769 if ((p = crypt_ask_for_key (_("Sign as: "), NULL, KEYFLAG_CANSIGN,
3770 is_smime ? APPLICATION_SMIME :
3771 APPLICATION_PGP, NULL))) {
3772 snprintf (input_signas, sizeof (input_signas), "0x%s", crypt_keyid (p));
3773 m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs,
3775 crypt_free_key (&p);
3777 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3779 *redraw = REDRAW_FULL;
3782 case 4: /* (b)oth */
3784 (is_smime ? (SMIMEENCRYPT | SMIMESIGN) : (PGPENCRYPT | PGPSIGN));
3787 case 5: /* (p)gp or s/(m)ime */
3788 is_smime = !is_smime;
3791 case 6: /* (c)lear */
3796 if (choice == 6 || choice == 7);
3797 else if (is_smime) {
3798 msg->security &= ~APPLICATION_PGP;
3799 msg->security |= APPLICATION_SMIME;
3802 msg->security &= ~APPLICATION_SMIME;
3803 msg->security |= APPLICATION_PGP;
3806 return (msg->security);
3809 int crypt_pgp_send_menu(HEADER * msg, int *redraw)
3811 return gpgme_send_menu(msg, redraw, 0);
3814 int crypt_smime_send_menu(HEADER * msg, int *redraw)
3816 return gpgme_send_menu (msg, redraw, 1);
3819 int crypt_smime_verify_sender (HEADER * h)
3821 address_t *sender = NULL;
3822 unsigned int ret = 1;
3825 h->env->from = mutt_expand_aliases (h->env->from);
3826 sender = h->env->from;
3828 else if (h->env->sender) {
3829 h->env->sender = mutt_expand_aliases (h->env->sender);
3830 sender = h->env->sender;
3834 if (signature_key) {
3835 gpgme_key_t key = signature_key;
3836 gpgme_user_id_t uid = NULL;
3837 int sender_length = 0;
3840 sender_length = m_strlen(sender->mailbox);
3841 for (uid = key->uids; uid && ret; uid = uid->next) {
3842 uid_length = m_strlen(uid->email);
3843 if (1 && (uid->email[0] == '<')
3844 && (uid->email[uid_length - 1] == '>')
3845 && (uid_length == sender_length + 2)
3846 && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
3851 mutt_any_key_to_continue ("Failed to verify sender");
3854 mutt_any_key_to_continue ("Failed to figure out sender");
3856 if (signature_key) {
3857 gpgme_key_release (signature_key);
3858 signature_key = NULL;
3864 void crypt_invoke_import(FILE *stream, int smime)
3866 gpgme_ctx_t ctx = create_gpgme_context(smime);
3870 err = gpgme_data_new_from_stream(&data, stream);
3872 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
3877 err = gpgme_op_import(ctx, data);
3879 mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
3880 gpgme_data_release(data);
3885 gpgme_data_release(data);
3890 static void pgp_extract_keys_from_attachment(FILE * fp, BODY * top)
3893 FILE *tmpfp = tmpfile();
3895 if (tmpfp == NULL) {
3896 mutt_perror (_("Can't create temporary file"));
3903 mutt_body_handler(top, &s);
3906 crypt_invoke_import(tmpfp, 0);
3910 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
3913 set_option (OPTDONTHANDLEPGPKEYS);
3915 for (; top; top = top->next) {
3916 if (!tag || top->tagged)
3917 pgp_extract_keys_from_attachment (fp, top);
3923 unset_option (OPTDONTHANDLEPGPKEYS);
3929 /* fixme: needs documentation. */
3930 void crypt_pgp_invoke_getkeys (address_t * addr)
3934 /* Generate a PGP public key attachment. */
3935 BODY *crypt_pgp_make_key_attachment (char *tempf)
3942 /* fixme: Needs documentation. */
3943 void crypt_smime_getkeys (ENVELOPE * env)