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/curses.h>
20 #include <lib-ui/enter.h>
21 #include <lib-ui/menu.h>
22 #include <lib-mx/mx.h>
29 #include "recvattach.h"
32 /* Values used for comparing addresses. */
33 #define CRYPT_KV_VALID 1
34 #define CRYPT_KV_ADDR 2
35 #define CRYPT_KV_STRING 4
36 #define CRYPT_KV_STRONGID 8
37 #define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
44 /* We work based on user IDs, getting from a user ID to the key is
45 check and does not need any memory (gpgme uses reference counting). */
46 typedef struct cryptkey_t {
47 struct cryptkey_t *next;
48 int idx; /* and the user ID at this index */
49 int flags; /* global and per uid flags (for convenience) */
51 const char *uid; /* and for convenience point to this user ID */
54 DO_INIT(cryptkey_t, cryptkey);
55 static void cryptkey_wipe(cryptkey_t *key) {
56 gpgme_key_release(key->kobj);
58 DO_NEW(cryptkey_t, cryptkey);
59 DO_DELETE(cryptkey_t, cryptkey);
60 DO_SLIST(cryptkey_t, key, cryptkey_delete);
62 static cryptkey_t *cryptkey_dup(const cryptkey_t *k)
64 cryptkey_t *res = cryptkey_new();
67 gpgme_key_ref(k->kobj);
71 typedef struct crypt_entry {
76 static gpgme_key_t signature_key = NULL;
78 static void convert_to_7bit (BODY * a)
80 for (; a; a = a->next) {
81 int tok = mime_which_token(a->subtype, -1);
83 if (a->type == TYPEMULTIPART) {
84 a->encoding = ENC7BIT;
85 convert_to_7bit(a->parts);
86 } else if (a->type == TYPEMESSAGE && tok == MIME_DELIVERY_STATUS) {
87 if (a->encoding != ENC7BIT)
88 mutt_message_to_7bit(a, NULL);
89 } else if (a->encoding == ENC8BIT) {
90 a->encoding = ENCQUOTEDPRINTABLE;
91 } else if (a->encoding == ENCBINARY) {
92 a->encoding = ENCBASE64;
93 } else if (a->content && a->encoding != ENCBASE64
94 && (a->content->from || a->content->space))
96 a->encoding = ENCQUOTEDPRINTABLE;
101 /* Print the utf-8 encoded string BUF of length LEN bytes to stream
102 FP. Convert the character set. */
103 static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
107 tstr = p_dupstr(buf, len);
108 mutt_convert_string(&tstr, "utf-8", MCharset.charset, M_ICONV_HOOK_FROM);
113 /* Return the keyID for the key K. Note that this string is valid as
114 long as K is valid */
115 static const char *crypt_keyid (cryptkey_t * k)
117 if (k->kobj && k->kobj->subkeys) {
118 const char *s = k->kobj->subkeys->keyid;
119 return m_strlen(s) == 16 ? s + 8 : s;
125 /* Return the hexstring fingerprint from the key K. */
126 static const char *crypt_fpr (cryptkey_t * k)
128 return k->kobj && k->kobj->subkeys ? k->kobj->subkeys->fpr : "";
131 /* Parse FLAGS and return a statically allocated(!) string with them. */
132 static char *crypt_key_abilities (int flags)
134 static char buff[3] = "es";
136 if (!(flags & KEYFLAG_CANENCRYPT))
138 else if (flags & KEYFLAG_PREFER_SIGNING)
141 if (!(flags & KEYFLAG_CANSIGN))
143 else if (flags & KEYFLAG_PREFER_ENCRYPTION)
149 /* Parse FLAGS and return a character describing the most important flag. */
150 static char crypt_flags(int flags)
152 if (flags & KEYFLAG_REVOKED)
154 if (flags & KEYFLAG_EXPIRED)
156 if (flags & KEYFLAG_DISABLED)
158 if (flags & KEYFLAG_CRITICAL)
163 /* Return true whe validity of KEY is sufficient. */
164 static int crypt_id_is_strong (cryptkey_t * key)
169 if (key->flags & KEYFLAG_ISX509)
172 for (i = 0, uid = key->kobj->uids; uid; i++, uid = uid->next) {
174 return uid->validity == GPGME_VALIDITY_FULL
175 || uid->validity == GPGME_VALIDITY_ULTIMATE;
182 /* Return a bit vector describing how well the addresses ADDR and
183 U_ADDR match and whether KEY is valid. */
185 crypt_id_matches_addr(address_t *addr, address_t *u_addr, cryptkey_t *key)
189 if (!(key->flags & KEYFLAG_CANTUSE))
190 rv |= CRYPT_KV_VALID;
192 if (crypt_id_is_strong(key))
193 rv |= CRYPT_KV_STRONGID;
195 if (addr->mailbox && !m_strcasecmp(addr->mailbox, u_addr->mailbox))
198 if (addr->personal && m_strcasecmp(addr->personal, u_addr->personal))
199 rv |= CRYPT_KV_STRING;
205 /* Create a new gpgme context and return it. With FOR_SMIME set to
206 true, the protocol of the context is set to CMS. */
207 static gpgme_ctx_t create_gpgme_context(int for_smime)
212 err = gpgme_new (&ctx);
214 mutt_error(_("error creating gpgme context: %s\n"),
215 gpgme_strerror(err));
222 err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
224 mutt_error(_("error enabling CMS protocol: %s\n"), gpgme_strerror(err));
231 /* Create a new gpgme data object. This is a wrapper to die on
233 static gpgme_data_t create_gpgme_data(void)
238 err = gpgme_data_new(&data);
240 mutt_error(_("error creating gpgme data object: %s\n"),
241 gpgme_strerror(err));
248 /* Create a new GPGME Data object from the mail body A. With CONVERT
249 passed as true, the lines are converted to CR,LF if required.
250 Return NULL on error or the gpgme_data_t object on success. */
251 static gpgme_data_t body_to_data_object(BODY *a, int convert)
257 if (!(fptmp = tmpfile())) {
258 mutt_perror (_("Can't create temporary file"));
262 mutt_write_mime_header(a, fptmp);
264 mutt_write_mime_body(a, fptmp);
271 data = create_gpgme_data();
273 while (fgets(buf + spare, sizeof(buf) - 1, fptmp)) {
274 int l = m_strlen(buf);
276 spare = buf[l - 1] != '\n';
277 if (!spare && (l <= 1 || buf[l - 2] != '\r')) {
281 gpgme_data_write(data, buf, l - spare);
286 gpgme_data_write(data, buf, 1);
287 gpgme_data_seek(data, 0, SEEK_SET);
292 err = gpgme_data_new_from_stream(&data, fptmp);
295 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
301 /* Create a GPGME data object from the stream FP but limit the object
302 to LENGTH bytes starting at OFFSET bytes from the beginning of the
304 static gpgme_data_t file_to_data_object(FILE *fp, long offset, long length)
309 err = gpgme_data_new_from_filepart(&data, NULL, fp, offset, length);
311 mutt_error(_("error allocating data object: %s\n"),
312 gpgme_strerror(err));
319 /* Write a GPGME data object to the stream FP. */
320 static int data_object_to_stream(gpgme_data_t data, FILE *fp)
326 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
327 ? gpgme_error_from_errno (errno) : 0);
329 mutt_error (_("error rewinding data object: %s\n"),
330 gpgme_strerror(err));
334 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
335 /* fixme: we are not really converting CRLF to LF but just
336 skipping CR. Doing it correctly needs a more complex logic */
337 for (p = buf; nread; p++, nread--) {
343 mutt_perror ("[tempfile]");
348 mutt_error(_("error reading data object: %s\n"), strerror(errno));
354 /* Copy a data object to a newly created temporay file and return that
355 filename. Caller must free. With RET_FP not NULL, don't close the
356 stream but return it there. */
357 static char *data_object_to_tempfile(gpgme_data_t data, FILE **ret_fp)
360 char tempfile[_POSIX_PATH_MAX];
364 fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
366 mutt_perror (_("Can't create temporary file"));
370 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
371 ? gpgme_error_from_errno (errno) : 0);
375 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
376 if (fwrite (buf, nread, 1, fp) != 1) {
377 mutt_perror (_("Can't create temporary file"));
386 mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
397 return m_strdup(tempfile);
401 /* FIXME: stolen from gpgme to avoid "ambiguous identity" errors */
403 gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
409 if (!ctx || !r_key || !fpr)
410 return gpg_error (GPG_ERR_INV_VALUE);
412 if (strlen (fpr) < 8) /* We have at least a key ID. */
413 return gpg_error (GPG_ERR_INV_VALUE);
415 /* FIXME: We use our own context because we have to avoid the user's
416 I/O callback handlers. */
417 err = gpgme_new (&listctx);
420 gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
421 err = gpgme_op_keylist_start (listctx, fpr, secret);
423 err = gpgme_op_keylist_next (listctx, r_key);
424 gpgme_release (listctx);
428 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
429 The keys must be space delimited. */
430 static gpgme_key_t *create_recipient_set(const char *s, int smime)
432 gpgme_ctx_t ctx = create_gpgme_context(smime);
433 gpgme_key_t *rset = NULL;
440 const char *p = m_strnextsp(s);
443 m_strncpy(buf, sizeof(buf), s, p - s);
444 if (p - s > 1 && p[-1] == '!') {
445 /* user wants to override the valididy of that key. */
447 buf[p - s - 1] = '\0';
448 err = gpgme_get_key2(ctx, buf, &key, 0);
450 key->uids->validity = GPGME_VALIDITY_FULL;
452 err = gpgme_get_key2(ctx, buf, &key, 0);
456 mutt_error(_("error adding recipient `%.*s': %s\n"),
457 (int)(p - s), s, gpgme_strerror(err));
462 p_realloc(&rset, rset_n + 1);
463 rset[rset_n++] = key;
468 /* NULL terminate. */
469 p_realloc(&rset, rset_n + 1);
470 rset[rset_n++] = NULL;
478 /* Make sure that the correct signer is set. Returns 0 on success. */
479 static int set_signer(gpgme_ctx_t ctx, int for_smime)
481 const char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
485 if (m_strisempty(signid))
488 err = gpgme_get_key(ctx, signid, &key, 1);
490 mutt_error(_("error getting secret key `%s': %s\n"), signid,
491 gpgme_strerror(err));
495 gpgme_signers_clear(ctx);
496 err = gpgme_signers_add(ctx, key);
497 gpgme_key_unref(key);
499 mutt_error(_("error setting secret key `%s': %s\n"), signid,
500 gpgme_strerror(err));
507 /* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
508 and return an allocated filename to a temporary file containing the
509 enciphered text. With USE_SMIME set to true, the smime backend is
510 used. With COMBINED_SIGNED a PGP message is signed and
511 encrypted. Returns NULL in case of error */
512 static char *encrypt_gpgme_object(gpgme_data_t plaintext, gpgme_key_t *rset,
513 int use_smime, int combined_signed)
517 gpgme_data_t ciphertext;
520 ctx = create_gpgme_context (use_smime);
522 gpgme_set_armor (ctx, 1);
524 ciphertext = create_gpgme_data ();
526 if (combined_signed) {
527 if (set_signer(ctx, use_smime)) {
528 gpgme_data_release(ciphertext);
532 err = gpgme_op_encrypt_sign(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
533 plaintext, ciphertext);
535 err = gpgme_op_encrypt(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
536 plaintext, ciphertext);
539 mutt_need_hard_redraw();
541 mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
542 gpgme_data_release (ciphertext);
549 outfile = data_object_to_tempfile(ciphertext, NULL);
550 gpgme_data_release(ciphertext);
554 /* Find the "micalg" parameter from the last Gpgme operation on
555 context CTX. It is expected that this operation was a sign
556 operation. Return the algorithm name as a C string in buffer BUF
557 which must have been allocated by the caller with size BUFLEN.
558 Returns 0 on success or -1 in case of an error. The return string
559 is truncted to BUFLEN - 1. */
560 static int get_micalg(gpgme_ctx_t ctx, char *buf, ssize_t buflen)
562 gpgme_sign_result_t result = NULL;
563 const char *algorithm_name = NULL;
566 result = gpgme_op_sign_result(ctx);
568 algorithm_name = gpgme_hash_algo_name (result->signatures->hash_algo);
569 if (algorithm_name) {
570 m_strcpy(buf, buflen, algorithm_name);
574 return *buf ? 0 : -1;
577 static void print_time(time_t t, STATE *s)
581 setlocale(LC_TIME, "");
582 #ifdef HAVE_LANGINFO_D_T_FMT
583 strftime(p, sizeof(p), nl_langinfo(D_T_FMT), localtime(&t));
585 strftime(p, sizeof(p), "%c", localtime(&t));
587 setlocale(LC_TIME, "C");
588 state_attach_puts(p, s);
591 /* Implementation of `sign_message'. */
593 /* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
594 USE_SMIME is passed as true. Returns the new body or NULL on
596 static BODY *sign_message(BODY * a, int use_smime)
603 gpgme_data_t message, signature;
605 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
607 message = body_to_data_object(a, 1);
610 signature = create_gpgme_data ();
612 ctx = create_gpgme_context (use_smime);
614 gpgme_set_armor (ctx, 1);
616 if (set_signer (ctx, use_smime)) {
617 gpgme_data_release (signature);
622 err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH);
623 mutt_need_hard_redraw ();
624 gpgme_data_release (message);
626 gpgme_data_release (signature);
628 mutt_error (_("error signing data: %s\n"), gpgme_strerror (err));
632 sigfile = data_object_to_tempfile(signature, NULL);
633 gpgme_data_release (signature);
640 t->type = TYPEMULTIPART;
641 t->subtype = m_strdup("signed");
642 t->encoding = ENC7BIT;
644 t->disposition = DISPINLINE;
646 parameter_set_boundary(&t->parameter);
647 parameter_setval(&t->parameter, "protocol",
648 use_smime ? "application/pkcs7-signature"
649 : "application/pgp-signature");
650 /* Get the micalg from gpgme. Old gpgme versions don't support this
651 for S/MIME so we assume sha-1 in this case. */
652 if (!get_micalg (ctx, buf, sizeof buf))
653 parameter_setval(&t->parameter, "micalg", buf);
655 parameter_setval(&t->parameter, "micalg", "sha1");
661 t->parts->next = body_new();
663 t->type = TYPEAPPLICATION;
665 t->subtype = m_strdup("pkcs7-signature");
666 parameter_setval(&t->parameter, "name", "smime.p7s");
667 t->encoding = ENCBASE64;
669 t->disposition = DISPATTACH;
670 t->d_filename = m_strdup("smime.p7s");
673 t->subtype = m_strdup("pgp-signature");
675 t->disposition = DISPINLINE;
676 t->encoding = ENC7BIT;
678 t->filename = sigfile;
679 t->unlink = 1; /* ok to remove this file after sending. */
684 /* Encrypt the mail body A to all keys given as space separated keyids
685 or fingerprints in KEYLIST and return the encrypted body. */
686 static BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
688 char *outfile = NULL;
690 gpgme_key_t *rset = NULL;
691 gpgme_data_t plaintext;
693 rset = create_recipient_set(keylist, 0);
699 plaintext = body_to_data_object(a, 0);
705 outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
706 gpgme_data_release (plaintext);
712 t->type = TYPEMULTIPART;
713 t->subtype = m_strdup("encrypted");
714 t->encoding = ENC7BIT;
716 t->disposition = DISPINLINE;
718 parameter_set_boundary(&t->parameter);
719 parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
721 t->parts = body_new();
722 t->parts->type = TYPEAPPLICATION;
723 t->parts->subtype = m_strdup("pgp-encrypted");
724 t->parts->encoding = ENC7BIT;
726 t->parts->next = body_new();
727 t->parts->next->type = TYPEAPPLICATION;
728 t->parts->next->subtype = m_strdup("octet-stream");
729 t->parts->next->encoding = ENC7BIT;
730 t->parts->next->filename = outfile;
731 t->parts->next->use_disp = 1;
732 t->parts->next->disposition = DISPINLINE;
733 t->parts->next->unlink = 1; /* delete after sending the message */
734 t->parts->next->d_filename = m_strdup("msg.asc");
739 /* Encrypt the mail body A to all keys given as space separated
740 fingerprints in KEYLIST and return the S/MIME encrypted body. */
741 static BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
743 char *outfile = NULL;
745 gpgme_key_t *rset = NULL;
746 gpgme_data_t plaintext;
748 rset = create_recipient_set(keylist, 1);
752 plaintext = body_to_data_object(a, 0);
758 outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
759 gpgme_data_release (plaintext);
765 t->type = TYPEAPPLICATION;
766 t->subtype = m_strdup("pkcs7-mime");
767 parameter_setval(&t->parameter, "name", "smime.p7m");
768 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
769 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
771 t->disposition = DISPATTACH;
772 t->d_filename = m_strdup("smime.p7m");
773 t->filename = outfile;
774 t->unlink = 1; /*delete after sending the message */
781 /* Display the common attributes of the signature summary SUM.
782 Return 1 if there is is a severe warning.
784 static int show_sig_summary (unsigned long sum,
785 gpgme_ctx_t ctx, gpgme_key_t key, int idx,
790 if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
791 state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
795 if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
796 time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
799 state_attach_puts (_("Warning: The key used to create the "
800 "signature expired at: "), s);
802 state_attach_puts ("\n", s);
805 state_attach_puts (_("Warning: At least one certification key "
806 "has expired\n"), s);
809 if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
810 gpgme_verify_result_t result;
811 gpgme_signature_t sig;
814 result = gpgme_op_verify_result (ctx);
816 for (sig = result->signatures, i = 0; sig && (i < idx);
817 sig = sig->next, i++);
819 state_attach_puts (_("Warning: The signature expired at: "), s);
820 print_time (sig ? sig->exp_timestamp : 0, s);
821 state_attach_puts ("\n", s);
824 if ((sum & GPGME_SIGSUM_KEY_MISSING))
825 state_attach_puts (_("Can't verify due to a missing "
826 "key or certificate\n"), s);
828 if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
829 state_attach_puts (_("The CRL is not available\n"), s);
833 if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
834 state_attach_puts (_("Available CRL is too old\n"), s);
838 if ((sum & GPGME_SIGSUM_BAD_POLICY))
839 state_attach_puts (_("A policy requirement was not met\n"), s);
841 if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
842 const char *t0 = NULL, *t1 = NULL;
843 gpgme_verify_result_t result;
844 gpgme_signature_t sig;
847 state_attach_puts (_("A system error occurred"), s);
849 /* Try to figure out some more detailed system error information. */
850 result = gpgme_op_verify_result (ctx);
851 for (sig = result->signatures, i = 0; sig && (i < idx);
852 sig = sig->next, i++);
855 t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
859 state_attach_puts (": ", s);
861 state_attach_puts (t0, s);
862 if (t1 && !(t0 && !m_strcmp(t0, t1))) {
864 state_attach_puts (",", s);
865 state_attach_puts (t1, s);
868 state_attach_puts ("\n", s);
875 static void show_fingerprint (gpgme_key_t key, STATE * state)
880 const char *prefix = _("Fingerprint: ");
885 s = key->subkeys ? key->subkeys->fpr : NULL;
888 is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
890 bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
891 buf = p_new(char, bufsize);
892 m_strcpy(buf, bufsize, prefix);
893 p = buf + m_strlen(buf);
894 if (is_pgp && m_strlen(s) == 40) { /* PGP v4 style formatted. */
895 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
906 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
909 *p++ = is_pgp ? ' ' : ':';
910 if (is_pgp && i == 7)
915 /* just in case print remaining odd digits */
920 state_attach_puts (buf, state);
924 /* Show the valididy of a key used for one signature. */
925 static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
927 gpgme_verify_result_t result = NULL;
928 gpgme_signature_t sig = NULL;
929 const char *txt = NULL;
931 result = gpgme_op_verify_result (ctx);
933 for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
935 switch (sig ? sig->validity : 0) {
936 case GPGME_VALIDITY_UNKNOWN:
937 txt = _("WARNING: We have NO indication whether "
938 "the key belongs to the person named " "as shown above\n");
940 case GPGME_VALIDITY_UNDEFINED:
942 case GPGME_VALIDITY_NEVER:
943 txt = _("WARNING: The key does NOT BELONG to "
944 "the person named as shown above\n");
946 case GPGME_VALIDITY_MARGINAL:
947 txt = _("WARNING: It is NOT certain that the key "
948 "belongs to the person named as shown above\n");
950 case GPGME_VALIDITY_FULL:
951 case GPGME_VALIDITY_ULTIMATE:
956 state_attach_puts (txt, s);
959 /* Show information about one signature. This fucntion is called with
960 the context CTX of a sucessful verification operation and the
961 enumerator IDX which should start at 0 and incremete for each
964 Return values are: 0 for normal procession, 1 for a bad signature,
965 2 for a signature with a warning or -1 for no more signature. */
966 static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
969 const char *fpr, *uid;
970 gpgme_key_t key = NULL;
971 int i, anybad = 0, anywarn = 0;
973 gpgme_user_id_t uids = NULL;
974 gpgme_verify_result_t result;
975 gpgme_signature_t sig;
976 gpgme_error_t err = GPG_ERR_NO_ERROR;
978 result = gpgme_op_verify_result (ctx);
980 /* FIXME: this code should use a static variable and remember
981 the current position in the list of signatures, IMHO.
984 for (i = 0, sig = result->signatures; sig && (i < idx);
985 i++, sig = sig->next);
987 return -1; /* Signature not found. */
990 gpgme_key_unref(signature_key);
991 signature_key = NULL;
994 created = sig->timestamp;
998 if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
1001 err = gpgme_get_key2 (ctx, fpr, &key, 0); /* secret key? */
1003 uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
1005 signature_key = key;
1008 key = NULL; /* Old gpgme versions did not set KEY to NULL on
1009 error. Do it here to avoid a double free. */
1013 if (!s || !s->fpout || !(s->flags & M_DISPLAY)); /* No state information so no way to print anything. */
1015 state_attach_puts (_("Error getting key information: "), s);
1016 state_attach_puts (gpg_strerror (err), s);
1017 state_attach_puts ("\n", s);
1020 else if ((sum & GPGME_SIGSUM_GREEN)) {
1021 state_attach_puts (_("Good signature from: "), s);
1022 state_attach_puts (uid, s);
1023 state_attach_puts ("\n", s);
1024 for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
1026 /* Skip primary UID. */
1030 state_attach_puts (_(" aka: "), s);
1031 state_attach_puts (uids->uid, s);
1032 state_attach_puts ("\n", s);
1034 state_attach_puts (_(" created: "), s);
1035 print_time (created, s);
1036 state_attach_puts ("\n", s);
1037 if (show_sig_summary (sum, ctx, key, idx, s))
1039 show_one_sig_validity (ctx, idx, s);
1041 else if ((sum & GPGME_SIGSUM_RED)) {
1042 state_attach_puts (_("*BAD* signature claimed to be from: "), s);
1043 state_attach_puts (uid, s);
1044 state_attach_puts ("\n", s);
1045 show_sig_summary (sum, ctx, key, idx, s);
1047 else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) { /* We can't decide (yellow) but this is a PGP key with a good
1048 signature, so we display what a PGP user expects: The name,
1049 fingerprint and the key validity (which is neither fully or
1051 state_attach_puts (_("Good signature from: "), s);
1052 state_attach_puts (uid, s);
1053 state_attach_puts ("\n", s);
1054 state_attach_puts (_(" created: "), s);
1055 print_time (created, s);
1056 state_attach_puts ("\n", s);
1057 show_one_sig_validity (ctx, idx, s);
1058 show_fingerprint (key, s);
1059 if (show_sig_summary (sum, ctx, key, idx, s))
1062 else { /* can't decide (yellow) */
1064 state_attach_puts (_("Error checking signature"), s);
1065 state_attach_puts ("\n", s);
1066 show_sig_summary (sum, ctx, key, idx, s);
1069 if (key != signature_key)
1070 gpgme_key_unref(key);
1073 return anybad ? 1 : anywarn ? 2 : 0;
1076 /* Do the actual verification step. With IS_SMIME set to true we
1077 assume S/MIME (surprise!) */
1078 static int crypt_verify_one(BODY *sigbdy, STATE *s, FILE *fp, int is_smime)
1084 gpgme_data_t signature, message;
1086 signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
1090 /* We need to tell gpgme about the encoding because the backend can't
1091 auto-detect plain base-64 encoding which is used by S/MIME. */
1093 gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
1095 err = gpgme_data_new_from_stream(&message, fp);
1097 gpgme_data_release (signature);
1098 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
1101 ctx = create_gpgme_context (is_smime);
1103 /* Note: We don't need a current time output because GPGME avoids
1104 such an attack by separating the meta information from the
1106 state_attach_puts (_("[-- Begin signature information --]\n"), s);
1108 err = gpgme_op_verify (ctx, signature, message, NULL);
1109 mutt_need_hard_redraw ();
1113 snprintf (buf, sizeof (buf) - 1,
1114 _("Error: verification failed: %s\n"), gpgme_strerror (err));
1115 state_attach_puts (buf, s);
1117 else { /* Verification succeeded, see what the result is. */
1121 if (signature_key) {
1122 gpgme_key_unref(signature_key);
1123 signature_key = NULL;
1126 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1137 gpgme_verify_result_t result;
1138 gpgme_sig_notation_t notation;
1139 gpgme_signature_t sig;
1141 result = gpgme_op_verify_result (ctx);
1143 for (sig = result->signatures; sig; sig = sig->next) {
1144 if (sig->notations) {
1145 state_attach_puts ("*** Begin Notation (signature by: ", s);
1146 state_attach_puts (sig->fpr, s);
1147 state_attach_puts (") ***\n", s);
1148 for (notation = sig->notations; notation; notation = notation->next)
1150 if (notation->name) {
1151 state_attach_puts (notation->name, s);
1152 state_attach_puts ("=", s);
1154 if (notation->value) {
1155 state_attach_puts (notation->value, s);
1156 if (!(*notation->value
1157 && (notation->value[m_strlen(notation->value) - 1] ==
1159 state_attach_puts ("\n", s);
1162 state_attach_puts ("*** End Notation ***\n", s);
1168 gpgme_release (ctx);
1170 state_attach_puts (_("[-- End signature information --]\n\n"), s);
1172 return badsig ? 1 : anywarn ? 2 : 0;
1175 /* Decrypt a PGP or SMIME message (depending on the boolean flag
1176 IS_SMIME) with body A described further by state S. Write
1177 plaintext out to file FPOUT and return a new body. For PGP returns
1178 a flag in R_IS_SIGNED to indicate whether this is a combined
1179 encrypted and signed message, for S/MIME it returns true when it is
1180 not a encrypted but a signed message. */
1182 decrypt_part(BODY *a, STATE *s, FILE *fpout, int is_smime, int *r_is_signed)
1188 gpgme_data_t ciphertext, plaintext;
1189 int maybe_signed = 0;
1196 ctx = create_gpgme_context (is_smime);
1199 /* Make a data object from the body, create context etc. */
1200 ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
1203 plaintext = create_gpgme_data ();
1205 /* Do the decryption or the verification in case of the S/MIME hack. */
1206 if ((!is_smime) || maybe_signed) {
1208 err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
1209 else if (maybe_signed)
1210 err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
1213 /* Check wether signatures have been verified. */
1214 gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
1216 if (verify_result->signatures)
1221 err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
1222 gpgme_data_release (ciphertext);
1224 if (is_smime && !maybe_signed && gpg_err_code (err) == GPG_ERR_NO_DATA) {
1225 /* Check whether this might be a signed message despite what
1226 the mime header told us. Retry then. gpgsm returns the
1227 error information "unsupported Algorithm '?'" but gpgme
1228 will not store this unknown algorithm, thus we test that
1229 it has not been set. */
1230 gpgme_decrypt_result_t result;
1232 result = gpgme_op_decrypt_result (ctx);
1233 if (!result->unsupported_algorithm) {
1235 gpgme_data_release (plaintext);
1239 mutt_need_hard_redraw ();
1240 if ((s->flags & M_DISPLAY)) {
1243 snprintf (buf, sizeof (buf) - 1,
1244 _("[-- Error: decryption failed: %s --]\n\n"),
1245 gpgme_strerror (err));
1246 state_attach_puts (buf, s);
1248 gpgme_data_release (plaintext);
1249 gpgme_release (ctx);
1252 mutt_need_hard_redraw ();
1254 /* Read the output from GPGME, and make sure to change CRLF to LF,
1255 otherwise read_mime_header has a hard time parsing the message. */
1256 if (data_object_to_stream (plaintext, fpout)) {
1257 gpgme_data_release (plaintext);
1258 gpgme_release (ctx);
1261 gpgme_data_release (plaintext);
1263 a->is_signed_data = 0;
1269 a->is_signed_data = 1;
1271 *r_is_signed = -1; /* A signature exists. */
1273 if ((s->flags & M_DISPLAY))
1274 state_attach_puts (_("[-- Begin signature " "information --]\n"), s);
1275 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1281 if (!anybad && idx && r_is_signed && *r_is_signed)
1282 *r_is_signed = anywarn ? 2 : 1; /* Good signature. */
1284 if ((s->flags & M_DISPLAY))
1285 state_attach_puts (_("[-- End signature " "information --]\n\n"), s);
1287 gpgme_release (ctx);
1292 tattach = mutt_read_mime_header (fpout, 0);
1295 * Need to set the length of this body part.
1297 fstat (fileno (fpout), &info);
1298 tattach->length = info.st_size - tattach->offset;
1300 tattach->warnsig = anywarn;
1302 /* See if we need to recurse on this MIME part. */
1303 mutt_parse_part (fpout, tattach);
1309 /* Decrypt a PGP/MIME message in FPIN and B and return a new body and
1310 the stream in CUR and FPOUT. Returns 0 on success. */
1311 int crypt_pgp_decrypt_mime (FILE * fpin, FILE **fpout, BODY *b, BODY **cur)
1314 BODY *first_part = b;
1317 first_part->goodsig = 0;
1318 first_part->warnsig = 0;
1320 if (!mutt_is_multipart_encrypted(b) || !b->parts || !b->parts->next)
1329 mutt_perror (_("Can't create temporary file"));
1333 *cur = decrypt_part(b, &s, *fpout, 0, &is_signed);
1335 first_part->goodsig = is_signed > 0;
1336 return *cur ? 0 : -1;
1340 /* Decrypt a S/MIME message in FPIN and B and return a new body and
1341 the stream in CUR and FPOUT. Returns 0 on success. */
1342 int crypt_smime_decrypt_mime(FILE *fpin, FILE **fpout, BODY *b, BODY **cur)
1347 long saved_b_offset;
1348 ssize_t saved_b_length;
1351 if (!mutt_is_application_smime (b))
1357 /* Decode the body - we need to pass binary CMS to the
1358 backend. The backend allows for Base64 encoded data but it does
1359 not allow for QP which I have seen in some messages. So better
1361 saved_b_type = b->type;
1362 saved_b_offset = b->offset;
1363 saved_b_length = b->length;
1366 fseeko (s.fpin, b->offset, 0);
1369 mutt_perror (_("Can't create temporary file"));
1374 mutt_decode_attachment (b, &s);
1376 b->length = ftello (s.fpout);
1385 mutt_perror (_("Can't create temporary file"));
1389 *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
1391 (*cur)->goodsig = is_signed > 0;
1392 b->type = saved_b_type;
1393 b->length = saved_b_length;
1394 b->offset = saved_b_offset;
1397 if (*cur && !is_signed && !(*cur)->parts
1398 && mutt_is_application_smime (*cur)) {
1399 /* Assume that this is a opaque signed s/mime message. This is
1400 an ugly way of doing it but we have anyway a problem with
1401 arbitrary encoded S/MIME messages: Only the outer part may be
1402 encrypted. The entire mime parsing should be revamped,
1403 probably by keeping the temportary files so that we don't
1404 need to decrypt them all the time. Inner parts of an
1405 encrypted part can then pint into this file and tehre won't
1406 never be a need to decrypt again. This needs a partial
1407 rewrite of the MIME engine. */
1411 saved_b_type = bb->type;
1412 saved_b_offset = bb->offset;
1413 saved_b_length = bb->length;
1416 fseeko (s.fpin, bb->offset, 0);
1419 mutt_perror (_("Can't create temporary file"));
1424 mutt_decode_attachment (bb, &s);
1426 bb->length = ftello (s.fpout);
1436 mutt_perror (_("Can't create temporary file"));
1440 tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
1442 tmp_b->goodsig = is_signed > 0;
1443 bb->type = saved_b_type;
1444 bb->length = saved_b_length;
1445 bb->offset = saved_b_offset;
1448 body_list_wipe(cur);
1451 return *cur ? 0 : -1;
1456 pgp_check_traditional_one_body(FILE *fp, BODY *b, int tagged_only)
1458 char tempfile[_POSIX_PATH_MAX];
1459 char buf[HUGE_STRING];
1466 if (b->type != TYPETEXT)
1469 if (tagged_only && !b->tagged)
1472 tempfd = m_tempfd(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1473 if (mutt_decode_save_attachment (fp, b, tempfd, 0) != 0) {
1478 if ((tfp = fopen(tempfile, "r")) == NULL) {
1483 while (fgets (buf, sizeof (buf), tfp)) {
1484 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1485 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1487 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15))
1497 /* fix the content type */
1499 parameter_setval(&b->parameter, "format", "fixed");
1500 parameter_setval(&b->parameter, "x-action",
1501 enc ? "pgp-encrypted" : "pgp-signed");
1505 int crypt_pgp_check_traditional(FILE *fp, BODY *b, int tagged_only)
1509 for (; b; b = b->next) {
1510 if (is_multipart(b))
1511 rv |= crypt_pgp_check_traditional(fp, b->parts, tagged_only);
1512 if (b->type == TYPETEXT) {
1514 if ((r = mutt_is_application_pgp(b))) {
1517 rv |= pgp_check_traditional_one_body(fp, b, tagged_only);
1526 Copy a clearsigned message, and strip the signature and PGP's
1529 XXX - charset handling: We assume that it is safe to do
1530 character set decoding first, dash decoding second here, while
1531 we do it the other way around in the main handler.
1533 (Note that we aren't worse than Outlook & Cie in this, and also
1534 note that we can successfully handle anything produced by any
1535 existing versions of mutt.) */
1536 static void copy_clearsigned(gpgme_data_t data, STATE * s, char *charset)
1538 char buf[HUGE_STRING];
1539 short complete, armor_header;
1544 fname = data_object_to_tempfile(data, &fp);
1550 fc = fgetconv_open (fp, charset, MCharset.charset, M_ICONV_HOOK_FROM);
1552 for (complete = 1, armor_header = 1;
1553 fgetconvs (buf, sizeof (buf), fc) != NULL;
1554 complete = strchr (buf, '\n') != NULL) {
1557 state_puts (buf, s);
1561 if (!m_strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
1571 state_puts (s->prefix, s);
1573 if (buf[0] == '-' && buf[1] == ' ')
1574 state_puts (buf + 2, s);
1576 state_puts (buf, s);
1579 fgetconv_close (&fc);
1583 /* Support for classic_application/pgp */
1584 int crypt_pgp_application_pgp_handler(BODY *m, STATE *s)
1586 int needpass = -1, pgp_keyblock = 0;
1590 off_t last_pos, offset;
1591 char buf[HUGE_STRING];
1592 FILE *pgpout = NULL;
1594 gpgme_error_t err = 0;
1595 gpgme_data_t armored_data = NULL;
1597 short maybe_goodsig = 1;
1598 short have_any_sigs = 0;
1600 char body_charset[STRING]; /* Only used for clearsigned messages. */
1602 /* For clearsigned messages we won't be able to get a character set
1603 but we know that this may only be text thus we assume Latin-1
1605 if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
1606 m_strcpy(body_charset, sizeof(body_charset), "iso-8859-1");
1608 fseeko (s->fpin, m->offset, 0);
1609 last_pos = m->offset;
1611 for (bytes = m->length; bytes > 0;) {
1612 if (fgets (buf, sizeof (buf), s->fpin) == NULL)
1615 offset = ftello (s->fpin);
1616 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1619 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1621 start_pos = last_pos;
1623 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1625 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15)) {
1629 else if (!m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
1634 /* XXX - we may wish to recode here */
1636 state_puts (s->prefix, s);
1637 state_puts (buf, s);
1641 have_any_sigs = (have_any_sigs || (clearsign && (s->flags & M_VERIFY)));
1643 /* Copy PGP material to an data container */
1644 armored_data = create_gpgme_data ();
1645 gpgme_data_write (armored_data, buf, m_strlen(buf));
1646 while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) {
1647 offset = ftello (s->fpin);
1648 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1651 gpgme_data_write (armored_data, buf, m_strlen(buf));
1653 if ((needpass && !m_strcmp("-----END PGP MESSAGE-----\n", buf))
1655 && (!m_strcmp("-----END PGP SIGNATURE-----\n", buf)
1656 || !m_strcmp("-----END PGP PUBLIC KEY BLOCK-----\n",
1661 /* Invoke PGP if needed */
1662 if (!clearsign || (s->flags & M_VERIFY)) {
1663 unsigned int sig_stat = 0;
1664 gpgme_data_t plaintext;
1667 plaintext = create_gpgme_data ();
1668 ctx = create_gpgme_context (0);
1671 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1673 err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
1674 if (gpg_err_code (err) == GPG_ERR_NO_DATA) {
1675 /* Decrypt verify can't handle signed only messages. */
1676 err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
1677 ? gpgme_error_from_errno (errno) : 0;
1678 /* Must release plaintext so that we supply an
1679 uninitialized object. */
1680 gpgme_data_release (plaintext);
1681 plaintext = create_gpgme_data ();
1682 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1689 snprintf (errbuf, sizeof (errbuf) - 1,
1690 _("Error: decryption/verification failed: %s\n"),
1691 gpgme_strerror (err));
1692 state_attach_puts (errbuf, s);
1694 else { /* Decryption/Verification succeeded */
1698 /* Check wether signatures have been verified. */
1699 gpgme_verify_result_t verify_result;
1701 verify_result = gpgme_op_verify_result (ctx);
1702 if (verify_result->signatures)
1708 if ((s->flags & M_DISPLAY) && sig_stat) {
1713 state_attach_puts (_("[-- Begin signature "
1714 "information --]\n"), s);
1717 (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1726 state_attach_puts (_("[-- End signature "
1727 "information --]\n\n"), s);
1730 tmpfname = data_object_to_tempfile(plaintext, &pgpout);
1733 state_attach_puts (_("Error: copy data failed\n"), s);
1737 p_delete(&tmpfname);
1740 gpgme_release (ctx);
1744 * Now, copy cleartext to the screen. NOTE - we expect that PGP
1745 * outputs utf-8 cleartext. This may not always be true, but it
1746 * seems to be a reasonable guess.
1749 if (s->flags & M_DISPLAY) {
1751 state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
1752 else if (pgp_keyblock)
1753 state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s);
1755 state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s);
1759 copy_clearsigned (armored_data, s, body_charset);
1766 fc = fgetconv_open (pgpout, "utf-8", MCharset.charset, 0);
1767 while ((c = fgetconv (fc)) != EOF) {
1769 if (c == '\n' && s->prefix)
1770 state_puts (s->prefix, s);
1772 fgetconv_close (&fc);
1775 if (s->flags & M_DISPLAY) {
1776 state_putc ('\n', s);
1778 state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
1779 else if (pgp_keyblock)
1780 state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
1782 state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
1790 /* XXX - we may wish to recode here */
1792 state_puts (s->prefix, s);
1793 state_puts (buf, s);
1797 m->goodsig = (maybe_goodsig && have_any_sigs);
1799 if (needpass == -1) {
1800 state_attach_puts (_("[-- Error: could not find beginning"
1801 " of PGP message! --]\n\n"), s);
1807 /* MIME handler for pgp/mime encrypted messages. */
1808 int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
1810 char tempfile[_POSIX_PATH_MAX];
1813 BODY *orig_body = a;
1818 if (!a || a->type != TYPEAPPLICATION || !a->subtype
1819 || ascii_strcasecmp ("pgp-encrypted", a->subtype)
1820 || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
1821 || ascii_strcasecmp ("octet-stream", a->next->subtype)) {
1822 if (s->flags & M_DISPLAY)
1823 state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
1828 /* Move forward to the application/pgp-encrypted body. */
1831 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1833 if (s->flags & M_DISPLAY)
1834 state_attach_puts (_("[-- Error: could not create temporary file! "
1839 tattach = decrypt_part (a, s, fpout, 0, &is_signed);
1841 tattach->goodsig = is_signed > 0;
1843 if (s->flags & M_DISPLAY)
1844 state_attach_puts (is_signed ?
1846 ("[-- The following data is PGP/MIME signed and encrypted --]\n\n") :
1847 _("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
1850 FILE *savefp = s->fpin;
1853 rc = mutt_body_handler (tattach, s);
1858 * if a multipart/signed is the _only_ sub-part of a
1859 * multipart/encrypted, cache signature verification
1862 if (mutt_is_multipart_signed (tattach) && !tattach->next)
1863 orig_body->goodsig |= tattach->goodsig;
1865 if (s->flags & M_DISPLAY) {
1866 state_puts ("\n", s);
1867 state_attach_puts (is_signed ?
1869 ("[-- End of PGP/MIME signed and encrypted data --]\n")
1870 : _("[-- End of PGP/MIME encrypted data --]\n"), s);
1873 body_list_wipe(&tattach);
1877 mutt_unlink (tempfile);
1881 /* Support for application/smime */
1882 int crypt_smime_application_smime_handler (BODY * a, STATE * s)
1884 char tempfile[_POSIX_PATH_MAX];
1891 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1893 if (s->flags & M_DISPLAY)
1894 state_attach_puts (_("[-- Error: could not create temporary file! "
1899 tattach = decrypt_part (a, s, fpout, 1, &is_signed);
1901 tattach->goodsig = is_signed > 0;
1903 if (s->flags & M_DISPLAY)
1904 state_attach_puts (is_signed ?
1905 _("[-- The following data is S/MIME signed --]\n\n") :
1906 _("[-- The following data is S/MIME encrypted --]\n\n"), s);
1909 FILE *savefp = s->fpin;
1912 rc = mutt_body_handler (tattach, s);
1917 * if a multipart/signed is the _only_ sub-part of a
1918 * multipart/encrypted, cache signature verification
1921 if (mutt_is_multipart_signed (tattach) && !tattach->next) {
1922 if (!(a->goodsig = tattach->goodsig))
1923 a->warnsig = tattach->warnsig;
1925 else if (tattach->goodsig) {
1927 a->warnsig = tattach->warnsig;
1930 if (s->flags & M_DISPLAY) {
1931 state_puts ("\n", s);
1932 state_attach_puts (is_signed ?
1933 _("[-- End of S/MIME signed data --]\n") :
1934 _("[-- End of S/MIME encrypted data --]\n"), s);
1937 body_list_wipe(&tattach);
1941 mutt_unlink (tempfile);
1947 * Format an entry on the CRYPT key selection menu.
1950 * %k key id %K key id of the principal key
1952 * %a algorithm %A algorithm of the princ. key
1953 * %l length %L length of the princ. key
1954 * %f flags %F flags of the princ. key
1955 * %c capabilities %C capabilities of the princ. key
1956 * %t trust/validity of the key-uid association
1958 * %[...] date of key using strftime(3)
1962 crypt_entry_fmt (char *dest, ssize_t destlen, char op,
1963 const char *src, const char *prefix,
1964 const char *ifstr, const char *elstr,
1965 anytype data, format_flag flags)
1968 crypt_entry_t *entry;
1971 int optional = (flags & M_FORMAT_OPTIONAL);
1972 const char *s = NULL;
1978 /* if (isupper ((unsigned char) op)) */
1981 kflags = (key->flags /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
1984 switch (ascii_tolower (op)) {
1988 char buf2[STRING], *p;
2004 while (len > 0 && *cp != ']') {
2013 break; /* not enough space */
2023 if (do_locales && Locale)
2024 setlocale (LC_TIME, Locale);
2029 if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
2030 tt = key->kobj->subkeys->timestamp;
2032 tm = localtime (&tt);
2034 strftime (buf2, sizeof (buf2), dest, tm);
2037 setlocale (LC_TIME, "C");
2039 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2040 snprintf (dest, destlen, fmt, buf2);
2047 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
2048 snprintf (dest, destlen, fmt, entry->num);
2053 /* fixme: we need a way to distinguish between main and subkeys.
2054 Store the idx in entry? */
2055 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2056 snprintf (dest, destlen, fmt, crypt_keyid (key));
2061 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2062 snprintf (dest, destlen, fmt, key->uid);
2067 snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
2068 if (key->kobj->subkeys)
2069 s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
2072 snprintf (dest, destlen, fmt, s);
2077 snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
2078 if (key->kobj->subkeys)
2079 val = key->kobj->subkeys->length;
2082 snprintf (dest, destlen, fmt, val);
2087 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2088 snprintf (dest, destlen, fmt, crypt_flags (kflags));
2090 else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
2095 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2096 snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
2098 else if (!(kflags & (KEYFLAG_ABILITIES)))
2102 if ((kflags & KEYFLAG_ISX509))
2105 gpgme_user_id_t uid = NULL;
2108 for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
2109 i++, uid = uid->next);
2111 switch (uid->validity) {
2112 case GPGME_VALIDITY_UNDEFINED:
2115 case GPGME_VALIDITY_NEVER:
2118 case GPGME_VALIDITY_MARGINAL:
2121 case GPGME_VALIDITY_FULL:
2124 case GPGME_VALIDITY_ULTIMATE:
2127 case GPGME_VALIDITY_UNKNOWN:
2133 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2134 snprintf (dest, destlen, fmt, s ? *s : 'B');
2137 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2138 snprintf (dest, destlen, fmt,
2139 gpgme_get_protocol_name (key->kobj->protocol));
2146 if (flags & M_FORMAT_OPTIONAL)
2147 m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
2148 mutt_attach_fmt, data, 0);
2152 /* Used by the display fucntion to format a line. */
2153 static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
2155 cryptkey_t **cryptkey_table = (cryptkey_t **) menu->data;
2156 crypt_entry_t entry;
2158 entry.key = cryptkey_table[num];
2159 entry.num = num + 1;
2161 m_strformat(s, l, COLS - SW, PgpEntryFormat, crypt_entry_fmt, &entry,
2162 option(OPTARROWCURSOR) ? M_FORMAT_ARROWCURSOR : 0);
2165 /* Compare two addresses and the keyid to be used for sorting. */
2166 static int _crypt_compare_address (const void *a, const void *b)
2168 cryptkey_t **s = (cryptkey_t **) a;
2169 cryptkey_t **t = (cryptkey_t **) b;
2172 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2175 return m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t)) > 0;
2178 static int crypt_compare_address (const void *a, const void *b)
2180 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_address (a, b)
2181 : _crypt_compare_address (a, b));
2185 /* Compare two key IDs and the addresses to be used for sorting. */
2186 static int _crypt_compare_keyid (const void *a, const void *b)
2188 cryptkey_t **s = (cryptkey_t **) a;
2189 cryptkey_t **t = (cryptkey_t **) b;
2192 if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
2195 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2198 static int crypt_compare_keyid (const void *a, const void *b)
2200 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
2201 : _crypt_compare_keyid (a, b));
2204 /* Compare 2 creation dates and the addresses. For sorting. */
2205 static int _crypt_compare_date (const void *a, const void *b)
2207 cryptkey_t **s = (cryptkey_t **) a;
2208 cryptkey_t **t = (cryptkey_t **) b;
2209 unsigned long ts = 0, tt = 0;
2211 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2212 ts = (*s)->kobj->subkeys->timestamp;
2213 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2214 tt = (*t)->kobj->subkeys->timestamp;
2221 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2224 static int crypt_compare_date (const void *a, const void *b)
2226 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
2227 : _crypt_compare_date (a, b));
2230 /* Compare two trust values, the key length, the creation dates. the
2231 addresses and the key IDs. For sorting. */
2232 static int _crypt_compare_trust (const void *a, const void *b)
2234 cryptkey_t **s = (cryptkey_t **) a;
2235 cryptkey_t **t = (cryptkey_t **) b;
2236 unsigned long ts = 0, tt = 0;
2239 if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
2240 - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
2243 if ((*s)->kobj->uids)
2244 ts = (*s)->kobj->uids->validity;
2245 if ((*t)->kobj->uids)
2246 tt = (*t)->kobj->uids->validity;
2247 if ((r = (tt - ts)))
2250 if ((*s)->kobj->subkeys)
2251 ts = (*s)->kobj->subkeys->length;
2252 if ((*t)->kobj->subkeys)
2253 tt = (*t)->kobj->subkeys->length;
2257 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2258 ts = (*s)->kobj->subkeys->timestamp;
2259 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2260 tt = (*t)->kobj->subkeys->timestamp;
2266 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2268 return (m_strcasecmp(crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
2271 static int crypt_compare_trust (const void *a, const void *b)
2273 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
2274 : _crypt_compare_trust (a, b));
2277 /* Print the X.500 Distinguished Name part KEY from the array of parts
2279 static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
2283 for (; dn->key; dn++) {
2284 if (!m_strcmp(dn->key, key)) {
2287 print_utf8 (fp, dn->value, m_strlen(dn->value));
2294 /* Print all parts of a DN in a standard sequence. */
2295 static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
2297 const char *stdpart[] = {
2298 "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
2300 int any = 0, any2 = 0, i;
2302 for (i = 0; stdpart[i]; i++) {
2305 any = print_dn_part (fp, dn, stdpart[i]);
2307 /* now print the rest without any specific ordering */
2308 for (; dn->key; dn++) {
2309 for (i = 0; stdpart[i]; i++) {
2310 if (!m_strcmp(dn->key, stdpart[i]))
2318 any = print_dn_part (fp, dn, dn->key);
2327 /* Parse an RDN; this is a helper to parse_dn(). */
2328 static const unsigned char *parse_dn_part (struct dn_array_s *array,
2329 const unsigned char *string)
2331 const unsigned char *s, *s1;
2335 /* parse attributeType */
2336 for (s = string + 1; *s && *s != '='; s++);
2338 return NULL; /* error */
2341 return NULL; /* empty key */
2342 array->key = p_dupstr(string, n );
2343 p = (unsigned char *) array->key;
2346 if (*string == '#') { /* hexstring */
2348 for (s = string; hexval(*s) >= 0; s++)
2352 return NULL; /* empty or odd number of digits */
2354 p = p_new(unsigned char, n + 1);
2355 array->value = (char *) p;
2356 for (s1 = string; n; s1 += 2, n--)
2357 *p++ = (hexval(*s1) << 8) | hexval(*s1);
2360 else { /* regular v3 quoted string */
2361 for (n = 0, s = string; *s; s++) {
2362 if (*s == '\\') { /* pair */
2364 if (*s == ',' || *s == '=' || *s == '+'
2365 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
2366 || *s == '\\' || *s == '\"' || *s == ' ')
2368 else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
2373 return NULL; /* invalid escape sequence */
2375 else if (*s == '\"')
2376 return NULL; /* invalid encoding */
2377 else if (*s == ',' || *s == '=' || *s == '+'
2378 || *s == '<' || *s == '>' || *s == '#' || *s == ';')
2384 p = p_new(unsigned char, n + 1);
2385 array->value = (char *) p;
2386 for (s = string; n; s++, n--) {
2389 if (hexval(*s) >= 0) {
2390 *p++ = (hexval(*s) << 8) | hexval(*s + 1);
2405 /* Parse a DN and return an array-ized one. This is not a validating
2406 parser and it does not support any old-stylish syntax; gpgme is
2407 expected to return only rfc2253 compatible strings. */
2408 static struct dn_array_s *parse_dn (const unsigned char *string)
2410 struct dn_array_s *array;
2411 ssize_t arrayidx, arraysize;
2414 arraysize = 7; /* C,ST,L,O,OU,CN,email */
2415 array = p_new(struct dn_array_s, arraysize + 1);
2418 while (*string == ' ')
2422 if (arrayidx >= arraysize) { /* mutt lacks a real safe_realoc - so we need to copy */
2423 struct dn_array_s *a2;
2426 a2 = p_new(struct dn_array_s, arraysize + 1);
2427 for (i = 0; i < arrayidx; i++) {
2428 a2[i].key = array[i].key;
2429 a2[i].value = array[i].value;
2434 array[arrayidx].key = NULL;
2435 array[arrayidx].value = NULL;
2436 string = parse_dn_part (array + arrayidx, string);
2440 while (*string == ' ')
2442 if (*string && *string != ',' && *string != ';' && *string != '+')
2443 goto failure; /* invalid delimiter */
2447 array[arrayidx].key = NULL;
2448 array[arrayidx].value = NULL;
2452 for (i = 0; i < arrayidx; i++) {
2453 p_delete(&array[i].key);
2454 p_delete(&array[i].value);
2461 /* Print a nice representation of the USERID and make sure it is
2462 displayed in a proper way, which does mean to reorder some parts
2463 for S/MIME's DNs. USERID is a string as returned by the gpgme key
2464 functions. It is utf-8 encoded. */
2465 static void parse_and_print_user_id(FILE * fp, const char *userid)
2470 if (*userid == '<') {
2471 s = strchr (userid + 1, '>');
2473 print_utf8 (fp, userid + 1, s - userid - 1);
2475 else if (*userid == '(')
2476 fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
2477 else if (*userid & ~127 || __m_strdigits[(int)*userid] == 255)
2478 fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
2480 struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
2483 fputs (_("[Can't display this user ID (invalid DN)]"), fp);
2485 print_dn_parts (fp, dn);
2486 for (i = 0; dn[i].key; i++) {
2487 p_delete(&dn[i].key);
2488 p_delete(&dn[i].value);
2496 KEY_CAP_CAN_ENCRYPT,
2501 static unsigned int key_check_cap(gpgme_key_t key, key_cap_t cap)
2503 gpgme_subkey_t subkey = NULL;
2504 unsigned int ret = 0;
2507 case KEY_CAP_CAN_ENCRYPT:
2508 if (!(ret = key->can_encrypt))
2509 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2510 if ((ret = subkey->can_encrypt))
2513 case KEY_CAP_CAN_SIGN:
2514 if (!(ret = key->can_sign))
2515 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2516 if ((ret = subkey->can_sign))
2519 case KEY_CAP_CAN_CERTIFY:
2520 if (!(ret = key->can_certify))
2521 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2522 if ((ret = subkey->can_certify))
2531 /* Print verbose information about a key or certificate to FP. */
2532 static void print_key_info (gpgme_key_t key, FILE * fp)
2535 const char *s = NULL, *s2 = NULL;
2538 char shortbuf[STRING];
2539 unsigned long aval = 0;
2543 gpgme_user_id_t uid = NULL;
2546 setlocale (LC_TIME, Locale);
2548 is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
2550 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2555 fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
2558 fputs (_("[Invalid]"), fp);
2562 print_utf8 (fp, s, m_strlen(s));
2564 parse_and_print_user_id (fp, s);
2568 if (key->subkeys && (key->subkeys->timestamp > 0)) {
2569 tt = key->subkeys->timestamp;
2571 tm = localtime (&tt);
2572 #ifdef HAVE_LANGINFO_D_T_FMT
2573 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2575 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2577 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2580 if (key->subkeys && (key->subkeys->expires > 0)) {
2581 tt = key->subkeys->expires;
2583 tm = localtime (&tt);
2584 #ifdef HAVE_LANGINFO_D_T_FMT
2585 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2587 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2589 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2593 s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
2597 s2 = is_pgp ? "PGP" : "X.509";
2600 aval = key->subkeys->length;
2602 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
2604 fprintf (fp, _("Key Usage .: "));
2607 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
2608 fprintf (fp, "%s%s", delim, _("encryption"));
2611 if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
2612 fprintf (fp, "%s%s", delim, _("signing"));
2615 if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
2616 fprintf (fp, "%s%s", delim, _("certification"));
2622 s = key->subkeys->fpr;
2623 fputs (_("Fingerprint: "), fp);
2624 if (is_pgp && m_strlen(s) == 40) {
2625 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
2630 putc (is_pgp ? ' ' : ':', fp);
2631 if (is_pgp && i == 4)
2636 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
2639 putc (is_pgp ? ' ' : ':', fp);
2640 if (is_pgp && i == 7)
2644 fprintf (fp, "%s\n", s);
2647 if (key->issuer_serial) {
2648 s = key->issuer_serial;
2650 fprintf (fp, _("Serial-No .: 0x%s\n"), s);
2653 if (key->issuer_name) {
2654 s = key->issuer_name;
2656 fprintf (fp, _("Issued By .: "));
2657 parse_and_print_user_id (fp, s);
2662 /* For PGP we list all subkeys. */
2664 gpgme_subkey_t subkey = NULL;
2666 for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
2670 if (m_strlen(s) == 16)
2671 s += 8; /* display only the short keyID */
2672 fprintf (fp, _("Subkey ....: 0x%s"), s);
2673 if (subkey->revoked) {
2675 fputs (_("[Revoked]"), fp);
2677 if (subkey->invalid) {
2679 fputs (_("[Invalid]"), fp);
2681 if (subkey->expired) {
2683 fputs (_("[Expired]"), fp);
2685 if (subkey->disabled) {
2687 fputs (_("[Disabled]"), fp);
2691 if (subkey->timestamp > 0) {
2692 tt = subkey->timestamp;
2694 tm = localtime (&tt);
2695 #ifdef HAVE_LANGINFO_D_T_FMT
2696 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2698 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2700 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2703 if (subkey->expires > 0) {
2704 tt = subkey->expires;
2706 tm = localtime (&tt);
2707 #ifdef HAVE_LANGINFO_D_T_FMT
2708 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2710 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2712 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2716 s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
2721 aval = subkey->length;
2725 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
2727 fprintf (fp, _("Key Usage .: "));
2730 if (subkey->can_encrypt) {
2731 fprintf (fp, "%s%s", delim, _("encryption"));
2734 if (subkey->can_sign) {
2735 fprintf (fp, "%s%s", delim, _("signing"));
2738 if (subkey->can_certify) {
2739 fprintf (fp, "%s%s", delim, _("certification"));
2747 setlocale (LC_TIME, "C");
2751 /* Show detailed information about the selected key */
2752 static void verify_key (cryptkey_t * key)
2755 char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
2757 gpgme_ctx_t listctx = NULL;
2759 gpgme_key_t k = NULL;
2762 fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2764 mutt_perror (_("Can't create temporary file"));
2767 mutt_message _("Collecting data...");
2769 print_key_info (key->kobj, fp);
2771 err = gpgme_new (&listctx);
2773 fprintf (fp, "Internal error: can't create gpgme context: %s\n",
2774 gpgme_strerror (err));
2777 if ((key->flags & KEYFLAG_ISX509))
2778 gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
2782 while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
2784 err = gpgme_op_keylist_start (listctx, s, 0);
2788 err = gpgme_op_keylist_next (listctx, &k);
2790 fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
2793 gpgme_op_keylist_end (listctx);
2795 print_key_info (k, fp);
2798 fputs (_("Error: certification chain to long - stopping here\n"), fp);
2805 gpgme_release (listctx);
2807 mutt_clear_error ();
2808 snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
2809 mutt_pager(cmd, tempfile, 0, NULL);
2812 /* Implementation of `findkeys'. */
2814 static void add_hints(string_array *arr, const char *s)
2820 int l = strcspn(s, " ,.:\"()<>\n");
2821 string_array_append(arr, p_dupstr(s, l));
2823 s += strspn(s, " ,.:\"()<>\n");
2827 /* Return a list of keys which are candidates for the selection. */
2829 get_candidates(string_array *hints, unsigned int app, int secret)
2831 cryptkey_t *res = NULL, **kend = &res;
2836 if (hints->len <= 0)
2838 string_array_append(hints, NULL);
2839 ctx = create_gpgme_context(0);
2841 if ((app & APPLICATION_PGP)) {
2842 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2845 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2846 gpgme_strerror(err));
2851 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2852 gpgme_user_id_t uid = NULL;
2853 unsigned int flags = 0;
2856 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
2857 flags |= KEYFLAG_CANENCRYPT;
2858 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
2859 flags |= KEYFLAG_CANSIGN;
2861 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2862 cryptkey_t *k = p_new(cryptkey_t, 1);
2871 if (gpg_err_code(err) != GPG_ERR_EOF)
2872 mutt_error(_("gpgme_op_keylist_next failed: %s"), gpgme_strerror(err));
2873 gpgme_op_keylist_end(ctx);
2876 if ((app & APPLICATION_SMIME)) {
2877 gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
2878 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2881 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2882 gpgme_strerror(err));
2887 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2888 gpgme_user_id_t uid = NULL;
2889 unsigned int flags = KEYFLAG_ISX509;
2892 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
2893 flags |= KEYFLAG_CANENCRYPT;
2894 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
2895 flags |= KEYFLAG_CANSIGN;
2897 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2898 cryptkey_t *k = p_new(cryptkey_t, 1);
2907 if (gpg_err_code(err) != GPG_ERR_EOF)
2908 mutt_error(_("gpgme_op_keylist_next failed: %s"),
2909 gpgme_strerror(err));
2910 gpgme_op_keylist_end(ctx);
2917 /* Display a menu to select a key from the array KEYS. FORCED_VALID
2918 will be set to true on return if the user did override the the
2920 static cryptkey_t *crypt_select_key (cryptkey_t * keys,
2921 address_t * p, const char *s,
2922 unsigned int app, int *forced_valid)
2925 cryptkey_t **cryptkey_table;
2928 char helpstr[STRING], buf[LONG_STRING];
2930 int (*f) (const void *, const void *);
2931 int menu_to_use = 0;
2936 /* build the key table */
2938 cryptkey_table = NULL;
2939 for (k = keys; k; k = k->next) {
2940 if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
2947 p_realloc(&cryptkey_table, keymax);
2950 cryptkey_table[i++] = k;
2953 if (!i && unusable) {
2954 mutt_error _("All matching keys are marked expired/revoked.");
2960 switch (PgpSortKeys & SORT_MASK) {
2962 f = crypt_compare_date;
2965 f = crypt_compare_keyid;
2968 f = crypt_compare_address;
2972 f = crypt_compare_trust;
2975 qsort (cryptkey_table, i, sizeof (cryptkey_t *), f);
2977 if (app & APPLICATION_PGP)
2978 menu_to_use = MENU_KEY_SELECT_PGP;
2979 else if (app & APPLICATION_SMIME)
2980 menu_to_use = MENU_KEY_SELECT_SMIME;
2983 mutt_make_help (buf, sizeof (buf), _("Exit "), menu_to_use, OP_EXIT);
2984 m_strcat(helpstr, sizeof(helpstr), buf);
2985 mutt_make_help (buf, sizeof (buf), _("Select "), menu_to_use,
2986 OP_GENERIC_SELECT_ENTRY);
2987 m_strcat(helpstr, sizeof(helpstr), buf);
2988 mutt_make_help (buf, sizeof (buf), _("Check key "),
2989 menu_to_use, OP_VERIFY_KEY);
2990 m_strcat(helpstr, sizeof(helpstr), buf);
2991 mutt_make_help (buf, sizeof (buf), _("Help"), menu_to_use, OP_HELP);
2992 m_strcat(helpstr, sizeof(helpstr), buf);
2994 menu = mutt_new_menu ();
2996 menu->make_entry = crypt_entry;
2997 menu->menu = menu_to_use;
2998 menu->help = helpstr;
2999 menu->data = cryptkey_table;
3004 if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
3005 ts = _("PGP and S/MIME keys matching");
3006 else if ((app & APPLICATION_PGP))
3007 ts = _("PGP keys matching");
3008 else if ((app & APPLICATION_SMIME))
3009 ts = _("S/MIME keys matching");
3011 ts = _("keys matching");
3014 snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
3016 snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
3020 mutt_clear_error ();
3024 switch (mutt_menuLoop (menu)) {
3026 verify_key (cryptkey_table[menu->current]);
3027 menu->redraw = REDRAW_FULL;
3031 mutt_message ("%s", cryptkey_table[menu->current]->uid);
3034 case OP_GENERIC_SELECT_ENTRY:
3035 /* FIXME make error reporting more verbose - this should be
3036 easy because gpgme provides more information */
3037 if (option (OPTPGPCHECKTRUST)) {
3038 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE ) {
3039 mutt_error(_("This key can't be used: "
3040 "expired/disabled/revoked."));
3045 if (option (OPTPGPCHECKTRUST) &&
3046 ((cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3047 || !crypt_id_is_strong (cryptkey_table[menu->current]))) {
3049 char buff[LONG_STRING];
3051 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3052 s = N_("ID is expired/disabled/revoked.");
3054 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
3055 gpgme_user_id_t uid = NULL;
3060 uid = cryptkey_table[menu->current]->kobj->uids;
3061 for (j = 0; (j < cryptkey_table[menu->current]->idx) && uid;
3062 j++, uid = uid->next);
3064 val = uid->validity;
3067 case GPGME_VALIDITY_UNKNOWN:
3068 case GPGME_VALIDITY_UNDEFINED:
3069 warn_s = N_("ID has undefined validity.");
3071 case GPGME_VALIDITY_NEVER:
3072 warn_s = N_("ID is not valid.");
3074 case GPGME_VALIDITY_MARGINAL:
3075 warn_s = N_("ID is only marginally valid.");
3077 case GPGME_VALIDITY_FULL:
3078 case GPGME_VALIDITY_ULTIMATE:
3082 snprintf (buff, sizeof (buff),
3083 _("%s Do you really want to use the key?"), _(warn_s));
3085 if (mutt_yesorno (buff, 0) != 1) {
3086 mutt_clear_error ();
3093 k = cryptkey_dup(cryptkey_table[menu->current]);
3104 mutt_menuDestroy (&menu);
3105 p_delete(&cryptkey_table);
3107 set_option (OPTNEEDREDRAW);
3113 crypt_getkeybyaddr(address_t * a, int abilities, int app, int *forced_valid)
3120 int this_key_has_strong;
3121 int this_key_has_weak;
3122 int this_key_has_invalid;
3125 cryptkey_t *keys, *k;
3126 cryptkey_t *the_valid_key = NULL;
3127 cryptkey_t *matches = NULL;
3128 cryptkey_t **matches_endp = &matches;
3134 string_array_init(&hints);
3135 add_hints(&hints, a->mailbox);
3136 add_hints(&hints, a->personal);
3138 mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
3139 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3140 string_array_wipe(&hints);
3146 for (k = keys; k; k = k->next) {
3147 if (abilities && !(k->flags & abilities)) {
3151 this_key_has_weak = 0; /* weak but valid match */
3152 this_key_has_invalid = 0; /* invalid match */
3153 this_key_has_strong = 0; /* strong and valid match */
3154 match = 0; /* any match */
3156 r = rfc822_parse_adrlist (NULL, k->uid);
3157 for (p = r; p; p = p->next) {
3158 int validity = crypt_id_matches_addr (a, p, k);
3160 if (validity & CRYPT_KV_MATCH) /* something matches */
3163 /* is this key a strong candidate? */
3164 if ((validity & CRYPT_KV_VALID)
3165 && (validity & CRYPT_KV_STRONGID)
3166 && (validity & CRYPT_KV_ADDR)) {
3167 if (the_valid_key && the_valid_key != k)
3170 this_key_has_strong = 1;
3172 else if ((validity & CRYPT_KV_MATCH)
3173 && !(validity & CRYPT_KV_VALID))
3174 this_key_has_invalid = 1;
3175 else if ((validity & CRYPT_KV_MATCH)
3176 && (!(validity & CRYPT_KV_STRONGID)
3177 || !(validity & CRYPT_KV_ADDR)))
3178 this_key_has_weak = 1;
3180 address_list_wipe(&r);
3185 if (!this_key_has_strong && this_key_has_invalid)
3187 if (!this_key_has_strong && this_key_has_weak)
3190 *matches_endp = tmp = cryptkey_dup(k);
3191 matches_endp = &tmp->next;
3192 the_valid_key = tmp;
3195 key_list_wipe(&keys);
3198 if (the_valid_key && !multi && !weak
3199 && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
3201 * There was precisely one strong match on a valid ID, there
3202 * were no valid keys with weak matches, and we aren't
3203 * interested in seeing invalid keys.
3205 * Proceed without asking the user.
3207 k = cryptkey_dup(the_valid_key);
3210 * Else: Ask the user.
3212 k = crypt_select_key (matches, a, NULL, app, forced_valid);
3214 key_list_wipe(&matches);
3224 crypt_getkeybystr(const char *p, int abilities, int app, int *forced_valid)
3227 cryptkey_t *matches = NULL;
3228 cryptkey_t **matches_endp = &matches;
3232 mutt_message (_("Looking for keys matching \"%s\"..."), p);
3238 string_array_init(&hints);
3239 add_hints(&hints, p);
3240 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3241 string_array_wipe(&hints);
3247 for (k = keys; k; k = k->next) {
3248 const char *s = crypt_keyid(k);
3250 if (abilities && !(k->flags & abilities))
3255 if (!*p || !m_strcasecmp(p, s)
3256 || (!m_strncasecmp(p, "0x", 2) && !m_strcasecmp(p + 2, s))
3257 || m_stristr(k->uid, p))
3261 *matches_endp = tmp = cryptkey_dup(k);
3262 matches_endp = &tmp->next;
3265 key_list_wipe(&keys);
3268 k = crypt_select_key (matches, NULL, p, app, forced_valid);
3269 key_list_wipe(&matches);
3276 /* Display TAG as a prompt to ask for a key.
3277 * ABILITIES describe the required key abilities (sign, encrypt) and APP the
3278 * type of the requested key; ether S/MIME or PGP.
3279 * Return a copy of the key or NULL if not found. */
3281 crypt_ask_for_key(const char *tag, int abilities, int app, int *forced_valid)
3288 forced_valid = &dummy;
3294 if (mutt_get_field(tag, resp, sizeof(resp), M_CLEAR) != 0)
3297 if (m_strisempty(resp))
3300 if ((key = crypt_getkeybystr(resp, abilities, app, forced_valid)))
3307 /* This routine attempts to find the keyids of the recipients of a
3308 message. It returns NULL if any of the keys can not be found. */
3309 static char *find_keys(ENVELOPE *env, unsigned int app)
3311 address_t *lst = NULL, *addr;
3312 buffer_t *keylist = buffer_new();
3315 address_t **last = &lst;
3316 *last = address_list_dup(env->to);
3317 last = address_list_last(last);
3318 *last = address_list_dup(env->cc);
3319 last = address_list_last(last);
3320 *last = address_list_dup(env->bcc);
3322 rfc822_qualify(lst, mutt_fqdn(1));
3323 address_list_uniq(lst);
3326 while ((addr = address_list_pop(&lst))) {
3328 int forced_valid = 0;
3330 cryptkey_t *key = NULL;
3332 if ((keyID = mutt_crypt_hook(addr))) {
3335 snprintf(buf, sizeof(buf), _("Use keyID = \"%s\" for %s?"), keyID,
3337 r = mutt_yesorno(buf, M_YES);
3340 address_list_wipe(&lst);
3341 address_list_wipe(&addr);
3342 buffer_delete(&keylist);
3348 /* check for e-mail address */
3349 if (strchr(keyID, '@') && (a = rfc822_parse_adrlist(NULL, keyID))) {
3350 rfc822_qualify(a, mutt_fqdn(1));
3351 address_list_wipe(&addr);
3354 key = crypt_getkeybystr(keyID, KEYFLAG_CANENCRYPT, app,
3361 key = crypt_getkeybyaddr(addr, KEYFLAG_CANENCRYPT, app, &forced_valid);
3364 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), addr->mailbox);
3365 key = crypt_ask_for_key(buf, KEYFLAG_CANENCRYPT, app,
3368 address_list_wipe(&lst);
3369 address_list_wipe(&addr);
3370 buffer_delete(&keylist);
3376 buffer_addch(keylist, ' ');
3377 buffer_addstr(keylist, "0x");
3378 buffer_addstr(keylist, crypt_fpr(key));
3380 buffer_addch(keylist, '!');
3382 key_list_wipe(&key);
3383 address_list_wipe(&addr);
3386 address_list_wipe(&lst);
3387 return buffer_unwrap(&keylist);
3390 int crypt_get_keys(HEADER *msg, char **keylist)
3392 /* Do a quick check to make sure that we can find all of the encryption
3393 * keys if the user has requested this service.
3398 if (msg->security & ENCRYPT) {
3399 if (msg->security & APPLICATION_PGP) {
3400 set_option(OPTPGPCHECKTRUST);
3401 *keylist = find_keys(msg->env, APPLICATION_PGP);
3402 unset_option(OPTPGPCHECKTRUST);
3407 if (msg->security & APPLICATION_SMIME) {
3408 *keylist = find_keys(msg->env, APPLICATION_SMIME);
3418 int crypt_send_menu (HEADER * msg, int *redraw, int is_smime)
3424 if (msg->security & APPLICATION_SMIME)
3426 if (msg->security & APPLICATION_PGP)
3430 ? mutt_multi_choice(_("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
3432 : mutt_multi_choice(_("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
3436 case 1: /* (e)ncrypt */
3437 msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3438 msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
3441 case 2: /* (s)ign */
3442 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3443 msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3446 case 3: /* sign (a)s */
3447 p = crypt_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN,
3448 is_smime ? APPLICATION_SMIME : APPLICATION_PGP,
3451 snprintf(buf, sizeof(buf), "0x%s", crypt_keyid(p));
3452 m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs, buf);
3454 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3456 *redraw = REDRAW_FULL;
3459 case 4: /* (b)oth */
3461 msg->security = SMIMEENCRYPT | SMIMESIGN;
3463 msg->security = PGPENCRYPT | PGPSIGN;
3467 case 5: /* (p)gp or s/(m)ime */
3468 is_smime = !is_smime;
3471 case 6: /* (c)lear */
3472 return msg->security = 0;
3476 msg->security &= ~APPLICATION_PGP;
3477 msg->security |= APPLICATION_SMIME;
3479 msg->security &= ~APPLICATION_SMIME;
3480 msg->security |= APPLICATION_PGP;
3483 return msg->security;
3486 int crypt_smime_verify_sender(HEADER *h)
3488 address_t *sender = NULL;
3489 unsigned int ret = 1;
3492 h->env->from = mutt_expand_aliases(h->env->from);
3493 sender = h->env->from;
3494 } else if (h->env->sender) {
3495 h->env->sender = mutt_expand_aliases (h->env->sender);
3496 sender = h->env->sender;
3500 mutt_any_key_to_continue ("Failed to figure out sender");
3504 if (signature_key) {
3505 gpgme_key_t key = signature_key;
3506 gpgme_user_id_t uid = NULL;
3507 int sender_length = 0;
3510 sender_length = m_strlen(sender->mailbox);
3511 for (uid = key->uids; uid && ret; uid = uid->next) {
3512 uid_length = m_strlen(uid->email);
3513 if (1 && (uid->email[0] == '<')
3514 && (uid->email[uid_length - 1] == '>')
3515 && (uid_length == sender_length + 2)
3516 && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
3520 mutt_any_key_to_continue ("Failed to verify sender");
3524 if (signature_key) {
3525 gpgme_key_unref(signature_key);
3526 signature_key = NULL;
3531 static void crypt_invoke_import(FILE *stream, int smime)
3533 gpgme_ctx_t ctx = create_gpgme_context(smime);
3537 err = gpgme_data_new_from_stream(&data, stream);
3539 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
3544 err = gpgme_op_import(ctx, data);
3546 mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
3547 gpgme_data_release(data);
3552 gpgme_data_release(data);
3557 static void pgp_extract_keys_from_attachment(FILE * fp, BODY * top)
3560 FILE *tmpfp = tmpfile();
3562 if (tmpfp == NULL) {
3563 mutt_perror (_("Can't create temporary file"));
3570 mutt_body_handler(top, &s);
3573 crypt_invoke_import(tmpfp, 0);
3577 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
3581 for (; top; top = top->next) {
3582 if (!tag || top->tagged)
3583 pgp_extract_keys_from_attachment (fp, top);
3590 void crypt_invoke_message (int type)
3592 if (type & APPLICATION_PGP) {
3593 mutt_message _("Invoking PGP...");
3595 else if (type & APPLICATION_SMIME) {
3596 mutt_message _("Invoking S/MIME...");
3600 int mutt_protect (HEADER * msg, char *keylist)
3602 BODY *pbody = NULL, *tmp_pbody = NULL;
3603 BODY *tmp_smime_pbody = NULL;
3604 BODY *tmp_pgp_pbody = NULL;
3605 int flags = msg->security;
3610 tmp_smime_pbody = msg->content;
3611 tmp_pgp_pbody = msg->content;
3613 if (msg->security & SIGN) {
3614 if (msg->security & APPLICATION_SMIME) {
3615 if (!(tmp_pbody = sign_message(msg->content, 1)))
3617 pbody = tmp_smime_pbody = tmp_pbody;
3620 if ((msg->security & APPLICATION_PGP)
3621 && (!(flags & ENCRYPT) || option (OPTPGPRETAINABLESIG))) {
3622 if (!(tmp_pbody = sign_message(msg->content, 0)))
3626 pbody = tmp_pgp_pbody = tmp_pbody;
3629 if ((msg->security & APPLICATION_SMIME)
3630 && (msg->security & APPLICATION_PGP)) {
3631 /* here comes the draft ;-) */
3636 if (msg->security & ENCRYPT) {
3637 if ((msg->security & APPLICATION_SMIME)) {
3638 if (!(tmp_pbody = crypt_smime_build_smime_entity (tmp_smime_pbody,
3640 /* signed ? free it! */
3643 /* free tmp_body if messages was signed AND encrypted ... */
3644 if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody) {
3645 /* detatch and dont't delete msg->content,
3646 which tmp_smime_pbody->parts after signing. */
3647 tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
3648 msg->content->next = NULL;
3649 body_list_wipe(&tmp_smime_pbody);
3654 if ((msg->security & APPLICATION_PGP)) {
3655 if (!(pbody = crypt_pgp_encrypt_message (tmp_pgp_pbody, keylist,
3658 /* did we perform a retainable signature? */
3659 if (flags != msg->security) {
3660 /* remove the outer multipart layer */
3661 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3662 /* get rid of the signature */
3663 body_list_wipe(&tmp_pgp_pbody->next);
3669 /* destroy temporary signature envelope when doing retainable
3673 if (flags != msg->security) {
3674 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3675 body_list_wipe(&tmp_pgp_pbody->next);
3681 msg->content = pbody;
3687 int crypt_query (BODY * m)
3694 if (m->type == TYPEAPPLICATION) {
3695 t |= mutt_is_application_pgp (m);
3697 t |= mutt_is_application_smime (m);
3698 if (t && m->goodsig)
3703 else if (m->type == TYPETEXT) {
3704 t |= mutt_is_application_pgp (m);
3705 if (t && m->goodsig)
3709 if (m->type == TYPEMULTIPART) {
3710 t |= mutt_is_multipart_encrypted (m);
3711 t |= mutt_is_multipart_signed (m);
3713 if (t && m->goodsig)
3717 if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE) {
3721 u = m->parts ? ~0 : 0; /* Bits set in all parts */
3722 w = 0; /* Bits set in any part */
3724 for (p = m->parts; p; p = p->next) {
3725 v = crypt_query (p);
3729 t |= u | (w & ~GOODSIGN);
3731 if ((w & GOODSIGN) && !(u & GOODSIGN))
3739 static void crypt_write_signed(BODY * a, STATE * s, FILE *fp)
3745 fseeko (s->fpin, a->hdr_offset, 0);
3746 bytes = a->length + a->offset - a->hdr_offset;
3749 if ((c = fgetc (s->fpin)) == EOF)
3757 if (c == '\n' && !hadcr)
3766 static void extract_keys_aux(FILE *fpout, HEADER *h)
3768 mutt_parse_mime_message (Context, h);
3771 if (h->security & APPLICATION_PGP) {
3772 mutt_copy_message(fpout, Context, h, M_CM_DECODE | M_CM_CHARCONV, 0);
3775 mutt_endwin (_("Trying to extract PGP keys...\n"));
3778 if (h->security & APPLICATION_SMIME) {
3779 if (h->security & ENCRYPT)
3780 mutt_copy_message (fpout, Context, h, M_CM_NOHEADER
3781 | M_CM_DECODE_CRYPT | M_CM_DECODE_SMIME, 0);
3783 mutt_copy_message(fpout, Context, h, 0, 0);
3786 mutt_message (_("Trying to extract S/MIME certificates...\n"));
3790 crypt_invoke_import(fpout, h->security & APPLICATION_SMIME);
3793 void crypt_extract_keys_from_messages(HEADER * h)
3795 FILE *tmpfp = tmpfile();
3797 mutt_error(_("Could not create temporary file"));
3803 for (i = 0; i < Context->vcount; i++) {
3804 if (!Context->hdrs[Context->v2r[i]]->tagged)
3806 extract_keys_aux(tmpfp, Context->hdrs[Context->v2r[i]]);
3809 extract_keys_aux(tmpfp, h);
3814 mutt_any_key_to_continue(NULL);
3817 static void crypt_fetch_signatures(BODY ***signatures, BODY * a, int *n)
3819 for (; a; a = a->next) {
3820 if (a->type == TYPEMULTIPART) {
3821 crypt_fetch_signatures(signatures, a->parts, n);
3824 p_realloc(signatures, *n + 6);
3826 (*signatures)[(*n)++] = a;
3831 int mutt_signed_handler(BODY *a, STATE *s)
3833 unsigned major, minor;
3835 int rc, i, goodsig = 1, sigcnt = 0;
3838 protocol = parameter_getval(a->parameter, "protocol");
3841 switch (mime_which_token(protocol, -1)) {
3842 case MIME_APPLICATION_PGP_SIGNATURE:
3843 major = TYPEAPPLICATION;
3844 minor = MIME_PGP_SIGNATURE;
3846 case MIME_APPLICATION_X_PKCS7_SIGNATURE:
3847 major = TYPEAPPLICATION;
3848 minor = MIME_X_PKCS7_SIGNATURE;
3850 case MIME_APPLICATION_PKCS7_SIGNATURE:
3851 major = TYPEAPPLICATION;
3852 minor = MIME_PKCS7_SIGNATURE;
3854 case MIME_MULTIPART_MIXED:
3855 major = TYPEMULTIPART;
3860 state_printf(s, _("[-- Error: "
3861 "Unknown multipart/signed protocol %s! --]\n\n"),
3863 return mutt_body_handler (a, s);
3866 /* consistency check */
3867 if (!(a && a->next && a->next->type == major &&
3868 mime_which_token(a->next->subtype, -1) == minor))
3870 state_attach_puts(_("[-- Error: "
3871 "Inconsistent multipart/signed structure! --]\n\n"),
3873 return mutt_body_handler (a, s);
3876 if (s->flags & M_DISPLAY) {
3879 crypt_fetch_signatures (&sigs, a->next, &sigcnt);
3881 FILE *tmpfp = tmpfile();
3884 mutt_error(_("Could not create temporary file"));
3886 crypt_write_signed(a, s, tmpfp);
3888 for (i = 0; i < sigcnt; i++) {
3889 if (sigs[i]->type == TYPEAPPLICATION) {
3892 switch ((subtype = mime_which_token(sigs[i]->subtype, -1))) {
3893 case MIME_PGP_SIGNATURE:
3894 case MIME_X_PKCS7_SIGNATURE:
3895 case MIME_PKCS7_SIGNATURE:
3896 if (crypt_verify_one(sigs[i], s, tmpfp, subtype != MIME_PGP_SIGNATURE) != 0)
3907 state_printf(s, _("[-- Warning: "
3908 "We can't verify %s/%s signatures. --]\n\n"),
3909 TYPE (sigs[i]), sigs[i]->subtype);
3913 b->goodsig = goodsig;
3914 b->badsig = !goodsig;
3916 /* Now display the signed body */
3917 state_attach_puts(_("[-- The following data is signed --]\n\n"), s);
3921 state_attach_puts(_("[-- Warning: Can't find any signatures. --]\n\n"),
3926 rc = mutt_body_handler (a, s);
3928 if (s->flags & M_DISPLAY && sigcnt)
3929 state_attach_puts (_("\n[-- End of signed data --]\n"), s);