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>
30 #include "recvattach.h"
33 /* Values used for comparing addresses. */
34 #define CRYPT_KV_VALID 1
35 #define CRYPT_KV_ADDR 2
36 #define CRYPT_KV_STRING 4
37 #define CRYPT_KV_STRONGID 8
38 #define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
45 /* We work based on user IDs, getting from a user ID to the key is
46 check and does not need any memory (gpgme uses reference counting). */
47 typedef struct cryptkey_t {
48 struct cryptkey_t *next;
49 int idx; /* and the user ID at this index */
50 int flags; /* global and per uid flags (for convenience) */
52 const char *uid; /* and for convenience point to this user ID */
55 DO_INIT(cryptkey_t, cryptkey);
56 static void cryptkey_wipe(cryptkey_t *key) {
57 gpgme_key_release(key->kobj);
59 DO_NEW(cryptkey_t, cryptkey);
60 DO_DELETE(cryptkey_t, cryptkey);
61 DO_SLIST(cryptkey_t, key, cryptkey_delete);
63 static cryptkey_t *cryptkey_dup(const cryptkey_t *k)
65 cryptkey_t *res = cryptkey_new();
68 gpgme_key_ref(k->kobj);
72 typedef struct crypt_entry {
77 static gpgme_key_t signature_key = NULL;
79 static void convert_to_7bit (BODY * a)
81 for (; a; a = a->next) {
82 int tok = mime_which_token(a->subtype, -1);
84 if (a->type == TYPEMULTIPART) {
85 a->encoding = ENC7BIT;
86 convert_to_7bit(a->parts);
87 } else if (a->type == TYPEMESSAGE && tok == MIME_DELIVERY_STATUS) {
88 if (a->encoding != ENC7BIT)
89 mutt_message_to_7bit(a, NULL);
90 } else if (a->encoding == ENC8BIT) {
91 a->encoding = ENCQUOTEDPRINTABLE;
92 } else if (a->encoding == ENCBINARY) {
93 a->encoding = ENCBASE64;
94 } else if (a->content && a->encoding != ENCBASE64
95 && (a->content->from || a->content->space))
97 a->encoding = ENCQUOTEDPRINTABLE;
102 /* Print the utf-8 encoded string BUF of length LEN bytes to stream
103 FP. Convert the character set. */
104 static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
108 tstr = p_dupstr(buf, len);
109 mutt_convert_string(&tstr, "utf-8", MCharset.charset, M_ICONV_HOOK_FROM);
114 /* Return the keyID for the key K. Note that this string is valid as
115 long as K is valid */
116 static const char *crypt_keyid (cryptkey_t * k)
118 if (k->kobj && k->kobj->subkeys) {
119 const char *s = k->kobj->subkeys->keyid;
120 return m_strlen(s) == 16 ? s + 8 : s;
126 /* Return the hexstring fingerprint from the key K. */
127 static const char *crypt_fpr (cryptkey_t * k)
129 return k->kobj && k->kobj->subkeys ? k->kobj->subkeys->fpr : "";
132 /* Parse FLAGS and return a statically allocated(!) string with them. */
133 static char *crypt_key_abilities (int flags)
135 static char buff[3] = "es";
137 if (!(flags & KEYFLAG_CANENCRYPT))
139 else if (flags & KEYFLAG_PREFER_SIGNING)
142 if (!(flags & KEYFLAG_CANSIGN))
144 else if (flags & KEYFLAG_PREFER_ENCRYPTION)
150 /* Parse FLAGS and return a character describing the most important flag. */
151 static char crypt_flags(int flags)
153 if (flags & KEYFLAG_REVOKED)
155 if (flags & KEYFLAG_EXPIRED)
157 if (flags & KEYFLAG_DISABLED)
159 if (flags & KEYFLAG_CRITICAL)
164 /* Return true whe validity of KEY is sufficient. */
165 static int crypt_id_is_strong (cryptkey_t * key)
170 if (key->flags & KEYFLAG_ISX509)
173 for (i = 0, uid = key->kobj->uids; uid; i++, uid = uid->next) {
175 return uid->validity == GPGME_VALIDITY_FULL
176 || uid->validity == GPGME_VALIDITY_ULTIMATE;
183 /* Return a bit vector describing how well the addresses ADDR and
184 U_ADDR match and whether KEY is valid. */
186 crypt_id_matches_addr(address_t *addr, address_t *u_addr, cryptkey_t *key)
190 if (!(key->flags & KEYFLAG_CANTUSE))
191 rv |= CRYPT_KV_VALID;
193 if (crypt_id_is_strong(key))
194 rv |= CRYPT_KV_STRONGID;
196 if (addr->mailbox && !m_strcasecmp(addr->mailbox, u_addr->mailbox))
199 if (addr->personal && m_strcasecmp(addr->personal, u_addr->personal))
200 rv |= CRYPT_KV_STRING;
206 /* Create a new gpgme context and return it. With FOR_SMIME set to
207 true, the protocol of the context is set to CMS. */
208 static gpgme_ctx_t create_gpgme_context(int for_smime)
213 err = gpgme_new (&ctx);
215 mutt_error(_("error creating gpgme context: %s\n"),
216 gpgme_strerror(err));
223 err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
225 mutt_error(_("error enabling CMS protocol: %s\n"), gpgme_strerror(err));
232 /* Create a new gpgme data object. This is a wrapper to die on
234 static gpgme_data_t create_gpgme_data(void)
239 err = gpgme_data_new(&data);
241 mutt_error(_("error creating gpgme data object: %s\n"),
242 gpgme_strerror(err));
249 /* Create a new GPGME Data object from the mail body A. With CONVERT
250 passed as true, the lines are converted to CR,LF if required.
251 Return NULL on error or the gpgme_data_t object on success. */
252 static gpgme_data_t body_to_data_object(BODY *a, int convert)
258 if (!(fptmp = tmpfile())) {
259 mutt_perror (_("Can't create temporary file"));
263 mutt_write_mime_header(a, fptmp);
265 mutt_write_mime_body(a, fptmp);
272 data = create_gpgme_data();
274 while (fgets(buf + spare, sizeof(buf) - 1, fptmp)) {
275 int l = m_strlen(buf);
277 spare = buf[l - 1] != '\n';
278 if (!spare && (l <= 1 || buf[l - 2] != '\r')) {
282 gpgme_data_write(data, buf, l - spare);
287 gpgme_data_write(data, buf, 1);
288 gpgme_data_seek(data, 0, SEEK_SET);
293 err = gpgme_data_new_from_stream(&data, fptmp);
296 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
302 /* Create a GPGME data object from the stream FP but limit the object
303 to LENGTH bytes starting at OFFSET bytes from the beginning of the
305 static gpgme_data_t file_to_data_object(FILE *fp, long offset, long length)
310 err = gpgme_data_new_from_filepart(&data, NULL, fp, offset, length);
312 mutt_error(_("error allocating data object: %s\n"),
313 gpgme_strerror(err));
320 /* Write a GPGME data object to the stream FP. */
321 static int data_object_to_stream(gpgme_data_t data, FILE *fp)
327 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
328 ? gpgme_error_from_errno (errno) : 0);
330 mutt_error (_("error rewinding data object: %s\n"),
331 gpgme_strerror(err));
335 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
336 /* fixme: we are not really converting CRLF to LF but just
337 skipping CR. Doing it correctly needs a more complex logic */
338 for (p = buf; nread; p++, nread--) {
344 mutt_perror ("[tempfile]");
349 mutt_error(_("error reading data object: %s\n"), strerror(errno));
355 /* Copy a data object to a newly created temporay file and return that
356 filename. Caller must free. With RET_FP not NULL, don't close the
357 stream but return it there. */
358 static char *data_object_to_tempfile(gpgme_data_t data, FILE **ret_fp)
361 char tempfile[_POSIX_PATH_MAX];
365 fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
367 mutt_perror (_("Can't create temporary file"));
371 err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
372 ? gpgme_error_from_errno (errno) : 0);
376 while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
377 if (fwrite (buf, nread, 1, fp) != 1) {
378 mutt_perror (_("Can't create temporary file"));
387 mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
398 return m_strdup(tempfile);
402 /* FIXME: stolen from gpgme to avoid "ambiguous identity" errors */
404 gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
410 if (!ctx || !r_key || !fpr)
411 return gpg_error (GPG_ERR_INV_VALUE);
413 if (strlen (fpr) < 8) /* We have at least a key ID. */
414 return gpg_error (GPG_ERR_INV_VALUE);
416 /* FIXME: We use our own context because we have to avoid the user's
417 I/O callback handlers. */
418 err = gpgme_new (&listctx);
421 gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
422 err = gpgme_op_keylist_start (listctx, fpr, secret);
424 err = gpgme_op_keylist_next (listctx, r_key);
425 gpgme_release (listctx);
429 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
430 The keys must be space delimited. */
431 static gpgme_key_t *create_recipient_set(const char *s, int smime)
433 gpgme_ctx_t ctx = create_gpgme_context(smime);
434 gpgme_key_t *rset = NULL;
441 const char *p = m_strnextsp(s);
444 m_strncpy(buf, sizeof(buf), s, p - s);
445 if (p - s > 1 && p[-1] == '!') {
446 /* user wants to override the valididy of that key. */
448 buf[p - s - 1] = '\0';
449 err = gpgme_get_key2(ctx, buf, &key, 0);
451 key->uids->validity = GPGME_VALIDITY_FULL;
453 err = gpgme_get_key2(ctx, buf, &key, 0);
457 mutt_error(_("error adding recipient `%.*s': %s\n"),
458 (int)(p - s), s, gpgme_strerror(err));
463 p_realloc(&rset, rset_n + 1);
464 rset[rset_n++] = key;
469 /* NULL terminate. */
470 p_realloc(&rset, rset_n + 1);
471 rset[rset_n++] = NULL;
479 /* Make sure that the correct signer is set. Returns 0 on success. */
480 static int set_signer(gpgme_ctx_t ctx, int for_smime)
482 const char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
486 if (m_strisempty(signid))
489 err = gpgme_get_key(ctx, signid, &key, 1);
491 mutt_error(_("error getting secret key `%s': %s\n"), signid,
492 gpgme_strerror(err));
496 gpgme_signers_clear(ctx);
497 err = gpgme_signers_add(ctx, key);
498 gpgme_key_unref(key);
500 mutt_error(_("error setting secret key `%s': %s\n"), signid,
501 gpgme_strerror(err));
508 /* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
509 and return an allocated filename to a temporary file containing the
510 enciphered text. With USE_SMIME set to true, the smime backend is
511 used. With COMBINED_SIGNED a PGP message is signed and
512 encrypted. Returns NULL in case of error */
513 static char *encrypt_gpgme_object(gpgme_data_t plaintext, gpgme_key_t *rset,
514 int use_smime, int combined_signed)
518 gpgme_data_t ciphertext;
521 ctx = create_gpgme_context (use_smime);
523 gpgme_set_armor (ctx, 1);
525 ciphertext = create_gpgme_data ();
527 if (combined_signed) {
528 if (set_signer(ctx, use_smime)) {
529 gpgme_data_release(ciphertext);
533 err = gpgme_op_encrypt_sign(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
534 plaintext, ciphertext);
536 err = gpgme_op_encrypt(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
537 plaintext, ciphertext);
540 mutt_need_hard_redraw();
542 mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
543 gpgme_data_release (ciphertext);
550 outfile = data_object_to_tempfile(ciphertext, NULL);
551 gpgme_data_release(ciphertext);
555 /* Find the "micalg" parameter from the last Gpgme operation on
556 context CTX. It is expected that this operation was a sign
557 operation. Return the algorithm name as a C string in buffer BUF
558 which must have been allocated by the caller with size BUFLEN.
559 Returns 0 on success or -1 in case of an error. The return string
560 is truncted to BUFLEN - 1. */
561 static int get_micalg(gpgme_ctx_t ctx, char *buf, ssize_t buflen)
563 gpgme_sign_result_t result = NULL;
564 const char *algorithm_name = NULL;
567 result = gpgme_op_sign_result(ctx);
569 algorithm_name = gpgme_hash_algo_name (result->signatures->hash_algo);
570 if (algorithm_name) {
571 m_strcpy(buf, buflen, algorithm_name);
575 return *buf ? 0 : -1;
578 static void print_time(time_t t, STATE *s)
582 setlocale(LC_TIME, "");
583 #ifdef HAVE_LANGINFO_D_T_FMT
584 strftime(p, sizeof(p), nl_langinfo(D_T_FMT), localtime(&t));
586 strftime(p, sizeof(p), "%c", localtime(&t));
588 setlocale(LC_TIME, "C");
589 state_attach_puts(p, s);
592 /* Implementation of `sign_message'. */
594 /* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
595 USE_SMIME is passed as true. Returns the new body or NULL on
597 static BODY *sign_message(BODY * a, int use_smime)
604 gpgme_data_t message, signature;
606 convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
608 message = body_to_data_object(a, 1);
611 signature = create_gpgme_data ();
613 ctx = create_gpgme_context (use_smime);
615 gpgme_set_armor (ctx, 1);
617 if (set_signer (ctx, use_smime)) {
618 gpgme_data_release (signature);
623 err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH);
624 mutt_need_hard_redraw ();
625 gpgme_data_release (message);
627 gpgme_data_release (signature);
629 mutt_error (_("error signing data: %s\n"), gpgme_strerror (err));
633 sigfile = data_object_to_tempfile(signature, NULL);
634 gpgme_data_release (signature);
641 t->type = TYPEMULTIPART;
642 t->subtype = m_strdup("signed");
643 t->encoding = ENC7BIT;
645 t->disposition = DISPINLINE;
647 parameter_set_boundary(&t->parameter);
648 parameter_setval(&t->parameter, "protocol",
649 use_smime ? "application/pkcs7-signature"
650 : "application/pgp-signature");
651 /* Get the micalg from gpgme. Old gpgme versions don't support this
652 for S/MIME so we assume sha-1 in this case. */
653 if (!get_micalg (ctx, buf, sizeof buf))
654 parameter_setval(&t->parameter, "micalg", buf);
656 parameter_setval(&t->parameter, "micalg", "sha1");
662 t->parts->next = body_new();
664 t->type = TYPEAPPLICATION;
666 t->subtype = m_strdup("pkcs7-signature");
667 parameter_setval(&t->parameter, "name", "smime.p7s");
668 t->encoding = ENCBASE64;
670 t->disposition = DISPATTACH;
671 t->d_filename = m_strdup("smime.p7s");
674 t->subtype = m_strdup("pgp-signature");
676 t->disposition = DISPINLINE;
677 t->encoding = ENC7BIT;
679 t->filename = sigfile;
680 t->unlink = 1; /* ok to remove this file after sending. */
685 /* Encrypt the mail body A to all keys given as space separated keyids
686 or fingerprints in KEYLIST and return the encrypted body. */
687 static BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
689 char *outfile = NULL;
691 gpgme_key_t *rset = NULL;
692 gpgme_data_t plaintext;
694 rset = create_recipient_set(keylist, 0);
700 plaintext = body_to_data_object(a, 0);
706 outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
707 gpgme_data_release (plaintext);
713 t->type = TYPEMULTIPART;
714 t->subtype = m_strdup("encrypted");
715 t->encoding = ENC7BIT;
717 t->disposition = DISPINLINE;
719 parameter_set_boundary(&t->parameter);
720 parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
722 t->parts = body_new();
723 t->parts->type = TYPEAPPLICATION;
724 t->parts->subtype = m_strdup("pgp-encrypted");
725 t->parts->encoding = ENC7BIT;
727 t->parts->next = body_new();
728 t->parts->next->type = TYPEAPPLICATION;
729 t->parts->next->subtype = m_strdup("octet-stream");
730 t->parts->next->encoding = ENC7BIT;
731 t->parts->next->filename = outfile;
732 t->parts->next->use_disp = 1;
733 t->parts->next->disposition = DISPINLINE;
734 t->parts->next->unlink = 1; /* delete after sending the message */
735 t->parts->next->d_filename = m_strdup("msg.asc");
740 /* Encrypt the mail body A to all keys given as space separated
741 fingerprints in KEYLIST and return the S/MIME encrypted body. */
742 static BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
744 char *outfile = NULL;
746 gpgme_key_t *rset = NULL;
747 gpgme_data_t plaintext;
749 rset = create_recipient_set(keylist, 1);
753 plaintext = body_to_data_object(a, 0);
759 outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
760 gpgme_data_release (plaintext);
766 t->type = TYPEAPPLICATION;
767 t->subtype = m_strdup("pkcs7-mime");
768 parameter_setval(&t->parameter, "name", "smime.p7m");
769 parameter_setval(&t->parameter, "smime-type", "enveloped-data");
770 t->encoding = ENCBASE64; /* The output of OpenSSL SHOULD be binary */
772 t->disposition = DISPATTACH;
773 t->d_filename = m_strdup("smime.p7m");
774 t->filename = outfile;
775 t->unlink = 1; /*delete after sending the message */
782 /* Display the common attributes of the signature summary SUM.
783 Return 1 if there is is a severe warning.
785 static int show_sig_summary (unsigned long sum,
786 gpgme_ctx_t ctx, gpgme_key_t key, int idx,
791 if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
792 state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
796 if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
797 time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
800 state_attach_puts (_("Warning: The key used to create the "
801 "signature expired at: "), s);
803 state_attach_puts ("\n", s);
806 state_attach_puts (_("Warning: At least one certification key "
807 "has expired\n"), s);
810 if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
811 gpgme_verify_result_t result;
812 gpgme_signature_t sig;
815 result = gpgme_op_verify_result (ctx);
817 for (sig = result->signatures, i = 0; sig && (i < idx);
818 sig = sig->next, i++);
820 state_attach_puts (_("Warning: The signature expired at: "), s);
821 print_time (sig ? sig->exp_timestamp : 0, s);
822 state_attach_puts ("\n", s);
825 if ((sum & GPGME_SIGSUM_KEY_MISSING))
826 state_attach_puts (_("Can't verify due to a missing "
827 "key or certificate\n"), s);
829 if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
830 state_attach_puts (_("The CRL is not available\n"), s);
834 if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
835 state_attach_puts (_("Available CRL is too old\n"), s);
839 if ((sum & GPGME_SIGSUM_BAD_POLICY))
840 state_attach_puts (_("A policy requirement was not met\n"), s);
842 if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
843 const char *t0 = NULL, *t1 = NULL;
844 gpgme_verify_result_t result;
845 gpgme_signature_t sig;
848 state_attach_puts (_("A system error occurred"), s);
850 /* Try to figure out some more detailed system error information. */
851 result = gpgme_op_verify_result (ctx);
852 for (sig = result->signatures, i = 0; sig && (i < idx);
853 sig = sig->next, i++);
856 t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
860 state_attach_puts (": ", s);
862 state_attach_puts (t0, s);
863 if (t1 && !(t0 && !m_strcmp(t0, t1))) {
865 state_attach_puts (",", s);
866 state_attach_puts (t1, s);
869 state_attach_puts ("\n", s);
876 static void show_fingerprint (gpgme_key_t key, STATE * state)
881 const char *prefix = _("Fingerprint: ");
886 s = key->subkeys ? key->subkeys->fpr : NULL;
889 is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
891 bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
892 buf = p_new(char, bufsize);
893 m_strcpy(buf, bufsize, prefix);
894 p = buf + m_strlen(buf);
895 if (is_pgp && m_strlen(s) == 40) { /* PGP v4 style formatted. */
896 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
907 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
910 *p++ = is_pgp ? ' ' : ':';
911 if (is_pgp && i == 7)
916 /* just in case print remaining odd digits */
921 state_attach_puts (buf, state);
925 /* Show the valididy of a key used for one signature. */
926 static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
928 gpgme_verify_result_t result = NULL;
929 gpgme_signature_t sig = NULL;
930 const char *txt = NULL;
932 result = gpgme_op_verify_result (ctx);
934 for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
936 switch (sig ? sig->validity : 0) {
937 case GPGME_VALIDITY_UNKNOWN:
938 txt = _("WARNING: We have NO indication whether "
939 "the key belongs to the person named " "as shown above\n");
941 case GPGME_VALIDITY_UNDEFINED:
943 case GPGME_VALIDITY_NEVER:
944 txt = _("WARNING: The key does NOT BELONG to "
945 "the person named as shown above\n");
947 case GPGME_VALIDITY_MARGINAL:
948 txt = _("WARNING: It is NOT certain that the key "
949 "belongs to the person named as shown above\n");
951 case GPGME_VALIDITY_FULL:
952 case GPGME_VALIDITY_ULTIMATE:
957 state_attach_puts (txt, s);
960 /* Show information about one signature. This fucntion is called with
961 the context CTX of a sucessful verification operation and the
962 enumerator IDX which should start at 0 and incremete for each
965 Return values are: 0 for normal procession, 1 for a bad signature,
966 2 for a signature with a warning or -1 for no more signature. */
967 static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
970 const char *fpr, *uid;
971 gpgme_key_t key = NULL;
972 int i, anybad = 0, anywarn = 0;
974 gpgme_user_id_t uids = NULL;
975 gpgme_verify_result_t result;
976 gpgme_signature_t sig;
977 gpgme_error_t err = GPG_ERR_NO_ERROR;
979 result = gpgme_op_verify_result (ctx);
981 /* FIXME: this code should use a static variable and remember
982 the current position in the list of signatures, IMHO.
985 for (i = 0, sig = result->signatures; sig && (i < idx);
986 i++, sig = sig->next);
988 return -1; /* Signature not found. */
991 gpgme_key_unref(signature_key);
992 signature_key = NULL;
995 created = sig->timestamp;
999 if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
1002 err = gpgme_get_key2 (ctx, fpr, &key, 0); /* secret key? */
1004 uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
1006 signature_key = key;
1009 key = NULL; /* Old gpgme versions did not set KEY to NULL on
1010 error. Do it here to avoid a double free. */
1014 if (!s || !s->fpout || !(s->flags & M_DISPLAY)); /* No state information so no way to print anything. */
1016 state_attach_puts (_("Error getting key information: "), s);
1017 state_attach_puts (gpg_strerror (err), s);
1018 state_attach_puts ("\n", s);
1021 else if ((sum & GPGME_SIGSUM_GREEN)) {
1022 state_attach_puts (_("Good signature from: "), s);
1023 state_attach_puts (uid, s);
1024 state_attach_puts ("\n", s);
1025 for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
1027 /* Skip primary UID. */
1031 state_attach_puts (_(" aka: "), s);
1032 state_attach_puts (uids->uid, s);
1033 state_attach_puts ("\n", s);
1035 state_attach_puts (_(" created: "), s);
1036 print_time (created, s);
1037 state_attach_puts ("\n", s);
1038 if (show_sig_summary (sum, ctx, key, idx, s))
1040 show_one_sig_validity (ctx, idx, s);
1042 else if ((sum & GPGME_SIGSUM_RED)) {
1043 state_attach_puts (_("*BAD* signature claimed to be from: "), s);
1044 state_attach_puts (uid, s);
1045 state_attach_puts ("\n", s);
1046 show_sig_summary (sum, ctx, key, idx, s);
1048 else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) { /* We can't decide (yellow) but this is a PGP key with a good
1049 signature, so we display what a PGP user expects: The name,
1050 fingerprint and the key validity (which is neither fully or
1052 state_attach_puts (_("Good signature from: "), s);
1053 state_attach_puts (uid, s);
1054 state_attach_puts ("\n", s);
1055 state_attach_puts (_(" created: "), s);
1056 print_time (created, s);
1057 state_attach_puts ("\n", s);
1058 show_one_sig_validity (ctx, idx, s);
1059 show_fingerprint (key, s);
1060 if (show_sig_summary (sum, ctx, key, idx, s))
1063 else { /* can't decide (yellow) */
1065 state_attach_puts (_("Error checking signature"), s);
1066 state_attach_puts ("\n", s);
1067 show_sig_summary (sum, ctx, key, idx, s);
1070 if (key != signature_key)
1071 gpgme_key_unref(key);
1074 return anybad ? 1 : anywarn ? 2 : 0;
1077 /* Do the actual verification step. With IS_SMIME set to true we
1078 assume S/MIME (surprise!) */
1079 static int crypt_verify_one(BODY *sigbdy, STATE *s, FILE *fp, int is_smime)
1085 gpgme_data_t signature, message;
1087 signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
1091 /* We need to tell gpgme about the encoding because the backend can't
1092 auto-detect plain base-64 encoding which is used by S/MIME. */
1094 gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
1096 err = gpgme_data_new_from_stream(&message, fp);
1098 gpgme_data_release (signature);
1099 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
1102 ctx = create_gpgme_context (is_smime);
1104 /* Note: We don't need a current time output because GPGME avoids
1105 such an attack by separating the meta information from the
1107 state_attach_puts (_("[-- Begin signature information --]\n"), s);
1109 err = gpgme_op_verify (ctx, signature, message, NULL);
1110 mutt_need_hard_redraw ();
1114 snprintf (buf, sizeof (buf) - 1,
1115 _("Error: verification failed: %s\n"), gpgme_strerror (err));
1116 state_attach_puts (buf, s);
1118 else { /* Verification succeeded, see what the result is. */
1122 if (signature_key) {
1123 gpgme_key_unref(signature_key);
1124 signature_key = NULL;
1127 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1138 gpgme_verify_result_t result;
1139 gpgme_sig_notation_t notation;
1140 gpgme_signature_t sig;
1142 result = gpgme_op_verify_result (ctx);
1144 for (sig = result->signatures; sig; sig = sig->next) {
1145 if (sig->notations) {
1146 state_attach_puts ("*** Begin Notation (signature by: ", s);
1147 state_attach_puts (sig->fpr, s);
1148 state_attach_puts (") ***\n", s);
1149 for (notation = sig->notations; notation; notation = notation->next)
1151 if (notation->name) {
1152 state_attach_puts (notation->name, s);
1153 state_attach_puts ("=", s);
1155 if (notation->value) {
1156 state_attach_puts (notation->value, s);
1157 if (!(*notation->value
1158 && (notation->value[m_strlen(notation->value) - 1] ==
1160 state_attach_puts ("\n", s);
1163 state_attach_puts ("*** End Notation ***\n", s);
1169 gpgme_release (ctx);
1171 state_attach_puts (_("[-- End signature information --]\n\n"), s);
1173 return badsig ? 1 : anywarn ? 2 : 0;
1176 /* Decrypt a PGP or SMIME message (depending on the boolean flag
1177 IS_SMIME) with body A described further by state S. Write
1178 plaintext out to file FPOUT and return a new body. For PGP returns
1179 a flag in R_IS_SIGNED to indicate whether this is a combined
1180 encrypted and signed message, for S/MIME it returns true when it is
1181 not a encrypted but a signed message. */
1183 decrypt_part(BODY *a, STATE *s, FILE *fpout, int is_smime, int *r_is_signed)
1189 gpgme_data_t ciphertext, plaintext;
1190 int maybe_signed = 0;
1197 ctx = create_gpgme_context (is_smime);
1200 /* Make a data object from the body, create context etc. */
1201 ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
1204 plaintext = create_gpgme_data ();
1206 /* Do the decryption or the verification in case of the S/MIME hack. */
1207 if ((!is_smime) || maybe_signed) {
1209 err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
1210 else if (maybe_signed)
1211 err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
1214 /* Check wether signatures have been verified. */
1215 gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
1217 if (verify_result->signatures)
1222 err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
1223 gpgme_data_release (ciphertext);
1225 if (is_smime && !maybe_signed && gpg_err_code (err) == GPG_ERR_NO_DATA) {
1226 /* Check whether this might be a signed message despite what
1227 the mime header told us. Retry then. gpgsm returns the
1228 error information "unsupported Algorithm '?'" but gpgme
1229 will not store this unknown algorithm, thus we test that
1230 it has not been set. */
1231 gpgme_decrypt_result_t result;
1233 result = gpgme_op_decrypt_result (ctx);
1234 if (!result->unsupported_algorithm) {
1236 gpgme_data_release (plaintext);
1240 mutt_need_hard_redraw ();
1241 if ((s->flags & M_DISPLAY)) {
1244 snprintf (buf, sizeof (buf) - 1,
1245 _("[-- Error: decryption failed: %s --]\n\n"),
1246 gpgme_strerror (err));
1247 state_attach_puts (buf, s);
1249 gpgme_data_release (plaintext);
1250 gpgme_release (ctx);
1253 mutt_need_hard_redraw ();
1255 /* Read the output from GPGME, and make sure to change CRLF to LF,
1256 otherwise read_mime_header has a hard time parsing the message. */
1257 if (data_object_to_stream (plaintext, fpout)) {
1258 gpgme_data_release (plaintext);
1259 gpgme_release (ctx);
1262 gpgme_data_release (plaintext);
1264 a->is_signed_data = 0;
1270 a->is_signed_data = 1;
1272 *r_is_signed = -1; /* A signature exists. */
1274 if ((s->flags & M_DISPLAY))
1275 state_attach_puts (_("[-- Begin signature " "information --]\n"), s);
1276 for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1282 if (!anybad && idx && r_is_signed && *r_is_signed)
1283 *r_is_signed = anywarn ? 2 : 1; /* Good signature. */
1285 if ((s->flags & M_DISPLAY))
1286 state_attach_puts (_("[-- End signature " "information --]\n\n"), s);
1288 gpgme_release (ctx);
1293 tattach = mutt_read_mime_header (fpout, 0);
1296 * Need to set the length of this body part.
1298 fstat (fileno (fpout), &info);
1299 tattach->length = info.st_size - tattach->offset;
1301 tattach->warnsig = anywarn;
1303 /* See if we need to recurse on this MIME part. */
1304 mutt_parse_part (fpout, tattach);
1310 /* Decrypt a PGP/MIME message in FPIN and B and return a new body and
1311 the stream in CUR and FPOUT. Returns 0 on success. */
1312 int crypt_pgp_decrypt_mime (FILE * fpin, FILE **fpout, BODY *b, BODY **cur)
1315 BODY *first_part = b;
1318 first_part->goodsig = 0;
1319 first_part->warnsig = 0;
1321 if (!mutt_is_multipart_encrypted(b) || !b->parts || !b->parts->next)
1330 mutt_perror (_("Can't create temporary file"));
1334 *cur = decrypt_part(b, &s, *fpout, 0, &is_signed);
1336 first_part->goodsig = is_signed > 0;
1337 return *cur ? 0 : -1;
1341 /* Decrypt a S/MIME message in FPIN and B and return a new body and
1342 the stream in CUR and FPOUT. Returns 0 on success. */
1343 int crypt_smime_decrypt_mime(FILE *fpin, FILE **fpout, BODY *b, BODY **cur)
1348 long saved_b_offset;
1349 ssize_t saved_b_length;
1352 if (!mutt_is_application_smime (b))
1358 /* Decode the body - we need to pass binary CMS to the
1359 backend. The backend allows for Base64 encoded data but it does
1360 not allow for QP which I have seen in some messages. So better
1362 saved_b_type = b->type;
1363 saved_b_offset = b->offset;
1364 saved_b_length = b->length;
1367 fseeko (s.fpin, b->offset, 0);
1370 mutt_perror (_("Can't create temporary file"));
1375 mutt_decode_attachment (b, &s);
1377 b->length = ftello (s.fpout);
1386 mutt_perror (_("Can't create temporary file"));
1390 *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
1392 (*cur)->goodsig = is_signed > 0;
1393 b->type = saved_b_type;
1394 b->length = saved_b_length;
1395 b->offset = saved_b_offset;
1398 if (*cur && !is_signed && !(*cur)->parts
1399 && mutt_is_application_smime (*cur)) {
1400 /* Assume that this is a opaque signed s/mime message. This is
1401 an ugly way of doing it but we have anyway a problem with
1402 arbitrary encoded S/MIME messages: Only the outer part may be
1403 encrypted. The entire mime parsing should be revamped,
1404 probably by keeping the temportary files so that we don't
1405 need to decrypt them all the time. Inner parts of an
1406 encrypted part can then pint into this file and tehre won't
1407 never be a need to decrypt again. This needs a partial
1408 rewrite of the MIME engine. */
1412 saved_b_type = bb->type;
1413 saved_b_offset = bb->offset;
1414 saved_b_length = bb->length;
1417 fseeko (s.fpin, bb->offset, 0);
1420 mutt_perror (_("Can't create temporary file"));
1425 mutt_decode_attachment (bb, &s);
1427 bb->length = ftello (s.fpout);
1437 mutt_perror (_("Can't create temporary file"));
1441 tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
1443 tmp_b->goodsig = is_signed > 0;
1444 bb->type = saved_b_type;
1445 bb->length = saved_b_length;
1446 bb->offset = saved_b_offset;
1449 body_list_wipe(cur);
1452 return *cur ? 0 : -1;
1457 pgp_check_traditional_one_body(FILE *fp, BODY *b, int tagged_only)
1459 char tempfile[_POSIX_PATH_MAX];
1460 char buf[HUGE_STRING];
1467 if (b->type != TYPETEXT)
1470 if (tagged_only && !b->tagged)
1473 tempfd = m_tempfd(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1474 if (mutt_decode_save_attachment (fp, b, tempfd, 0) != 0) {
1479 if ((tfp = fopen(tempfile, "r")) == NULL) {
1484 while (fgets (buf, sizeof (buf), tfp)) {
1485 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1486 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1488 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15))
1498 /* fix the content type */
1500 parameter_setval(&b->parameter, "format", "fixed");
1501 parameter_setval(&b->parameter, "x-action",
1502 enc ? "pgp-encrypted" : "pgp-signed");
1506 int crypt_pgp_check_traditional(FILE *fp, BODY *b, int tagged_only)
1510 for (; b; b = b->next) {
1511 if (is_multipart(b))
1512 rv |= crypt_pgp_check_traditional(fp, b->parts, tagged_only);
1513 if (b->type == TYPETEXT) {
1515 if ((r = mutt_is_application_pgp(b))) {
1518 rv |= pgp_check_traditional_one_body(fp, b, tagged_only);
1527 Copy a clearsigned message, and strip the signature and PGP's
1530 XXX - charset handling: We assume that it is safe to do
1531 character set decoding first, dash decoding second here, while
1532 we do it the other way around in the main handler.
1534 (Note that we aren't worse than Outlook & Cie in this, and also
1535 note that we can successfully handle anything produced by any
1536 existing versions of mutt.) */
1537 static void copy_clearsigned(gpgme_data_t data, STATE * s, char *charset)
1539 char buf[HUGE_STRING];
1540 short complete, armor_header;
1545 fname = data_object_to_tempfile(data, &fp);
1551 fc = fgetconv_open (fp, charset, MCharset.charset, M_ICONV_HOOK_FROM);
1553 for (complete = 1, armor_header = 1;
1554 fgetconvs (buf, sizeof (buf), fc) != NULL;
1555 complete = strchr (buf, '\n') != NULL) {
1558 state_puts (buf, s);
1562 if (!m_strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
1572 state_puts (s->prefix, s);
1574 if (buf[0] == '-' && buf[1] == ' ')
1575 state_puts (buf + 2, s);
1577 state_puts (buf, s);
1580 fgetconv_close (&fc);
1584 /* Support for classic_application/pgp */
1585 int crypt_pgp_application_pgp_handler(BODY *m, STATE *s)
1587 int needpass = -1, pgp_keyblock = 0;
1591 off_t last_pos, offset;
1592 char buf[HUGE_STRING];
1593 FILE *pgpout = NULL;
1595 gpgme_error_t err = 0;
1596 gpgme_data_t armored_data = NULL;
1598 short maybe_goodsig = 1;
1599 short have_any_sigs = 0;
1601 char body_charset[STRING]; /* Only used for clearsigned messages. */
1603 /* For clearsigned messages we won't be able to get a character set
1604 but we know that this may only be text thus we assume Latin-1
1606 if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
1607 m_strcpy(body_charset, sizeof(body_charset), "iso-8859-1");
1609 fseeko (s->fpin, m->offset, 0);
1610 last_pos = m->offset;
1612 for (bytes = m->length; bytes > 0;) {
1613 if (fgets (buf, sizeof (buf), s->fpin) == NULL)
1616 offset = ftello (s->fpin);
1617 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1620 if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1622 start_pos = last_pos;
1624 if (!m_strcmp("MESSAGE-----\n", buf + 15))
1626 else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15)) {
1630 else if (!m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
1635 /* XXX - we may wish to recode here */
1637 state_puts (s->prefix, s);
1638 state_puts (buf, s);
1642 have_any_sigs = (have_any_sigs || (clearsign && (s->flags & M_VERIFY)));
1644 /* Copy PGP material to an data container */
1645 armored_data = create_gpgme_data ();
1646 gpgme_data_write (armored_data, buf, m_strlen(buf));
1647 while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) {
1648 offset = ftello (s->fpin);
1649 bytes -= (offset - last_pos); /* don't rely on m_strlen(buf) */
1652 gpgme_data_write (armored_data, buf, m_strlen(buf));
1654 if ((needpass && !m_strcmp("-----END PGP MESSAGE-----\n", buf))
1656 && (!m_strcmp("-----END PGP SIGNATURE-----\n", buf)
1657 || !m_strcmp("-----END PGP PUBLIC KEY BLOCK-----\n",
1662 /* Invoke PGP if needed */
1663 if (!clearsign || (s->flags & M_VERIFY)) {
1664 unsigned int sig_stat = 0;
1665 gpgme_data_t plaintext;
1668 plaintext = create_gpgme_data ();
1669 ctx = create_gpgme_context (0);
1672 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1674 err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
1675 if (gpg_err_code (err) == GPG_ERR_NO_DATA) {
1676 /* Decrypt verify can't handle signed only messages. */
1677 err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
1678 ? gpgme_error_from_errno (errno) : 0;
1679 /* Must release plaintext so that we supply an
1680 uninitialized object. */
1681 gpgme_data_release (plaintext);
1682 plaintext = create_gpgme_data ();
1683 err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1690 snprintf (errbuf, sizeof (errbuf) - 1,
1691 _("Error: decryption/verification failed: %s\n"),
1692 gpgme_strerror (err));
1693 state_attach_puts (errbuf, s);
1695 else { /* Decryption/Verification succeeded */
1699 /* Check wether signatures have been verified. */
1700 gpgme_verify_result_t verify_result;
1702 verify_result = gpgme_op_verify_result (ctx);
1703 if (verify_result->signatures)
1709 if ((s->flags & M_DISPLAY) && sig_stat) {
1714 state_attach_puts (_("[-- Begin signature "
1715 "information --]\n"), s);
1718 (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1727 state_attach_puts (_("[-- End signature "
1728 "information --]\n\n"), s);
1731 tmpfname = data_object_to_tempfile(plaintext, &pgpout);
1734 state_attach_puts (_("Error: copy data failed\n"), s);
1738 p_delete(&tmpfname);
1741 gpgme_release (ctx);
1745 * Now, copy cleartext to the screen. NOTE - we expect that PGP
1746 * outputs utf-8 cleartext. This may not always be true, but it
1747 * seems to be a reasonable guess.
1750 if (s->flags & M_DISPLAY) {
1752 state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
1753 else if (pgp_keyblock)
1754 state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s);
1756 state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s);
1760 copy_clearsigned (armored_data, s, body_charset);
1767 fc = fgetconv_open (pgpout, "utf-8", MCharset.charset, 0);
1768 while ((c = fgetconv (fc)) != EOF) {
1770 if (c == '\n' && s->prefix)
1771 state_puts (s->prefix, s);
1773 fgetconv_close (&fc);
1776 if (s->flags & M_DISPLAY) {
1777 state_putc ('\n', s);
1779 state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
1780 else if (pgp_keyblock)
1781 state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
1783 state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
1791 /* XXX - we may wish to recode here */
1793 state_puts (s->prefix, s);
1794 state_puts (buf, s);
1798 m->goodsig = (maybe_goodsig && have_any_sigs);
1800 if (needpass == -1) {
1801 state_attach_puts (_("[-- Error: could not find beginning"
1802 " of PGP message! --]\n\n"), s);
1808 /* MIME handler for pgp/mime encrypted messages. */
1809 int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
1811 char tempfile[_POSIX_PATH_MAX];
1814 BODY *orig_body = a;
1819 if (!a || a->type != TYPEAPPLICATION || !a->subtype
1820 || ascii_strcasecmp ("pgp-encrypted", a->subtype)
1821 || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
1822 || ascii_strcasecmp ("octet-stream", a->next->subtype)) {
1823 if (s->flags & M_DISPLAY)
1824 state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
1829 /* Move forward to the application/pgp-encrypted body. */
1832 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1834 if (s->flags & M_DISPLAY)
1835 state_attach_puts (_("[-- Error: could not create temporary file! "
1840 tattach = decrypt_part (a, s, fpout, 0, &is_signed);
1842 tattach->goodsig = is_signed > 0;
1844 if (s->flags & M_DISPLAY)
1845 state_attach_puts (is_signed ?
1847 ("[-- The following data is PGP/MIME signed and encrypted --]\n\n") :
1848 _("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
1851 FILE *savefp = s->fpin;
1854 rc = mutt_body_handler (tattach, s);
1859 * if a multipart/signed is the _only_ sub-part of a
1860 * multipart/encrypted, cache signature verification
1863 if (mutt_is_multipart_signed (tattach) && !tattach->next)
1864 orig_body->goodsig |= tattach->goodsig;
1866 if (s->flags & M_DISPLAY) {
1867 state_puts ("\n", s);
1868 state_attach_puts (is_signed ?
1870 ("[-- End of PGP/MIME signed and encrypted data --]\n")
1871 : _("[-- End of PGP/MIME encrypted data --]\n"), s);
1874 body_list_wipe(&tattach);
1878 mutt_unlink (tempfile);
1882 /* Support for application/smime */
1883 int crypt_smime_application_smime_handler (BODY * a, STATE * s)
1885 char tempfile[_POSIX_PATH_MAX];
1892 fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1894 if (s->flags & M_DISPLAY)
1895 state_attach_puts (_("[-- Error: could not create temporary file! "
1900 tattach = decrypt_part (a, s, fpout, 1, &is_signed);
1902 tattach->goodsig = is_signed > 0;
1904 if (s->flags & M_DISPLAY)
1905 state_attach_puts (is_signed ?
1906 _("[-- The following data is S/MIME signed --]\n\n") :
1907 _("[-- The following data is S/MIME encrypted --]\n\n"), s);
1910 FILE *savefp = s->fpin;
1913 rc = mutt_body_handler (tattach, s);
1918 * if a multipart/signed is the _only_ sub-part of a
1919 * multipart/encrypted, cache signature verification
1922 if (mutt_is_multipart_signed (tattach) && !tattach->next) {
1923 if (!(a->goodsig = tattach->goodsig))
1924 a->warnsig = tattach->warnsig;
1926 else if (tattach->goodsig) {
1928 a->warnsig = tattach->warnsig;
1931 if (s->flags & M_DISPLAY) {
1932 state_puts ("\n", s);
1933 state_attach_puts (is_signed ?
1934 _("[-- End of S/MIME signed data --]\n") :
1935 _("[-- End of S/MIME encrypted data --]\n"), s);
1938 body_list_wipe(&tattach);
1942 mutt_unlink (tempfile);
1948 * Format an entry on the CRYPT key selection menu.
1951 * %k key id %K key id of the principal key
1953 * %a algorithm %A algorithm of the princ. key
1954 * %l length %L length of the princ. key
1955 * %f flags %F flags of the princ. key
1956 * %c capabilities %C capabilities of the princ. key
1957 * %t trust/validity of the key-uid association
1959 * %[...] date of key using strftime(3)
1963 crypt_entry_fmt (char *dest, ssize_t destlen, char op,
1964 const char *src, const char *prefix,
1965 const char *ifstr, const char *elstr,
1966 anytype data, format_flag flags)
1969 crypt_entry_t *entry;
1972 int optional = (flags & M_FORMAT_OPTIONAL);
1973 const char *s = NULL;
1979 /* if (isupper ((unsigned char) op)) */
1982 kflags = (key->flags /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
1985 switch (ascii_tolower (op)) {
1989 char buf2[STRING], *p;
2005 while (len > 0 && *cp != ']') {
2014 break; /* not enough space */
2024 if (do_locales && Locale)
2025 setlocale (LC_TIME, Locale);
2030 if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
2031 tt = key->kobj->subkeys->timestamp;
2033 tm = localtime (&tt);
2035 strftime (buf2, sizeof (buf2), dest, tm);
2038 setlocale (LC_TIME, "C");
2040 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2041 snprintf (dest, destlen, fmt, buf2);
2048 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
2049 snprintf (dest, destlen, fmt, entry->num);
2054 /* fixme: we need a way to distinguish between main and subkeys.
2055 Store the idx in entry? */
2056 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2057 snprintf (dest, destlen, fmt, crypt_keyid (key));
2062 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2063 snprintf (dest, destlen, fmt, key->uid);
2068 snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
2069 if (key->kobj->subkeys)
2070 s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
2073 snprintf (dest, destlen, fmt, s);
2078 snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
2079 if (key->kobj->subkeys)
2080 val = key->kobj->subkeys->length;
2083 snprintf (dest, destlen, fmt, val);
2088 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2089 snprintf (dest, destlen, fmt, crypt_flags (kflags));
2091 else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
2096 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2097 snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
2099 else if (!(kflags & (KEYFLAG_ABILITIES)))
2103 if ((kflags & KEYFLAG_ISX509))
2106 gpgme_user_id_t uid = NULL;
2109 for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
2110 i++, uid = uid->next);
2112 switch (uid->validity) {
2113 case GPGME_VALIDITY_UNDEFINED:
2116 case GPGME_VALIDITY_NEVER:
2119 case GPGME_VALIDITY_MARGINAL:
2122 case GPGME_VALIDITY_FULL:
2125 case GPGME_VALIDITY_ULTIMATE:
2128 case GPGME_VALIDITY_UNKNOWN:
2134 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2135 snprintf (dest, destlen, fmt, s ? *s : 'B');
2138 snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2139 snprintf (dest, destlen, fmt,
2140 gpgme_get_protocol_name (key->kobj->protocol));
2147 if (flags & M_FORMAT_OPTIONAL)
2148 m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
2149 mutt_attach_fmt, data, 0);
2153 /* Used by the display fucntion to format a line. */
2154 static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
2156 cryptkey_t **cryptkey_table = (cryptkey_t **) menu->data;
2157 crypt_entry_t entry;
2159 entry.key = cryptkey_table[num];
2160 entry.num = num + 1;
2162 m_strformat(s, l, COLS - SW, PgpEntryFormat, crypt_entry_fmt, &entry,
2163 option(OPTARROWCURSOR) ? M_FORMAT_ARROWCURSOR : 0);
2166 /* Compare two addresses and the keyid to be used for sorting. */
2167 static int _crypt_compare_address (const void *a, const void *b)
2169 cryptkey_t **s = (cryptkey_t **) a;
2170 cryptkey_t **t = (cryptkey_t **) b;
2173 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2176 return m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t)) > 0;
2179 static int crypt_compare_address (const void *a, const void *b)
2181 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_address (a, b)
2182 : _crypt_compare_address (a, b));
2186 /* Compare two key IDs and the addresses to be used for sorting. */
2187 static int _crypt_compare_keyid (const void *a, const void *b)
2189 cryptkey_t **s = (cryptkey_t **) a;
2190 cryptkey_t **t = (cryptkey_t **) b;
2193 if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
2196 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2199 static int crypt_compare_keyid (const void *a, const void *b)
2201 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
2202 : _crypt_compare_keyid (a, b));
2205 /* Compare 2 creation dates and the addresses. For sorting. */
2206 static int _crypt_compare_date (const void *a, const void *b)
2208 cryptkey_t **s = (cryptkey_t **) a;
2209 cryptkey_t **t = (cryptkey_t **) b;
2210 unsigned long ts = 0, tt = 0;
2212 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2213 ts = (*s)->kobj->subkeys->timestamp;
2214 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2215 tt = (*t)->kobj->subkeys->timestamp;
2222 return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2225 static int crypt_compare_date (const void *a, const void *b)
2227 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
2228 : _crypt_compare_date (a, b));
2231 /* Compare two trust values, the key length, the creation dates. the
2232 addresses and the key IDs. For sorting. */
2233 static int _crypt_compare_trust (const void *a, const void *b)
2235 cryptkey_t **s = (cryptkey_t **) a;
2236 cryptkey_t **t = (cryptkey_t **) b;
2237 unsigned long ts = 0, tt = 0;
2240 if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
2241 - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
2244 if ((*s)->kobj->uids)
2245 ts = (*s)->kobj->uids->validity;
2246 if ((*t)->kobj->uids)
2247 tt = (*t)->kobj->uids->validity;
2248 if ((r = (tt - ts)))
2251 if ((*s)->kobj->subkeys)
2252 ts = (*s)->kobj->subkeys->length;
2253 if ((*t)->kobj->subkeys)
2254 tt = (*t)->kobj->subkeys->length;
2258 if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2259 ts = (*s)->kobj->subkeys->timestamp;
2260 if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2261 tt = (*t)->kobj->subkeys->timestamp;
2267 if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2269 return (m_strcasecmp(crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
2272 static int crypt_compare_trust (const void *a, const void *b)
2274 return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
2275 : _crypt_compare_trust (a, b));
2278 /* Print the X.500 Distinguished Name part KEY from the array of parts
2280 static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
2284 for (; dn->key; dn++) {
2285 if (!m_strcmp(dn->key, key)) {
2288 print_utf8 (fp, dn->value, m_strlen(dn->value));
2295 /* Print all parts of a DN in a standard sequence. */
2296 static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
2298 const char *stdpart[] = {
2299 "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
2301 int any = 0, any2 = 0, i;
2303 for (i = 0; stdpart[i]; i++) {
2306 any = print_dn_part (fp, dn, stdpart[i]);
2308 /* now print the rest without any specific ordering */
2309 for (; dn->key; dn++) {
2310 for (i = 0; stdpart[i]; i++) {
2311 if (!m_strcmp(dn->key, stdpart[i]))
2319 any = print_dn_part (fp, dn, dn->key);
2328 /* Parse an RDN; this is a helper to parse_dn(). */
2329 static const unsigned char *parse_dn_part (struct dn_array_s *array,
2330 const unsigned char *string)
2332 const unsigned char *s, *s1;
2336 /* parse attributeType */
2337 for (s = string + 1; *s && *s != '='; s++);
2339 return NULL; /* error */
2342 return NULL; /* empty key */
2343 array->key = p_dupstr(string, n );
2344 p = (unsigned char *) array->key;
2347 if (*string == '#') { /* hexstring */
2349 for (s = string; hexval(*s) >= 0; s++)
2353 return NULL; /* empty or odd number of digits */
2355 p = p_new(unsigned char, n + 1);
2356 array->value = (char *) p;
2357 for (s1 = string; n; s1 += 2, n--)
2358 *p++ = (hexval(*s1) << 8) | hexval(*s1);
2361 else { /* regular v3 quoted string */
2362 for (n = 0, s = string; *s; s++) {
2363 if (*s == '\\') { /* pair */
2365 if (*s == ',' || *s == '=' || *s == '+'
2366 || *s == '<' || *s == '>' || *s == '#' || *s == ';'
2367 || *s == '\\' || *s == '\"' || *s == ' ')
2369 else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
2374 return NULL; /* invalid escape sequence */
2376 else if (*s == '\"')
2377 return NULL; /* invalid encoding */
2378 else if (*s == ',' || *s == '=' || *s == '+'
2379 || *s == '<' || *s == '>' || *s == '#' || *s == ';')
2385 p = p_new(unsigned char, n + 1);
2386 array->value = (char *) p;
2387 for (s = string; n; s++, n--) {
2390 if (hexval(*s) >= 0) {
2391 *p++ = (hexval(*s) << 8) | hexval(*s + 1);
2406 /* Parse a DN and return an array-ized one. This is not a validating
2407 parser and it does not support any old-stylish syntax; gpgme is
2408 expected to return only rfc2253 compatible strings. */
2409 static struct dn_array_s *parse_dn (const unsigned char *string)
2411 struct dn_array_s *array;
2412 ssize_t arrayidx, arraysize;
2415 arraysize = 7; /* C,ST,L,O,OU,CN,email */
2416 array = p_new(struct dn_array_s, arraysize + 1);
2419 while (*string == ' ')
2423 if (arrayidx >= arraysize) { /* mutt lacks a real safe_realoc - so we need to copy */
2424 struct dn_array_s *a2;
2427 a2 = p_new(struct dn_array_s, arraysize + 1);
2428 for (i = 0; i < arrayidx; i++) {
2429 a2[i].key = array[i].key;
2430 a2[i].value = array[i].value;
2435 array[arrayidx].key = NULL;
2436 array[arrayidx].value = NULL;
2437 string = parse_dn_part (array + arrayidx, string);
2441 while (*string == ' ')
2443 if (*string && *string != ',' && *string != ';' && *string != '+')
2444 goto failure; /* invalid delimiter */
2448 array[arrayidx].key = NULL;
2449 array[arrayidx].value = NULL;
2453 for (i = 0; i < arrayidx; i++) {
2454 p_delete(&array[i].key);
2455 p_delete(&array[i].value);
2462 /* Print a nice representation of the USERID and make sure it is
2463 displayed in a proper way, which does mean to reorder some parts
2464 for S/MIME's DNs. USERID is a string as returned by the gpgme key
2465 functions. It is utf-8 encoded. */
2466 static void parse_and_print_user_id(FILE * fp, const char *userid)
2471 if (*userid == '<') {
2472 s = strchr (userid + 1, '>');
2474 print_utf8 (fp, userid + 1, s - userid - 1);
2476 else if (*userid == '(')
2477 fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
2478 else if (*userid & ~127 || __m_strdigits[(int)*userid] == 255)
2479 fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
2481 struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
2484 fputs (_("[Can't display this user ID (invalid DN)]"), fp);
2486 print_dn_parts (fp, dn);
2487 for (i = 0; dn[i].key; i++) {
2488 p_delete(&dn[i].key);
2489 p_delete(&dn[i].value);
2497 KEY_CAP_CAN_ENCRYPT,
2502 static unsigned int key_check_cap(gpgme_key_t key, key_cap_t cap)
2504 gpgme_subkey_t subkey = NULL;
2505 unsigned int ret = 0;
2508 case KEY_CAP_CAN_ENCRYPT:
2509 if (!(ret = key->can_encrypt))
2510 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2511 if ((ret = subkey->can_encrypt))
2514 case KEY_CAP_CAN_SIGN:
2515 if (!(ret = key->can_sign))
2516 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2517 if ((ret = subkey->can_sign))
2520 case KEY_CAP_CAN_CERTIFY:
2521 if (!(ret = key->can_certify))
2522 for (subkey = key->subkeys; subkey; subkey = subkey->next)
2523 if ((ret = subkey->can_certify))
2532 /* Print verbose information about a key or certificate to FP. */
2533 static void print_key_info (gpgme_key_t key, FILE * fp)
2536 const char *s = NULL, *s2 = NULL;
2539 char shortbuf[STRING];
2540 unsigned long aval = 0;
2544 gpgme_user_id_t uid = NULL;
2547 setlocale (LC_TIME, Locale);
2549 is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
2551 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2556 fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
2559 fputs (_("[Invalid]"), fp);
2563 print_utf8 (fp, s, m_strlen(s));
2565 parse_and_print_user_id (fp, s);
2569 if (key->subkeys && (key->subkeys->timestamp > 0)) {
2570 tt = key->subkeys->timestamp;
2572 tm = localtime (&tt);
2573 #ifdef HAVE_LANGINFO_D_T_FMT
2574 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2576 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2578 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2581 if (key->subkeys && (key->subkeys->expires > 0)) {
2582 tt = key->subkeys->expires;
2584 tm = localtime (&tt);
2585 #ifdef HAVE_LANGINFO_D_T_FMT
2586 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2588 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2590 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2594 s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
2598 s2 = is_pgp ? "PGP" : "X.509";
2601 aval = key->subkeys->length;
2603 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
2605 fprintf (fp, _("Key Usage .: "));
2608 if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
2609 fprintf (fp, "%s%s", delim, _("encryption"));
2612 if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
2613 fprintf (fp, "%s%s", delim, _("signing"));
2616 if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
2617 fprintf (fp, "%s%s", delim, _("certification"));
2623 s = key->subkeys->fpr;
2624 fputs (_("Fingerprint: "), fp);
2625 if (is_pgp && m_strlen(s) == 40) {
2626 for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
2631 putc (is_pgp ? ' ' : ':', fp);
2632 if (is_pgp && i == 4)
2637 for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
2640 putc (is_pgp ? ' ' : ':', fp);
2641 if (is_pgp && i == 7)
2645 fprintf (fp, "%s\n", s);
2648 if (key->issuer_serial) {
2649 s = key->issuer_serial;
2651 fprintf (fp, _("Serial-No .: 0x%s\n"), s);
2654 if (key->issuer_name) {
2655 s = key->issuer_name;
2657 fprintf (fp, _("Issued By .: "));
2658 parse_and_print_user_id (fp, s);
2663 /* For PGP we list all subkeys. */
2665 gpgme_subkey_t subkey = NULL;
2667 for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
2671 if (m_strlen(s) == 16)
2672 s += 8; /* display only the short keyID */
2673 fprintf (fp, _("Subkey ....: 0x%s"), s);
2674 if (subkey->revoked) {
2676 fputs (_("[Revoked]"), fp);
2678 if (subkey->invalid) {
2680 fputs (_("[Invalid]"), fp);
2682 if (subkey->expired) {
2684 fputs (_("[Expired]"), fp);
2686 if (subkey->disabled) {
2688 fputs (_("[Disabled]"), fp);
2692 if (subkey->timestamp > 0) {
2693 tt = subkey->timestamp;
2695 tm = localtime (&tt);
2696 #ifdef HAVE_LANGINFO_D_T_FMT
2697 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2699 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2701 fprintf (fp, _("Valid From : %s\n"), shortbuf);
2704 if (subkey->expires > 0) {
2705 tt = subkey->expires;
2707 tm = localtime (&tt);
2708 #ifdef HAVE_LANGINFO_D_T_FMT
2709 strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2711 strftime (shortbuf, sizeof shortbuf, "%c", tm);
2713 fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2717 s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
2722 aval = subkey->length;
2726 fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
2728 fprintf (fp, _("Key Usage .: "));
2731 if (subkey->can_encrypt) {
2732 fprintf (fp, "%s%s", delim, _("encryption"));
2735 if (subkey->can_sign) {
2736 fprintf (fp, "%s%s", delim, _("signing"));
2739 if (subkey->can_certify) {
2740 fprintf (fp, "%s%s", delim, _("certification"));
2748 setlocale (LC_TIME, "C");
2752 /* Show detailed information about the selected key */
2753 static void verify_key (cryptkey_t * key)
2756 char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
2758 gpgme_ctx_t listctx = NULL;
2760 gpgme_key_t k = NULL;
2763 fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2765 mutt_perror (_("Can't create temporary file"));
2768 mutt_message _("Collecting data...");
2770 print_key_info (key->kobj, fp);
2772 err = gpgme_new (&listctx);
2774 fprintf (fp, "Internal error: can't create gpgme context: %s\n",
2775 gpgme_strerror (err));
2778 if ((key->flags & KEYFLAG_ISX509))
2779 gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
2783 while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
2785 err = gpgme_op_keylist_start (listctx, s, 0);
2789 err = gpgme_op_keylist_next (listctx, &k);
2791 fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
2794 gpgme_op_keylist_end (listctx);
2796 print_key_info (k, fp);
2799 fputs (_("Error: certification chain to long - stopping here\n"), fp);
2806 gpgme_release (listctx);
2808 mutt_clear_error ();
2809 snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
2810 mutt_do_pager (cmd, tempfile, 0, NULL);
2813 /* Implementation of `findkeys'. */
2815 static void add_hints(string_array *arr, const char *s)
2821 int l = strcspn(s, " ,.:\"()<>\n");
2822 string_array_append(arr, p_dupstr(s, l));
2824 s += strspn(s, " ,.:\"()<>\n");
2828 /* Return a list of keys which are candidates for the selection. */
2830 get_candidates(string_array *hints, unsigned int app, int secret)
2832 cryptkey_t *res = NULL, **kend = &res;
2837 if (hints->len <= 0)
2839 string_array_append(hints, NULL);
2840 ctx = create_gpgme_context(0);
2842 if ((app & APPLICATION_PGP)) {
2843 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2846 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2847 gpgme_strerror(err));
2852 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2853 gpgme_user_id_t uid = NULL;
2854 unsigned int flags = 0;
2857 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
2858 flags |= KEYFLAG_CANENCRYPT;
2859 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
2860 flags |= KEYFLAG_CANSIGN;
2862 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2863 cryptkey_t *k = p_new(cryptkey_t, 1);
2872 if (gpg_err_code(err) != GPG_ERR_EOF)
2873 mutt_error(_("gpgme_op_keylist_next failed: %s"), gpgme_strerror(err));
2874 gpgme_op_keylist_end(ctx);
2877 if ((app & APPLICATION_SMIME)) {
2878 gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
2879 err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
2882 mutt_error(_("gpgme_op_keylist_start failed: %s"),
2883 gpgme_strerror(err));
2888 while (!(err = gpgme_op_keylist_next(ctx, &key))) {
2889 gpgme_user_id_t uid = NULL;
2890 unsigned int flags = KEYFLAG_ISX509;
2893 if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
2894 flags |= KEYFLAG_CANENCRYPT;
2895 if (key_check_cap(key, KEY_CAP_CAN_SIGN))
2896 flags |= KEYFLAG_CANSIGN;
2898 for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2899 cryptkey_t *k = p_new(cryptkey_t, 1);
2908 if (gpg_err_code(err) != GPG_ERR_EOF)
2909 mutt_error(_("gpgme_op_keylist_next failed: %s"),
2910 gpgme_strerror(err));
2911 gpgme_op_keylist_end(ctx);
2918 /* Display a menu to select a key from the array KEYS. FORCED_VALID
2919 will be set to true on return if the user did override the the
2921 static cryptkey_t *crypt_select_key (cryptkey_t * keys,
2922 address_t * p, const char *s,
2923 unsigned int app, int *forced_valid)
2926 cryptkey_t **cryptkey_table;
2929 char helpstr[STRING], buf[LONG_STRING];
2931 int (*f) (const void *, const void *);
2932 int menu_to_use = 0;
2937 /* build the key table */
2939 cryptkey_table = NULL;
2940 for (k = keys; k; k = k->next) {
2941 if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
2948 p_realloc(&cryptkey_table, keymax);
2951 cryptkey_table[i++] = k;
2954 if (!i && unusable) {
2955 mutt_error _("All matching keys are marked expired/revoked.");
2961 switch (PgpSortKeys & SORT_MASK) {
2963 f = crypt_compare_date;
2966 f = crypt_compare_keyid;
2969 f = crypt_compare_address;
2973 f = crypt_compare_trust;
2976 qsort (cryptkey_table, i, sizeof (cryptkey_t *), f);
2978 if (app & APPLICATION_PGP)
2979 menu_to_use = MENU_KEY_SELECT_PGP;
2980 else if (app & APPLICATION_SMIME)
2981 menu_to_use = MENU_KEY_SELECT_SMIME;
2984 mutt_make_help (buf, sizeof (buf), _("Exit "), menu_to_use, OP_EXIT);
2985 m_strcat(helpstr, sizeof(helpstr), buf);
2986 mutt_make_help (buf, sizeof (buf), _("Select "), menu_to_use,
2987 OP_GENERIC_SELECT_ENTRY);
2988 m_strcat(helpstr, sizeof(helpstr), buf);
2989 mutt_make_help (buf, sizeof (buf), _("Check key "),
2990 menu_to_use, OP_VERIFY_KEY);
2991 m_strcat(helpstr, sizeof(helpstr), buf);
2992 mutt_make_help (buf, sizeof (buf), _("Help"), menu_to_use, OP_HELP);
2993 m_strcat(helpstr, sizeof(helpstr), buf);
2995 menu = mutt_new_menu ();
2997 menu->make_entry = crypt_entry;
2998 menu->menu = menu_to_use;
2999 menu->help = helpstr;
3000 menu->data = cryptkey_table;
3005 if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
3006 ts = _("PGP and S/MIME keys matching");
3007 else if ((app & APPLICATION_PGP))
3008 ts = _("PGP keys matching");
3009 else if ((app & APPLICATION_SMIME))
3010 ts = _("S/MIME keys matching");
3012 ts = _("keys matching");
3015 snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
3017 snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
3021 mutt_clear_error ();
3025 switch (mutt_menuLoop (menu)) {
3027 verify_key (cryptkey_table[menu->current]);
3028 menu->redraw = REDRAW_FULL;
3032 mutt_message ("%s", cryptkey_table[menu->current]->uid);
3035 case OP_GENERIC_SELECT_ENTRY:
3036 /* FIXME make error reporting more verbose - this should be
3037 easy because gpgme provides more information */
3038 if (option (OPTPGPCHECKTRUST)) {
3039 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE ) {
3040 mutt_error(_("This key can't be used: "
3041 "expired/disabled/revoked."));
3046 if (option (OPTPGPCHECKTRUST) &&
3047 ((cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3048 || !crypt_id_is_strong (cryptkey_table[menu->current]))) {
3050 char buff[LONG_STRING];
3052 if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
3053 s = N_("ID is expired/disabled/revoked.");
3055 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
3056 gpgme_user_id_t uid = NULL;
3061 uid = cryptkey_table[menu->current]->kobj->uids;
3062 for (j = 0; (j < cryptkey_table[menu->current]->idx) && uid;
3063 j++, uid = uid->next);
3065 val = uid->validity;
3068 case GPGME_VALIDITY_UNKNOWN:
3069 case GPGME_VALIDITY_UNDEFINED:
3070 warn_s = N_("ID has undefined validity.");
3072 case GPGME_VALIDITY_NEVER:
3073 warn_s = N_("ID is not valid.");
3075 case GPGME_VALIDITY_MARGINAL:
3076 warn_s = N_("ID is only marginally valid.");
3078 case GPGME_VALIDITY_FULL:
3079 case GPGME_VALIDITY_ULTIMATE:
3083 snprintf (buff, sizeof (buff),
3084 _("%s Do you really want to use the key?"), _(warn_s));
3086 if (mutt_yesorno (buff, 0) != 1) {
3087 mutt_clear_error ();
3094 k = cryptkey_dup(cryptkey_table[menu->current]);
3105 mutt_menuDestroy (&menu);
3106 p_delete(&cryptkey_table);
3108 set_option (OPTNEEDREDRAW);
3114 crypt_getkeybyaddr(address_t * a, int abilities, int app, int *forced_valid)
3121 int this_key_has_strong;
3122 int this_key_has_weak;
3123 int this_key_has_invalid;
3126 cryptkey_t *keys, *k;
3127 cryptkey_t *the_valid_key = NULL;
3128 cryptkey_t *matches = NULL;
3129 cryptkey_t **matches_endp = &matches;
3135 string_array_init(&hints);
3136 add_hints(&hints, a->mailbox);
3137 add_hints(&hints, a->personal);
3139 mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
3140 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3141 string_array_wipe(&hints);
3147 for (k = keys; k; k = k->next) {
3148 if (abilities && !(k->flags & abilities)) {
3152 this_key_has_weak = 0; /* weak but valid match */
3153 this_key_has_invalid = 0; /* invalid match */
3154 this_key_has_strong = 0; /* strong and valid match */
3155 match = 0; /* any match */
3157 r = rfc822_parse_adrlist (NULL, k->uid);
3158 for (p = r; p; p = p->next) {
3159 int validity = crypt_id_matches_addr (a, p, k);
3161 if (validity & CRYPT_KV_MATCH) /* something matches */
3164 /* is this key a strong candidate? */
3165 if ((validity & CRYPT_KV_VALID)
3166 && (validity & CRYPT_KV_STRONGID)
3167 && (validity & CRYPT_KV_ADDR)) {
3168 if (the_valid_key && the_valid_key != k)
3171 this_key_has_strong = 1;
3173 else if ((validity & CRYPT_KV_MATCH)
3174 && !(validity & CRYPT_KV_VALID))
3175 this_key_has_invalid = 1;
3176 else if ((validity & CRYPT_KV_MATCH)
3177 && (!(validity & CRYPT_KV_STRONGID)
3178 || !(validity & CRYPT_KV_ADDR)))
3179 this_key_has_weak = 1;
3181 address_list_wipe(&r);
3186 if (!this_key_has_strong && this_key_has_invalid)
3188 if (!this_key_has_strong && this_key_has_weak)
3191 *matches_endp = tmp = cryptkey_dup(k);
3192 matches_endp = &tmp->next;
3193 the_valid_key = tmp;
3196 key_list_wipe(&keys);
3199 if (the_valid_key && !multi && !weak
3200 && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
3202 * There was precisely one strong match on a valid ID, there
3203 * were no valid keys with weak matches, and we aren't
3204 * interested in seeing invalid keys.
3206 * Proceed without asking the user.
3208 k = cryptkey_dup(the_valid_key);
3211 * Else: Ask the user.
3213 k = crypt_select_key (matches, a, NULL, app, forced_valid);
3215 key_list_wipe(&matches);
3225 crypt_getkeybystr(const char *p, int abilities, int app, int *forced_valid)
3228 cryptkey_t *matches = NULL;
3229 cryptkey_t **matches_endp = &matches;
3233 mutt_message (_("Looking for keys matching \"%s\"..."), p);
3239 string_array_init(&hints);
3240 add_hints(&hints, p);
3241 keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
3242 string_array_wipe(&hints);
3248 for (k = keys; k; k = k->next) {
3249 const char *s = crypt_keyid(k);
3251 if (abilities && !(k->flags & abilities))
3256 if (!*p || !m_strcasecmp(p, s)
3257 || (!m_strncasecmp(p, "0x", 2) && !m_strcasecmp(p + 2, s))
3258 || m_stristr(k->uid, p))
3262 *matches_endp = tmp = cryptkey_dup(k);
3263 matches_endp = &tmp->next;
3266 key_list_wipe(&keys);
3269 k = crypt_select_key (matches, NULL, p, app, forced_valid);
3270 key_list_wipe(&matches);
3277 /* Display TAG as a prompt to ask for a key.
3278 * ABILITIES describe the required key abilities (sign, encrypt) and APP the
3279 * type of the requested key; ether S/MIME or PGP.
3280 * Return a copy of the key or NULL if not found. */
3282 crypt_ask_for_key(const char *tag, int abilities, int app, int *forced_valid)
3289 forced_valid = &dummy;
3295 if (mutt_get_field(tag, resp, sizeof(resp), M_CLEAR) != 0)
3298 if (m_strisempty(resp))
3301 if ((key = crypt_getkeybystr(resp, abilities, app, forced_valid)))
3308 /* This routine attempts to find the keyids of the recipients of a
3309 message. It returns NULL if any of the keys can not be found. */
3310 static char *find_keys(ENVELOPE *env, unsigned int app)
3312 address_t *lst = NULL, *addr;
3313 buffer_t *keylist = buffer_new();
3316 address_t **last = &lst;
3317 *last = address_list_dup(env->to);
3318 last = address_list_last(last);
3319 *last = address_list_dup(env->cc);
3320 last = address_list_last(last);
3321 *last = address_list_dup(env->bcc);
3323 rfc822_qualify(lst, mutt_fqdn(1));
3324 address_list_uniq(lst);
3327 while ((addr = address_list_pop(&lst))) {
3329 int forced_valid = 0;
3331 cryptkey_t *key = NULL;
3333 if ((keyID = mutt_crypt_hook(addr))) {
3336 snprintf(buf, sizeof(buf), _("Use keyID = \"%s\" for %s?"), keyID,
3338 r = mutt_yesorno(buf, M_YES);
3341 address_list_wipe(&lst);
3342 address_list_wipe(&addr);
3343 buffer_delete(&keylist);
3349 /* check for e-mail address */
3350 if (strchr(keyID, '@') && (a = rfc822_parse_adrlist(NULL, keyID))) {
3351 rfc822_qualify(a, mutt_fqdn(1));
3352 address_list_wipe(&addr);
3355 key = crypt_getkeybystr(keyID, KEYFLAG_CANENCRYPT, app,
3362 key = crypt_getkeybyaddr(addr, KEYFLAG_CANENCRYPT, app, &forced_valid);
3365 snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), addr->mailbox);
3366 key = crypt_ask_for_key(buf, KEYFLAG_CANENCRYPT, app,
3369 address_list_wipe(&lst);
3370 address_list_wipe(&addr);
3371 buffer_delete(&keylist);
3377 buffer_addch(keylist, ' ');
3378 buffer_addstr(keylist, "0x");
3379 buffer_addstr(keylist, crypt_fpr(key));
3381 buffer_addch(keylist, '!');
3383 key_list_wipe(&key);
3384 address_list_wipe(&addr);
3387 address_list_wipe(&lst);
3388 return buffer_unwrap(&keylist);
3391 int crypt_get_keys(HEADER *msg, char **keylist)
3393 /* Do a quick check to make sure that we can find all of the encryption
3394 * keys if the user has requested this service.
3399 if (msg->security & ENCRYPT) {
3400 if (msg->security & APPLICATION_PGP) {
3401 set_option(OPTPGPCHECKTRUST);
3402 *keylist = find_keys(msg->env, APPLICATION_PGP);
3403 unset_option(OPTPGPCHECKTRUST);
3408 if (msg->security & APPLICATION_SMIME) {
3409 *keylist = find_keys(msg->env, APPLICATION_SMIME);
3419 int crypt_send_menu (HEADER * msg, int *redraw, int is_smime)
3425 if (msg->security & APPLICATION_SMIME)
3427 if (msg->security & APPLICATION_PGP)
3431 ? mutt_multi_choice(_("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
3433 : mutt_multi_choice(_("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
3437 case 1: /* (e)ncrypt */
3438 msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3439 msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
3442 case 2: /* (s)ign */
3443 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3444 msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3447 case 3: /* sign (a)s */
3448 p = crypt_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN,
3449 is_smime ? APPLICATION_SMIME : APPLICATION_PGP,
3452 snprintf(buf, sizeof(buf), "0x%s", crypt_keyid(p));
3453 m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs, buf);
3455 msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3457 *redraw = REDRAW_FULL;
3460 case 4: /* (b)oth */
3462 msg->security = SMIMEENCRYPT | SMIMESIGN;
3464 msg->security = PGPENCRYPT | PGPSIGN;
3468 case 5: /* (p)gp or s/(m)ime */
3469 is_smime = !is_smime;
3472 case 6: /* (c)lear */
3473 return msg->security = 0;
3477 msg->security &= ~APPLICATION_PGP;
3478 msg->security |= APPLICATION_SMIME;
3480 msg->security &= ~APPLICATION_SMIME;
3481 msg->security |= APPLICATION_PGP;
3484 return msg->security;
3487 int crypt_smime_verify_sender(HEADER *h)
3489 address_t *sender = NULL;
3490 unsigned int ret = 1;
3493 h->env->from = mutt_expand_aliases(h->env->from);
3494 sender = h->env->from;
3495 } else if (h->env->sender) {
3496 h->env->sender = mutt_expand_aliases (h->env->sender);
3497 sender = h->env->sender;
3501 mutt_any_key_to_continue ("Failed to figure out sender");
3505 if (signature_key) {
3506 gpgme_key_t key = signature_key;
3507 gpgme_user_id_t uid = NULL;
3508 int sender_length = 0;
3511 sender_length = m_strlen(sender->mailbox);
3512 for (uid = key->uids; uid && ret; uid = uid->next) {
3513 uid_length = m_strlen(uid->email);
3514 if (1 && (uid->email[0] == '<')
3515 && (uid->email[uid_length - 1] == '>')
3516 && (uid_length == sender_length + 2)
3517 && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
3521 mutt_any_key_to_continue ("Failed to verify sender");
3525 if (signature_key) {
3526 gpgme_key_unref(signature_key);
3527 signature_key = NULL;
3532 static void crypt_invoke_import(FILE *stream, int smime)
3534 gpgme_ctx_t ctx = create_gpgme_context(smime);
3538 err = gpgme_data_new_from_stream(&data, stream);
3540 mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
3545 err = gpgme_op_import(ctx, data);
3547 mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
3548 gpgme_data_release(data);
3553 gpgme_data_release(data);
3558 static void pgp_extract_keys_from_attachment(FILE * fp, BODY * top)
3561 FILE *tmpfp = tmpfile();
3563 if (tmpfp == NULL) {
3564 mutt_perror (_("Can't create temporary file"));
3571 mutt_body_handler(top, &s);
3574 crypt_invoke_import(tmpfp, 0);
3578 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
3582 for (; top; top = top->next) {
3583 if (!tag || top->tagged)
3584 pgp_extract_keys_from_attachment (fp, top);
3591 void crypt_invoke_message (int type)
3593 if (type & APPLICATION_PGP) {
3594 mutt_message _("Invoking PGP...");
3596 else if (type & APPLICATION_SMIME) {
3597 mutt_message _("Invoking S/MIME...");
3601 int mutt_protect (HEADER * msg, char *keylist)
3603 BODY *pbody = NULL, *tmp_pbody = NULL;
3604 BODY *tmp_smime_pbody = NULL;
3605 BODY *tmp_pgp_pbody = NULL;
3606 int flags = msg->security;
3611 tmp_smime_pbody = msg->content;
3612 tmp_pgp_pbody = msg->content;
3614 if (msg->security & SIGN) {
3615 if (msg->security & APPLICATION_SMIME) {
3616 if (!(tmp_pbody = sign_message(msg->content, 1)))
3618 pbody = tmp_smime_pbody = tmp_pbody;
3621 if ((msg->security & APPLICATION_PGP)
3622 && (!(flags & ENCRYPT) || option (OPTPGPRETAINABLESIG))) {
3623 if (!(tmp_pbody = sign_message(msg->content, 0)))
3627 pbody = tmp_pgp_pbody = tmp_pbody;
3630 if ((msg->security & APPLICATION_SMIME)
3631 && (msg->security & APPLICATION_PGP)) {
3632 /* here comes the draft ;-) */
3637 if (msg->security & ENCRYPT) {
3638 if ((msg->security & APPLICATION_SMIME)) {
3639 if (!(tmp_pbody = crypt_smime_build_smime_entity (tmp_smime_pbody,
3641 /* signed ? free it! */
3644 /* free tmp_body if messages was signed AND encrypted ... */
3645 if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody) {
3646 /* detatch and dont't delete msg->content,
3647 which tmp_smime_pbody->parts after signing. */
3648 tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
3649 msg->content->next = NULL;
3650 body_list_wipe(&tmp_smime_pbody);
3655 if ((msg->security & APPLICATION_PGP)) {
3656 if (!(pbody = crypt_pgp_encrypt_message (tmp_pgp_pbody, keylist,
3659 /* did we perform a retainable signature? */
3660 if (flags != msg->security) {
3661 /* remove the outer multipart layer */
3662 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3663 /* get rid of the signature */
3664 body_list_wipe(&tmp_pgp_pbody->next);
3670 /* destroy temporary signature envelope when doing retainable
3674 if (flags != msg->security) {
3675 tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3676 body_list_wipe(&tmp_pgp_pbody->next);
3682 msg->content = pbody;
3688 int crypt_query (BODY * m)
3695 if (m->type == TYPEAPPLICATION) {
3696 t |= mutt_is_application_pgp (m);
3698 t |= mutt_is_application_smime (m);
3699 if (t && m->goodsig)
3704 else if (m->type == TYPETEXT) {
3705 t |= mutt_is_application_pgp (m);
3706 if (t && m->goodsig)
3710 if (m->type == TYPEMULTIPART) {
3711 t |= mutt_is_multipart_encrypted (m);
3712 t |= mutt_is_multipart_signed (m);
3714 if (t && m->goodsig)
3718 if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE) {
3722 u = m->parts ? ~0 : 0; /* Bits set in all parts */
3723 w = 0; /* Bits set in any part */
3725 for (p = m->parts; p; p = p->next) {
3726 v = crypt_query (p);
3730 t |= u | (w & ~GOODSIGN);
3732 if ((w & GOODSIGN) && !(u & GOODSIGN))
3740 static void crypt_write_signed(BODY * a, STATE * s, FILE *fp)
3746 fseeko (s->fpin, a->hdr_offset, 0);
3747 bytes = a->length + a->offset - a->hdr_offset;
3750 if ((c = fgetc (s->fpin)) == EOF)
3758 if (c == '\n' && !hadcr)
3767 static void extract_keys_aux(FILE *fpout, HEADER *h)
3769 mutt_parse_mime_message (Context, h);
3772 if (h->security & APPLICATION_PGP) {
3773 mutt_copy_message(fpout, Context, h, M_CM_DECODE | M_CM_CHARCONV, 0);
3776 mutt_endwin (_("Trying to extract PGP keys...\n"));
3779 if (h->security & APPLICATION_SMIME) {
3780 if (h->security & ENCRYPT)
3781 mutt_copy_message (fpout, Context, h, M_CM_NOHEADER
3782 | M_CM_DECODE_CRYPT | M_CM_DECODE_SMIME, 0);
3784 mutt_copy_message(fpout, Context, h, 0, 0);
3787 mutt_message (_("Trying to extract S/MIME certificates...\n"));
3791 crypt_invoke_import(fpout, h->security & APPLICATION_SMIME);
3794 void crypt_extract_keys_from_messages(HEADER * h)
3796 FILE *tmpfp = tmpfile();
3798 mutt_error(_("Could not create temporary file"));
3804 for (i = 0; i < Context->vcount; i++) {
3805 if (!Context->hdrs[Context->v2r[i]]->tagged)
3807 extract_keys_aux(tmpfp, Context->hdrs[Context->v2r[i]]);
3810 extract_keys_aux(tmpfp, h);
3815 mutt_any_key_to_continue(NULL);
3818 static void crypt_fetch_signatures(BODY ***signatures, BODY * a, int *n)
3820 for (; a; a = a->next) {
3821 if (a->type == TYPEMULTIPART) {
3822 crypt_fetch_signatures(signatures, a->parts, n);
3825 p_realloc(signatures, *n + 6);
3827 (*signatures)[(*n)++] = a;
3832 int mutt_signed_handler(BODY *a, STATE *s)
3834 unsigned major, minor;
3836 int rc, i, goodsig = 1, sigcnt = 0;
3839 protocol = parameter_getval(a->parameter, "protocol");
3842 switch (mime_which_token(protocol, -1)) {
3843 case MIME_APPLICATION_PGP_SIGNATURE:
3844 major = TYPEAPPLICATION;
3845 minor = MIME_PGP_SIGNATURE;
3847 case MIME_APPLICATION_X_PKCS7_SIGNATURE:
3848 major = TYPEAPPLICATION;
3849 minor = MIME_X_PKCS7_SIGNATURE;
3851 case MIME_APPLICATION_PKCS7_SIGNATURE:
3852 major = TYPEAPPLICATION;
3853 minor = MIME_PKCS7_SIGNATURE;
3855 case MIME_MULTIPART_MIXED:
3856 major = TYPEMULTIPART;
3861 state_printf(s, _("[-- Error: "
3862 "Unknown multipart/signed protocol %s! --]\n\n"),
3864 return mutt_body_handler (a, s);
3867 /* consistency check */
3868 if (!(a && a->next && a->next->type == major &&
3869 mime_which_token(a->next->subtype, -1) == minor))
3871 state_attach_puts(_("[-- Error: "
3872 "Inconsistent multipart/signed structure! --]\n\n"),
3874 return mutt_body_handler (a, s);
3877 if (s->flags & M_DISPLAY) {
3880 crypt_fetch_signatures (&sigs, a->next, &sigcnt);
3882 FILE *tmpfp = tmpfile();
3885 mutt_error(_("Could not create temporary file"));
3887 crypt_write_signed(a, s, tmpfp);
3889 for (i = 0; i < sigcnt; i++) {
3890 if (sigs[i]->type == TYPEAPPLICATION) {
3893 switch ((subtype = mime_which_token(sigs[i]->subtype, -1))) {
3894 case MIME_PGP_SIGNATURE:
3895 case MIME_X_PKCS7_SIGNATURE:
3896 case MIME_PKCS7_SIGNATURE:
3897 if (crypt_verify_one(sigs[i], s, tmpfp, subtype != MIME_PGP_SIGNATURE) != 0)
3908 state_printf(s, _("[-- Warning: "
3909 "We can't verify %s/%s signatures. --]\n\n"),
3910 TYPE (sigs[i]), sigs[i]->subtype);
3914 b->goodsig = goodsig;
3915 b->badsig = !goodsig;
3917 /* Now display the signed body */
3918 state_attach_puts(_("[-- The following data is signed --]\n\n"), s);
3922 state_attach_puts(_("[-- Warning: Can't find any signatures. --]\n\n"),
3927 rc = mutt_body_handler (a, s);
3929 if (s->flags & M_DISPLAY && sigcnt)
3930 state_attach_puts (_("\n[-- End of signed data --]\n"), s);