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
11 * Copyright © 2006 Pierre Habouzit
14 #include <lib-lib/lib-lib.h>
18 #include <lib-mime/mime.h>
19 #include <lib-ui/lib-ui.h>
20 #include <lib-ui/enter.h>
21 #include <lib-ui/menu.h>
22 #include <lib-mx/mx.h>
29 #include "recvattach.h"
32 @import "lib-lua/base.cpkg"
38 ** This variable controls whether or not Madmutt may automatically enable
39 ** S/MIME encryption/signing for messages. See also ``$$crypt_autoencrypt'',
40 ** ``$$crypt_replyencrypt'',
41 ** ``$$crypt_autosign'', ``$$crypt_replysign'' and ``$$smime_is_default''.
46 ** This variable controls whether or not Madmutt may automatically enable
47 ** PGP encryption/signing for messages. See also ``$$crypt_autoencrypt'',
48 ** ``$$crypt_replyencrypt'',
49 ** ``$$crypt_autosign'', ``$$crypt_replysign'' and ``$$smime_is_default''.
54 ** Setting this variable will cause Madmutt to always attempt to
55 ** cryptographically sign outgoing messages. This can be overridden
56 ** by use of the \fIpgp-menu\fP, when signing is not required or
57 ** encryption is requested as well. If ``$$smime_is_default'' is \fIset\fP,
58 ** then OpenSSL is used instead to create S/MIME messages and settings can
59 ** be overridden by use of the \fIsmime-menu\fP.
65 ** Setting this variable will cause Madmutt to always attempt to PGP
66 ** encrypt outgoing messages. This is probably only useful in
67 ** connection to the \fIsend-hook\fP command. It can be overridden
68 ** by use of the \fIpgp-menu\fP, when encryption is not required or
69 ** signing is requested as well. If ``$$smime_is_default'' is \fIset\fP,
70 ** then OpenSSL is used instead to create S/MIME messages and
71 ** settings can be overridden by use of the \fIsmime-menu\fP.
74 bool replyencrypt = 1;
77 ** If \fIset\fP, automatically PGP or OpenSSL encrypt replies to messages which are
84 ** If \fIset\fP, automatically PGP or OpenSSL sign replies to messages which are
87 ** \fBNote:\fP this does not work on messages that are encrypted \fBand\fP signed!
90 bool replysignencrypted = 1;
93 ** If \fIset\fP, automatically PGP or OpenSSL sign replies to messages
94 ** which are encrypted. This makes sense in combination with
95 ** ``$$crypt_replyencrypt'', because it allows you to sign all
96 ** messages which are automatically encrypted. This works around
97 ** the problem noted in ``$$crypt_replysign'', that Madmutt is not able
98 ** to find out whether an encrypted message is also signed.
101 bool smime_is_default = 0;
104 ** The default behaviour of Madmutt is to use PGP on all auto-sign/encryption
105 ** operations. To override and to use OpenSSL instead this must be \fIset\fP.
107 ** However, this has no effect while replying, since Madmutt will automatically
108 ** select the same application that was used to sign/encrypt the original
111 ** (Note that this variable can be overridden by unsetting $$crypt_autosmime.)
114 quadopt_t verify_sig = M_YES;
117 ** If ``\fIyes\fP'', always attempt to verify PGP or S/MIME signatures.
118 ** If ``\fIask\fP'', ask whether or not to verify the signature.
119 ** If ``\fIno\fP'', never attempt to verify cryptographic signatures.
123 string_t pgp_entry_format = m_strdup("%4n %t%f %4l/0x%k %-4a %2c %u");
126 ** This variable allows you to customize the PGP key selection menu to
127 ** your personal taste. This string is similar to ``$$index_format'', but
128 ** has its own set of \fTprintf(3)\fP-like sequences:
133 ** .dt %u .dd user id
134 ** .dt %a .dd algorithm
135 ** .dt %l .dd key length
137 ** .dt %c .dd capabilities
138 ** .dt %t .dd trust/validity of the key-uid association
139 ** .dt %[<s>] .dd date of the key where <s> is an \fTstrftime(3)\fP expression
146 /* Values used for comparing addresses. */
147 #define CRYPT_KV_VALID 1
148 #define CRYPT_KV_ADDR 2
149 #define CRYPT_KV_STRING 4
150 #define CRYPT_KV_STRONGID 8
151 #define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
158 /* We work based on user IDs, getting from a user ID to the key is
159 check and does not need any memory (gpgme uses reference counting). */
160 typedef struct cryptkey_t {
161 struct cryptkey_t *next;
162 int idx; /* and the user ID at this index */
163 int flags; /* global and per uid flags (for convenience) */
165 const char *uid; /* and for convenience point to this user ID */
168 DO_INIT(cryptkey_t, cryptkey);
169 static void cryptkey_wipe(cryptkey_t *key) {
170 gpgme_key_release(key->kobj);
172 DO_NEW(cryptkey_t, cryptkey);
173 DO_DELETE(cryptkey_t, cryptkey);
174 DO_SLIST(cryptkey_t, key, cryptkey_delete);
176 static cryptkey_t *cryptkey_dup(const cryptkey_t *k)
178 cryptkey_t *res = cryptkey_new();
181 gpgme_key_ref(k->kobj);
185 typedef struct crypt_entry {
190 static gpgme_key_t signature_key = NULL;
192 static void convert_to_7bit (BODY * a)
194 for (; a; a = a->next) {
195 int tok = mime_which_token(a->subtype, -1);
197 if (a->type == TYPEMULTIPART) {
198 a->encoding = ENC7BIT;
199 convert_to_7bit(a->parts);
200 } else if (a->type == TYPEMESSAGE && tok != MIME_DELIVERY_STATUS) {
201 if (a->encoding != ENC7BIT)
202 mutt_message_to_7bit(a, NULL);
203 } else if (a->encoding == ENC8BIT) {
204 a->encoding = ENCQUOTEDPRINTABLE;
205 } else if (a->encoding == ENCBINARY) {
206 a->encoding = ENCBASE64;
207 } else if (a->content && a->encoding != ENCBASE64
208 && (a->content->from || a->content->space))
210 a->encoding = ENCQUOTEDPRINTABLE;
215 /* Print the utf-8 encoded string BUF of length LEN bytes to stream
216 FP. Convert the character set. */
217 static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
221 tstr = p_dupstr(buf, len);
222 mutt_convert_string(&tstr, "utf-8", mod_cset.charset, M_ICONV_HOOK_FROM);
227 /* Return the keyID for the key K. Note that this string is valid as
228 long as K is valid */
229 static const char *crypt_keyid (cryptkey_t * k)
231 if (k->kobj && k->kobj->subkeys) {
232 const char *s = k->kobj->subkeys->keyid;
233 return m_strlen(s) == 16 ? s + 8 : s;
239 /* Return the hexstring fingerprint from the key K. */
240 static const char *crypt_fpr (cryptkey_t * k)
242 return k->kobj && k->kobj->subkeys ? k->kobj->subkeys->fpr : "";
245 /* Parse FLAGS and return a statically allocated(!) string with them. */
246 static char *crypt_key_abilities (int flags)
248 static char buff[3] = "es";
250 if (!(flags & KEYFLAG_CANENCRYPT))
252 else if (flags & KEYFLAG_PREFER_SIGNING)
255 if (!(flags & KEYFLAG_CANSIGN))
257 else if (flags & KEYFLAG_PREFER_ENCRYPTION)
263 /* Parse FLAGS and return a character describing the most important flag. */
264 static char crypt_flags(int flags)
266 if (flags & KEYFLAG_REVOKED)
268 if (flags & KEYFLAG_EXPIRED)
270 if (flags & KEYFLAG_DISABLED)
272 if (flags & KEYFLAG_CRITICAL)
277 /* Return true whe validity of KEY is sufficient. */
278 static int crypt_id_is_strong (cryptkey_t * key)
283 if (key->flags & KEYFLAG_ISX509)
286 for (i = 0, uid = key->kobj->uids; uid; i++, uid = uid->next) {
288 return uid->validity == GPGME_VALIDITY_FULL
289 || uid->validity == GPGME_VALIDITY_ULTIMATE;
296 /* Return a bit vector describing how well the addresses ADDR and
297 U_ADDR match and whether KEY is valid. */
299 crypt_id_matches_addr(address_t *addr, address_t *u_addr, cryptkey_t *key)
303 if (!(key->flags & KEYFLAG_CANTUSE))
304 rv |= CRYPT_KV_VALID;
306 if (crypt_id_is_strong(key))
307 rv |= CRYPT_KV_STRONGID;
309 if (addr->mailbox && !m_strcasecmp(addr->mailbox, u_addr->mailbox))
312 if (addr->personal && m_strcasecmp(addr->personal, u_addr->personal))
313 rv |= CRYPT_KV_STRING;
319 /* Create a new gpgme context and return it. With FOR_SMIME set to
320 true, the protocol of the context is set to CMS. */
321 static gpgme_ctx_t create_gpgme_context(int for_smime)
326 err = gpgme_new (&ctx);
328 mutt_error(_("error creating gpgme context: %s\n"),
329 gpgme_strerror(err));
336 err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
338 mutt_error(_("error enabling CMS protocol: %s\n"), gpgme_strerror(err));
345 /* Create a new gpgme data object. This is a wrapper to die on
347 static gpgme_data_t create_gpgme_data(void)
352 err = gpgme_data_new(&data);
354 mutt_error(_("error creating gpgme data object: %s\n"),
355 gpgme_strerror(err));
362 /* Create a new GPGME Data object from the mail body A. With CONVERT
363 passed as true, the lines are converted to CR,LF if required.
364 Return NULL on error or the gpgme_data_t object on success. */
365 static gpgme_data_t body_to_data_object(BODY *a, int convert)
371 if (!(fptmp = tmpfile())) {
372 mutt_perror (_("Can't create temporary file"));
376 mutt_write_mime_header(a, fptmp);
378 mutt_write_mime_body(a, fptmp);
385 data = create_gpgme_data();
387 while (fgets(buf + spare, sizeof(buf) - 1, fptmp)) {
388 int l = m_strlen(buf);
390 spare = buf[l - 1] != '\n';
391 if (!spare && (l <= 1 || buf[l - 2] != '\r')) {
395 gpgme_data_write(data, buf, l - spare);
400 gpgme_data_write(data, buf, 1);
401 gpgme_data_seek(data, 0, SEEK_SET);
406 err = gpgme_data_new_from_stream(&data, fptmp);
409 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
415 /* Create a GPGME data object from the stream FP but limit the object
416 to LENGTH bytes starting at OFFSET bytes from the beginning of the
418 static gpgme_data_t file_to_data_object(FILE *fp, long offset, long length)
423 err = gpgme_data_new_from_filepart(&data, NULL, fp, offset, length);
425 mutt_error(_("error allocating data object: %s\n"),
426 gpgme_strerror(err));
433 /* Write a GPGME data object to the stream FP. */
434 static int data_object_to_stream(gpgme_data_t data, FILE *fp)
440 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
441 ? gpgme_error_from_errno (errno) : 0);
443 mutt_error (_("error rewinding data object: %s\n"),
444 gpgme_strerror(err));
448 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
449 /* fixme: we are not really converting CRLF to LF but just
450 skipping CR. Doing it correctly needs a more complex logic */
451 for (p = buf; nread; p++, nread--) {
457 mutt_perror ("[tempfile]");
462 mutt_error(_("error reading data object: %s\n"), strerror(errno));
468 /* Copy a data object to a newly created temporay file and return that
469 filename. Caller must free. With RET_FP not NULL, don't close the
470 stream but return it there. */
471 static char *data_object_to_tempfile(gpgme_data_t data, FILE **ret_fp)
474 char tempfile[_POSIX_PATH_MAX];
478 fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
480 mutt_perror (_("Can't create temporary file"));
484 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
485 ? gpgme_error_from_errno (errno) : 0);
489 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
490 if (fwrite (buf, nread, 1, fp) != 1) {
491 mutt_perror (_("Can't create temporary file"));
500 mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
511 return m_strdup(tempfile);
515 /* FIXME: stolen from gpgme to avoid "ambiguous identity" errors */
517 gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
523 if (!ctx || !r_key || !fpr)
524 return gpg_error (GPG_ERR_INV_VALUE);
526 if (strlen (fpr) < 8) /* We have at least a key ID. */
527 return gpg_error (GPG_ERR_INV_VALUE);
529 /* FIXME: We use our own context because we have to avoid the user's
530 I/O callback handlers. */
531 err = gpgme_new (&listctx);
534 gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
535 err = gpgme_op_keylist_start (listctx, fpr, secret);
537 err = gpgme_op_keylist_next (listctx, r_key);
538 gpgme_release (listctx);
542 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
543 The keys must be space delimited. */
544 static gpgme_key_t *create_recipient_set(const char *s, int smime)
546 gpgme_ctx_t ctx = create_gpgme_context(smime);
547 gpgme_key_t *rset = NULL;
554 const char *p = m_strnextsp(s);
557 m_strncpy(buf, sizeof(buf), s, p - s);
558 if (p - s > 1 && p[-1] == '!') {
559 /* user wants to override the valididy of that key. */
561 buf[p - s - 1] = '\0';
562 err = gpgme_get_key2(ctx, buf, &key, 0);
564 key->uids->validity = GPGME_VALIDITY_FULL;
566 err = gpgme_get_key2(ctx, buf, &key, 0);
570 mutt_error(_("error adding recipient `%.*s': %s\n"),
571 (int)(p - s), s, gpgme_strerror(err));
576 p_realloc(&rset, rset_n + 1);
577 rset[rset_n++] = key;
582 /* NULL terminate. */
583 p_realloc(&rset, rset_n + 1);
584 rset[rset_n++] = NULL;
592 /* Make sure that the correct signer is set. Returns 0 on success. */
593 static int set_signer(gpgme_ctx_t ctx, int for_smime)
595 const char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
599 if (m_strisempty(signid))
602 err = gpgme_get_key(ctx, signid, &key, 1);
604 mutt_error(_("error getting secret key `%s': %s\n"), signid,
605 gpgme_strerror(err));
609 gpgme_signers_clear(ctx);
610 err = gpgme_signers_add(ctx, key);
611 gpgme_key_unref(key);
613 mutt_error(_("error setting secret key `%s': %s\n"), signid,
614 gpgme_strerror(err));
621 /* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
622 and return an allocated filename to a temporary file containing the
623 enciphered text. With USE_SMIME set to true, the smime backend is
624 used. With COMBINED_SIGNED a PGP message is signed and
625 encrypted. Returns NULL in case of error */
626 static char *encrypt_gpgme_object(gpgme_data_t plaintext, gpgme_key_t *rset,
627 int use_smime, int combined_signed)
631 gpgme_data_t ciphertext;
634 ctx = create_gpgme_context (use_smime);
636 gpgme_set_armor (ctx, 1);
638 ciphertext = create_gpgme_data ();
640 if (combined_signed) {
641 if (set_signer(ctx, use_smime)) {
642 gpgme_data_release(ciphertext);
646 err = gpgme_op_encrypt_sign(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
647 plaintext, ciphertext);
649 err = gpgme_op_encrypt(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
650 plaintext, ciphertext);
653 mutt_need_hard_redraw();
655 mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
656 gpgme_data_release (ciphertext);
663 outfile = data_object_to_tempfile(ciphertext, NULL);
664 gpgme_data_release(ciphertext);
668 /* Find the "micalg" parameter from the last Gpgme operation on
669 context CTX. It is expected that this operation was a sign
670 operation. Return the algorithm name as a C string in buffer BUF
671 which must have been allocated by the caller with size BUFLEN.
672 Returns 0 on success or -1 in case of an error. The return string
673 is truncted to BUFLEN - 1. */
674 static int get_micalg(gpgme_ctx_t ctx, char *buf, ssize_t buflen)
676 gpgme_sign_result_t result = NULL;
677 const char *alg = NULL;
679 result = gpgme_op_sign_result(ctx);
680 if (result && result->signatures) {
681 alg = gpgme_hash_algo_name(result->signatures->hash_algo);
683 m_strcpy(buf, buflen, NONULL(alg));
688 static void print_time(time_t t, STATE *s)
692 setlocale(LC_TIME, "");
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. */
795 /* Encrypt the mail body A to all keys given as space separated keyids
796 or fingerprints in KEYLIST and return the encrypted body. */
797 static BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
799 char *outfile = NULL;
801 gpgme_key_t *rset = NULL;
802 gpgme_data_t plaintext;
804 rset = create_recipient_set(keylist, 0);
810 plaintext = body_to_data_object(a, 0);
816 outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
817 gpgme_data_release (plaintext);
823 t->type = TYPEMULTIPART;
824 t->subtype = m_strdup("encrypted");
825 t->encoding = ENC7BIT;
827 t->disposition = DISPINLINE;
829 parameter_set_boundary(&t->parameter);
830 parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
832 t->parts = body_new();
833 t->parts->type = TYPEAPPLICATION;
834 t->parts->subtype = m_strdup("pgp-encrypted");
835 t->parts->encoding = ENC7BIT;
837 t->parts->next = body_new();
838 t->parts->next->type = TYPEAPPLICATION;
839 t->parts->next->subtype = m_strdup("octet-stream");
840 t->parts->next->encoding = ENC7BIT;
841 t->parts->next->filename = outfile;
842 t->parts->next->use_disp = 1;
843 t->parts->next->disposition = DISPINLINE;
844 t->parts->next->unlink = 1; /* delete after sending the message */
845 t->parts->next->d_filename = m_strdup("msg.asc");
850 /* Encrypt the mail body A to all keys given as space separated
851 fingerprints in KEYLIST and return the S/MIME encrypted body. */
852 static BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
854 char *outfile = NULL;
856 gpgme_key_t *rset = NULL;
857 gpgme_data_t plaintext;
859 rset = create_recipient_set(keylist, 1);
863 plaintext = body_to_data_object(a, 0);
869 outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
870 gpgme_data_release (plaintext);
876 t->type = TYPEAPPLICATION;
877 t->subtype = m_strdup("pkcs7-mime");
878 parameter_setval(&t->parameter, "name", "smime.p7m");
879 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
880 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
882 t->disposition = DISPATTACH;
883 t->d_filename = m_strdup("smime.p7m");
884 t->filename = outfile;
885 t->unlink = 1; /*delete after sending the message */
892 /* Display the common attributes of the signature summary SUM.
893 Return 1 if there is is a severe warning.
895 static int show_sig_summary (unsigned long sum,
896 gpgme_ctx_t ctx, gpgme_key_t key, int idx,
901 if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
902 state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
906 if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
907 time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
910 state_attach_puts (_("Warning: The key used to create the "
911 "signature expired at: "), s);
913 state_attach_puts ("\n", s);
916 state_attach_puts (_("Warning: At least one certification key "
917 "has expired\n"), s);
920 if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
921 gpgme_verify_result_t result;
922 gpgme_signature_t sig;
925 result = gpgme_op_verify_result (ctx);
927 for (sig = result->signatures, i = 0; sig && (i < idx);
928 sig = sig->next, i++);
930 state_attach_puts (_("Warning: The signature expired at: "), s);
931 print_time (sig ? sig->exp_timestamp : 0, s);
932 state_attach_puts ("\n", s);
935 if ((sum & GPGME_SIGSUM_KEY_MISSING))
936 state_attach_puts (_("Can't verify due to a missing "
937 "key or certificate\n"), s);
939 if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
940 state_attach_puts (_("The CRL is not available\n"), s);
944 if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
945 state_attach_puts (_("Available CRL is too old\n"), s);
949 if ((sum & GPGME_SIGSUM_BAD_POLICY))
950 state_attach_puts (_("A policy requirement was not met\n"), s);
952 if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
953 const char *t0 = NULL, *t1 = NULL;
954 gpgme_verify_result_t result;
955 gpgme_signature_t sig;
958 state_attach_puts (_("A system error occurred"), s);
960 /* Try to figure out some more detailed system error information. */
961 result = gpgme_op_verify_result (ctx);
962 for (sig = result->signatures, i = 0; sig && (i < idx);
963 sig = sig->next, i++);
966 t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
970 state_attach_puts (": ", s);
972 state_attach_puts (t0, s);
973 if (t1 && !(t0 && !m_strcmp(t0, t1))) {
975 state_attach_puts (",", s);
976 state_attach_puts (t1, s);
979 state_attach_puts ("\n", s);
986 static void show_fingerprint (gpgme_key_t key, STATE * state)
991 const char *prefix = _("Fingerprint: ");
996 s = key->subkeys ? key->subkeys->fpr : NULL;
999 is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
1001 bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
1002 buf = p_new(char, bufsize);
1003 m_strcpy(buf, bufsize, prefix);
1004 p = buf + m_strlen(buf);
1005 if (is_pgp && m_strlen(s) == 40) { /* PGP v4 style formatted. */
1006 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
1017 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
1020 *p++ = is_pgp ? ' ' : ':';
1021 if (is_pgp && i == 7)
1026 /* just in case print remaining odd digits */
1031 state_attach_puts (buf, state);
1035 /* Show the valididy of a key used for one signature. */
1036 static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
1038 gpgme_verify_result_t result = NULL;
1039 gpgme_signature_t sig = NULL;
1040 const char *txt = NULL;
1042 result = gpgme_op_verify_result (ctx);
1044 for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
1046 switch (sig ? sig->validity : 0) {
1047 case GPGME_VALIDITY_UNKNOWN:
1048 txt = _("WARNING: We have NO indication whether "
1049 "the key belongs to the person named " "as shown above\n");
1051 case GPGME_VALIDITY_UNDEFINED:
1053 case GPGME_VALIDITY_NEVER:
1054 txt = _("WARNING: The key does NOT BELONG to "
1055 "the person named as shown above\n");
1057 case GPGME_VALIDITY_MARGINAL:
1058 txt = _("WARNING: It is NOT certain that the key "
1059 "belongs to the person named as shown above\n");
1061 case GPGME_VALIDITY_FULL:
1062 case GPGME_VALIDITY_ULTIMATE:
1067 state_attach_puts (txt, s);
1070 /* Show information about one signature. This fucntion is called with
1071 the context CTX of a sucessful verification operation and the
1072 enumerator IDX which should start at 0 and incremete for each
1075 Return values are: 0 for normal procession, 1 for a bad signature,
1076 2 for a signature with a warning or -1 for no more signature. */
1077 static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
1080 const char *fpr, *uid;
1081 gpgme_key_t key = NULL;
1082 int i, anybad = 0, anywarn = 0;
1084 gpgme_user_id_t uids = NULL;
1085 gpgme_verify_result_t result;
1086 gpgme_signature_t sig;
1087 gpgme_error_t err = GPG_ERR_NO_ERROR;
1089 result = gpgme_op_verify_result (ctx);
1091 /* FIXME: this code should use a static variable and remember
1092 the current position in the list of signatures, IMHO.
1095 for (i = 0, sig = result->signatures; sig && (i < idx);
1096 i++, sig = sig->next);
1098 return -1; /* Signature not found. */
1100 if (signature_key) {
1101 gpgme_key_unref(signature_key);
1102 signature_key = NULL;
1105 created = sig->timestamp;
1109 if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
1112 err = gpgme_get_key2 (ctx, fpr, &key, 0); /* secret key? */
1114 uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
1116 signature_key = key;
1119 key = NULL; /* Old gpgme versions did not set KEY to NULL on
1120 error. Do it here to avoid a double free. */
1124 if (!s || !s->fpout || !(s->flags & M_DISPLAY)); /* No state information so no way to print anything. */
1126 state_attach_puts (_("Error getting key information: "), s);
1127 state_attach_puts (gpg_strerror (err), s);
1128 state_attach_puts ("\n", s);
1131 else if ((sum & GPGME_SIGSUM_GREEN)) {
1132 state_attach_puts (_("Good signature from: "), s);
1133 state_attach_puts (uid, s);
1134 state_attach_puts ("\n", s);
1135 for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
1137 /* Skip primary UID. */
1141 state_attach_puts (_(" aka: "), s);
1142 state_attach_puts (uids->uid, s);
1143 state_attach_puts ("\n", s);
1145 state_attach_puts (_(" created: "), s);
1146 print_time (created, s);
1147 state_attach_puts ("\n", s);
1148 if (show_sig_summary (sum, ctx, key, idx, s))
1150 show_one_sig_validity (ctx, idx, s);
1152 else if ((sum & GPGME_SIGSUM_RED)) {
1153 state_attach_puts (_("*BAD* signature claimed to be from: "), s);
1154 state_attach_puts (uid, s);
1155 state_attach_puts ("\n", s);
1156 show_sig_summary (sum, ctx, key, idx, s);
1158 else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) { /* We can't decide (yellow) but this is a PGP key with a good
1159 signature, so we display what a PGP user expects: The name,
1160 fingerprint and the key validity (which is neither fully or
1162 state_attach_puts (_("Good signature from: "), s);
1163 state_attach_puts (uid, s);
1164 state_attach_puts ("\n", s);
1165 state_attach_puts (_(" created: "), s);
1166 print_time (created, s);
1167 state_attach_puts ("\n", s);
1168 show_one_sig_validity (ctx, idx, s);
1169 show_fingerprint (key, s);
1170 if (show_sig_summary (sum, ctx, key, idx, s))
1173 else { /* can't decide (yellow) */
1175 state_attach_puts (_("Error checking signature"), s);
1176 state_attach_puts ("\n", s);
1177 show_sig_summary (sum, ctx, key, idx, s);
1180 if (key != signature_key)
1181 gpgme_key_unref(key);
1184 return anybad ? 1 : anywarn ? 2 : 0;
1187 /* Do the actual verification step. With IS_SMIME set to true we
1188 assume S/MIME (surprise!) */
1189 static int crypt_verify_one(BODY *sigbdy, STATE *s, FILE *fp, int is_smime)
1195 gpgme_data_t signature, message;
1197 signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
1201 /* We need to tell gpgme about the encoding because the backend can't
1202 auto-detect plain base-64 encoding which is used by S/MIME. */
1204 gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
1206 err = gpgme_data_new_from_stream(&message, fp);
1208 gpgme_data_release (signature);
1209 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
1212 ctx = create_gpgme_context (is_smime);
1214 /* Note: We don't need a current time output because GPGME avoids
1215 such an attack by separating the meta information from the
1217 state_attach_puts (_("[-- Begin signature information --]\n"), s);
1219 err = gpgme_op_verify (ctx, signature, message, NULL);
1223 snprintf (buf, sizeof (buf) - 1,
1224 _("Error: verification failed: %s\n"), gpgme_strerror (err));
1225 state_attach_puts (buf, s);
1227 else { /* Verification succeeded, see what the result is. */
1231 if (signature_key) {
1232 gpgme_key_unref(signature_key);
1233 signature_key = NULL;
1236 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1247 gpgme_verify_result_t result;
1248 gpgme_sig_notation_t notation;
1249 gpgme_signature_t sig;
1251 result = gpgme_op_verify_result (ctx);
1253 for (sig = result->signatures; sig; sig = sig->next) {
1254 if (sig->notations) {
1255 state_attach_puts ("*** Begin Notation (signature by: ", s);
1256 state_attach_puts (sig->fpr, s);
1257 state_attach_puts (") ***\n", s);
1258 for (notation = sig->notations; notation; notation = notation->next)
1260 if (notation->name) {
1261 state_attach_puts (notation->name, s);
1262 state_attach_puts ("=", s);
1264 if (notation->value) {
1265 state_attach_puts (notation->value, s);
1266 if (!(*notation->value
1267 && (notation->value[m_strlen(notation->value) - 1] ==
1269 state_attach_puts ("\n", s);
1272 state_attach_puts ("*** End Notation ***\n", s);
1278 gpgme_release (ctx);
1280 state_attach_puts (_("[-- End signature information --]\n\n"), s);
1282 return badsig ? 1 : anywarn ? 2 : 0;
1285 /* Decrypt a PGP or SMIME message (depending on the boolean flag
1286 IS_SMIME) with body A described further by state S. Write
1287 plaintext out to file FPOUT and return a new body. For PGP returns
1288 a flag in R_IS_SIGNED to indicate whether this is a combined
1289 encrypted and signed message, for S/MIME it returns true when it is
1290 not a encrypted but a signed message. */
1292 decrypt_part(BODY *a, STATE *s, FILE *fpout, int is_smime, int *r_is_signed)
1298 gpgme_data_t ciphertext, plaintext;
1299 int maybe_signed = 0;
1306 ctx = create_gpgme_context (is_smime);
1309 /* Make a data object from the body, create context etc. */
1310 ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
1313 plaintext = create_gpgme_data ();
1315 /* Do the decryption or the verification in case of the S/MIME hack. */
1316 if ((!is_smime) || maybe_signed) {
1318 err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
1319 else if (maybe_signed)
1320 err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
1323 /* Check wether signatures have been verified. */
1324 gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
1326 if (verify_result->signatures)
1331 err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
1332 gpgme_data_release (ciphertext);
1334 if (is_smime && !maybe_signed && gpg_err_code (err) == GPG_ERR_NO_DATA) {
1335 /* Check whether this might be a signed message despite what
1336 the mime header told us. Retry then. gpgsm returns the
1337 error information "unsupported Algorithm '?'" but gpgme
1338 will not store this unknown algorithm, thus we test that
1339 it has not been set. */
1340 gpgme_decrypt_result_t result;
1342 result = gpgme_op_decrypt_result (ctx);
1343 if (!result->unsupported_algorithm) {
1345 gpgme_data_release (plaintext);
1349 mutt_need_hard_redraw ();
1350 if ((s->flags & M_DISPLAY)) {
1353 snprintf (buf, sizeof (buf) - 1,
1354 _("[-- Error: decryption failed: %s --]\n\n"),
1355 gpgme_strerror (err));
1356 state_attach_puts (buf, s);
1358 gpgme_data_release (plaintext);
1359 gpgme_release (ctx);
1362 mutt_need_hard_redraw ();
1364 /* Read the output from GPGME, and make sure to change CRLF to LF,
1365 otherwise read_mime_header has a hard time parsing the message. */
1366 if (data_object_to_stream (plaintext, fpout)) {
1367 gpgme_data_release (plaintext);
1368 gpgme_release (ctx);
1371 gpgme_data_release (plaintext);
1373 a->is_signed_data = 0;
1379 a->is_signed_data = 1;
1381 *r_is_signed = -1; /* A signature exists. */
1383 if ((s->flags & M_DISPLAY))
1384 state_attach_puts (_("[-- Begin signature " "information --]\n"), s);
1385 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1391 if (!anybad && idx && r_is_signed && *r_is_signed)
1392 *r_is_signed = anywarn ? 2 : 1; /* Good signature. */
1394 if ((s->flags & M_DISPLAY))
1395 state_attach_puts (_("[-- End signature " "information --]\n\n"), s);
1397 gpgme_release (ctx);
1402 tattach = mutt_read_mime_header (fpout, 0);
1405 * Need to set the length of this body part.
1407 fstat (fileno (fpout), &info);
1408 tattach->length = info.st_size - tattach->offset;
1410 tattach->warnsig = anywarn;
1412 /* See if we need to recurse on this MIME part. */
1413 mutt_parse_part (fpout, tattach);
1419 /* Decrypt a PGP/MIME message in FPIN and B and return a new body and
1420 the stream in CUR and FPOUT. Returns 0 on success. */
1421 int crypt_pgp_decrypt_mime (FILE * fpin, FILE **fpout, BODY *b, BODY **cur)
1424 BODY *first_part = b;
1427 first_part->goodsig = 0;
1428 first_part->warnsig = 0;
1430 if (!mutt_is_multipart_encrypted(b) || !b->parts || !b->parts->next)
1439 mutt_perror (_("Can't create temporary file"));
1443 *cur = decrypt_part(b, &s, *fpout, 0, &is_signed);
1445 first_part->goodsig = is_signed > 0;
1446 return *cur ? 0 : -1;
1450 /* Decrypt a S/MIME message in FPIN and B and return a new body and
1451 the stream in CUR and FPOUT. Returns 0 on success. */
1452 int crypt_smime_decrypt_mime(FILE *fpin, FILE **fpout, BODY *b, BODY **cur)
1457 long saved_b_offset;
1458 ssize_t saved_b_length;
1461 if (!mutt_is_application_smime (b))
1467 /* Decode the body - we need to pass binary CMS to the
1468 backend. The backend allows for Base64 encoded data but it does
1469 not allow for QP which I have seen in some messages. So better
1471 saved_b_type = b->type;
1472 saved_b_offset = b->offset;
1473 saved_b_length = b->length;
1476 fseeko (s.fpin, b->offset, 0);
1479 mutt_perror (_("Can't create temporary file"));
1484 mutt_decode_attachment (b, &s);
1486 b->length = ftello (s.fpout);
1495 mutt_perror (_("Can't create temporary file"));
1499 *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
1501 (*cur)->goodsig = is_signed > 0;
1502 b->type = saved_b_type;
1503 b->length = saved_b_length;
1504 b->offset = saved_b_offset;
1507 if (*cur && !is_signed && !(*cur)->parts
1508 && mutt_is_application_smime (*cur)) {
1509 /* Assume that this is a opaque signed s/mime message. This is
1510 an ugly way of doing it but we have anyway a problem with
1511 arbitrary encoded S/MIME messages: Only the outer part may be
1512 encrypted. The entire mime parsing should be revamped,
1513 probably by keeping the temportary files so that we don't
1514 need to decrypt them all the time. Inner parts of an
1515 encrypted part can then pint into this file and tehre won't
1516 never be a need to decrypt again. This needs a partial
1517 rewrite of the MIME engine. */
1521 saved_b_type = bb->type;
1522 saved_b_offset = bb->offset;
1523 saved_b_length = bb->length;
1526 fseeko (s.fpin, bb->offset, 0);
1529 mutt_perror (_("Can't create temporary file"));
1534 mutt_decode_attachment (bb, &s);
1536 bb->length = ftello (s.fpout);
1546 mutt_perror (_("Can't create temporary file"));
1550 tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
1552 tmp_b->goodsig = is_signed > 0;
1553 bb->type = saved_b_type;
1554 bb->length = saved_b_length;
1555 bb->offset = saved_b_offset;
1558 body_list_wipe(cur);
1561 return *cur ? 0 : -1;
1566 pgp_check_traditional_one_body(FILE *fp, BODY *b, int tagged_only)
1568 char tempfile[_POSIX_PATH_MAX];
1569 char buf[HUGE_STRING];
1576 if (b->type != TYPETEXT)
1579 if (tagged_only && !b->tagged)
1582 tempfd = m_tempfd(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
1583 if (mutt_decode_save_attachment (fp, b, tempfd, 0) != 0) {
1588 if ((tfp = fopen(tempfile, "r")) == NULL) {
1593 while (fgets (buf, sizeof (buf), tfp)) {
1594 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1595 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1597 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15))
1607 /* fix the content type */
1609 parameter_setval(&b->parameter, "format", "fixed");
1610 parameter_setval(&b->parameter, "x-action",
1611 enc ? "pgp-encrypted" : "pgp-signed");
1615 int crypt_pgp_check_traditional(FILE *fp, BODY *b, int tagged_only)
1619 for (; b; b = b->next) {
1620 if (is_multipart(b))
1621 rv |= crypt_pgp_check_traditional(fp, b->parts, tagged_only);
1622 if (b->type == TYPETEXT) {
1624 if ((r = mutt_is_application_pgp(b))) {
1627 rv |= pgp_check_traditional_one_body(fp, b, tagged_only);
1636 Copy a clearsigned message, and strip the signature and PGP's
1639 XXX - charset handling: We assume that it is safe to do
1640 character set decoding first, dash decoding second here, while
1641 we do it the other way around in the main handler.
1643 (Note that we aren't worse than Outlook & Cie in this, and also
1644 note that we can successfully handle anything produced by any
1645 existing versions of mutt.) */
1646 static void copy_clearsigned(gpgme_data_t data, STATE * s, char *charset)
1648 char buf[HUGE_STRING];
1649 short complete, armor_header;
1654 fname = data_object_to_tempfile(data, &fp);
1660 fc = fgetconv_open (fp, charset, mod_cset.charset, M_ICONV_HOOK_FROM);
1662 for (complete = 1, armor_header = 1;
1663 fgetconvs (buf, sizeof (buf), fc) != NULL;
1664 complete = strchr (buf, '\n') != NULL) {
1667 state_puts (buf, s);
1671 if (!m_strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
1681 state_puts (s->prefix, s);
1683 if (buf[0] == '-' && buf[1] == ' ')
1684 state_puts (buf + 2, s);
1686 state_puts (buf, s);
1689 fgetconv_close (&fc);
1693 /* Support for classic_application/pgp */
1694 int crypt_pgp_application_pgp_handler(BODY *m, STATE *s)
1696 int needpass = -1, pgp_keyblock = 0;
1700 off_t last_pos, offset;
1701 char buf[HUGE_STRING];
1702 FILE *pgpout = NULL;
1704 gpgme_error_t err = 0;
1705 gpgme_data_t armored_data = NULL;
1707 short maybe_goodsig = 1;
1708 short have_any_sigs = 0;
1710 char body_charset[STRING]; /* Only used for clearsigned messages. */
1712 /* For clearsigned messages we won't be able to get a character set
1713 but we know that this may only be text thus we assume Latin-1
1715 if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
1716 m_strcpy(body_charset, sizeof(body_charset), "iso-8859-1");
1718 fseeko (s->fpin, m->offset, 0);
1719 last_pos = m->offset;
1721 for (bytes = m->length; bytes > 0;) {
1722 if (fgets (buf, sizeof (buf), s->fpin) == NULL)
1725 offset = ftello (s->fpin);
1726 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1729 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1731 start_pos = last_pos;
1733 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1735 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15)) {
1739 else if (!m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
1744 /* XXX - we may wish to recode here */
1746 state_puts (s->prefix, s);
1747 state_puts (buf, s);
1751 have_any_sigs = (have_any_sigs || (clearsign && (s->flags & M_VERIFY)));
1753 /* Copy PGP material to an data container */
1754 armored_data = create_gpgme_data ();
1755 gpgme_data_write (armored_data, buf, m_strlen(buf));
1756 while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) {
1757 offset = ftello (s->fpin);
1758 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1761 gpgme_data_write (armored_data, buf, m_strlen(buf));
1763 if ((needpass && !m_strcmp("-----END PGP MESSAGE-----\n", buf))
1765 && (!m_strcmp("-----END PGP SIGNATURE-----\n", buf)
1766 || !m_strcmp("-----END PGP PUBLIC KEY BLOCK-----\n",
1771 /* Invoke PGP if needed */
1772 if (!clearsign || (s->flags & M_VERIFY)) {
1773 unsigned int sig_stat = 0;
1774 gpgme_data_t plaintext;
1777 plaintext = create_gpgme_data ();
1778 ctx = create_gpgme_context (0);
1781 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1783 err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
1784 if (gpg_err_code (err) == GPG_ERR_NO_DATA) {
1785 /* Decrypt verify can't handle signed only messages. */
1786 err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
1787 ? gpgme_error_from_errno (errno) : 0;
1788 /* Must release plaintext so that we supply an
1789 uninitialized object. */
1790 gpgme_data_release (plaintext);
1791 plaintext = create_gpgme_data ();
1792 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1799 snprintf (errbuf, sizeof (errbuf) - 1,
1800 _("Error: decryption/verification failed: %s\n"),
1801 gpgme_strerror (err));
1802 state_attach_puts (errbuf, s);
1804 else { /* Decryption/Verification succeeded */
1808 /* Check wether signatures have been verified. */
1809 gpgme_verify_result_t verify_result;
1811 verify_result = gpgme_op_verify_result (ctx);
1812 if (verify_result->signatures)
1818 if ((s->flags & M_DISPLAY) && sig_stat) {
1823 state_attach_puts (_("[-- Begin signature "
1824 "information --]\n"), s);
1827 (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1836 state_attach_puts (_("[-- End signature "
1837 "information --]\n\n"), s);
1840 tmpfname = data_object_to_tempfile(plaintext, &pgpout);
1843 state_attach_puts (_("Error: copy data failed\n"), s);
1847 p_delete(&tmpfname);
1850 gpgme_release (ctx);
1854 * Now, copy cleartext to the screen. NOTE - we expect that PGP
1855 * outputs utf-8 cleartext. This may not always be true, but it
1856 * seems to be a reasonable guess.
1859 if (s->flags & M_DISPLAY) {
1861 state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
1862 else if (pgp_keyblock)
1863 state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s);
1865 state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s);
1869 copy_clearsigned (armored_data, s, body_charset);
1876 fc = fgetconv_open (pgpout, "utf-8", mod_cset.charset, 0);
1877 while ((c = fgetconv (fc)) != EOF) {
1879 if (c == '\n' && s->prefix)
1880 state_puts (s->prefix, s);
1882 fgetconv_close (&fc);
1885 if (s->flags & M_DISPLAY) {
1886 state_putc ('\n', s);
1888 state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
1889 else if (pgp_keyblock)
1890 state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
1892 state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
1900 /* XXX - we may wish to recode here */
1902 state_puts (s->prefix, s);
1903 state_puts (buf, s);
1907 m->goodsig = (maybe_goodsig && have_any_sigs);
1909 if (needpass == -1) {
1910 state_attach_puts (_("[-- Error: could not find beginning"
1911 " of PGP message! --]\n\n"), s);
1917 /* MIME handler for pgp/mime encrypted messages. */
1918 int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
1920 char tempfile[_POSIX_PATH_MAX];
1923 BODY *orig_body = a;
1928 if (!a || a->type != TYPEAPPLICATION || !a->subtype
1929 || ascii_strcasecmp ("pgp-encrypted", a->subtype)
1930 || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
1931 || ascii_strcasecmp ("octet-stream", a->next->subtype)) {
1932 if (s->flags & M_DISPLAY)
1933 state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
1938 /* Move forward to the application/pgp-encrypted body. */
1941 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
1943 if (s->flags & M_DISPLAY)
1944 state_attach_puts (_("[-- Error: could not create temporary file! "
1949 tattach = decrypt_part (a, s, fpout, 0, &is_signed);
1951 tattach->goodsig = is_signed > 0;
1953 if (s->flags & M_DISPLAY)
1954 state_attach_puts (is_signed ?
1956 ("[-- The following data is PGP/MIME signed and encrypted --]\n\n") :
1957 _("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
1960 FILE *savefp = s->fpin;
1963 rc = mutt_body_handler (tattach, s);
1968 * if a multipart/signed is the _only_ sub-part of a
1969 * multipart/encrypted, cache signature verification
1972 if (mutt_is_multipart_signed (tattach) && !tattach->next)
1973 orig_body->goodsig |= tattach->goodsig;
1975 if (s->flags & M_DISPLAY) {
1976 state_puts ("\n", s);
1977 state_attach_puts (is_signed ?
1979 ("[-- End of PGP/MIME signed and encrypted data --]\n")
1980 : _("[-- End of PGP/MIME encrypted data --]\n"), s);
1983 body_list_wipe(&tattach);
1987 mutt_unlink (tempfile);
1991 /* Support for application/smime */
1992 int crypt_smime_application_smime_handler (BODY * a, STATE * s)
1994 char tempfile[_POSIX_PATH_MAX];
2001 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
2003 if (s->flags & M_DISPLAY)
2004 state_attach_puts (_("[-- Error: could not create temporary file! "
2009 tattach = decrypt_part (a, s, fpout, 1, &is_signed);
2011 tattach->goodsig = is_signed > 0;
2013 if (s->flags & M_DISPLAY)
2014 state_attach_puts (is_signed ?
2015 _("[-- The following data is S/MIME signed --]\n\n") :
2016 _("[-- The following data is S/MIME encrypted --]\n\n"), s);
2019 FILE *savefp = s->fpin;
2022 rc = mutt_body_handler (tattach, s);
2027 * if a multipart/signed is the _only_ sub-part of a
2028 * multipart/encrypted, cache signature verification
2031 if (mutt_is_multipart_signed (tattach) && !tattach->next) {
2032 if (!(a->goodsig = tattach->goodsig))
2033 a->warnsig = tattach->warnsig;
2035 else if (tattach->goodsig) {
2037 a->warnsig = tattach->warnsig;
2040 if (s->flags & M_DISPLAY) {
2041 state_puts ("\n", s);
2042 state_attach_puts (is_signed ?
2043 _("[-- End of S/MIME signed data --]\n") :
2044 _("[-- End of S/MIME encrypted data --]\n"), s);
2047 body_list_wipe(&tattach);
2051 mutt_unlink (tempfile);
2057 * Format an entry on the CRYPT key selection menu.
2060 * %k key id %K key id of the principal key
2062 * %a algorithm %A algorithm of the princ. key
2063 * %l length %L length of the princ. key
2064 * %f flags %F flags of the princ. key
2065 * %c capabilities %C capabilities of the princ. key
2066 * %t trust/validity of the key-uid association
2068 * %[...] date of key using strftime(3)
2072 crypt_entry_fmt (char *dest, ssize_t destlen, char op,
2073 const char *src, const char *prefix,
2074 const char *ifstr, const char *elstr,
2075 anytype data, format_flag flags)
2078 crypt_entry_t *entry;
2081 int optional = (flags & M_FORMAT_OPTIONAL);
2082 const char *s = NULL;
2088 /* if (isupper ((unsigned char) op)) */
2091 kflags = (key->flags /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
2094 switch (ascii_tolower (op)) {
2098 char buf2[STRING], *p;
2114 while (len > 0 && *cp != ']') {
2123 break; /* not enough space */
2133 if (do_locales && Locale)
2134 setlocale (LC_TIME, Locale);
2139 if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
2140 tt = key->kobj->subkeys->timestamp;
2142 tm = localtime (&tt);
2144 strftime (buf2, sizeof (buf2), dest, tm);
2147 setlocale (LC_TIME, "C");
2149 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2150 snprintf (dest, destlen, fmt, buf2);
2157 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
2158 snprintf (dest, destlen, fmt, entry->num);
2163 /* fixme: we need a way to distinguish between main and subkeys.
2164 Store the idx in entry? */
2165 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2166 snprintf (dest, destlen, fmt, crypt_keyid (key));
2171 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2172 snprintf (dest, destlen, fmt, key->uid);
2177 snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
2178 if (key->kobj->subkeys)
2179 s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
2182 snprintf (dest, destlen, fmt, s);
2187 snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
2188 if (key->kobj->subkeys)
2189 val = key->kobj->subkeys->length;
2192 snprintf (dest, destlen, fmt, val);
2197 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2198 snprintf (dest, destlen, fmt, crypt_flags (kflags));
2200 else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
2205 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2206 snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
2208 else if (!(kflags & (KEYFLAG_ABILITIES)))
2212 if ((kflags & KEYFLAG_ISX509))
2215 gpgme_user_id_t uid = NULL;
2218 for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
2219 i++, uid = uid->next);
2221 switch (uid->validity) {
2222 case GPGME_VALIDITY_UNDEFINED:
2225 case GPGME_VALIDITY_NEVER:
2228 case GPGME_VALIDITY_MARGINAL:
2231 case GPGME_VALIDITY_FULL:
2234 case GPGME_VALIDITY_ULTIMATE:
2237 case GPGME_VALIDITY_UNKNOWN:
2243 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2244 snprintf (dest, destlen, fmt, s ? *s : 'B');
2247 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2248 snprintf (dest, destlen, fmt,
2249 gpgme_get_protocol_name (key->kobj->protocol));
2256 if (flags & M_FORMAT_OPTIONAL)
2257 m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
2258 mutt_attach_fmt, data, 0);
2262 /* Used by the display fucntion to format a line. */
2263 static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
2265 cryptkey_t **cryptkey_table = (cryptkey_t **) menu->data;
2266 crypt_entry_t entry;
2268 entry.key = cryptkey_table[num];
2269 entry.num = num + 1;
2271 m_strformat(s, l, getmaxx(main_w), mod_crypt.pgp_entry_format,
2272 crypt_entry_fmt, &entry, 0);
2275 /* Compare two addresses and the keyid to be used for sorting. */
2276 static int _crypt_compare_address (const void *a, const void *b)
2278 cryptkey_t **s = (cryptkey_t **) a;
2279 cryptkey_t **t = (cryptkey_t **) b;
2282 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2285 return m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t)) > 0;
2288 static int crypt_compare_address (const void *a, const void *b)
2290 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_address (a, b)
2291 : _crypt_compare_address (a, b));
2295 /* Compare two key IDs and the addresses to be used for sorting. */
2296 static int _crypt_compare_keyid (const void *a, const void *b)
2298 cryptkey_t **s = (cryptkey_t **) a;
2299 cryptkey_t **t = (cryptkey_t **) b;
2302 if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
2305 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2308 static int crypt_compare_keyid (const void *a, const void *b)
2310 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
2311 : _crypt_compare_keyid (a, b));
2314 /* Compare 2 creation dates and the addresses. For sorting. */
2315 static int _crypt_compare_date (const void *a, const void *b)
2317 cryptkey_t **s = (cryptkey_t **) a;
2318 cryptkey_t **t = (cryptkey_t **) b;
2319 unsigned long ts = 0, tt = 0;
2321 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2322 ts = (*s)->kobj->subkeys->timestamp;
2323 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2324 tt = (*t)->kobj->subkeys->timestamp;
2331 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2334 static int crypt_compare_date (const void *a, const void *b)
2336 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
2337 : _crypt_compare_date (a, b));
2340 /* Compare two trust values, the key length, the creation dates. the
2341 addresses and the key IDs. For sorting. */
2342 static int _crypt_compare_trust (const void *a, const void *b)
2344 cryptkey_t **s = (cryptkey_t **) a;
2345 cryptkey_t **t = (cryptkey_t **) b;
2346 unsigned long ts = 0, tt = 0;
2349 if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
2350 - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
2353 if ((*s)->kobj->uids)
2354 ts = (*s)->kobj->uids->validity;
2355 if ((*t)->kobj->uids)
2356 tt = (*t)->kobj->uids->validity;
2357 if ((r = (tt - ts)))
2360 if ((*s)->kobj->subkeys)
2361 ts = (*s)->kobj->subkeys->length;
2362 if ((*t)->kobj->subkeys)
2363 tt = (*t)->kobj->subkeys->length;
2367 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2368 ts = (*s)->kobj->subkeys->timestamp;
2369 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2370 tt = (*t)->kobj->subkeys->timestamp;
2376 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2378 return (m_strcasecmp(crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
2381 static int crypt_compare_trust (const void *a, const void *b)
2383 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
2384 : _crypt_compare_trust (a, b));
2387 /* Print the X.500 Distinguished Name part KEY from the array of parts
2389 static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
2393 for (; dn->key; dn++) {
2394 if (!m_strcmp(dn->key, key)) {
2397 print_utf8 (fp, dn->value, m_strlen(dn->value));
2404 /* Print all parts of a DN in a standard sequence. */
2405 static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
2407 const char *stdpart[] = {
2408 "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
2410 int any = 0, any2 = 0, i;
2412 for (i = 0; stdpart[i]; i++) {
2415 any = print_dn_part (fp, dn, stdpart[i]);
2417 /* now print the rest without any specific ordering */
2418 for (; dn->key; dn++) {
2419 for (i = 0; stdpart[i]; i++) {
2420 if (!m_strcmp(dn->key, stdpart[i]))
2428 any = print_dn_part (fp, dn, dn->key);
2437 /* Parse an RDN; this is a helper to parse_dn(). */
2438 static const unsigned char *parse_dn_part (struct dn_array_s *array,
2439 const unsigned char *string)
2441 const unsigned char *s, *s1;
2445 /* parse attributeType */
2446 for (s = string + 1; *s && *s != '='; s++);
2448 return NULL; /* error */
2451 return NULL; /* empty key */
2452 array->key = p_dupstr(string, n );
2453 p = (unsigned char *) array->key;
2456 if (*string == '#') { /* hexstring */
2458 for (s = string; hexval(*s) >= 0; s++)
2462 return NULL; /* empty or odd number of digits */
2464 p = p_new(unsigned char, n + 1);
2465 array->value = (char *) p;
2466 for (s1 = string; n; s1 += 2, n--)
2467 *p++ = (hexval(*s1) << 8) | hexval(*s1);
2470 else { /* regular v3 quoted string */
2471 for (n = 0, s = string; *s; s++) {
2472 if (*s == '\\') { /* pair */
2474 if (*s == ',' || *s == '=' || *s == '+'
2475 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
2476 || *s == '\\' || *s == '"' || *s == ' ')
2478 else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
2483 return NULL; /* invalid escape sequence */
2486 return NULL; /* invalid encoding */
2487 else if (*s == ',' || *s == '=' || *s == '+'
2488 || *s == '<' || *s == '>' || *s == '#' || *s == ';')
2494 p = p_new(unsigned char, n + 1);
2495 array->value = (char *) p;
2496 for (s = string; n; s++, n--) {
2499 if (hexval(*s) >= 0) {
2500 *p++ = (hexval(*s) << 8) | hexval(*s + 1);
2515 /* Parse a DN and return an array-ized one. This is not a validating
2516 parser and it does not support any old-stylish syntax; gpgme is
2517 expected to return only rfc2253 compatible strings. */
2518 static struct dn_array_s *parse_dn (const unsigned char *string)
2520 struct dn_array_s *array;
2521 ssize_t arrayidx, arraysize;
2524 arraysize = 7; /* C,ST,L,O,OU,CN,email */
2525 array = p_new(struct dn_array_s, arraysize + 1);
2528 while (*string == ' ')
2532 if (arrayidx >= arraysize) { /* mutt lacks a real safe_realoc - so we need to copy */
2533 struct dn_array_s *a2;
2536 a2 = p_new(struct dn_array_s, arraysize + 1);
2537 for (i = 0; i < arrayidx; i++) {
2538 a2[i].key = array[i].key;
2539 a2[i].value = array[i].value;
2544 array[arrayidx].key = NULL;
2545 array[arrayidx].value = NULL;
2546 string = parse_dn_part (array + arrayidx, string);
2550 while (*string == ' ')
2552 if (*string && *string != ',' && *string != ';' && *string != '+')
2553 goto failure; /* invalid delimiter */
2557 array[arrayidx].key = NULL;
2558 array[arrayidx].value = NULL;
2562 for (i = 0; i < arrayidx; i++) {
2563 p_delete(&array[i].key);
2564 p_delete(&array[i].value);
2571 /* Print a nice representation of the USERID and make sure it is
2572 displayed in a proper way, which does mean to reorder some parts
2573 for S/MIME's DNs. USERID is a string as returned by the gpgme key
2574 functions. It is utf-8 encoded. */
2575 static void parse_and_print_user_id(FILE * fp, const char *userid)
2580 if (*userid == '<') {
2581 s = strchr (userid + 1, '>');
2583 print_utf8 (fp, userid + 1, s - userid - 1);
2585 else if (*userid == '(')
2586 fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
2587 else if (*userid & ~127 || __m_strdigits[(int)*userid] == 255)
2588 fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
2590 struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
2593 fputs (_("[Can't display this user ID (invalid DN)]"), fp);
2595 print_dn_parts (fp, dn);
2596 for (i = 0; dn[i].key; i++) {
2597 p_delete(&dn[i].key);
2598 p_delete(&dn[i].value);
2606 KEY_CAP_CAN_ENCRYPT,
2611 static unsigned int key_check_cap(gpgme_key_t key, key_cap_t cap)
2613 gpgme_subkey_t subkey = NULL;
2614 unsigned int ret = 0;
2617 case KEY_CAP_CAN_ENCRYPT:
2618 if (!(ret = key->can_encrypt))
2619 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2620 if ((ret = subkey->can_encrypt))
2623 case KEY_CAP_CAN_SIGN:
2624 if (!(ret = key->can_sign))
2625 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2626 if ((ret = subkey->can_sign))
2629 case KEY_CAP_CAN_CERTIFY:
2630 if (!(ret = key->can_certify))
2631 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2632 if ((ret = subkey->can_certify))
2641 /* Print verbose information about a key or certificate to FP. */
2642 static void print_key_info (gpgme_key_t key, FILE * fp)
2645 const char *s = NULL, *s2 = NULL;
2648 char shortbuf[STRING];
2649 unsigned long aval = 0;
2653 gpgme_user_id_t uid = NULL;
2656 setlocale (LC_TIME, Locale);
2658 is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
2660 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2665 fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
2668 fputs (_("[Invalid]"), fp);
2672 print_utf8 (fp, s, m_strlen(s));
2674 parse_and_print_user_id (fp, s);
2678 if (key->subkeys && (key->subkeys->timestamp > 0)) {
2679 tt = key->subkeys->timestamp;
2681 tm = localtime (&tt);
2683 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2685 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2687 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2690 if (key->subkeys && (key->subkeys->expires > 0)) {
2691 tt = key->subkeys->expires;
2693 tm = localtime (&tt);
2695 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2697 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2699 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2703 s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
2707 s2 = is_pgp ? "PGP" : "X.509";
2710 aval = key->subkeys->length;
2712 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
2714 fprintf (fp, _("Key Usage .: "));
2717 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
2718 fprintf (fp, "%s%s", delim, _("encryption"));
2721 if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
2722 fprintf (fp, "%s%s", delim, _("signing"));
2725 if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
2726 fprintf (fp, "%s%s", delim, _("certification"));
2732 s = key->subkeys->fpr;
2733 fputs (_("Fingerprint: "), fp);
2734 if (is_pgp && m_strlen(s) == 40) {
2735 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
2740 putc (is_pgp ? ' ' : ':', fp);
2741 if (is_pgp && i == 4)
2746 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
2749 putc (is_pgp ? ' ' : ':', fp);
2750 if (is_pgp && i == 7)
2754 fprintf (fp, "%s\n", s);
2757 if (key->issuer_serial) {
2758 s = key->issuer_serial;
2760 fprintf (fp, _("Serial-No .: 0x%s\n"), s);
2763 if (key->issuer_name) {
2764 s = key->issuer_name;
2766 fprintf (fp, _("Issued By .: "));
2767 parse_and_print_user_id (fp, s);
2772 /* For PGP we list all subkeys. */
2774 gpgme_subkey_t subkey = NULL;
2776 for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
2780 if (m_strlen(s) == 16)
2781 s += 8; /* display only the short keyID */
2782 fprintf (fp, _("Subkey ....: 0x%s"), s);
2783 if (subkey->revoked) {
2785 fputs (_("[Revoked]"), fp);
2787 if (subkey->invalid) {
2789 fputs (_("[Invalid]"), fp);
2791 if (subkey->expired) {
2793 fputs (_("[Expired]"), fp);
2795 if (subkey->disabled) {
2797 fputs (_("[Disabled]"), fp);
2801 if (subkey->timestamp > 0) {
2802 tt = subkey->timestamp;
2804 tm = localtime (&tt);
2806 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2808 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2810 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2813 if (subkey->expires > 0) {
2814 tt = subkey->expires;
2816 tm = localtime (&tt);
2818 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2820 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2822 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2826 s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
2831 aval = subkey->length;
2835 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
2837 fprintf (fp, _("Key Usage .: "));
2840 if (subkey->can_encrypt) {
2841 fprintf (fp, "%s%s", delim, _("encryption"));
2844 if (subkey->can_sign) {
2845 fprintf (fp, "%s%s", delim, _("signing"));
2848 if (subkey->can_certify) {
2849 fprintf (fp, "%s%s", delim, _("certification"));
2857 setlocale (LC_TIME, "C");
2861 /* Show detailed information about the selected key */
2862 static void verify_key (cryptkey_t * key)
2865 char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
2867 gpgme_ctx_t listctx = NULL;
2869 gpgme_key_t k = NULL;
2872 fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(mod_core.tmpdir), NULL);
2874 mutt_perror (_("Can't create temporary file"));
2877 mutt_message _("Collecting data...");
2879 print_key_info (key->kobj, fp);
2881 err = gpgme_new (&listctx);
2883 fprintf (fp, "Internal error: can't create gpgme context: %s\n",
2884 gpgme_strerror (err));
2887 if ((key->flags & KEYFLAG_ISX509))
2888 gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
2892 while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
2894 err = gpgme_op_keylist_start (listctx, s, 0);
2898 err = gpgme_op_keylist_next (listctx, &k);
2900 fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
2903 gpgme_op_keylist_end (listctx);
2905 print_key_info (k, fp);
2908 fputs (_("Error: certification chain to long - stopping here\n"), fp);
2915 gpgme_release (listctx);
2917 mutt_clear_error ();
2918 snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
2919 mutt_pager(cmd, tempfile, 0, NULL);
2922 /* Implementation of `findkeys'. */
2924 static void add_hints(string_array *arr, const char *s)
2930 int l = strcspn(s, " ,.:\"()<>\n");
2931 string_array_append(arr, p_dupstr(s, l));
2933 s += strspn(s, " ,.:\"()<>\n");
2937 /* Return a list of keys which are candidates for the selection. */
2939 get_candidates(string_array *hints, unsigned int app, int secret)
2941 cryptkey_t *res = NULL, **kend = &res;
2946 if (hints->len <= 0)
2948 string_array_append(hints, NULL);
2949 ctx = create_gpgme_context(0);
2951 if ((app & APPLICATION_PGP)) {
2952 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2955 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2956 gpgme_strerror(err));
2961 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2962 gpgme_user_id_t uid = NULL;
2963 unsigned int flags = 0;
2966 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
2967 flags |= KEYFLAG_CANENCRYPT;
2968 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
2969 flags |= KEYFLAG_CANSIGN;
2971 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2972 cryptkey_t *k = p_new(cryptkey_t, 1);
2981 if (gpg_err_code(err) != GPG_ERR_EOF)
2982 mutt_error(_("gpgme_op_keylist_next failed: %s"), gpgme_strerror(err));
2983 gpgme_op_keylist_end(ctx);
2986 if ((app & APPLICATION_SMIME)) {
2987 gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
2988 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2991 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2992 gpgme_strerror(err));
2997 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2998 gpgme_user_id_t uid = NULL;
2999 unsigned int flags = KEYFLAG_ISX509;
3002 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
3003 flags |= KEYFLAG_CANENCRYPT;
3004 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
3005 flags |= KEYFLAG_CANSIGN;
3007 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
3008 cryptkey_t *k = p_new(cryptkey_t, 1);
3017 if (gpg_err_code(err) != GPG_ERR_EOF)
3018 mutt_error(_("gpgme_op_keylist_next failed: %s"),
3019 gpgme_strerror(err));
3020 gpgme_op_keylist_end(ctx);
3027 /* Display a menu to select a key from the array KEYS. FORCED_VALID
3028 will be set to true on return if the user did override the the
3030 static cryptkey_t *crypt_select_key (cryptkey_t * keys,
3031 address_t * p, const char *s,
3032 unsigned int app, int *forced_valid)
3035 cryptkey_t **cryptkey_table;
3038 char buf[LONG_STRING];
3040 int (*f) (const void *, const void *);
3041 int menu_to_use = 0;
3046 /* build the key table */
3048 cryptkey_table = NULL;
3049 for (k = keys; k; k = k->next) {
3050 if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
3057 p_realloc(&cryptkey_table, keymax);
3060 cryptkey_table[i++] = k;
3063 if (!i && unusable) {
3064 mutt_error _("All matching keys are marked expired/revoked.");
3070 switch (PgpSortKeys & SORT_MASK) {
3072 f = crypt_compare_date;
3075 f = crypt_compare_keyid;
3078 f = crypt_compare_address;
3082 f = crypt_compare_trust;
3085 qsort (cryptkey_table, i, sizeof (cryptkey_t *), f);
3087 if (app & APPLICATION_PGP)
3088 menu_to_use = MENU_KEY_SELECT_PGP;
3089 else if (app & APPLICATION_SMIME)
3090 menu_to_use = MENU_KEY_SELECT_SMIME;
3092 menu = mutt_new_menu ();
3094 menu->make_entry = crypt_entry;
3095 menu->menu = menu_to_use;
3096 menu->data = cryptkey_table;
3101 if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
3102 ts = _("PGP and S/MIME keys matching");
3103 else if ((app & APPLICATION_PGP))
3104 ts = _("PGP keys matching");
3105 else if ((app & APPLICATION_SMIME))
3106 ts = _("S/MIME keys matching");
3108 ts = _("keys matching");
3111 snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
3113 snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
3117 mutt_clear_error ();
3121 switch (mutt_menuLoop (menu)) {
3123 verify_key (cryptkey_table[menu->current]);
3124 menu->redraw = REDRAW_FULL;
3128 mutt_message ("%s", cryptkey_table[menu->current]->uid);
3131 case OP_GENERIC_SELECT_ENTRY:
3132 /* FIXME make error reporting more verbose - this should be
3133 easy because gpgme provides more information */
3134 if (option (OPTPGPCHECKTRUST)) {
3135 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE ) {
3136 mutt_error(_("This key can't be used: "
3137 "expired/disabled/revoked."));
3142 if (option (OPTPGPCHECKTRUST) &&
3143 ((cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3144 || !crypt_id_is_strong (cryptkey_table[menu->current]))) {
3146 char buff[LONG_STRING];
3148 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3149 s = N_("ID is expired/disabled/revoked.");
3151 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
3152 gpgme_user_id_t uid = NULL;
3157 uid = cryptkey_table[menu->current]->kobj->uids;
3158 for (j = 0; (j < cryptkey_table[menu->current]->idx) && uid;
3159 j++, uid = uid->next);
3161 val = uid->validity;
3164 case GPGME_VALIDITY_UNKNOWN:
3165 case GPGME_VALIDITY_UNDEFINED:
3166 warn_s = N_("ID has undefined validity.");
3168 case GPGME_VALIDITY_NEVER:
3169 warn_s = N_("ID is not valid.");
3171 case GPGME_VALIDITY_MARGINAL:
3172 warn_s = N_("ID is only marginally valid.");
3174 case GPGME_VALIDITY_FULL:
3175 case GPGME_VALIDITY_ULTIMATE:
3179 snprintf (buff, sizeof (buff),
3180 _("%s Do you really want to use the key?"), _(warn_s));
3182 if (mutt_yesorno (buff, 0) != 1) {
3183 mutt_clear_error ();
3190 k = cryptkey_dup(cryptkey_table[menu->current]);
3201 mutt_menuDestroy (&menu);
3202 p_delete(&cryptkey_table);
3204 set_option (OPTNEEDREDRAW);
3210 crypt_getkeybyaddr(address_t * a, int abilities, int app, int *forced_valid)
3217 int this_key_has_strong;
3218 int this_key_has_weak;
3219 int this_key_has_invalid;
3222 cryptkey_t *keys, *k;
3223 cryptkey_t *the_valid_key = NULL;
3224 cryptkey_t *matches = NULL;
3225 cryptkey_t **matches_endp = &matches;
3231 string_array_init(&hints);
3232 add_hints(&hints, a->mailbox);
3233 add_hints(&hints, a->personal);
3235 mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
3236 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3237 string_array_wipe(&hints);
3243 for (k = keys; k; k = k->next) {
3244 if (abilities && !(k->flags & abilities)) {
3248 this_key_has_weak = 0; /* weak but valid match */
3249 this_key_has_invalid = 0; /* invalid match */
3250 this_key_has_strong = 0; /* strong and valid match */
3251 match = 0; /* any match */
3253 r = rfc822_parse_adrlist (NULL, k->uid);
3254 for (p = r; p; p = p->next) {
3255 int validity = crypt_id_matches_addr (a, p, k);
3257 if (validity & CRYPT_KV_MATCH) /* something matches */
3260 /* is this key a strong candidate? */
3261 if ((validity & CRYPT_KV_VALID)
3262 && (validity & CRYPT_KV_STRONGID)
3263 && (validity & CRYPT_KV_ADDR)) {
3264 if (the_valid_key && the_valid_key != k)
3267 this_key_has_strong = 1;
3269 else if ((validity & CRYPT_KV_MATCH)
3270 && !(validity & CRYPT_KV_VALID))
3271 this_key_has_invalid = 1;
3272 else if ((validity & CRYPT_KV_MATCH)
3273 && (!(validity & CRYPT_KV_STRONGID)
3274 || !(validity & CRYPT_KV_ADDR)))
3275 this_key_has_weak = 1;
3277 address_list_wipe(&r);
3282 if (!this_key_has_strong && this_key_has_invalid)
3284 if (!this_key_has_strong && this_key_has_weak)
3287 *matches_endp = tmp = cryptkey_dup(k);
3288 matches_endp = &tmp->next;
3289 the_valid_key = tmp;
3292 key_list_wipe(&keys);
3295 if (the_valid_key && !multi && !weak
3296 && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
3298 * There was precisely one strong match on a valid ID, there
3299 * were no valid keys with weak matches, and we aren't
3300 * interested in seeing invalid keys.
3302 * Proceed without asking the user.
3304 k = cryptkey_dup(the_valid_key);
3307 * Else: Ask the user.
3309 k = crypt_select_key (matches, a, NULL, app, forced_valid);
3311 key_list_wipe(&matches);
3321 crypt_getkeybystr(const char *p, int abilities, int app, int *forced_valid)
3324 cryptkey_t *matches = NULL;
3325 cryptkey_t **matches_endp = &matches;
3329 mutt_message (_("Looking for keys matching \"%s\"..."), p);
3335 string_array_init(&hints);
3336 add_hints(&hints, p);
3337 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3338 string_array_wipe(&hints);
3344 for (k = keys; k; k = k->next) {
3345 const char *s = crypt_keyid(k);
3347 if (abilities && !(k->flags & abilities))
3352 if (!*p || !m_strcasecmp(p, s)
3353 || (!m_strncasecmp(p, "0x", 2) && !m_strcasecmp(p + 2, s))
3354 || m_stristr(k->uid, p))
3358 *matches_endp = tmp = cryptkey_dup(k);
3359 matches_endp = &tmp->next;
3362 key_list_wipe(&keys);
3365 k = crypt_select_key (matches, NULL, p, app, forced_valid);
3366 key_list_wipe(&matches);
3373 /* Display TAG as a prompt to ask for a key.
3374 * ABILITIES describe the required key abilities (sign, encrypt) and APP the
3375 * type of the requested key; ether S/MIME or PGP.
3376 * Return a copy of the key or NULL if not found. */
3378 crypt_ask_for_key(const char *tag, int abilities, int app, int *forced_valid)
3385 forced_valid = &dummy;
3391 if (mutt_get_field(tag, resp, sizeof(resp), M_CLEAR) != 0)
3394 if (m_strisempty(resp))
3397 if ((key = crypt_getkeybystr(resp, abilities, app, forced_valid)))
3404 /* This routine attempts to find the keyids of the recipients of a
3405 message. It returns NULL if any of the keys can not be found. */
3406 static char *find_keys(ENVELOPE *env, unsigned int app)
3408 address_t *lst = NULL, *addr;
3409 buffer_t *keylist = buffer_new();
3412 address_t **last = &lst;
3413 *last = address_list_dup(env->to);
3414 last = address_list_last(last);
3415 *last = address_list_dup(env->cc);
3416 last = address_list_last(last);
3417 *last = address_list_dup(env->bcc);
3419 rfc822_qualify(lst, mutt_fqdn(1));
3420 address_list_uniq(lst);
3423 while ((addr = address_list_pop(&lst))) {
3425 int forced_valid = 0;
3427 cryptkey_t *key = NULL;
3429 if ((keyID = mutt_crypt_hook(addr))) {
3432 snprintf(buf, sizeof(buf), _("Use keyID = \"%s\" for %s?"), keyID,
3434 r = mutt_yesorno(buf, M_YES);
3437 address_list_wipe(&lst);
3438 address_list_wipe(&addr);
3439 buffer_delete(&keylist);
3445 /* check for e-mail address */
3446 if (strchr(keyID, '@') && (a = rfc822_parse_adrlist(NULL, keyID))) {
3447 rfc822_qualify(a, mutt_fqdn(1));
3448 address_list_wipe(&addr);
3451 key = crypt_getkeybystr(keyID, KEYFLAG_CANENCRYPT, app,
3458 key = crypt_getkeybyaddr(addr, KEYFLAG_CANENCRYPT, app, &forced_valid);
3461 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), addr->mailbox);
3462 key = crypt_ask_for_key(buf, KEYFLAG_CANENCRYPT, app,
3465 address_list_wipe(&lst);
3466 address_list_wipe(&addr);
3467 buffer_delete(&keylist);
3473 buffer_addch(keylist, ' ');
3474 buffer_addstr(keylist, "0x");
3475 buffer_addstr(keylist, crypt_fpr(key));
3477 buffer_addch(keylist, '!');
3479 key_list_wipe(&key);
3480 address_list_wipe(&addr);
3483 address_list_wipe(&lst);
3484 return buffer_unwrap(&keylist);
3487 int crypt_get_keys(HEADER *msg, char **keylist)
3489 /* Do a quick check to make sure that we can find all of the encryption
3490 * keys if the user has requested this service.
3495 if (msg->security & ENCRYPT) {
3496 if (msg->security & APPLICATION_PGP) {
3497 set_option(OPTPGPCHECKTRUST);
3498 *keylist = find_keys(msg->env, APPLICATION_PGP);
3499 unset_option(OPTPGPCHECKTRUST);
3504 if (msg->security & APPLICATION_SMIME) {
3505 *keylist = find_keys(msg->env, APPLICATION_SMIME);
3515 int crypt_send_menu (HEADER * msg, int *redraw, int is_smime)
3521 if (msg->security & APPLICATION_SMIME)
3523 if (msg->security & APPLICATION_PGP)
3527 ? mutt_multi_choice(_("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
3529 : mutt_multi_choice(_("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
3533 case 1: /* (e)ncrypt */
3534 msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3535 msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
3538 case 2: /* (s)ign */
3539 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3540 msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3543 case 3: /* sign (a)s */
3544 p = crypt_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN,
3545 is_smime ? APPLICATION_SMIME : APPLICATION_PGP,
3548 snprintf(buf, sizeof(buf), "0x%s", crypt_keyid(p));
3549 m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs, buf);
3551 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3553 *redraw = REDRAW_FULL;
3556 case 4: /* (b)oth */
3558 msg->security = SMIMEENCRYPT | SMIMESIGN;
3560 msg->security = PGPENCRYPT | PGPSIGN;
3564 case 5: /* (p)gp or s/(m)ime */
3565 is_smime = !is_smime;
3568 case 6: /* (c)lear */
3569 return msg->security = 0;
3573 msg->security &= ~APPLICATION_PGP;
3574 msg->security |= APPLICATION_SMIME;
3576 msg->security &= ~APPLICATION_SMIME;
3577 msg->security |= APPLICATION_PGP;
3580 return msg->security;
3583 int crypt_smime_verify_sender(HEADER *h)
3585 address_t *sender = NULL;
3586 unsigned int ret = 1;
3589 h->env->from = mutt_expand_aliases(h->env->from);
3590 sender = h->env->from;
3591 } else if (h->env->sender) {
3592 h->env->sender = mutt_expand_aliases (h->env->sender);
3593 sender = h->env->sender;
3597 mutt_any_key_to_continue ("Failed to figure out sender");
3601 if (signature_key) {
3602 gpgme_key_t key = signature_key;
3603 gpgme_user_id_t uid = NULL;
3604 int sender_length = 0;
3607 sender_length = m_strlen(sender->mailbox);
3608 for (uid = key->uids; uid && ret; uid = uid->next) {
3609 uid_length = m_strlen(uid->email);
3610 if (1 && (uid->email[0] == '<')
3611 && (uid->email[uid_length - 1] == '>')
3612 && (uid_length == sender_length + 2)
3613 && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
3617 mutt_any_key_to_continue ("Failed to verify sender");
3621 if (signature_key) {
3622 gpgme_key_unref(signature_key);
3623 signature_key = NULL;
3628 static void crypt_invoke_import(FILE *stream, int smime)
3630 gpgme_ctx_t ctx = create_gpgme_context(smime);
3634 err = gpgme_data_new_from_stream(&data, stream);
3636 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
3641 err = gpgme_op_import(ctx, data);
3643 mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
3644 gpgme_data_release(data);
3649 gpgme_data_release(data);
3654 static void pgp_extract_keys_from_attachment(FILE * fp, BODY * top)
3657 FILE *tmpfp = tmpfile();
3659 if (tmpfp == NULL) {
3660 mutt_perror (_("Can't create temporary file"));
3667 mutt_body_handler(top, &s);
3670 crypt_invoke_import(tmpfp, 0);
3674 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
3678 for (; top; top = top->next) {
3679 if (!tag || top->tagged)
3680 pgp_extract_keys_from_attachment (fp, top);
3687 void crypt_invoke_message (int type)
3689 if (type & APPLICATION_PGP) {
3690 mutt_message _("Invoking PGP...");
3692 else if (type & APPLICATION_SMIME) {
3693 mutt_message _("Invoking S/MIME...");
3697 int mutt_protect (HEADER * msg, char *keylist)
3699 BODY *pbody = NULL, *tmp_pbody = NULL;
3700 BODY *tmp_smime_pbody = NULL;
3701 BODY *tmp_pgp_pbody = NULL;
3702 int flags = msg->security;
3707 tmp_smime_pbody = msg->content;
3708 tmp_pgp_pbody = msg->content;
3710 if (msg->security & SIGN) {
3711 if (msg->security & APPLICATION_SMIME) {
3712 if (!(tmp_pbody = sign_message(msg->content, 1)))
3714 pbody = tmp_smime_pbody = tmp_pbody;
3717 if ((msg->security & APPLICATION_PGP)
3718 && (!(flags & ENCRYPT) || option (OPTPGPRETAINABLESIG))) {
3719 if (!(tmp_pbody = sign_message(msg->content, 0)))
3723 pbody = tmp_pgp_pbody = tmp_pbody;
3726 if ((msg->security & APPLICATION_SMIME)
3727 && (msg->security & APPLICATION_PGP)) {
3728 /* here comes the draft ;-) */
3733 if (msg->security & ENCRYPT) {
3734 if ((msg->security & APPLICATION_SMIME)) {
3735 if (!(tmp_pbody = crypt_smime_build_smime_entity (tmp_smime_pbody,
3737 /* signed ? free it! */
3740 /* free tmp_body if messages was signed AND encrypted ... */
3741 if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody) {
3742 /* detatch and dont't delete msg->content,
3743 which tmp_smime_pbody->parts after signing. */
3744 tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
3745 msg->content->next = NULL;
3746 body_list_wipe(&tmp_smime_pbody);
3751 if ((msg->security & APPLICATION_PGP)) {
3752 if (!(pbody = crypt_pgp_encrypt_message (tmp_pgp_pbody, keylist,
3755 /* did we perform a retainable signature? */
3756 if (flags != msg->security) {
3757 /* remove the outer multipart layer */
3758 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3759 /* get rid of the signature */
3760 body_list_wipe(&tmp_pgp_pbody->next);
3766 /* destroy temporary signature envelope when doing retainable
3770 if (flags != msg->security) {
3771 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3772 body_list_wipe(&tmp_pgp_pbody->next);
3778 msg->content = pbody;
3784 int crypt_query (BODY * m)
3791 if (m->type == TYPEAPPLICATION) {
3792 t |= mutt_is_application_pgp (m);
3794 t |= mutt_is_application_smime (m);
3795 if (t && m->goodsig)
3800 else if (m->type == TYPETEXT) {
3801 t |= mutt_is_application_pgp (m);
3802 if (t && m->goodsig)
3806 if (m->type == TYPEMULTIPART) {
3807 t |= mutt_is_multipart_encrypted (m);
3808 t |= mutt_is_multipart_signed (m);
3810 if (t && m->goodsig)
3814 if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE) {
3818 u = m->parts ? ~0 : 0; /* Bits set in all parts */
3819 w = 0; /* Bits set in any part */
3821 for (p = m->parts; p; p = p->next) {
3822 v = crypt_query (p);
3826 t |= u | (w & ~GOODSIGN);
3828 if ((w & GOODSIGN) && !(u & GOODSIGN))
3836 static void crypt_write_signed(BODY * a, STATE * s, FILE *fp)
3842 fseeko (s->fpin, a->hdr_offset, 0);
3843 bytes = a->length + a->offset - a->hdr_offset;
3846 if ((c = fgetc (s->fpin)) == EOF)
3854 if (c == '\n' && !hadcr)
3863 static void extract_keys_aux(FILE *fpout, HEADER *h)
3865 mutt_parse_mime_message (Context, h);
3868 if (h->security & APPLICATION_PGP) {
3869 mutt_copy_message(fpout, Context, h, M_CM_DECODE | M_CM_CHARCONV, 0);
3872 mutt_endwin (_("Trying to extract PGP keys...\n"));
3875 if (h->security & APPLICATION_SMIME) {
3876 if (h->security & ENCRYPT)
3877 mutt_copy_message (fpout, Context, h, M_CM_NOHEADER
3878 | M_CM_DECODE_CRYPT | M_CM_DECODE_SMIME, 0);
3880 mutt_copy_message(fpout, Context, h, 0, 0);
3883 mutt_message (_("Trying to extract S/MIME certificates...\n"));