b22d616f52dd25c878b2c1fe74f9f05a48a11e75
[apps/madmutt.git] / crypt.c
1 /*
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
9  */
10 /*
11  * Copyright © 2006 Pierre Habouzit
12  */
13
14 #include <lib-lib/lib-lib.h>
15
16 #include <gpgme.h>
17
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>
23
24 #include "crypt.h"
25
26 #include "lib.h"
27 #include "alias.h"
28 #include "handler.h"
29 #include "copy.h"
30 #include "pager.h"
31 #include "recvattach.h"
32 #include "sort.h"
33
34 /* Values used for comparing addresses. */
35 #define CRYPT_KV_VALID    1
36 #define CRYPT_KV_ADDR     2
37 #define CRYPT_KV_STRING   4
38 #define CRYPT_KV_STRONGID 8
39 #define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
40
41 /*
42  * Type definitions.
43  */
44
45 struct crypt_cache {
46   char *what;
47   char *dflt;
48   struct crypt_cache *next;
49 };
50
51 struct dn_array_s {
52   char *key;
53   char *value;
54 };
55
56 /* We work based on user IDs, getting from a user ID to the key is
57    check and does not need any memory (gpgme uses reference counting). */
58 typedef struct crypt_keyinfo {
59   struct crypt_keyinfo *next;
60   gpgme_key_t kobj;
61   int idx;                      /* and the user ID at this index */
62   const char *uid;              /* and for convenience point to this user ID */
63   unsigned int flags;           /* global and per uid flags (for convenience) */
64 } crypt_key_t;
65
66 typedef struct crypt_entry {
67   ssize_t num;
68   crypt_key_t *key;
69 } crypt_entry_t;
70
71
72 static struct crypt_cache *id_defaults = NULL;
73 static gpgme_key_t signature_key = NULL;
74
75 /*
76  * General helper functions.
77  */
78
79 /* return true when S points to a didgit or letter. */
80 static int digit_or_letter (const unsigned char *s)
81 {
82   return ((*s >= '0' && *s <= '9')
83           || (*s >= 'A' && *s <= 'Z')
84           || (*s >= 'a' && *s <= 'z'));
85 }
86
87
88 /* Print the utf-8 encoded string BUF of length LEN bytes to stream
89    FP. Convert the character set. */
90 static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
91 {
92   char *tstr;
93
94   tstr = p_dupstr(buf, len);
95   mutt_convert_string (&tstr, "utf-8", MCharset.charset, M_ICONV_HOOK_FROM);
96   fputs (tstr, fp);
97   p_delete(&tstr);
98 }
99
100
101 /*
102  * Key management.
103  */
104
105 /* Return the keyID for the key K.  Note that this string is valid as
106    long as K is valid */
107 static const char *crypt_keyid (crypt_key_t * k)
108 {
109   const char *s = "????????";
110
111   if (k->kobj && k->kobj->subkeys) {
112     s = k->kobj->subkeys->keyid;
113     if ((!option (OPTPGPLONGIDS)) && (m_strlen(s) == 16))
114       /* Return only the short keyID.  */
115       s += 8;
116   }
117
118   return s;
119 }
120
121 /* Return the hexstring fingerprint from the key K. */
122 static const char *crypt_fpr (crypt_key_t * k)
123 {
124   const char *s = "";
125
126   if (k->kobj && k->kobj->subkeys)
127     s = k->kobj->subkeys->fpr;
128
129   return s;
130 }
131
132 /* Parse FLAGS and return a statically allocated(!) string with them. */
133 static char *crypt_key_abilities (int flags)
134 {
135   static char buff[3];
136
137   if (!(flags & KEYFLAG_CANENCRYPT))
138     buff[0] = '-';
139   else if (flags & KEYFLAG_PREFER_SIGNING)
140     buff[0] = '.';
141   else
142     buff[0] = 'e';
143
144   if (!(flags & KEYFLAG_CANSIGN))
145     buff[1] = '-';
146   else if (flags & KEYFLAG_PREFER_ENCRYPTION)
147     buff[1] = '.';
148   else
149     buff[1] = 's';
150
151   buff[2] = '\0';
152
153   return buff;
154 }
155
156 /* Parse FLAGS and return a character describing the most important flag. */
157 static char crypt_flags (int flags)
158 {
159   if (flags & KEYFLAG_REVOKED)
160     return 'R';
161   else if (flags & KEYFLAG_EXPIRED)
162     return 'X';
163   else if (flags & KEYFLAG_DISABLED)
164     return 'd';
165   else if (flags & KEYFLAG_CRITICAL)
166     return 'c';
167   else
168     return ' ';
169 }
170
171 /* Return a copy of KEY. */
172 static crypt_key_t *crypt_copy_key (crypt_key_t *key)
173 {
174   crypt_key_t *k;
175
176   k = p_new(crypt_key_t, 1);
177   k->kobj = key->kobj;
178   gpgme_key_ref (key->kobj);
179   k->idx = key->idx;
180   k->uid = key->uid;
181   k->flags = key->flags;
182
183   return k;
184 }
185
186 /* Release all the keys at the address of KEYLIST and set the address
187    to NULL. */
188 static void crypt_free_key (crypt_key_t ** keylist)
189 {
190   while (*keylist) {
191     crypt_key_t *k = (*keylist)->next;
192
193     p_delete(&k);
194     *keylist = k;
195   }
196 }
197
198 /* Return trute when key K is valid. */
199 static int crypt_key_is_valid (crypt_key_t * k)
200 {
201   if (k->flags & KEYFLAG_CANTUSE)
202     return 0;
203   return 1;
204 }
205
206 /* Return true whe validity of KEY is sufficient. */
207 static int crypt_id_is_strong (crypt_key_t * key)
208 {
209   gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
210   gpgme_user_id_t uid = NULL;
211   int is_strong = 0;
212   int i = 0;
213
214   if ((key->flags & KEYFLAG_ISX509))
215     return 1;
216
217   for (i = 0, uid = key->kobj->uids; (i < key->idx) && uid;
218        i++, uid = uid->next);
219   if (uid)
220     val = uid->validity;
221
222   switch (val) {
223   case GPGME_VALIDITY_UNKNOWN:
224   case GPGME_VALIDITY_UNDEFINED:
225   case GPGME_VALIDITY_NEVER:
226   case GPGME_VALIDITY_MARGINAL:
227     is_strong = 0;
228     break;
229
230   case GPGME_VALIDITY_FULL:
231   case GPGME_VALIDITY_ULTIMATE:
232     is_strong = 1;
233     break;
234   }
235
236   return is_strong;
237 }
238
239 /* Return true when the KEY is valid, i.e. not marked as unusable. */
240 static int crypt_id_is_valid (crypt_key_t * key)
241 {
242   return !(key->flags & KEYFLAG_CANTUSE);
243 }
244
245 /* Return a bit vector describing how well the addresses ADDR and
246    U_ADDR match and whether KEY is valid. */
247 static int crypt_id_matches_addr (address_t * addr, address_t * u_addr,
248                                   crypt_key_t * key)
249 {
250   int rv = 0;
251
252   if (crypt_id_is_valid (key))
253     rv |= CRYPT_KV_VALID;
254
255   if (crypt_id_is_strong (key))
256     rv |= CRYPT_KV_STRONGID;
257
258   if (addr->mailbox && u_addr->mailbox
259       && m_strcasecmp(addr->mailbox, u_addr->mailbox) == 0)
260     rv |= CRYPT_KV_ADDR;
261
262   if (addr->personal && u_addr->personal
263       && m_strcasecmp(addr->personal, u_addr->personal) == 0)
264     rv |= CRYPT_KV_STRING;
265
266   return rv;
267 }
268
269
270 /*
271  * GPGME convenient functions.
272  */
273
274 /* Create a new gpgme context and return it.  With FOR_SMIME set to
275    true, the protocol of the context is set to CMS. */
276 static gpgme_ctx_t create_gpgme_context (int for_smime)
277 {
278   gpgme_error_t err;
279   gpgme_ctx_t ctx;
280
281   err = gpgme_new (&ctx);
282   if (err) {
283     mutt_error (_("error creating gpgme context: %s\n"), gpgme_strerror (err));
284     sleep (2);
285     mutt_exit (1);
286   }
287
288   if (for_smime) {
289     err = gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
290     if (err) {
291       mutt_error (_("error enabling CMS protocol: %s\n"), gpgme_strerror (err));
292       sleep (2);
293       mutt_exit (1);
294     }
295   }
296
297   return ctx;
298 }
299
300 /* Create a new gpgme data object.  This is a wrapper to die on
301    error. */
302 static gpgme_data_t create_gpgme_data (void)
303 {
304   gpgme_error_t err;
305   gpgme_data_t data;
306
307   err = gpgme_data_new (&data);
308   if (err) {
309     mutt_error (_("error creating gpgme data object: %s\n"),
310                 gpgme_strerror (err));
311     sleep (2);
312     mutt_exit (1);
313   }
314   return data;
315 }
316
317 /* Create a new GPGME Data object from the mail body A.  With CONVERT
318    passed as true, the lines are converted to CR,LF if required.
319    Return NULL on error or the gpgme_data_t object on success. */
320 static gpgme_data_t body_to_data_object (BODY * a, int convert)
321 {
322   char tempfile[_POSIX_PATH_MAX];
323   FILE *fptmp;
324   int err = 0;
325   gpgme_data_t data;
326
327   fptmp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
328   if (!fptmp) {
329     mutt_perror (_("Can't create temporary file"));
330     return NULL;
331   }
332
333   mutt_write_mime_header (a, fptmp);
334   fputc ('\n', fptmp);
335   mutt_write_mime_body (a, fptmp);
336
337   if (convert) {
338     int c, hadcr = 0;
339     unsigned char buf[1];
340
341     data = create_gpgme_data ();
342     rewind (fptmp);
343     while ((c = fgetc (fptmp)) != EOF) {
344       if (c == '\r')
345         hadcr = 1;
346       else {
347         if (c == '\n' && !hadcr) {
348           buf[0] = '\r';
349           gpgme_data_write (data, buf, 1);
350         }
351
352         hadcr = 0;
353       }
354       /* FIXME: This is quite suboptimal */
355       buf[0] = c;
356       gpgme_data_write (data, buf, 1);
357     }
358     gpgme_data_seek (data, 0, SEEK_SET);
359   } else {
360     err = gpgme_data_new_from_file (&data, tempfile, 1);
361   }
362   m_fclose(&fptmp);
363   unlink (tempfile);
364   if (err) {
365     mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
366     return NULL;
367   }
368
369   return data;
370 }
371
372 /* Create a GPGME data object from the stream FP but limit the object
373    to LENGTH bytes starting at OFFSET bytes from the beginning of the
374    file. */
375 static gpgme_data_t file_to_data_object (FILE * fp, long offset, long length)
376 {
377   int err = 0;
378   gpgme_data_t data;
379
380   err = gpgme_data_new_from_filepart (&data, NULL, fp, offset, length);
381   if (err) {
382     mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
383     return NULL;
384   }
385
386   return data;
387 }
388
389 /* Write a GPGME data object to the stream FP. */
390 static int data_object_to_stream (gpgme_data_t data, FILE * fp)
391 {
392   int err;
393   char buf[4096], *p;
394   ssize_t nread;
395
396   err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
397          ? gpgme_error_from_errno (errno) : 0);
398   if (err) {
399     mutt_error (_("error rewinding data object: %s\n"), gpgme_strerror (err));
400     return -1;
401   }
402
403   while ((nread = gpgme_data_read (data, buf, sizeof (buf)))) {
404     /* fixme: we are not really converting CRLF to LF but just
405        skipping CR. Doing it correctly needs a more complex logic */
406     for (p = buf; nread; p++, nread--) {
407       if (*p != '\r')
408         putc (*p, fp);
409     }
410
411     if (ferror (fp)) {
412       mutt_perror ("[tempfile]");
413       return -1;
414     }
415   }
416   if (nread == -1) {
417     mutt_error (_("error reading data object: %s\n"), strerror (errno));
418     return -1;
419   }
420   return 0;
421 }
422
423 /* Copy a data object to a newly created temporay file and return that
424    filename. Caller must free.  With RET_FP not NULL, don't close the
425    stream but return it there. */
426 static char *data_object_to_tempfile (gpgme_data_t data, FILE ** ret_fp)
427 {
428   int err;
429   char tempfile[_POSIX_PATH_MAX];
430   FILE *fp;
431   ssize_t nread = 0;
432
433   fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
434   if (!fp) {
435     mutt_perror (_("Can't create temporary file"));
436     return NULL;
437   }
438
439   err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
440          ? gpgme_error_from_errno (errno) : 0);
441   if (!err) {
442     char buf[4096];
443
444     while ((nread = gpgme_data_read (data, buf, sizeof (buf)))) {
445       if (fwrite (buf, nread, 1, fp) != 1) {
446         mutt_perror (_("Can't create temporary file"));
447         m_fclose(&fp);
448         unlink (tempfile);
449         return NULL;
450       }
451     }
452   }
453   if (ret_fp)
454     rewind (fp);
455   else
456     m_fclose(&fp);
457   if (nread == -1) {
458     mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
459     unlink (tempfile);
460     m_fclose(&fp);
461     return NULL;
462   }
463   if (ret_fp)
464     *ret_fp = fp;
465   return m_strdup(tempfile);
466 }
467
468
469 /* FIXME: stolen from gpgme to avoid "ambiguous identity" errors */
470 static gpgme_error_t
471 gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
472                int secret)
473 {
474   gpgme_ctx_t listctx;
475   gpgme_error_t err;
476
477   if (!ctx || !r_key || !fpr)
478     return gpg_error (GPG_ERR_INV_VALUE);
479
480   if (strlen (fpr) < 8) /* We have at least a key ID.  */
481     return gpg_error (GPG_ERR_INV_VALUE);
482
483   /* FIXME: We use our own context because we have to avoid the user's
484      I/O callback handlers.  */
485   err = gpgme_new (&listctx);
486   if (err)
487     return err;
488   gpgme_set_protocol (listctx, gpgme_get_protocol (ctx));
489   err = gpgme_op_keylist_start (listctx, fpr, secret);
490   if (!err)
491     err = gpgme_op_keylist_next (listctx, r_key);
492   gpgme_release (listctx);
493   return err;
494 }
495
496 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
497    The keys must be space delimited. */
498 static gpgme_key_t *create_recipient_set (const char *keylist,
499                                           gpgme_protocol_t protocol)
500 {
501   int err;
502   const char *s;
503   char buf[100];
504   int i;
505   gpgme_key_t *rset = NULL;
506   unsigned int rset_n = 0;
507   gpgme_key_t key = NULL;
508   gpgme_ctx_t context = NULL;
509
510   err = gpgme_new (&context);
511   if (!err)
512     err = gpgme_set_protocol (context, protocol);
513
514   if (!err) {
515     s = keylist;
516     do {
517       while (*s == ' ')
518         s++;
519       for (i = 0; *s && *s != ' ' && i < ssizeof(buf) - 1;)
520         buf[i++] = *s++;
521       buf[i] = 0;
522       if (*buf) {
523         if (i > 1 && buf[i - 1] == '!') {
524           /* The user selected to override the valididy of that
525              key. */
526           buf[i - 1] = 0;
527
528           err = gpgme_get_key2 (context, buf, &key, 0);
529           if (!err)
530             key->uids->validity = GPGME_VALIDITY_FULL;
531           buf[i - 1] = '!';
532         }
533         else
534           err = gpgme_get_key2 (context, buf, &key, 0);
535
536         if (!err) {
537           p_realloc(&rset, rset_n + 1);
538           rset[rset_n++] = key;
539         }
540         else {
541           mutt_error (_("error adding recipient `%s': %s\n"),
542                       buf, gpgme_strerror (err));
543           p_delete(&rset);
544           return NULL;
545         }
546       }
547     } while (*s);
548   }
549
550   /* NULL terminate.  */
551   p_realloc(&rset, rset_n + 1);
552   rset[rset_n++] = NULL;
553
554   if (context)
555     gpgme_release (context);
556
557   return rset;
558 }
559
560
561 /* Make sure that the correct signer is set. Returns 0 on success. */
562 static int set_signer (gpgme_ctx_t ctx, int for_smime)
563 {
564   char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
565   gpgme_error_t err;
566   gpgme_ctx_t listctx;
567   gpgme_key_t key, key2;
568
569   if (!signid || !*signid)
570     return 0;
571
572   listctx = create_gpgme_context (for_smime);
573   err = gpgme_op_keylist_start (listctx, signid, 1);
574   if (!err)
575     err = gpgme_op_keylist_next (listctx, &key);
576   if (err) {
577     gpgme_release (listctx);
578     mutt_error (_("secret key `%s' not found: %s\n"),
579                 signid, gpgme_strerror (err));
580     return -1;
581   }
582   err = gpgme_op_keylist_next (listctx, &key2);
583   if (!err) {
584     gpgme_key_release (key);
585     gpgme_key_release (key2);
586     gpgme_release (listctx);
587     mutt_error (_("ambiguous specification of secret key `%s'\n"), signid);
588     return -1;
589   }
590   gpgme_op_keylist_end (listctx);
591   gpgme_release (listctx);
592
593   gpgme_signers_clear (ctx);
594   err = gpgme_signers_add (ctx, key);
595   gpgme_key_release (key);
596   if (err) {
597     mutt_error (_("error setting secret key `%s': %s\n"),
598                 signid, gpgme_strerror (err));
599     return -1;
600   }
601   return 0;
602 }
603
604
605 /* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
606    and return an allocated filename to a temporary file containing the
607    enciphered text.  With USE_SMIME set to true, the smime backend is
608    used.  With COMBINED_SIGNED a PGP message is signed and
609    encrypted.  Returns NULL in case of error */
610 static char *encrypt_gpgme_object (gpgme_data_t plaintext, gpgme_key_t * rset,
611                                    int use_smime, int combined_signed)
612 {
613   int err;
614   gpgme_ctx_t ctx;
615   gpgme_data_t ciphertext;
616   char *outfile;
617
618   ctx = create_gpgme_context (use_smime);
619   if (!use_smime)
620     gpgme_set_armor (ctx, 1);
621
622   ciphertext = create_gpgme_data ();
623
624   if (combined_signed) {
625     if (set_signer (ctx, use_smime)) {
626       gpgme_data_release (ciphertext);
627       gpgme_release (ctx);
628       return NULL;
629     }
630     err = gpgme_op_encrypt_sign (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
631                                  plaintext, ciphertext);
632   }
633   else
634     err = gpgme_op_encrypt (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
635                             plaintext, ciphertext);
636   mutt_need_hard_redraw ();
637   if (err) {
638     mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
639     gpgme_data_release (ciphertext);
640     gpgme_release (ctx);
641     return NULL;
642   }
643
644   gpgme_release (ctx);
645
646   outfile = data_object_to_tempfile (ciphertext, NULL);
647   gpgme_data_release (ciphertext);
648   return outfile;
649 }
650
651 /* Find the "micalg" parameter from the last Gpgme operation on
652    context CTX.  It is expected that this operation was a sign
653    operation.  Return the algorithm name as a C string in buffer BUF
654    which must have been allocated by the caller with size BUFLEN.
655    Returns 0 on success or -1 in case of an error.  The return string
656    is truncted to BUFLEN - 1. */
657 static int get_micalg (gpgme_ctx_t ctx, char *buf, ssize_t buflen)
658 {
659   gpgme_sign_result_t result = NULL;
660   const char *algorithm_name = NULL;
661
662   if (!buflen)
663     return -1;
664
665   *buf = 0;
666   result = gpgme_op_sign_result (ctx);
667   if (result) {
668     algorithm_name = gpgme_hash_algo_name (result->signatures->hash_algo);
669     if (algorithm_name) {
670       m_strcpy(buf, buflen, algorithm_name);
671     }
672   }
673
674   return *buf ? 0 : -1;
675 }
676
677 static void print_time (time_t t, STATE * s)
678 {
679   char p[STRING];
680
681   setlocale (LC_TIME, "");
682 #ifdef HAVE_LANGINFO_D_T_FMT
683   strftime (p, sizeof (p), nl_langinfo (D_T_FMT), localtime (&t));
684 #else
685   strftime (p, sizeof (p), "%c", localtime (&t));
686 #endif
687   setlocale (LC_TIME, "C");
688   state_attach_puts (p, s);
689 }
690
691 /* Implementation of `sign_message'. */
692
693 /* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
694    USE_SMIME is passed as true.  Returns the new body or NULL on
695    error. */
696 static BODY *sign_message (BODY * a, int use_smime)
697 {
698   BODY *t;
699   char *sigfile;
700   int err = 0;
701   char buf[100];
702   gpgme_ctx_t ctx;
703   gpgme_data_t message, signature;
704
705   convert_to_7bit (a);          /* Signed data _must_ be in 7-bit format. */
706
707   message = body_to_data_object (a, 1);
708   if (!message)
709     return NULL;
710   signature = create_gpgme_data ();
711
712   ctx = create_gpgme_context (use_smime);
713   if (!use_smime)
714     gpgme_set_armor (ctx, 1);
715
716   if (set_signer (ctx, use_smime)) {
717     gpgme_data_release (signature);
718     gpgme_release (ctx);
719     return NULL;
720   }
721
722   err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH);
723   mutt_need_hard_redraw ();
724   gpgme_data_release (message);
725   if (err) {
726     gpgme_data_release (signature);
727     gpgme_release (ctx);
728     mutt_error (_("error signing data: %s\n"), gpgme_strerror (err));
729     return NULL;
730   }
731
732   sigfile = data_object_to_tempfile (signature, NULL);
733   gpgme_data_release (signature);
734   if (!sigfile) {
735     gpgme_release (ctx);
736     return NULL;
737   }
738
739   t = body_new();
740   t->type = TYPEMULTIPART;
741   t->subtype = m_strdup("signed");
742   t->encoding = ENC7BIT;
743   t->use_disp = 0;
744   t->disposition = DISPINLINE;
745
746   parameter_set_boundary(&t->parameter);
747   parameter_setval(&t->parameter, "protocol",
748                    use_smime ? "application/pkcs7-signature"
749                              : "application/pgp-signature");
750   /* Get the micalg from gpgme.  Old gpgme versions don't support this
751      for S/MIME so we assume sha-1 in this case. */
752   if (!get_micalg (ctx, buf, sizeof buf))
753     parameter_setval(&t->parameter, "micalg", buf);
754   else if (use_smime)
755     parameter_setval(&t->parameter, "micalg", "sha1");
756   gpgme_release (ctx);
757
758   t->parts = a;
759   a = t;
760
761   t->parts->next = body_new();
762   t = t->parts->next;
763   t->type = TYPEAPPLICATION;
764   if (use_smime) {
765     t->subtype = m_strdup("pkcs7-signature");
766     parameter_setval(&t->parameter, "name", "smime.p7s");
767     t->encoding = ENCBASE64;
768     t->use_disp = 1;
769     t->disposition = DISPATTACH;
770     t->d_filename = m_strdup("smime.p7s");
771   }
772   else {
773     t->subtype = m_strdup("pgp-signature");
774     t->use_disp = 0;
775     t->disposition = DISPINLINE;
776     t->encoding = ENC7BIT;
777   }
778   t->filename = sigfile;
779   t->unlink = 1;                /* ok to remove this file after sending. */
780
781   return a;
782 }
783
784
785 BODY *crypt_pgp_sign_message (BODY * a)
786 {
787   return sign_message (a, 0);
788 }
789
790 BODY *crypt_smime_sign_message (BODY * a)
791 {
792   return sign_message (a, 1);
793 }
794
795 /*
796  * Implementation of `encrypt_message'.
797  */
798
799 /* Encrypt the mail body A to all keys given as space separated keyids
800    or fingerprints in KEYLIST and return the encrypted body.  */
801 BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
802 {
803   char *outfile = NULL;
804   BODY *t;
805   gpgme_key_t *rset = NULL;
806   gpgme_data_t plaintext;
807
808   rset = create_recipient_set (keylist, GPGME_PROTOCOL_OpenPGP);
809   if (!rset)
810     return NULL;
811
812   if (sign)
813     convert_to_7bit (a);
814   plaintext = body_to_data_object (a, 0);
815   if (!plaintext) {
816     p_delete(&rset);
817     return NULL;
818   }
819
820   outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
821   gpgme_data_release (plaintext);
822   p_delete(&rset);
823   if (!outfile)
824     return NULL;
825
826   t = body_new();
827   t->type = TYPEMULTIPART;
828   t->subtype = m_strdup("encrypted");
829   t->encoding = ENC7BIT;
830   t->use_disp = 0;
831   t->disposition = DISPINLINE;
832
833   parameter_set_boundary(&t->parameter);
834   parameter_setval(&t->parameter, "protocol", "application/pgp-encrypted");
835
836   t->parts = body_new();
837   t->parts->type = TYPEAPPLICATION;
838   t->parts->subtype = m_strdup("pgp-encrypted");
839   t->parts->encoding = ENC7BIT;
840
841   t->parts->next = body_new();
842   t->parts->next->type = TYPEAPPLICATION;
843   t->parts->next->subtype = m_strdup("octet-stream");
844   t->parts->next->encoding = ENC7BIT;
845   t->parts->next->filename = outfile;
846   t->parts->next->use_disp = 1;
847   t->parts->next->disposition = DISPINLINE;
848   t->parts->next->unlink = 1;   /* delete after sending the message */
849   t->parts->next->d_filename = m_strdup("msg.asc"); /* non pgp/mime
850                                                            can save */
851
852   return t;
853 }
854
855 /*
856  * Implementation of `smime_build_smime_entity'.
857  */
858
859 /* Encrypt the mail body A to all keys given as space separated
860    fingerprints in KEYLIST and return the S/MIME encrypted body.  */
861 BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
862 {
863   char *outfile = NULL;
864   BODY *t;
865   gpgme_key_t *rset = NULL;
866   gpgme_data_t plaintext;
867
868   rset = create_recipient_set (keylist, GPGME_PROTOCOL_CMS);
869   if (!rset)
870     return NULL;
871
872   plaintext = body_to_data_object (a, 0);
873   if (!plaintext) {
874     p_delete(&rset);
875     return NULL;
876   }
877
878   outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
879   gpgme_data_release (plaintext);
880   p_delete(&rset);
881   if (!outfile)
882     return NULL;
883
884   t = body_new();
885   t->type = TYPEAPPLICATION;
886   t->subtype = m_strdup("pkcs7-mime");
887   parameter_setval(&t->parameter, "name", "smime.p7m");
888   parameter_setval(&t->parameter, "smime-type", "enveloped-data");
889   t->encoding = ENCBASE64;      /* The output of OpenSSL SHOULD be binary */
890   t->use_disp = 1;
891   t->disposition = DISPATTACH;
892   t->d_filename = m_strdup("smime.p7m");
893   t->filename = outfile;
894   t->unlink = 1;                /*delete after sending the message */
895   t->parts = 0;
896   t->next = 0;
897
898   return t;
899 }
900
901
902 /* Implementation of `verify_one'. */
903
904 /* Display the common attributes of the signature summary SUM.
905    Return 1 if there is is a severe warning.
906  */
907 static int show_sig_summary (unsigned long sum,
908                              gpgme_ctx_t ctx, gpgme_key_t key, int idx,
909                              STATE * s)
910 {
911   int severe = 0;
912
913   if ((sum & GPGME_SIGSUM_KEY_REVOKED)) {
914     state_attach_puts (_("Warning: One of the keys has been revoked\n"), s);
915     severe = 1;
916   }
917
918   if ((sum & GPGME_SIGSUM_KEY_EXPIRED)) {
919     time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
920
921     if (at) {
922       state_attach_puts (_("Warning: The key used to create the "
923                            "signature expired at: "), s);
924       print_time (at, s);
925       state_attach_puts ("\n", s);
926     }
927     else
928       state_attach_puts (_("Warning: At least one certification key "
929                            "has expired\n"), s);
930   }
931
932   if ((sum & GPGME_SIGSUM_SIG_EXPIRED)) {
933     gpgme_verify_result_t result;
934     gpgme_signature_t sig;
935     int i;
936
937     result = gpgme_op_verify_result (ctx);
938
939     for (sig = result->signatures, i = 0; sig && (i < idx);
940          sig = sig->next, i++);
941
942     state_attach_puts (_("Warning: The signature expired at: "), s);
943     print_time (sig ? sig->exp_timestamp : 0, s);
944     state_attach_puts ("\n", s);
945   }
946
947   if ((sum & GPGME_SIGSUM_KEY_MISSING))
948     state_attach_puts (_("Can't verify due to a missing "
949                          "key or certificate\n"), s);
950
951   if ((sum & GPGME_SIGSUM_CRL_MISSING)) {
952     state_attach_puts (_("The CRL is not available\n"), s);
953     severe = 1;
954   }
955
956   if ((sum & GPGME_SIGSUM_CRL_TOO_OLD)) {
957     state_attach_puts (_("Available CRL is too old\n"), s);
958     severe = 1;
959   }
960
961   if ((sum & GPGME_SIGSUM_BAD_POLICY))
962     state_attach_puts (_("A policy requirement was not met\n"), s);
963
964   if ((sum & GPGME_SIGSUM_SYS_ERROR)) {
965     const char *t0 = NULL, *t1 = NULL;
966     gpgme_verify_result_t result;
967     gpgme_signature_t sig;
968     int i;
969
970     state_attach_puts (_("A system error occurred"), s);
971
972     /* Try to figure out some more detailed system error information. */
973     result = gpgme_op_verify_result (ctx);
974     for (sig = result->signatures, i = 0; sig && (i < idx);
975          sig = sig->next, i++);
976     if (sig) {
977       t0 = "";
978       t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
979     }
980
981     if (t0 || t1) {
982       state_attach_puts (": ", s);
983       if (t0)
984         state_attach_puts (t0, s);
985       if (t1 && !(t0 && !m_strcmp(t0, t1))) {
986         if (t0)
987           state_attach_puts (",", s);
988         state_attach_puts (t1, s);
989       }
990     }
991     state_attach_puts ("\n", s);
992   }
993
994   return severe;
995 }
996
997
998 static void show_fingerprint (gpgme_key_t key, STATE * state)
999 {
1000   const char *s;
1001   int i, is_pgp;
1002   char *buf, *p;
1003   const char *prefix = _("Fingerprint: ");
1004   ssize_t bufsize;
1005
1006   if (!key)
1007     return;
1008   s = key->subkeys ? key->subkeys->fpr : NULL;
1009   if (!s)
1010     return;
1011   is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
1012
1013   bufsize = m_strlen(prefix) + m_strlen(s) * 4 + 2;
1014   buf = p_new(char, bufsize);
1015   m_strcpy(buf, bufsize, prefix);
1016   p = buf + m_strlen(buf);
1017   if (is_pgp && m_strlen(s) == 40) {     /* PGP v4 style formatted. */
1018     for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
1019       *p++ = s[0];
1020       *p++ = s[1];
1021       *p++ = s[2];
1022       *p++ = s[3];
1023       *p++ = ' ';
1024       if (i == 4)
1025         *p++ = ' ';
1026     }
1027   }
1028   else {
1029     for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
1030       *p++ = s[0];
1031       *p++ = s[1];
1032       *p++ = is_pgp ? ' ' : ':';
1033       if (is_pgp && i == 7)
1034         *p++ = ' ';
1035     }
1036   }
1037
1038   /* just in case print remaining odd digits */
1039   for (; *s; s++)
1040     *p++ = *s;
1041   *p++ = '\n';
1042   *p = 0;
1043   state_attach_puts (buf, state);
1044   p_delete(&buf);
1045 }
1046
1047 /* Show the valididy of a key used for one signature. */
1048 static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE * s)
1049 {
1050   gpgme_verify_result_t result = NULL;
1051   gpgme_signature_t sig = NULL;
1052   const char *txt = NULL;
1053
1054   result = gpgme_op_verify_result (ctx);
1055   if (result)
1056     for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
1057
1058   switch (sig ? sig->validity : 0) {
1059   case GPGME_VALIDITY_UNKNOWN:
1060     txt = _("WARNING: We have NO indication whether "
1061             "the key belongs to the person named " "as shown above\n");
1062     break;
1063   case GPGME_VALIDITY_UNDEFINED:
1064     break;
1065   case GPGME_VALIDITY_NEVER:
1066     txt = _("WARNING: The key does NOT BELONG to "
1067             "the person named as shown above\n");
1068     break;
1069   case GPGME_VALIDITY_MARGINAL:
1070     txt = _("WARNING: It is NOT certain that the key "
1071             "belongs to the person named as shown above\n");
1072     break;
1073   case GPGME_VALIDITY_FULL:
1074   case GPGME_VALIDITY_ULTIMATE:
1075     txt = NULL;
1076     break;
1077   }
1078   if (txt)
1079     state_attach_puts (txt, s);
1080 }
1081
1082 /* Show information about one signature.  This fucntion is called with
1083    the context CTX of a sucessful verification operation and the
1084    enumerator IDX which should start at 0 and incremete for each
1085    call/signature.
1086
1087    Return values are: 0 for normal procession, 1 for a bad signature,
1088    2 for a signature with a warning or -1 for no more signature.  */
1089 static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
1090 {
1091   time_t created;
1092   const char *fpr, *uid;
1093   gpgme_key_t key = NULL;
1094   int i, anybad = 0, anywarn = 0;
1095   unsigned int sum;
1096   gpgme_user_id_t uids = NULL;
1097   gpgme_verify_result_t result;
1098   gpgme_signature_t sig;
1099   gpgme_error_t err = GPG_ERR_NO_ERROR;
1100
1101   result = gpgme_op_verify_result (ctx);
1102   if (result) {
1103     /* FIXME: this code should use a static variable and remember
1104        the current position in the list of signatures, IMHO.
1105        -moritz.  */
1106
1107     for (i = 0, sig = result->signatures; sig && (i < idx);
1108          i++, sig = sig->next);
1109     if (!sig)
1110       return -1;                /* Signature not found.  */
1111
1112     if (signature_key) {
1113       gpgme_key_release (signature_key);
1114       signature_key = NULL;
1115     }
1116
1117     created = sig->timestamp;
1118     fpr = sig->fpr;
1119     sum = sig->summary;
1120
1121     if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
1122       anybad = 1;
1123
1124     err = gpgme_get_key2 (ctx, fpr, &key, 0);    /* secret key?  */
1125     if (!err) {
1126       uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
1127       if (!signature_key)
1128         signature_key = key;
1129     }
1130     else {
1131       key = NULL;               /* Old gpgme versions did not set KEY to NULL on
1132                                    error.   Do it here to avoid a double free. */
1133       uid = "[?]";
1134     }
1135
1136     if (!s || !s->fpout || !(s->flags & M_DISPLAY));    /* No state information so no way to print anything. */
1137     else if (err) {
1138       state_attach_puts (_("Error getting key information: "), s);
1139       state_attach_puts (gpg_strerror (err), s);
1140       state_attach_puts ("\n", s);
1141       anybad = 1;
1142     }
1143     else if ((sum & GPGME_SIGSUM_GREEN)) {
1144       state_attach_puts (_("Good signature from: "), s);
1145       state_attach_puts (uid, s);
1146       state_attach_puts ("\n", s);
1147       for (i = 1, uids = key->uids; uids; i++, uids = uids->next) {
1148         if (i == 1)
1149           /* Skip primary UID.  */
1150           continue;
1151         if (uids->revoked)
1152           continue;
1153         state_attach_puts (_("                aka: "), s);
1154         state_attach_puts (uids->uid, s);
1155         state_attach_puts ("\n", s);
1156       }
1157       state_attach_puts (_("            created: "), s);
1158       print_time (created, s);
1159       state_attach_puts ("\n", s);
1160       if (show_sig_summary (sum, ctx, key, idx, s))
1161         anywarn = 1;
1162       show_one_sig_validity (ctx, idx, s);
1163     }
1164     else if ((sum & GPGME_SIGSUM_RED)) {
1165       state_attach_puts (_("*BAD* signature claimed to be from: "), s);
1166       state_attach_puts (uid, s);
1167       state_attach_puts ("\n", s);
1168       show_sig_summary (sum, ctx, key, idx, s);
1169     }
1170     else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP)) {     /* We can't decide (yellow) but this is a PGP key with a good
1171                                                                                    signature, so we display what a PGP user expects: The name,
1172                                                                                    fingerprint and the key validity (which is neither fully or
1173                                                                                    ultimate). */
1174       state_attach_puts (_("Good signature from: "), s);
1175       state_attach_puts (uid, s);
1176       state_attach_puts ("\n", s);
1177       state_attach_puts (_("            created: "), s);
1178       print_time (created, s);
1179       state_attach_puts ("\n", s);
1180       show_one_sig_validity (ctx, idx, s);
1181       show_fingerprint (key, s);
1182       if (show_sig_summary (sum, ctx, key, idx, s))
1183         anywarn = 1;
1184     }
1185     else {                      /* can't decide (yellow) */
1186
1187       state_attach_puts (_("Error checking signature"), s);
1188       state_attach_puts ("\n", s);
1189       show_sig_summary (sum, ctx, key, idx, s);
1190     }
1191
1192     if (key != signature_key)
1193       gpgme_key_release (key);
1194   }
1195
1196   return anybad ? 1 : anywarn ? 2 : 0;
1197 }
1198
1199 /* Do the actual verification step. With IS_SMIME set to true we
1200    assume S/MIME (surprise!) */
1201 int crypt_verify_one(BODY *sigbdy, STATE *s, FILE *fp, int is_smime)
1202 {
1203   int badsig = -1;
1204   int anywarn = 0;
1205   int err;
1206   gpgme_ctx_t ctx;
1207   gpgme_data_t signature, message;
1208
1209   signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
1210   if (!signature)
1211     return -1;
1212
1213   /* We need to tell gpgme about the encoding because the backend can't
1214      auto-detect plain base-64 encoding which is used by S/MIME. */
1215   if (is_smime)
1216     gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
1217
1218   err = gpgme_data_new_from_stream(&message, fp);
1219   if (err) {
1220     gpgme_data_release (signature);
1221     mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
1222     return -1;
1223   }
1224   ctx = create_gpgme_context (is_smime);
1225
1226   /* Note: We don't need a current time output because GPGME avoids
1227      such an attack by separating the meta information from the
1228      data. */
1229   state_attach_puts (_("[-- Begin signature information --]\n"), s);
1230
1231   err = gpgme_op_verify (ctx, signature, message, NULL);
1232   mutt_need_hard_redraw ();
1233   if (err) {
1234     char buf[200];
1235
1236     snprintf (buf, sizeof (buf) - 1,
1237               _("Error: verification failed: %s\n"), gpgme_strerror (err));
1238     state_attach_puts (buf, s);
1239   }
1240   else {                        /* Verification succeeded, see what the result is. */
1241     int res, idx;
1242     int anybad = 0;
1243
1244     if (signature_key) {
1245       gpgme_key_release (signature_key);
1246       signature_key = NULL;
1247     }
1248
1249     for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1250       if (res == 1)
1251         anybad = 1;
1252       else if (res == 2)
1253         anywarn = 2;
1254     }
1255     if (!anybad)
1256       badsig = 0;
1257   }
1258
1259   if (!badsig) {
1260     gpgme_verify_result_t result;
1261     gpgme_sig_notation_t notation;
1262     gpgme_signature_t sig;
1263
1264     result = gpgme_op_verify_result (ctx);
1265     if (result) {
1266       for (sig = result->signatures; sig; sig = sig->next) {
1267         if (sig->notations) {
1268           state_attach_puts ("*** Begin Notation (signature by: ", s);
1269           state_attach_puts (sig->fpr, s);
1270           state_attach_puts (") ***\n", s);
1271           for (notation = sig->notations; notation; notation = notation->next)
1272           {
1273             if (notation->name) {
1274               state_attach_puts (notation->name, s);
1275               state_attach_puts ("=", s);
1276             }
1277             if (notation->value) {
1278               state_attach_puts (notation->value, s);
1279               if (!(*notation->value
1280                     && (notation->value[m_strlen(notation->value) - 1] ==
1281                         '\n')))
1282                 state_attach_puts ("\n", s);
1283             }
1284           }
1285           state_attach_puts ("*** End Notation ***\n", s);
1286         }
1287       }
1288     }
1289   }
1290
1291   gpgme_release (ctx);
1292
1293   state_attach_puts (_("[-- End signature information --]\n\n"), s);
1294
1295   return badsig ? 1 : anywarn ? 2 : 0;
1296 }
1297
1298 /*
1299  * Implementation of `decrypt_part'.
1300  */
1301
1302 /* Decrypt a PGP or SMIME message (depending on the boolean flag
1303    IS_SMIME) with body A described further by state S.  Write
1304    plaintext out to file FPOUT and return a new body.  For PGP returns
1305    a flag in R_IS_SIGNED to indicate whether this is a combined
1306    encrypted and signed message, for S/MIME it returns true when it is
1307    not a encrypted but a signed message.  */
1308 static BODY *decrypt_part (BODY * a, STATE * s, FILE * fpout, int is_smime,
1309                            int *r_is_signed)
1310 {
1311   struct stat info;
1312   BODY *tattach;
1313   int err = 0;
1314   gpgme_ctx_t ctx;
1315   gpgme_data_t ciphertext, plaintext;
1316   int maybe_signed = 0;
1317   int anywarn = 0;
1318   int sig_stat = 0;
1319
1320   if (r_is_signed)
1321     *r_is_signed = 0;
1322
1323   ctx = create_gpgme_context (is_smime);
1324
1325 restart:
1326   /* Make a data object from the body, create context etc. */
1327   ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
1328   if (!ciphertext)
1329     return NULL;
1330   plaintext = create_gpgme_data ();
1331
1332   /* Do the decryption or the verification in case of the S/MIME hack. */
1333   if ((!is_smime) || maybe_signed) {
1334     if (!is_smime)
1335       err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
1336     else if (maybe_signed)
1337       err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
1338
1339     {
1340       /* Check wether signatures have been verified.  */
1341       gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
1342
1343       if (verify_result->signatures)
1344         sig_stat = 1;
1345     }
1346   }
1347   else
1348     err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
1349   gpgme_data_release (ciphertext);
1350   if (err) {
1351     if (is_smime && !maybe_signed && gpg_err_code (err) == GPG_ERR_NO_DATA) {
1352       /* Check whether this might be a signed message despite what
1353          the mime header told us.  Retry then.  gpgsm returns the
1354          error information "unsupported Algorithm '?'" but gpgme
1355          will not store this unknown algorithm, thus we test that
1356          it has not been set. */
1357       gpgme_decrypt_result_t result;
1358
1359       result = gpgme_op_decrypt_result (ctx);
1360       if (!result->unsupported_algorithm) {
1361         maybe_signed = 1;
1362         gpgme_data_release (plaintext);
1363         goto restart;
1364       }
1365     }
1366     mutt_need_hard_redraw ();
1367     if ((s->flags & M_DISPLAY)) {
1368       char buf[200];
1369
1370       snprintf (buf, sizeof (buf) - 1,
1371                 _("[-- Error: decryption failed: %s --]\n\n"),
1372                 gpgme_strerror (err));
1373       state_attach_puts (buf, s);
1374     }
1375     gpgme_data_release (plaintext);
1376     gpgme_release (ctx);
1377     return NULL;
1378   }
1379   mutt_need_hard_redraw ();
1380
1381   /* Read the output from GPGME, and make sure to change CRLF to LF,
1382      otherwise read_mime_header has a hard time parsing the message.  */
1383   if (data_object_to_stream (plaintext, fpout)) {
1384     gpgme_data_release (plaintext);
1385     gpgme_release (ctx);
1386     return NULL;
1387   }
1388   gpgme_data_release (plaintext);
1389
1390   a->is_signed_data = 0;
1391   if (sig_stat) {
1392     int res, idx;
1393     int anybad = 0;
1394
1395     if (maybe_signed)
1396       a->is_signed_data = 1;
1397     if (r_is_signed)
1398       *r_is_signed = -1;        /* A signature exists. */
1399
1400     if ((s->flags & M_DISPLAY))
1401       state_attach_puts (_("[-- Begin signature " "information --]\n"), s);
1402     for (idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1403       if (res == 1)
1404         anybad = 1;
1405       else if (res == 2)
1406         anywarn = 1;
1407     }
1408     if (!anybad && idx && r_is_signed && *r_is_signed)
1409       *r_is_signed = anywarn ? 2 : 1;   /* Good signature. */
1410
1411     if ((s->flags & M_DISPLAY))
1412       state_attach_puts (_("[-- End signature " "information --]\n\n"), s);
1413   }
1414   gpgme_release (ctx);
1415   ctx = NULL;
1416
1417   fflush (fpout);
1418   rewind (fpout);
1419   tattach = mutt_read_mime_header (fpout, 0);
1420   if (tattach) {
1421     /*
1422      * Need to set the length of this body part.
1423      */
1424     fstat (fileno (fpout), &info);
1425     tattach->length = info.st_size - tattach->offset;
1426
1427     tattach->warnsig = anywarn;
1428
1429     /* See if we need to recurse on this MIME part.  */
1430     mutt_parse_part (fpout, tattach);
1431   }
1432
1433   return tattach;
1434 }
1435
1436 /* Decrypt a PGP/MIME message in FPIN and B and return a new body and
1437    the stream in CUR and FPOUT.  Returns 0 on success. */
1438 int crypt_pgp_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
1439 {
1440   char tempfile[_POSIX_PATH_MAX];
1441   STATE s;
1442   BODY *first_part = b;
1443   int is_signed;
1444
1445   first_part->goodsig = 0;
1446   first_part->warnsig = 0;
1447
1448   if (!mutt_is_multipart_encrypted (b))
1449     return -1;
1450
1451   if (!b->parts || !b->parts->next)
1452     return -1;
1453
1454   b = b->parts->next;
1455
1456   p_clear(&s, 1);
1457   s.fpin = fpin;
1458   *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1459   if (!*fpout) {
1460     mutt_perror (_("Can't create temporary file"));
1461     return -1;
1462   }
1463   unlink (tempfile);
1464
1465   *cur = decrypt_part (b, &s, *fpout, 0, &is_signed);
1466   rewind (*fpout);
1467   if (is_signed > 0)
1468     first_part->goodsig = 1;
1469
1470   return *cur ? 0 : -1;
1471 }
1472
1473
1474 /* Decrypt a S/MIME message in FPIN and B and return a new body and
1475    the stream in CUR and FPOUT.  Returns 0 on success. */
1476 int crypt_smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b,
1477                               BODY ** cur)
1478 {
1479   char tempfile[_POSIX_PATH_MAX];
1480   STATE s;
1481   FILE *tmpfp = NULL;
1482   int is_signed;
1483   long saved_b_offset;
1484   ssize_t saved_b_length;
1485   int saved_b_type;
1486
1487   if (!mutt_is_application_smime (b))
1488     return -1;
1489
1490   if (b->parts)
1491     return -1;
1492
1493   /* Decode the body - we need to pass binary CMS to the
1494      backend.  The backend allows for Base64 encoded data but it does
1495      not allow for QP which I have seen in some messages.  So better
1496      do it here. */
1497   saved_b_type = b->type;
1498   saved_b_offset = b->offset;
1499   saved_b_length = b->length;
1500   p_clear(&s, 1);
1501   s.fpin = fpin;
1502   fseeko (s.fpin, b->offset, 0);
1503   tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1504   if (!tmpfp) {
1505     mutt_perror (_("Can't create temporary file"));
1506     return -1;
1507   }
1508   mutt_unlink (tempfile);
1509
1510   s.fpout = tmpfp;
1511   mutt_decode_attachment (b, &s);
1512   fflush (tmpfp);
1513   b->length = ftello (s.fpout);
1514   b->offset = 0;
1515   rewind (tmpfp);
1516
1517   p_clear(&s, 1);
1518   s.fpin = tmpfp;
1519   s.fpout = 0;
1520   *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1521   if (!*fpout) {
1522     mutt_perror (_("Can't create temporary file"));
1523     return -1;
1524   }
1525   mutt_unlink (tempfile);
1526
1527   *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
1528   if (*cur)
1529     (*cur)->goodsig = is_signed > 0;
1530   b->type = saved_b_type;
1531   b->length = saved_b_length;
1532   b->offset = saved_b_offset;
1533   m_fclose(&tmpfp);
1534   rewind (*fpout);
1535   if (*cur && !is_signed && !(*cur)->parts
1536       && mutt_is_application_smime (*cur)) {
1537     /* Assume that this is a opaque signed s/mime message.  This is
1538        an ugly way of doing it but we have anyway a problem with
1539        arbitrary encoded S/MIME messages: Only the outer part may be
1540        encrypted.  The entire mime parsing should be revamped,
1541        probably by keeping the temportary files so that we don't
1542        need to decrypt them all the time.  Inner parts of an
1543        encrypted part can then pint into this file and tehre won't
1544        never be a need to decrypt again.  This needs a partial
1545        rewrite of the MIME engine. */
1546     BODY *bb = *cur;
1547     BODY *tmp_b;
1548
1549     saved_b_type = bb->type;
1550     saved_b_offset = bb->offset;
1551     saved_b_length = bb->length;
1552     p_clear(&s, 1);
1553     s.fpin = *fpout;
1554     fseeko (s.fpin, bb->offset, 0);
1555     tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1556     if (!tmpfp) {
1557       mutt_perror (_("Can't create temporary file"));
1558       return -1;
1559     }
1560     mutt_unlink (tempfile);
1561
1562     s.fpout = tmpfp;
1563     mutt_decode_attachment (bb, &s);
1564     fflush (tmpfp);
1565     bb->length = ftello (s.fpout);
1566     bb->offset = 0;
1567     rewind (tmpfp);
1568     m_fclose(&*fpout);
1569
1570     p_clear(&s, 1);
1571     s.fpin = tmpfp;
1572     s.fpout = 0;
1573     *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1574     if (!*fpout) {
1575       mutt_perror (_("Can't create temporary file"));
1576       return -1;
1577     }
1578     mutt_unlink (tempfile);
1579
1580     tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
1581     if (tmp_b)
1582       tmp_b->goodsig = is_signed > 0;
1583     bb->type = saved_b_type;
1584     bb->length = saved_b_length;
1585     bb->offset = saved_b_offset;
1586     m_fclose(&tmpfp);
1587     rewind (*fpout);
1588     body_list_wipe(cur);
1589     *cur = tmp_b;
1590   }
1591   return *cur ? 0 : -1;
1592 }
1593
1594
1595 /*
1596  * Implementation of `pgp_check_traditional'.
1597  */
1598
1599 static int pgp_check_traditional_one_body (FILE * fp, BODY * b,
1600                                            int tagged_only)
1601 {
1602   char tempfile[_POSIX_PATH_MAX];
1603   char buf[HUGE_STRING];
1604   FILE *tfp;
1605   int tempfd;
1606
1607   short sgn = 0;
1608   short enc = 0;
1609
1610   if (b->type != TYPETEXT)
1611     return 0;
1612
1613   if (tagged_only && !b->tagged)
1614     return 0;
1615
1616   tempfd = m_tempfd(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1617   if (mutt_decode_save_attachment (fp, b, tempfd, 0) != 0) {
1618     unlink (tempfile);
1619     return 0;
1620   }
1621
1622   if ((tfp = fopen(tempfile, "r")) == NULL) {
1623     unlink (tempfile);
1624     return 0;
1625   }
1626
1627   while (fgets (buf, sizeof (buf), tfp)) {
1628     if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1629       if (!m_strcmp("MESSAGE-----\n", buf + 15))
1630         enc = 1;
1631       else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15))
1632         sgn = 1;
1633     }
1634   }
1635   m_fclose(&tfp);
1636   unlink (tempfile);
1637
1638   if (!enc && !sgn)
1639     return 0;
1640
1641   /* fix the content type */
1642
1643   parameter_setval(&b->parameter, "format", "fixed");
1644   parameter_setval(&b->parameter, "x-action",
1645                    enc ? "pgp-encrypted" : "pgp-signed");
1646   return 1;
1647 }
1648
1649 int crypt_pgp_check_traditional (FILE * fp, BODY * b, int tagged_only)
1650 {
1651   int rv = 0;
1652   int r;
1653
1654   for (; b; b = b->next) {
1655     if (is_multipart (b))
1656       rv = (crypt_pgp_check_traditional (fp, b->parts, tagged_only) || rv);
1657     else if (b->type == TYPETEXT) {
1658       if ((r = mutt_is_application_pgp (b)))
1659         rv = (rv || r);
1660       else
1661         rv = (pgp_check_traditional_one_body (fp, b, tagged_only) || rv);
1662     }
1663   }
1664   return rv;
1665 }
1666
1667
1668 /* Implementation of `application_handler'. */
1669
1670 /*
1671   Copy a clearsigned message, and strip the signature and PGP's
1672   dash-escaping.
1673
1674   XXX - charset handling: We assume that it is safe to do
1675   character set decoding first, dash decoding second here, while
1676   we do it the other way around in the main handler.
1677
1678   (Note that we aren't worse than Outlook & Cie in this, and also
1679   note that we can successfully handle anything produced by any
1680   existing versions of mutt.)  */
1681
1682 static void copy_clearsigned (gpgme_data_t data, STATE * s, char *charset)
1683 {
1684   char buf[HUGE_STRING];
1685   short complete, armor_header;
1686   fgetconv_t *fc;
1687   char *fname;
1688   FILE *fp;
1689
1690   fname = data_object_to_tempfile (data, &fp);
1691   if (!fname)
1692     return;
1693   unlink (fname);
1694   p_delete(&fname);
1695
1696   fc = fgetconv_open (fp, charset, MCharset.charset, M_ICONV_HOOK_FROM);
1697
1698   for (complete = 1, armor_header = 1;
1699        fgetconvs (buf, sizeof (buf), fc) != NULL;
1700        complete = strchr (buf, '\n') != NULL) {
1701     if (!complete) {
1702       if (!armor_header)
1703         state_puts (buf, s);
1704       continue;
1705     }
1706
1707     if (!m_strcmp(buf, "-----BEGIN PGP SIGNATURE-----\n"))
1708       break;
1709
1710     if (armor_header) {
1711       if (buf[0] == '\n')
1712         armor_header = 0;
1713       continue;
1714     }
1715
1716     if (s->prefix)
1717       state_puts (s->prefix, s);
1718
1719     if (buf[0] == '-' && buf[1] == ' ')
1720       state_puts (buf + 2, s);
1721     else
1722       state_puts (buf, s);
1723   }
1724
1725   fgetconv_close (&fc);
1726   m_fclose(&fp);
1727 }
1728
1729
1730 /* Support for classic_application/pgp */
1731 int crypt_pgp_application_pgp_handler (BODY * m, STATE * s)
1732 {
1733   int needpass = -1, pgp_keyblock = 0;
1734   int clearsign = 0;
1735   long start_pos = 0;
1736   long bytes;
1737   off_t last_pos, offset;
1738   char buf[HUGE_STRING];
1739   FILE *pgpout = NULL;
1740
1741   gpgme_error_t err = 0;
1742   gpgme_data_t armored_data = NULL;
1743
1744   short maybe_goodsig = 1;
1745   short have_any_sigs = 0;
1746
1747   char body_charset[STRING];    /* Only used for clearsigned messages. */
1748
1749   /* For clearsigned messages we won't be able to get a character set
1750      but we know that this may only be text thus we assume Latin-1
1751      here. */
1752   if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
1753     m_strcpy(body_charset, sizeof(body_charset), "iso-8859-1");
1754
1755   fseeko (s->fpin, m->offset, 0);
1756   last_pos = m->offset;
1757
1758   for (bytes = m->length; bytes > 0;) {
1759     if (fgets (buf, sizeof (buf), s->fpin) == NULL)
1760       break;
1761
1762     offset = ftello (s->fpin);
1763     bytes -= (offset - last_pos);       /* don't rely on m_strlen(buf) */
1764     last_pos = offset;
1765
1766     if (!m_strncmp("-----BEGIN PGP ", buf, 15)) {
1767       clearsign = 0;
1768       start_pos = last_pos;
1769
1770       if (!m_strcmp("MESSAGE-----\n", buf + 15))
1771         needpass = 1;
1772       else if (!m_strcmp("SIGNED MESSAGE-----\n", buf + 15)) {
1773         clearsign = 1;
1774         needpass = 0;
1775       }
1776       else if (!option (OPTDONTHANDLEPGPKEYS) &&
1777                !m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
1778         needpass = 0;
1779         pgp_keyblock = 1;
1780       }
1781       else {
1782         /* XXX - we may wish to recode here */
1783         if (s->prefix)
1784           state_puts (s->prefix, s);
1785         state_puts (buf, s);
1786         continue;
1787       }
1788
1789       have_any_sigs = (have_any_sigs || (clearsign && (s->flags & M_VERIFY)));
1790
1791       /* Copy PGP material to an data container */
1792       armored_data = create_gpgme_data ();
1793       gpgme_data_write (armored_data, buf, m_strlen(buf));
1794       while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL) {
1795         offset = ftello (s->fpin);
1796         bytes -= (offset - last_pos);   /* don't rely on m_strlen(buf) */
1797         last_pos = offset;
1798
1799         gpgme_data_write (armored_data, buf, m_strlen(buf));
1800
1801         if ((needpass && !m_strcmp("-----END PGP MESSAGE-----\n", buf))
1802             || (!needpass
1803                 && (!m_strcmp("-----END PGP SIGNATURE-----\n", buf)
1804                     || !m_strcmp("-----END PGP PUBLIC KEY BLOCK-----\n",
1805                                      buf))))
1806           break;
1807       }
1808
1809       /* Invoke PGP if needed */
1810       if (!clearsign || (s->flags & M_VERIFY)) {
1811         unsigned int sig_stat = 0;
1812         gpgme_data_t plaintext;
1813         gpgme_ctx_t ctx;
1814
1815         plaintext = create_gpgme_data ();
1816         ctx = create_gpgme_context (0);
1817
1818         if (clearsign)
1819           err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1820         else {
1821           err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
1822           if (gpg_err_code (err) == GPG_ERR_NO_DATA) {
1823             /* Decrypt verify can't handle signed only messages. */
1824             err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
1825               ? gpgme_error_from_errno (errno) : 0;
1826             /* Must release plaintext so that we supply an
1827                uninitialized object. */
1828             gpgme_data_release (plaintext);
1829             plaintext = create_gpgme_data ();
1830             err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
1831           }
1832         }
1833
1834         if (err) {
1835           char errbuf[200];
1836
1837           snprintf (errbuf, sizeof (errbuf) - 1,
1838                     _("Error: decryption/verification failed: %s\n"),
1839                     gpgme_strerror (err));
1840           state_attach_puts (errbuf, s);
1841         }
1842         else {                  /* Decryption/Verification succeeded */
1843           char *tmpfname;
1844
1845           {
1846             /* Check wether signatures have been verified.  */
1847             gpgme_verify_result_t verify_result;
1848
1849             verify_result = gpgme_op_verify_result (ctx);
1850             if (verify_result->signatures)
1851               sig_stat = 1;
1852           }
1853
1854           have_any_sigs = 0;
1855           maybe_goodsig = 0;
1856           if ((s->flags & M_DISPLAY) && sig_stat) {
1857             int res, idx;
1858             int anybad = 0;
1859             int anywarn = 0;
1860
1861             state_attach_puts (_("[-- Begin signature "
1862                                  "information --]\n"), s);
1863             have_any_sigs = 1;
1864             for (idx = 0;
1865                  (res = show_one_sig_status (ctx, idx, s)) != -1; idx++) {
1866               if (res == 1)
1867                 anybad = 1;
1868               else if (res == 2)
1869                 anywarn = 1;
1870             }
1871             if (!anybad && idx)
1872               maybe_goodsig = 1;
1873
1874             state_attach_puts (_("[-- End signature "
1875                                  "information --]\n\n"), s);
1876           }
1877
1878           tmpfname = data_object_to_tempfile (plaintext, &pgpout);
1879           if (!tmpfname) {
1880             pgpout = NULL;
1881             state_attach_puts (_("Error: copy data failed\n"), s);
1882           }
1883           else {
1884             unlink (tmpfname);
1885             p_delete(&tmpfname);
1886           }
1887         }
1888         gpgme_release (ctx);
1889       }
1890
1891       /*
1892        * Now, copy cleartext to the screen.  NOTE - we expect that PGP
1893        * outputs utf-8 cleartext.  This may not always be true, but it
1894        * seems to be a reasonable guess.
1895        */
1896
1897       if (s->flags & M_DISPLAY) {
1898         if (needpass)
1899           state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
1900         else if (pgp_keyblock)
1901           state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"), s);
1902         else
1903           state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"), s);
1904       }
1905
1906       if (clearsign) {
1907         copy_clearsigned (armored_data, s, body_charset);
1908       }
1909       else if (pgpout) {
1910         fgetconv_t *fc;
1911         int c;
1912
1913         rewind (pgpout);
1914         fc = fgetconv_open (pgpout, "utf-8", MCharset.charset, 0);
1915         while ((c = fgetconv (fc)) != EOF) {
1916           state_putc (c, s);
1917           if (c == '\n' && s->prefix)
1918             state_puts (s->prefix, s);
1919         }
1920         fgetconv_close (&fc);
1921       }
1922
1923       if (s->flags & M_DISPLAY) {
1924         state_putc ('\n', s);
1925         if (needpass)
1926           state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
1927         else if (pgp_keyblock)
1928           state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
1929         else
1930           state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
1931       }
1932
1933       if (pgpout) {
1934         m_fclose(&pgpout);
1935       }
1936     }
1937     else {
1938       /* XXX - we may wish to recode here */
1939       if (s->prefix)
1940         state_puts (s->prefix, s);
1941       state_puts (buf, s);
1942     }
1943   }
1944
1945   m->goodsig = (maybe_goodsig && have_any_sigs);
1946
1947   if (needpass == -1) {
1948     state_attach_puts (_("[-- Error: could not find beginning"
1949                          " of PGP message! --]\n\n"), s);
1950     return (-1);
1951   }
1952   return (err);
1953 }
1954
1955 /* Implementation of `encrypted_handler'. */
1956
1957 /* MIME handler for pgp/mime encrypted messages. */
1958 int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
1959 {
1960   char tempfile[_POSIX_PATH_MAX];
1961   FILE *fpout;
1962   BODY *tattach;
1963   BODY *orig_body = a;
1964   int is_signed;
1965   int rc = 0;
1966
1967   a = a->parts;
1968   if (!a || a->type != TYPEAPPLICATION || !a->subtype
1969       || ascii_strcasecmp ("pgp-encrypted", a->subtype)
1970       || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
1971       || ascii_strcasecmp ("octet-stream", a->next->subtype)) {
1972     if (s->flags & M_DISPLAY)
1973       state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
1974                          s);
1975     return (-1);
1976   }
1977
1978   /* Move forward to the application/pgp-encrypted body. */
1979   a = a->next;
1980
1981   fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
1982   if (!fpout) {
1983     if (s->flags & M_DISPLAY)
1984       state_attach_puts (_("[-- Error: could not create temporary file! "
1985                            "--]\n"), s);
1986     return (-1);
1987   }
1988
1989   tattach = decrypt_part (a, s, fpout, 0, &is_signed);
1990   if (tattach) {
1991     tattach->goodsig = is_signed > 0;
1992
1993     if (s->flags & M_DISPLAY)
1994       state_attach_puts (is_signed ?
1995                          _
1996                          ("[-- The following data is PGP/MIME signed and encrypted --]\n\n") :
1997                          _("[-- The following data is PGP/MIME encrypted --]\n\n"), s);
1998
1999     {
2000       FILE *savefp = s->fpin;
2001
2002       s->fpin = fpout;
2003       rc = mutt_body_handler (tattach, s);
2004       s->fpin = savefp;
2005     }
2006
2007     /*
2008      * if a multipart/signed is the _only_ sub-part of a
2009      * multipart/encrypted, cache signature verification
2010      * status.
2011      */
2012     if (mutt_is_multipart_signed (tattach) && !tattach->next)
2013       orig_body->goodsig |= tattach->goodsig;
2014
2015     if (s->flags & M_DISPLAY) {
2016       state_puts ("\n", s);
2017       state_attach_puts (is_signed ?
2018                          _
2019                          ("[-- End of PGP/MIME signed and encrypted data --]\n")
2020                          : _("[-- End of PGP/MIME encrypted data --]\n"), s);
2021     }
2022
2023     body_list_wipe(&tattach);
2024   }
2025
2026   m_fclose(&fpout);
2027   mutt_unlink (tempfile);
2028   return (rc);
2029 }
2030
2031 /* Support for application/smime */
2032 int crypt_smime_application_smime_handler (BODY * a, STATE * s)
2033 {
2034   char tempfile[_POSIX_PATH_MAX];
2035   FILE *fpout;
2036   BODY *tattach;
2037   int is_signed;
2038   int rc = 0;
2039
2040   a->warnsig = 0;
2041   fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2042   if (!fpout) {
2043     if (s->flags & M_DISPLAY)
2044       state_attach_puts (_("[-- Error: could not create temporary file! "
2045                            "--]\n"), s);
2046     return (-1);
2047   }
2048
2049   tattach = decrypt_part (a, s, fpout, 1, &is_signed);
2050   if (tattach) {
2051     tattach->goodsig = is_signed > 0;
2052
2053     if (s->flags & M_DISPLAY)
2054       state_attach_puts (is_signed ?
2055                          _("[-- The following data is S/MIME signed --]\n\n") :
2056                          _("[-- The following data is S/MIME encrypted --]\n\n"), s);
2057
2058     {
2059       FILE *savefp = s->fpin;
2060
2061       s->fpin = fpout;
2062       rc = mutt_body_handler (tattach, s);
2063       s->fpin = savefp;
2064     }
2065
2066     /*
2067      * if a multipart/signed is the _only_ sub-part of a
2068      * multipart/encrypted, cache signature verification
2069      * status.
2070      */
2071     if (mutt_is_multipart_signed (tattach) && !tattach->next) {
2072       if (!(a->goodsig = tattach->goodsig))
2073         a->warnsig = tattach->warnsig;
2074     }
2075     else if (tattach->goodsig) {
2076       a->goodsig = 1;
2077       a->warnsig = tattach->warnsig;
2078     }
2079
2080     if (s->flags & M_DISPLAY) {
2081       state_puts ("\n", s);
2082       state_attach_puts (is_signed ?
2083                          _("[-- End of S/MIME signed data --]\n") :
2084                          _("[-- End of S/MIME encrypted data --]\n"), s);
2085     }
2086
2087     body_list_wipe(&tattach);
2088   }
2089
2090   m_fclose(&fpout);
2091   mutt_unlink (tempfile);
2092   return (rc);
2093 }
2094
2095
2096 /*
2097  * Format an entry on the CRYPT key selection menu.
2098  *
2099  * %n   number
2100  * %k   key id          %K      key id of the principal key
2101  * %u   user id
2102  * %a   algorithm       %A      algorithm of the princ. key
2103  * %l   length          %L      length of the princ. key
2104  * %f   flags           %F      flags of the princ. key
2105  * %c   capabilities    %C      capabilities of the princ. key
2106  * %t   trust/validity of the key-uid association
2107  * %p           protocol
2108  * %[...] date of key using strftime(3)
2109  */
2110
2111 static const char *
2112 crypt_entry_fmt (char *dest, ssize_t destlen, char op,
2113                  const char *src, const char *prefix,
2114                  const char *ifstr, const char *elstr,
2115                  anytype data, format_flag flags)
2116 {
2117   char fmt[16];
2118   crypt_entry_t *entry;
2119   crypt_key_t *key;
2120   int kflags = 0;
2121   int optional = (flags & M_FORMAT_OPTIONAL);
2122   const char *s = NULL;
2123   unsigned long val;
2124
2125   entry = data.ptr;
2126   key = entry->key;
2127
2128 /*    if (isupper ((unsigned char) op)) */
2129 /*      key = pkey; */
2130
2131   kflags = (key->flags          /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
2132                                    | uid->flags */ );
2133
2134   switch (ascii_tolower (op)) {
2135   case '[':
2136     {
2137       const char *cp;
2138       char buf2[STRING], *p;
2139       int do_locales;
2140       struct tm *tm;
2141       ssize_t len;
2142
2143       p = dest;
2144
2145       cp = src;
2146       if (*cp == '!') {
2147         do_locales = 0;
2148         cp++;
2149       }
2150       else
2151         do_locales = 1;
2152
2153       len = destlen - 1;
2154       while (len > 0 && *cp != ']') {
2155         if (*cp == '%') {
2156           cp++;
2157           if (len >= 2) {
2158             *p++ = '%';
2159             *p++ = *cp;
2160             len -= 2;
2161           }
2162           else
2163             break;              /* not enough space */
2164           cp++;
2165         }
2166         else {
2167           *p++ = *cp++;
2168           len--;
2169         }
2170       }
2171       *p = 0;
2172
2173       if (do_locales && Locale)
2174         setlocale (LC_TIME, Locale);
2175
2176       {
2177         time_t tt = 0;
2178
2179         if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
2180           tt = key->kobj->subkeys->timestamp;
2181
2182         tm = localtime (&tt);
2183       }
2184       strftime (buf2, sizeof (buf2), dest, tm);
2185
2186       if (do_locales)
2187         setlocale (LC_TIME, "C");
2188
2189       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2190       snprintf (dest, destlen, fmt, buf2);
2191       if (len > 0)
2192         src = cp + 1;
2193     }
2194     break;
2195   case 'n':
2196     if (!optional) {
2197       snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
2198       snprintf (dest, destlen, fmt, entry->num);
2199     }
2200     break;
2201   case 'k':
2202     if (!optional) {
2203       /* fixme: we need a way to distinguish between main and subkeys.
2204          Store the idx in entry? */
2205       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2206       snprintf (dest, destlen, fmt, crypt_keyid (key));
2207     }
2208     break;
2209   case 'u':
2210     if (!optional) {
2211       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2212       snprintf (dest, destlen, fmt, key->uid);
2213     }
2214     break;
2215   case 'a':
2216     if (!optional) {
2217       snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
2218       if (key->kobj->subkeys)
2219         s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
2220       else
2221         s = "?";
2222       snprintf (dest, destlen, fmt, s);
2223     }
2224     break;
2225   case 'l':
2226     if (!optional) {
2227       snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
2228       if (key->kobj->subkeys)
2229         val = key->kobj->subkeys->length;
2230       else
2231         val = 0;
2232       snprintf (dest, destlen, fmt, val);
2233     }
2234     break;
2235   case 'f':
2236     if (!optional) {
2237       snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2238       snprintf (dest, destlen, fmt, crypt_flags (kflags));
2239     }
2240     else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
2241       optional = 0;
2242     break;
2243   case 'c':
2244     if (!optional) {
2245       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2246       snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
2247     }
2248     else if (!(kflags & (KEYFLAG_ABILITIES)))
2249       optional = 0;
2250     break;
2251   case 't':
2252     if ((kflags & KEYFLAG_ISX509))
2253       s = "x";
2254     else {
2255       gpgme_user_id_t uid = NULL;
2256       int i = 0;
2257
2258       for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
2259            i++, uid = uid->next);
2260       if (uid)
2261         switch (uid->validity) {
2262         case GPGME_VALIDITY_UNDEFINED:
2263           s = "q";
2264           break;
2265         case GPGME_VALIDITY_NEVER:
2266           s = "n";
2267           break;
2268         case GPGME_VALIDITY_MARGINAL:
2269           s = "m";
2270           break;
2271         case GPGME_VALIDITY_FULL:
2272           s = "f";
2273           break;
2274         case GPGME_VALIDITY_ULTIMATE:
2275           s = "u";
2276           break;
2277         case GPGME_VALIDITY_UNKNOWN:
2278         default:
2279           s = "?";
2280           break;
2281         }
2282     }
2283     snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
2284     snprintf (dest, destlen, fmt, s ? *s : 'B');
2285     break;
2286   case 'p':
2287     snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
2288     snprintf (dest, destlen, fmt,
2289               gpgme_get_protocol_name (key->kobj->protocol));
2290     break;
2291
2292   default:
2293     *dest = '\0';
2294   }
2295
2296   if (flags & M_FORMAT_OPTIONAL)
2297     m_strformat(dest, destlen, 0, optional ? ifstr: elstr,
2298                 mutt_attach_fmt, data, 0);
2299   return src;
2300 }
2301
2302 /* Used by the display fucntion to format a line. */
2303 static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
2304 {
2305   crypt_key_t **key_table = (crypt_key_t **) menu->data;
2306   crypt_entry_t entry;
2307
2308   entry.key = key_table[num];
2309   entry.num = num + 1;
2310
2311   m_strformat(s, l, COLS - SW, PgpEntryFormat, crypt_entry_fmt, &entry,
2312               option(OPTARROWCURSOR) ? M_FORMAT_ARROWCURSOR : 0);
2313 }
2314
2315 /* Compare two addresses and the keyid to be used for sorting. */
2316 static int _crypt_compare_address (const void *a, const void *b)
2317 {
2318   crypt_key_t **s = (crypt_key_t **) a;
2319   crypt_key_t **t = (crypt_key_t **) b;
2320   int r;
2321
2322   if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2323     return r > 0;
2324   else
2325     return m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t)) > 0;
2326 }
2327
2328 static int crypt_compare_address (const void *a, const void *b)
2329 {
2330   return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_address (a, b)
2331           : _crypt_compare_address (a, b));
2332 }
2333
2334
2335 /* Compare two key IDs and the addresses to be used for sorting. */
2336 static int _crypt_compare_keyid (const void *a, const void *b)
2337 {
2338   crypt_key_t **s = (crypt_key_t **) a;
2339   crypt_key_t **t = (crypt_key_t **) b;
2340   int r;
2341
2342   if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
2343     return r > 0;
2344   else
2345     return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2346 }
2347
2348 static int crypt_compare_keyid (const void *a, const void *b)
2349 {
2350   return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
2351           : _crypt_compare_keyid (a, b));
2352 }
2353
2354 /* Compare 2 creation dates and the addresses.  For sorting. */
2355 static int _crypt_compare_date (const void *a, const void *b)
2356 {
2357   crypt_key_t **s = (crypt_key_t **) a;
2358   crypt_key_t **t = (crypt_key_t **) b;
2359   unsigned long ts = 0, tt = 0;
2360
2361   if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2362     ts = (*s)->kobj->subkeys->timestamp;
2363   if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2364     tt = (*t)->kobj->subkeys->timestamp;
2365
2366   if (ts > tt)
2367     return 1;
2368   if (ts < tt)
2369     return 0;
2370
2371   return m_strcasecmp((*s)->uid, (*t)->uid) > 0;
2372 }
2373
2374 static int crypt_compare_date (const void *a, const void *b)
2375 {
2376   return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
2377           : _crypt_compare_date (a, b));
2378 }
2379
2380 /* Compare two trust values, the key length, the creation dates. the
2381    addresses and the key IDs.  For sorting. */
2382 static int _crypt_compare_trust (const void *a, const void *b)
2383 {
2384   crypt_key_t **s = (crypt_key_t **) a;
2385   crypt_key_t **t = (crypt_key_t **) b;
2386   unsigned long ts = 0, tt = 0;
2387   int r;
2388
2389   if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
2390             - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
2391     return r > 0;
2392
2393   if ((*s)->kobj->uids)
2394     ts = (*s)->kobj->uids->validity;
2395   if ((*t)->kobj->uids)
2396     tt = (*t)->kobj->uids->validity;
2397   if ((r = (tt - ts)))
2398     return r < 0;
2399
2400   if ((*s)->kobj->subkeys)
2401     ts = (*s)->kobj->subkeys->length;
2402   if ((*t)->kobj->subkeys)
2403     tt = (*t)->kobj->subkeys->length;
2404   if (ts != tt)
2405     return ts > tt;
2406
2407   if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
2408     ts = (*s)->kobj->subkeys->timestamp;
2409   if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
2410     tt = (*t)->kobj->subkeys->timestamp;
2411   if (ts > tt)
2412     return 1;
2413   if (ts < tt)
2414     return 0;
2415
2416   if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
2417     return r > 0;
2418   return (m_strcasecmp(crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
2419 }
2420
2421 static int crypt_compare_trust (const void *a, const void *b)
2422 {
2423   return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
2424           : _crypt_compare_trust (a, b));
2425 }
2426
2427 /* Print the X.500 Distinguished Name part KEY from the array of parts
2428    DN to FP. */
2429 static int print_dn_part (FILE * fp, struct dn_array_s *dn, const char *key)
2430 {
2431   int any = 0;
2432
2433   for (; dn->key; dn++) {
2434     if (!m_strcmp(dn->key, key)) {
2435       if (any)
2436         fputs (" + ", fp);
2437       print_utf8 (fp, dn->value, m_strlen(dn->value));
2438       any = 1;
2439     }
2440   }
2441   return any;
2442 }
2443
2444 /* Print all parts of a DN in a standard sequence. */
2445 static void print_dn_parts (FILE * fp, struct dn_array_s *dn)
2446 {
2447   const char *stdpart[] = {
2448     "CN", "OU", "O", "STREET", "L", "ST", "C", NULL
2449   };
2450   int any = 0, any2 = 0, i;
2451
2452   for (i = 0; stdpart[i]; i++) {
2453     if (any)
2454       fputs (", ", fp);
2455     any = print_dn_part (fp, dn, stdpart[i]);
2456   }
2457   /* now print the rest without any specific ordering */
2458   for (; dn->key; dn++) {
2459     for (i = 0; stdpart[i]; i++) {
2460       if (!m_strcmp(dn->key, stdpart[i]))
2461         break;
2462     }
2463     if (!stdpart[i]) {
2464       if (any)
2465         fputs (", ", fp);
2466       if (!any2)
2467         fputs ("(", fp);
2468       any = print_dn_part (fp, dn, dn->key);
2469       any2 = 1;
2470     }
2471   }
2472   if (any2)
2473     fputs (")", fp);
2474 }
2475
2476
2477 /* Parse an RDN; this is a helper to parse_dn(). */
2478 static const unsigned char *parse_dn_part (struct dn_array_s *array,
2479                                            const unsigned char *string)
2480 {
2481   const unsigned char *s, *s1;
2482   ssize_t n;
2483   unsigned char *p;
2484
2485   /* parse attributeType */
2486   for (s = string + 1; *s && *s != '='; s++);
2487   if (!*s)
2488     return NULL;                /* error */
2489   n = s - string;
2490   if (!n)
2491     return NULL;                /* empty key */
2492   array->key = p_dupstr(string, n );
2493   p = (unsigned char *) array->key;
2494   string = s + 1;
2495
2496   if (*string == '#') {         /* hexstring */
2497     string++;
2498     for (s = string; hexval(*s) >= 0; s++)
2499       s++;
2500     n = s - string;
2501     if (!n || (n & 1))
2502       return NULL;              /* empty or odd number of digits */
2503     n /= 2;
2504     p = p_new(unsigned char, n + 1);
2505     array->value = (char *) p;
2506     for (s1 = string; n; s1 += 2, n--)
2507       *p++ = (hexval(*s1) << 8) | hexval(*s1);
2508     *p = 0;
2509   }
2510   else {                        /* regular v3 quoted string */
2511     for (n = 0, s = string; *s; s++) {
2512       if (*s == '\\') {         /* pair */
2513         s++;
2514         if (*s == ',' || *s == '=' || *s == '+'
2515             || *s == '<' || *s == '>' || *s == '#' || *s == ';'
2516             || *s == '\\' || *s == '\"' || *s == ' ')
2517           n++;
2518         else if (hexval(*s) >= 0 && hexval(*s + 1) >= 0) {
2519           s++;
2520           n++;
2521         }
2522         else
2523           return NULL;          /* invalid escape sequence */
2524       }
2525       else if (*s == '\"')
2526         return NULL;            /* invalid encoding */
2527       else if (*s == ',' || *s == '=' || *s == '+'
2528                || *s == '<' || *s == '>' || *s == '#' || *s == ';')
2529         break;
2530       else
2531         n++;
2532     }
2533
2534     p = p_new(unsigned char, n + 1);
2535     array->value = (char *) p;
2536     for (s = string; n; s++, n--) {
2537       if (*s == '\\') {
2538         s++;
2539         if (hexval(*s) >= 0) {
2540           *p++ = (hexval(*s) << 8) | hexval(*s + 1);
2541           s++;
2542         }
2543         else
2544           *p++ = *s;
2545       }
2546       else
2547         *p++ = *s;
2548     }
2549     *p = 0;
2550   }
2551   return s;
2552 }
2553
2554
2555 /* Parse a DN and return an array-ized one.  This is not a validating
2556    parser and it does not support any old-stylish syntax; gpgme is
2557    expected to return only rfc2253 compatible strings. */
2558 static struct dn_array_s *parse_dn (const unsigned char *string)
2559 {
2560   struct dn_array_s *array;
2561   ssize_t arrayidx, arraysize;
2562   int i;
2563
2564   arraysize = 7;                /* C,ST,L,O,OU,CN,email */
2565   array = p_new(struct dn_array_s, arraysize + 1);
2566   arrayidx = 0;
2567   while (*string) {
2568     while (*string == ' ')
2569       string++;
2570     if (!*string)
2571       break;                    /* ready */
2572     if (arrayidx >= arraysize) {        /* mutt lacks a real safe_realoc - so we need to copy */
2573       struct dn_array_s *a2;
2574
2575       arraysize += 5;
2576       a2 = p_new(struct dn_array_s, arraysize + 1);
2577       for (i = 0; i < arrayidx; i++) {
2578         a2[i].key = array[i].key;
2579         a2[i].value = array[i].value;
2580       }
2581       p_delete(&array);
2582       array = a2;
2583     }
2584     array[arrayidx].key = NULL;
2585     array[arrayidx].value = NULL;
2586     string = parse_dn_part (array + arrayidx, string);
2587     arrayidx++;
2588     if (!string)
2589       goto failure;
2590     while (*string == ' ')
2591       string++;
2592     if (*string && *string != ',' && *string != ';' && *string != '+')
2593       goto failure;             /* invalid delimiter */
2594     if (*string)
2595       string++;
2596   }
2597   array[arrayidx].key = NULL;
2598   array[arrayidx].value = NULL;
2599   return array;
2600
2601 failure:
2602   for (i = 0; i < arrayidx; i++) {
2603     p_delete(&array[i].key);
2604     p_delete(&array[i].value);
2605   }
2606   p_delete(&array);
2607   return NULL;
2608 }
2609
2610
2611 /* Print a nice representation of the USERID and make sure it is
2612    displayed in a proper way, which does mean to reorder some parts
2613    for S/MIME's DNs.  USERID is a string as returned by the gpgme key
2614    functions.  It is utf-8 encoded. */
2615 static void parse_and_print_user_id (FILE * fp, const char *userid)
2616 {
2617   const char *s;
2618   int i;
2619
2620   if (*userid == '<') {
2621     s = strchr (userid + 1, '>');
2622     if (s)
2623       print_utf8 (fp, userid + 1, s - userid - 1);
2624   }
2625   else if (*userid == '(')
2626     fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
2627   else if (!digit_or_letter ((const unsigned char *) userid))
2628     fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
2629   else {
2630     struct dn_array_s *dn = parse_dn ((const unsigned char *) userid);
2631
2632     if (!dn)
2633       fputs (_("[Can't display this user ID (invalid DN)]"), fp);
2634     else {
2635       print_dn_parts (fp, dn);
2636       for (i = 0; dn[i].key; i++) {
2637         p_delete(&dn[i].key);
2638         p_delete(&dn[i].value);
2639       }
2640       p_delete(&dn);
2641     }
2642   }
2643 }
2644
2645 typedef enum {
2646   KEY_CAP_CAN_ENCRYPT,
2647   KEY_CAP_CAN_SIGN,
2648   KEY_CAP_CAN_CERTIFY
2649 } key_cap_t;
2650
2651 static unsigned int key_check_cap (gpgme_key_t key, key_cap_t cap)
2652 {
2653   gpgme_subkey_t subkey = NULL;
2654   unsigned int ret = 0;
2655
2656   switch (cap) {
2657   case KEY_CAP_CAN_ENCRYPT:
2658     if (!(ret = key->can_encrypt))
2659       for (subkey = key->subkeys; subkey; subkey = subkey->next)
2660         if ((ret = subkey->can_encrypt))
2661           break;
2662     break;
2663   case KEY_CAP_CAN_SIGN:
2664     if (!(ret = key->can_sign))
2665       for (subkey = key->subkeys; subkey; subkey = subkey->next)
2666         if ((ret = subkey->can_sign))
2667           break;
2668     break;
2669   case KEY_CAP_CAN_CERTIFY:
2670     if (!(ret = key->can_certify))
2671       for (subkey = key->subkeys; subkey; subkey = subkey->next)
2672         if ((ret = subkey->can_certify))
2673           break;
2674     break;
2675   }
2676
2677   return ret;
2678 }
2679
2680
2681 /* Print verbose information about a key or certificate to FP. */
2682 static void print_key_info (gpgme_key_t key, FILE * fp)
2683 {
2684   int idx;
2685   const char *s = NULL, *s2 = NULL;
2686   time_t tt = 0;
2687   struct tm *tm;
2688   char shortbuf[STRING];
2689   unsigned long aval = 0;
2690   const char *delim;
2691   int is_pgp = 0;
2692   int i;
2693   gpgme_user_id_t uid = NULL;
2694
2695   if (Locale)
2696     setlocale (LC_TIME, Locale);
2697
2698   is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
2699
2700   for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
2701     if (uid->revoked)
2702       continue;
2703
2704     s = uid->uid;
2705     fputs (idx ? _(" aka ......: ") :_("Name ......: "), fp);
2706
2707     if (uid->invalid) {
2708       fputs (_("[Invalid]"), fp);
2709       putc (' ', fp);
2710     }
2711     if (is_pgp)
2712       print_utf8 (fp, s, m_strlen(s));
2713     else
2714       parse_and_print_user_id (fp, s);
2715     putc ('\n', fp);
2716   }
2717
2718   if (key->subkeys && (key->subkeys->timestamp > 0)) {
2719     tt = key->subkeys->timestamp;
2720
2721     tm = localtime (&tt);
2722 #ifdef HAVE_LANGINFO_D_T_FMT
2723     strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2724 #else
2725     strftime (shortbuf, sizeof shortbuf, "%c", tm);
2726 #endif
2727     fprintf (fp, _("Valid From : %s\n"), shortbuf);
2728   }
2729
2730   if (key->subkeys && (key->subkeys->expires > 0)) {
2731     tt = key->subkeys->expires;
2732
2733     tm = localtime (&tt);
2734 #ifdef HAVE_LANGINFO_D_T_FMT
2735     strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2736 #else
2737     strftime (shortbuf, sizeof shortbuf, "%c", tm);
2738 #endif
2739     fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2740   }
2741
2742   if (key->subkeys)
2743     s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
2744   else
2745     s = "?";
2746
2747   s2 = is_pgp ? "PGP" : "X.509";
2748
2749   if (key->subkeys)
2750     aval = key->subkeys->length;
2751
2752   fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), s2, aval, s);
2753
2754   fprintf (fp, _("Key Usage .: "));
2755   delim = "";
2756
2757   if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT)) {
2758     fprintf (fp, "%s%s", delim, _("encryption"));
2759     delim = _(", ");
2760   }
2761   if (key_check_cap (key, KEY_CAP_CAN_SIGN)) {
2762     fprintf (fp, "%s%s", delim, _("signing"));
2763     delim = _(", ");
2764   }
2765   if (key_check_cap (key, KEY_CAP_CAN_CERTIFY)) {
2766     fprintf (fp, "%s%s", delim, _("certification"));
2767     delim = _(", ");
2768   }
2769   putc ('\n', fp);
2770
2771   if (key->subkeys) {
2772     s = key->subkeys->fpr;
2773     fputs (_("Fingerprint: "), fp);
2774     if (is_pgp && m_strlen(s) == 40) {
2775       for (i = 0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++) {
2776         putc (*s, fp);
2777         putc (s[1], fp);
2778         putc (s[2], fp);
2779         putc (s[3], fp);
2780         putc (is_pgp ? ' ' : ':', fp);
2781         if (is_pgp && i == 4)
2782           putc (' ', fp);
2783       }
2784     }
2785     else {
2786       for (i = 0; *s && s[1] && s[2]; s += 2, i++) {
2787         putc (*s, fp);
2788         putc (s[1], fp);
2789         putc (is_pgp ? ' ' : ':', fp);
2790         if (is_pgp && i == 7)
2791           putc (' ', fp);
2792       }
2793     }
2794     fprintf (fp, "%s\n", s);
2795   }
2796
2797   if (key->issuer_serial) {
2798     s = key->issuer_serial;
2799     if (s)
2800       fprintf (fp, _("Serial-No .: 0x%s\n"), s);
2801   }
2802
2803   if (key->issuer_name) {
2804     s = key->issuer_name;
2805     if (s) {
2806       fprintf (fp, _("Issued By .: "));
2807       parse_and_print_user_id (fp, s);
2808       putc ('\n', fp);
2809     }
2810   }
2811
2812   /* For PGP we list all subkeys. */
2813   if (is_pgp) {
2814     gpgme_subkey_t subkey = NULL;
2815
2816     for (idx = 1, subkey = key->subkeys; subkey; idx++, subkey = subkey->next) {
2817       s = subkey->keyid;
2818
2819       putc ('\n', fp);
2820       if (m_strlen(s) == 16)
2821         s += 8;                 /* display only the short keyID */
2822       fprintf (fp, _("Subkey ....: 0x%s"), s);
2823       if (subkey->revoked) {
2824         putc (' ', fp);
2825         fputs (_("[Revoked]"), fp);
2826       }
2827       if (subkey->invalid) {
2828         putc (' ', fp);
2829         fputs (_("[Invalid]"), fp);
2830       }
2831       if (subkey->expired) {
2832         putc (' ', fp);
2833         fputs (_("[Expired]"), fp);
2834       }
2835       if (subkey->disabled) {
2836         putc (' ', fp);
2837         fputs (_("[Disabled]"), fp);
2838       }
2839       putc ('\n', fp);
2840
2841       if (subkey->timestamp > 0) {
2842         tt = subkey->timestamp;
2843
2844         tm = localtime (&tt);
2845 #ifdef HAVE_LANGINFO_D_T_FMT
2846         strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2847 #else
2848         strftime (shortbuf, sizeof shortbuf, "%c", tm);
2849 #endif
2850         fprintf (fp, _("Valid From : %s\n"), shortbuf);
2851       }
2852
2853       if (subkey->expires > 0) {
2854         tt = subkey->expires;
2855
2856         tm = localtime (&tt);
2857 #ifdef HAVE_LANGINFO_D_T_FMT
2858         strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
2859 #else
2860         strftime (shortbuf, sizeof shortbuf, "%c", tm);
2861 #endif
2862         fprintf (fp, _("Valid To ..: %s\n"), shortbuf);
2863       }
2864
2865       if (subkey)
2866         s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
2867       else
2868         s = "?";
2869
2870       if (subkey)
2871         aval = subkey->length;
2872       else
2873         aval = 0;
2874
2875       fprintf (fp, _("Key Type ..: %s, %lu bit %s\n"), "PGP", aval, s);
2876
2877       fprintf (fp, _("Key Usage .: "));
2878       delim = "";
2879
2880       if (subkey->can_encrypt) {
2881         fprintf (fp, "%s%s", delim, _("encryption"));
2882         delim = _(", ");
2883       }
2884       if (subkey->can_sign) {
2885         fprintf (fp, "%s%s", delim, _("signing"));
2886         delim = _(", ");
2887       }
2888       if (subkey->can_certify) {
2889         fprintf (fp, "%s%s", delim, _("certification"));
2890         delim = _(", ");
2891       }
2892       putc ('\n', fp);
2893     }
2894   }
2895
2896   if (Locale)
2897     setlocale (LC_TIME, "C");
2898 }
2899
2900
2901 /* Show detailed information about the selected key */
2902 static void verify_key (crypt_key_t * key)
2903 {
2904   FILE *fp;
2905   char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
2906   const char *s;
2907   gpgme_ctx_t listctx = NULL;
2908   gpgme_error_t err;
2909   gpgme_key_t k = NULL;
2910   int maxdepth = 100;
2911
2912   fp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
2913   if (!fp) {
2914     mutt_perror (_("Can't create temporary file"));
2915     return;
2916   }
2917   mutt_message _("Collecting data...");
2918
2919   print_key_info (key->kobj, fp);
2920
2921   err = gpgme_new (&listctx);
2922   if (err) {
2923     fprintf (fp, "Internal error: can't create gpgme context: %s\n",
2924              gpgme_strerror (err));
2925     goto leave;
2926   }
2927   if ((key->flags & KEYFLAG_ISX509))
2928     gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
2929
2930   k = key->kobj;
2931   gpgme_key_ref (k);
2932   while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
2933     putc ('\n', fp);
2934     err = gpgme_op_keylist_start (listctx, s, 0);
2935     gpgme_key_release (k);
2936     k = NULL;
2937     if (!err)
2938       err = gpgme_op_keylist_next (listctx, &k);
2939     if (err) {
2940       fprintf (fp, _("Error finding issuer key: %s\n"), gpgme_strerror (err));
2941       goto leave;
2942     }
2943     gpgme_op_keylist_end (listctx);
2944
2945     print_key_info (k, fp);
2946     if (!--maxdepth) {
2947       putc ('\n', fp);
2948       fputs (_("Error: certification chain to long - stopping here\n"), fp);
2949       break;
2950     }
2951   }
2952
2953 leave:
2954   gpgme_key_release (k);
2955   gpgme_release (listctx);
2956   m_fclose(&fp);
2957   mutt_clear_error ();
2958   snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
2959   mutt_do_pager (cmd, tempfile, 0, NULL);
2960 }
2961
2962 /* Implementation of `findkeys'. */
2963
2964 /* Convert string_list_t into a pattern string suitable to be passed to GPGME.
2965    We need to convert spaces in an item into a '+' and '%' into
2966    "%25". */
2967 static char *list_to_pattern (string_list_t * list)
2968 {
2969   string_list_t *l;
2970   char *pattern, *p;
2971   const char *s;
2972   ssize_t n;
2973
2974   n = 0;
2975   for (l = list; l; l = l->next) {
2976     for (s = l->data; *s; s++) {
2977       if (*s == '%')
2978         n += 2;
2979       n++;
2980     }
2981     n++;                        /* delimiter or end of string */
2982   }
2983   n++;                          /* make sure to allocate at least one byte */
2984   pattern = p = p_new(char, n);
2985   for (l = list; l; l = l->next) {
2986     s = l->data;
2987     if (*s) {
2988       if (l != list)
2989         *p++ = ' ';
2990       for (s = l->data; *s; s++) {
2991         if (*s == '%') {
2992           *p++ = '%';
2993           *p++ = '2';
2994           *p++ = '5';
2995         }
2996         else if (*s == '+') {
2997           *p++ = '%';
2998           *p++ = '2';
2999           *p++ = 'B';
3000         }
3001         else if (*s == ' ')
3002           *p++ = '+';
3003         else
3004           *p++ = *s;
3005       }
3006     }
3007   }
3008   *p = 0;
3009   return pattern;
3010 }
3011
3012 /* Return a list of keys which are candidates for the selection.
3013    Select by looking at the HINTS list. */
3014 static crypt_key_t *get_candidates (string_list_t * hints, unsigned int app,
3015                                     int secret)
3016 {
3017   crypt_key_t *db, *k, **kend;
3018   char *pattern;
3019   gpgme_error_t err;
3020   gpgme_ctx_t ctx;
3021   gpgme_key_t key;
3022   int idx;
3023   gpgme_user_id_t uid = NULL;
3024
3025   pattern = list_to_pattern (hints);
3026   if (!pattern)
3027     return NULL;
3028
3029   err = gpgme_new (&ctx);
3030   if (err) {
3031     mutt_error (_("gpgme_new failed: %s"), gpgme_strerror (err));
3032     p_delete(&pattern);
3033     return NULL;
3034   }
3035
3036   db = NULL;
3037   kend = &db;
3038
3039   if ((app & APPLICATION_PGP)) {
3040     /* Its all a mess.  That old GPGME expects different things
3041        depending on the protocol.  For gpg we don' t need percent
3042        escaped pappert but simple strings passed in an array to the
3043        keylist_ext_start function. */
3044     string_list_t *l;
3045     ssize_t n;
3046     char **patarr;
3047
3048     for (l = hints, n = 0; l; l = l->next) {
3049       if (l->data && *l->data)
3050         n++;
3051     }
3052     if (!n)
3053       goto no_pgphints;
3054
3055     patarr = p_new(char *, n + 1);
3056     for (l = hints, n = 0; l; l = l->next) {
3057       if (l->data && *l->data)
3058         patarr[n++] = m_strdup(l->data);
3059     }
3060     patarr[n] = NULL;
3061     err = gpgme_op_keylist_ext_start (ctx, (const char **) patarr, secret, 0);
3062     for (n = 0; patarr[n]; n++)
3063       p_delete(&patarr[n]);
3064     p_delete(&patarr);
3065     if (err) {
3066       mutt_error (_("gpgme_op_keylist_start failed: %s"), gpgme_strerror (err));
3067       gpgme_release (ctx);
3068       p_delete(&pattern);
3069       return NULL;
3070     }
3071
3072     while (!(err = gpgme_op_keylist_next (ctx, &key))) {
3073       unsigned int flags = 0;
3074
3075       if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
3076         flags |= KEYFLAG_CANENCRYPT;
3077       if (key_check_cap (key, KEY_CAP_CAN_SIGN))
3078         flags |= KEYFLAG_CANSIGN;
3079
3080       for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
3081         k = p_new(crypt_key_t, 1);
3082         k->kobj = key;
3083         k->idx = idx;
3084         k->uid = uid->uid;
3085         k->flags = flags;
3086         *kend = k;
3087         kend = &k->next;
3088       }
3089     }
3090     if (gpg_err_code (err) != GPG_ERR_EOF)
3091       mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err));
3092     gpgme_op_keylist_end (ctx);
3093   no_pgphints:
3094     ;
3095   }
3096
3097   if ((app & APPLICATION_SMIME)) {
3098     /* and now look for x509 certificates */
3099     gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
3100     err = gpgme_op_keylist_start (ctx, pattern, 0);
3101     if (err) {
3102       mutt_error (_("gpgme_op_keylist_start failed: %s"), gpgme_strerror (err));
3103       gpgme_release (ctx);
3104       p_delete(&pattern);
3105       return NULL;
3106     }
3107
3108     while (!(err = gpgme_op_keylist_next (ctx, &key))) {
3109       unsigned int flags = KEYFLAG_ISX509;
3110
3111       if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
3112         flags |= KEYFLAG_CANENCRYPT;
3113       if (key_check_cap (key, KEY_CAP_CAN_SIGN))
3114         flags |= KEYFLAG_CANSIGN;
3115
3116       for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
3117         k = p_new(crypt_key_t, 1);
3118         k->kobj = key;
3119         k->idx = idx;
3120         k->uid = uid->uid;
3121         k->flags = flags;
3122         *kend = k;
3123         kend = &k->next;
3124       }
3125     }
3126     if (gpg_err_code (err) != GPG_ERR_EOF)
3127       mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err));
3128     gpgme_op_keylist_end (ctx);
3129   }
3130
3131   gpgme_release (ctx);
3132   p_delete(&pattern);
3133   return db;
3134 }
3135
3136 /* Add the string STR to the list HINTS.  This list is later used to
3137    match addresses. */
3138 static string_list_t *crypt_add_string_to_hints (string_list_t * hints, const char *str)
3139 {
3140   char *scratch;
3141   char *t;
3142
3143   if ((scratch = m_strdup(str)) == NULL)
3144     return hints;
3145
3146   for (t = strtok (scratch, " ,.:\"()<>\n"); t;
3147        t = strtok (NULL, " ,.:\"()<>\n")) {
3148     if (m_strlen(t) > 3)
3149       hints = mutt_add_list(hints, t);
3150   }
3151
3152   p_delete(&scratch);
3153   return hints;
3154 }
3155
3156 /* Display a menu to select a key from the array KEYS. FORCED_VALID
3157    will be set to true on return if the user did override the the
3158    key's validity. */
3159 static crypt_key_t *crypt_select_key (crypt_key_t * keys,
3160                                       address_t * p, const char *s,
3161                                       unsigned int app, int *forced_valid)
3162 {
3163   int keymax;
3164   crypt_key_t **key_table;
3165   MUTTMENU *menu;
3166   int i, done = 0;
3167   char helpstr[STRING], buf[LONG_STRING];
3168   crypt_key_t *k;
3169   int (*f) (const void *, const void *);
3170   int menu_to_use = 0;
3171   int unusable = 0;
3172
3173   *forced_valid = 0;
3174
3175   /* build the key table */
3176   keymax = i = 0;
3177   key_table = NULL;
3178   for (k = keys; k; k = k->next) {
3179     if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
3180       unusable = 1;
3181       continue;
3182     }
3183
3184     if (i == keymax) {
3185       keymax += 20;
3186       p_realloc(&key_table, keymax);
3187     }
3188
3189     key_table[i++] = k;
3190   }
3191
3192   if (!i && unusable) {
3193     mutt_error _("All matching keys are marked expired/revoked.");
3194
3195     mutt_sleep (1);
3196     return NULL;
3197   }
3198
3199   switch (PgpSortKeys & SORT_MASK) {
3200   case SORT_DATE:
3201     f = crypt_compare_date;
3202     break;
3203   case SORT_KEYID:
3204     f = crypt_compare_keyid;
3205     break;
3206   case SORT_ADDRESS:
3207     f = crypt_compare_address;
3208     break;
3209   case SORT_TRUST:
3210   default:
3211     f = crypt_compare_trust;
3212     break;
3213   }
3214   qsort (key_table, i, sizeof (crypt_key_t *), f);
3215
3216   if (app & APPLICATION_PGP)
3217     menu_to_use = MENU_KEY_SELECT_PGP;
3218   else if (app & APPLICATION_SMIME)
3219     menu_to_use = MENU_KEY_SELECT_SMIME;
3220
3221   helpstr[0] = 0;
3222   mutt_make_help (buf, sizeof (buf), _("Exit  "), menu_to_use, OP_EXIT);
3223   m_strcat(helpstr, sizeof(helpstr), buf);
3224   mutt_make_help (buf, sizeof (buf), _("Select  "), menu_to_use,
3225                   OP_GENERIC_SELECT_ENTRY);
3226   m_strcat(helpstr, sizeof(helpstr), buf);
3227   mutt_make_help (buf, sizeof (buf), _("Check key  "),
3228                   menu_to_use, OP_VERIFY_KEY);
3229   m_strcat(helpstr, sizeof(helpstr), buf);
3230   mutt_make_help (buf, sizeof (buf), _("Help"), menu_to_use, OP_HELP);
3231   m_strcat(helpstr, sizeof(helpstr), buf);
3232
3233   menu = mutt_new_menu ();
3234   menu->max = i;
3235   menu->make_entry = crypt_entry;
3236   menu->menu = menu_to_use;
3237   menu->help = helpstr;
3238   menu->data = key_table;
3239
3240   {
3241     const char *ts;
3242
3243     if ((app & APPLICATION_PGP) && (app & APPLICATION_SMIME))
3244       ts = _("PGP and S/MIME keys matching");
3245     else if ((app & APPLICATION_PGP))
3246       ts = _("PGP keys matching");
3247     else if ((app & APPLICATION_SMIME))
3248       ts = _("S/MIME keys matching");
3249     else
3250       ts = _("keys matching");
3251
3252     if (p)
3253       snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
3254     else
3255       snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
3256     menu->title = buf;
3257   }
3258
3259   mutt_clear_error ();
3260   k = NULL;
3261   while (!done) {
3262     *forced_valid = 0;
3263     switch (mutt_menuLoop (menu)) {
3264     case OP_VERIFY_KEY:
3265       verify_key (key_table[menu->current]);
3266       menu->redraw = REDRAW_FULL;
3267       break;
3268
3269     case OP_VIEW_ID:
3270       mutt_message ("%s", key_table[menu->current]->uid);
3271       break;
3272
3273     case OP_GENERIC_SELECT_ENTRY:
3274       /* FIXME make error reporting more verbose - this should be
3275          easy because gpgme provides more information */
3276       if (option (OPTPGPCHECKTRUST)) {
3277         if (!crypt_key_is_valid (key_table[menu->current])) {
3278           mutt_error _("This key can't be used: "
3279                        "expired/disabled/revoked.");
3280           break;
3281         }
3282       }
3283
3284       if (option (OPTPGPCHECKTRUST) &&
3285           (!crypt_id_is_valid (key_table[menu->current])
3286            || !crypt_id_is_strong (key_table[menu->current]))) {
3287         const char *warn_s;
3288         char buff[LONG_STRING];
3289
3290         if (key_table[menu->current]->flags & KEYFLAG_CANTUSE)
3291           s = N_("ID is expired/disabled/revoked.");
3292         else {
3293           gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
3294           gpgme_user_id_t uid = NULL;
3295           int j = 0;
3296
3297           warn_s = "??";
3298
3299           uid = key_table[menu->current]->kobj->uids;
3300           for (j = 0; (j < key_table[menu->current]->idx) && uid;
3301                j++, uid = uid->next);
3302           if (uid)
3303             val = uid->validity;
3304
3305           switch (val) {
3306           case GPGME_VALIDITY_UNKNOWN:
3307           case GPGME_VALIDITY_UNDEFINED:
3308             warn_s = N_("ID has undefined validity.");
3309             break;
3310           case GPGME_VALIDITY_NEVER:
3311             warn_s = N_("ID is not valid.");
3312             break;
3313           case GPGME_VALIDITY_MARGINAL:
3314             warn_s = N_("ID is only marginally valid.");
3315             break;
3316           case GPGME_VALIDITY_FULL:
3317           case GPGME_VALIDITY_ULTIMATE:
3318             break;
3319           }
3320
3321           snprintf (buff, sizeof (buff),
3322                     _("%s Do you really want to use the key?"), _(warn_s));
3323
3324           if (mutt_yesorno (buff, 0) != 1) {
3325             mutt_clear_error ();
3326             break;
3327           }
3328           *forced_valid = 1;
3329         }
3330       }
3331
3332       k = crypt_copy_key (key_table[menu->current]);
3333       done = 1;
3334       break;
3335
3336     case OP_EXIT:
3337       k = NULL;
3338       done = 1;
3339       break;
3340     }
3341   }
3342
3343   mutt_menuDestroy (&menu);
3344   p_delete(&key_table);
3345
3346   set_option (OPTNEEDREDRAW);
3347
3348   return k;
3349 }
3350
3351 static crypt_key_t *crypt_getkeybyaddr (address_t * a, short abilities,
3352                                         unsigned int app, int *forced_valid)
3353 {
3354   address_t *r, *p;
3355   string_list_t *hints = NULL;
3356
3357   int weak = 0;
3358   int invalid = 0;
3359   int multi = 0;
3360   int this_key_has_strong;
3361   int this_key_has_weak;
3362   int this_key_has_invalid;
3363   int match;
3364
3365   crypt_key_t *keys, *k;
3366   crypt_key_t *the_valid_key = NULL;
3367   crypt_key_t *matches = NULL;
3368   crypt_key_t **matches_endp = &matches;
3369
3370   *forced_valid = 0;
3371
3372   if (a && a->mailbox)
3373     hints = crypt_add_string_to_hints (hints, a->mailbox);
3374   if (a && a->personal)
3375     hints = crypt_add_string_to_hints (hints, a->personal);
3376
3377   mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
3378   keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
3379
3380   string_list_wipe(&hints);
3381
3382   if (!keys)
3383     return NULL;
3384
3385   for (k = keys; k; k = k->next) {
3386     if (abilities && !(k->flags & abilities)) {
3387       continue;
3388     }
3389
3390     this_key_has_weak = 0;      /* weak but valid match   */
3391     this_key_has_invalid = 0;   /* invalid match          */
3392     this_key_has_strong = 0;    /* strong and valid match */
3393     match = 0;                  /* any match            */
3394
3395     r = rfc822_parse_adrlist (NULL, k->uid);
3396     for (p = r; p; p = p->next) {
3397       int validity = crypt_id_matches_addr (a, p, k);
3398
3399       if (validity & CRYPT_KV_MATCH)    /* something matches */
3400         match = 1;
3401
3402       /* is this key a strong candidate? */
3403       if ((validity & CRYPT_KV_VALID)
3404           && (validity & CRYPT_KV_STRONGID)
3405           && (validity & CRYPT_KV_ADDR)) {
3406         if (the_valid_key && the_valid_key != k)
3407           multi = 1;
3408         the_valid_key = k;
3409         this_key_has_strong = 1;
3410       }
3411       else if ((validity & CRYPT_KV_MATCH)
3412                && !(validity & CRYPT_KV_VALID))
3413         this_key_has_invalid = 1;
3414       else if ((validity & CRYPT_KV_MATCH)
3415                && (!(validity & CRYPT_KV_STRONGID)
3416                    || !(validity & CRYPT_KV_ADDR)))
3417         this_key_has_weak = 1;
3418     }
3419     address_list_wipe(&r);
3420
3421     if (match) {
3422       crypt_key_t *tmp;
3423
3424       if (!this_key_has_strong && this_key_has_invalid)
3425         invalid = 1;
3426       if (!this_key_has_strong && this_key_has_weak)
3427         weak = 1;
3428
3429       *matches_endp = tmp = crypt_copy_key (k);
3430       matches_endp = &tmp->next;
3431       the_valid_key = tmp;
3432     }
3433   }
3434
3435   crypt_free_key (&keys);
3436
3437   if (matches) {
3438     if (the_valid_key && !multi && !weak
3439         && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
3440       /*
3441        * There was precisely one strong match on a valid ID, there
3442        * were no valid keys with weak matches, and we aren't
3443        * interested in seeing invalid keys.
3444        *
3445        * Proceed without asking the user.
3446        */
3447       k = crypt_copy_key (the_valid_key);
3448     }
3449     else {
3450       /*
3451        * Else: Ask the user.
3452        */
3453       k = crypt_select_key (matches, a, NULL, app, forced_valid);
3454     }
3455     crypt_free_key (&matches);
3456   }
3457   else
3458     k = NULL;
3459
3460   return k;
3461 }
3462
3463
3464 static crypt_key_t *crypt_getkeybystr (const char *p, short abilities,
3465                                        unsigned int app, int *forced_valid)
3466 {
3467   string_list_t *hints = NULL;
3468   crypt_key_t *keys;
3469   crypt_key_t *matches = NULL;
3470   crypt_key_t **matches_endp = &matches;
3471   crypt_key_t *k;
3472   int match;
3473
3474   mutt_message (_("Looking for keys matching \"%s\"..."), p);
3475
3476   *forced_valid = 0;
3477
3478   hints = crypt_add_string_to_hints (hints, p);
3479   keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
3480   string_list_wipe(&hints);
3481
3482   if (!keys)
3483     return NULL;
3484
3485   for (k = keys; k; k = k->next) {
3486     if (abilities && !(k->flags & abilities))
3487       continue;
3488
3489     match = 0;
3490
3491     if (!*p || !m_strcasecmp(p, crypt_keyid (k))
3492         || (!m_strncasecmp(p, "0x", 2)
3493             && !m_strcasecmp(p + 2, crypt_keyid (k)))
3494         || (option (OPTPGPLONGIDS)
3495             && !m_strncasecmp(p, "0x", 2)
3496             && !m_strcasecmp(p + 2, crypt_keyid (k) + 8))
3497         || m_stristr(k->uid, p)) {
3498       crypt_key_t *tmp;
3499
3500       *matches_endp = tmp = crypt_copy_key (k);
3501       matches_endp = &tmp->next;
3502     }
3503   }
3504
3505   crypt_free_key (&keys);
3506
3507   if (matches) {
3508     k = crypt_select_key (matches, NULL, p, app, forced_valid);
3509     crypt_free_key (&matches);
3510     return k;
3511   }
3512
3513   return NULL;
3514 }
3515
3516 /* Display TAG as a prompt to ask for a key.  If WHATFOR is not null
3517    use it as default and store it under that label as the next
3518    default.  ABILITIES describe the required key abilities (sign,
3519    encrypt) and APP the type of the requested key; ether S/MIME or
3520    PGP.  Return a copy of the key or NULL if not found. */
3521 static crypt_key_t *crypt_ask_for_key (char *tag,
3522                                        char *whatfor,
3523                                        short abilities,
3524                                        unsigned int app, int *forced_valid)
3525 {
3526   crypt_key_t *key;
3527   char resp[STRING];
3528   struct crypt_cache *l = NULL;
3529   int dummy;
3530
3531   if (!forced_valid)
3532     forced_valid = &dummy;
3533
3534   mutt_clear_error ();
3535
3536   *forced_valid = 0;
3537   resp[0] = 0;
3538   if (whatfor) {
3539
3540     for (l = id_defaults; l; l = l->next)
3541       if (!m_strcasecmp(whatfor, l->what)) {
3542         m_strcpy(resp, sizeof(resp), NONULL(l->dflt));
3543         break;
3544       }
3545   }
3546
3547
3548   for (;;) {
3549     resp[0] = 0;
3550     if (mutt_get_field (tag, resp, sizeof (resp), M_CLEAR) != 0)
3551       return NULL;
3552
3553     if (whatfor) {
3554       if (l)
3555         m_strreplace(&l->dflt, resp);
3556       else {
3557         l = p_new(struct crypt_cache, 1);
3558         l->next = id_defaults;
3559         id_defaults = l;
3560         l->what = m_strdup(whatfor);
3561         l->dflt = m_strdup(resp);
3562       }
3563     }
3564
3565     if ((key = crypt_getkeybystr (resp, abilities, app, forced_valid)))
3566       return key;
3567
3568     BEEP ();
3569   }
3570   /* not reached */
3571 }
3572
3573 /* This routine attempts to find the keyids of the recipients of a
3574    message.  It returns NULL if any of the keys can not be found.  */
3575 static char *find_keys (address_t * to, address_t * cc, address_t * bcc,
3576                         unsigned int app)
3577 {
3578   char *keylist = NULL, *t;
3579   const char *keyID;
3580   ssize_t keylist_size = 0;
3581   ssize_t keylist_used = 0;
3582   address_t *tmp = NULL, *addr = NULL;
3583   address_t **last = &tmp;
3584   address_t *p, *q;
3585   int i;
3586   crypt_key_t *k_info, *key;
3587   const char *fqdn = mutt_fqdn (1);
3588
3589 #if 0
3590   *r_application = APPLICATION_PGP | APPLICATION_SMIME;
3591 #endif
3592
3593   for (i = 0; i < 3; i++) {
3594     switch (i) {
3595     case 0:
3596       p = to;
3597       break;
3598     case 1:
3599       p = cc;
3600       break;
3601     case 2:
3602       p = bcc;
3603       break;
3604     default:
3605       abort ();
3606     }
3607
3608     *last = address_list_dup (p);
3609     while (*last)
3610       last = &((*last)->next);
3611   }
3612
3613   rfc822_qualify(tmp, fqdn);
3614   address_list_uniq(tmp);
3615
3616   for (p = tmp; p; p = p->next) {
3617     char buf[LONG_STRING];
3618     int forced_valid = 0;
3619
3620     q = p;
3621     k_info = NULL;
3622
3623     if ((keyID = mutt_crypt_hook (p)) != NULL) {
3624       int r;
3625
3626       snprintf (buf, sizeof (buf), _("Use keyID = \"%s\" for %s?"),
3627                 keyID, p->mailbox);
3628       if ((r = mutt_yesorno (buf, M_YES)) == M_YES) {
3629         /* check for e-mail address */
3630         if ((t = strchr (keyID, '@')) &&
3631             (addr = rfc822_parse_adrlist (NULL, keyID))) {
3632           rfc822_qualify(addr, fqdn);
3633           q = addr;
3634         }
3635         else {
3636           k_info = crypt_getkeybystr (keyID, KEYFLAG_CANENCRYPT,
3637                                       app, &forced_valid);
3638         }
3639       }
3640       else if (r == -1) {
3641         p_delete(&keylist);
3642         address_list_wipe(&tmp);
3643         address_list_wipe(&addr);
3644         return NULL;
3645       }
3646     }
3647
3648     if (k_info == NULL
3649         && (k_info = crypt_getkeybyaddr (q, KEYFLAG_CANENCRYPT,
3650                                          app, &forced_valid)) == NULL) {
3651       snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
3652
3653       if ((key = crypt_ask_for_key (buf, q->mailbox, KEYFLAG_CANENCRYPT,
3654                                     app,
3655                                     &forced_valid)) == NULL) {
3656         p_delete(&keylist);
3657         address_list_wipe(&tmp);
3658         address_list_wipe(&addr);
3659         return NULL;
3660       }
3661     }
3662     else
3663       key = k_info;
3664
3665     {
3666       const char *s = crypt_fpr (key);
3667
3668       keylist_size += m_strlen(s) + 4 + 1;
3669       p_realloc(&keylist, keylist_size);
3670       sprintf (keylist + keylist_used, "%s0x%s%s",
3671                keylist_used ? " " : "", s, forced_valid ? "!" : "");
3672     }
3673     keylist_used = m_strlen(keylist);
3674
3675     crypt_free_key (&key);
3676     address_list_wipe(&addr);
3677   }
3678   address_list_wipe(&tmp);
3679   return (keylist);
3680 }
3681
3682 int crypt_get_keys (HEADER * msg, char **keylist)
3683 {
3684   /* Do a quick check to make sure that we can find all of the encryption
3685    * keys if the user has requested this service.
3686    */
3687
3688   *keylist = NULL;
3689
3690   if (msg->security & ENCRYPT) {
3691     if (msg->security & APPLICATION_PGP) {
3692       set_option(OPTPGPCHECKTRUST);
3693       *keylist = find_keys(msg->env->to, msg->env->cc, msg->env->bcc,
3694                            APPLICATION_PGP);
3695       unset_option(OPTPGPCHECKTRUST);
3696       if (!*keylist)
3697           return -1;
3698     }
3699
3700     if (msg->security & APPLICATION_SMIME) {
3701       *keylist = find_keys(msg->env->to, msg->env->cc, msg->env->bcc,
3702                            APPLICATION_SMIME);
3703       if (!*keylist)
3704           return -1;
3705     }
3706   }
3707
3708   return (0);
3709 }
3710
3711
3712 int crypt_send_menu (HEADER * msg, int *redraw, int is_smime)
3713 {
3714   crypt_key_t *p;
3715   char input_signas[STRING];
3716   int choice;
3717
3718   if (msg->security & APPLICATION_PGP)
3719     is_smime = 0;
3720   else if (msg->security & APPLICATION_SMIME)
3721     is_smime = 1;
3722
3723   if (is_smime)
3724     choice =
3725       mutt_multi_choice (_
3726                          ("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
3727                          _("esabpc"));
3728   else
3729     choice =
3730       mutt_multi_choice (_
3731                          ("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
3732                          _("esabmc"));
3733
3734   switch (choice) {
3735   case 1:                      /* (e)ncrypt */
3736     msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3737     msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
3738     break;
3739
3740   case 2:                      /* (s)ign */
3741     msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3742     msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
3743     break;
3744
3745   case 3:                      /* sign (a)s */
3746     if ((p = crypt_ask_for_key (_("Sign as: "), NULL, KEYFLAG_CANSIGN,
3747                                 is_smime ? APPLICATION_SMIME :
3748                                 APPLICATION_PGP, NULL))) {
3749       snprintf (input_signas, sizeof (input_signas), "0x%s", crypt_keyid (p));
3750       m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs,
3751                         input_signas);
3752       crypt_free_key (&p);
3753
3754       msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
3755     }
3756     *redraw = REDRAW_FULL;
3757     break;
3758
3759   case 4:                      /* (b)oth */
3760     msg->security =
3761       (is_smime ? (SMIMEENCRYPT | SMIMESIGN) : (PGPENCRYPT | PGPSIGN));
3762     break;
3763
3764   case 5:                      /* (p)gp or s/(m)ime */
3765     is_smime = !is_smime;
3766     break;
3767
3768   case 6:                      /* (c)lear */
3769     return msg->security = 0;
3770   }
3771
3772   if (is_smime) {
3773     msg->security &= ~APPLICATION_PGP;
3774     msg->security |= APPLICATION_SMIME;
3775   } else {
3776     msg->security &= ~APPLICATION_SMIME;
3777     msg->security |= APPLICATION_PGP;
3778   }
3779
3780   return msg->security;
3781 }
3782
3783 int crypt_smime_verify_sender (HEADER * h)
3784 {
3785   address_t *sender = NULL;
3786   unsigned int ret = 1;
3787
3788   if (h->env->from) {
3789     h->env->from = mutt_expand_aliases (h->env->from);
3790     sender = h->env->from;
3791   }
3792   else if (h->env->sender) {
3793     h->env->sender = mutt_expand_aliases (h->env->sender);
3794     sender = h->env->sender;
3795   }
3796
3797   if (sender) {
3798     if (signature_key) {
3799       gpgme_key_t key = signature_key;
3800       gpgme_user_id_t uid = NULL;
3801       int sender_length = 0;
3802       int uid_length = 0;
3803
3804       sender_length = m_strlen(sender->mailbox);
3805       for (uid = key->uids; uid && ret; uid = uid->next) {
3806         uid_length = m_strlen(uid->email);
3807         if (1 && (uid->email[0] == '<')
3808             && (uid->email[uid_length - 1] == '>')
3809             && (uid_length == sender_length + 2)
3810             && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
3811           ret = 0;
3812       }
3813     }
3814     else
3815       mutt_any_key_to_continue ("Failed to verify sender");
3816   }
3817   else
3818     mutt_any_key_to_continue ("Failed to figure out sender");
3819
3820   if (signature_key) {
3821     gpgme_key_release (signature_key);
3822     signature_key = NULL;
3823   }
3824
3825   return ret;
3826 }
3827
3828 void crypt_invoke_import(FILE *stream, int smime)
3829 {
3830     gpgme_ctx_t ctx = create_gpgme_context(smime);
3831     gpgme_data_t data;
3832     gpgme_error_t err;
3833
3834     err = gpgme_data_new_from_stream(&data, stream);
3835     if (err) {
3836         mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
3837         gpgme_release(ctx);
3838         return;
3839     }
3840
3841     err = gpgme_op_import(ctx, data);
3842     if (err) {
3843         mutt_error(_("error importing gpg data: %s\n"), gpgme_strerror(err));
3844         gpgme_data_release(data);
3845         gpgme_release(ctx);
3846         return;
3847     }
3848
3849     gpgme_data_release(data);
3850     gpgme_release(ctx);
3851     return;
3852 }
3853
3854 static void pgp_extract_keys_from_attachment(FILE * fp, BODY * top)
3855 {
3856     STATE s;
3857     FILE *tmpfp = tmpfile();
3858
3859     if (tmpfp == NULL) {
3860         mutt_perror (_("Can't create temporary file"));
3861         return;
3862     }
3863
3864     p_clear(&s, 1);
3865     s.fpin  = fp;
3866     s.fpout = tmpfp;
3867     mutt_body_handler(top, &s);
3868
3869     rewind(tmpfp);
3870     crypt_invoke_import(tmpfp, 0);
3871     m_fclose(&tmpfp);
3872 }
3873
3874 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
3875 {
3876   mutt_endwin (NULL);
3877   set_option (OPTDONTHANDLEPGPKEYS);
3878
3879   for (; top; top = top->next) {
3880     if (!tag || top->tagged)
3881       pgp_extract_keys_from_attachment (fp, top);
3882
3883     if (!tag)
3884       break;
3885   }
3886
3887   unset_option (OPTDONTHANDLEPGPKEYS);
3888 }
3889
3890
3891 /* TODO */
3892
3893 /* fixme: needs documentation. */
3894 void crypt_pgp_invoke_getkeys (address_t * addr)
3895 {
3896 }
3897
3898 /* Generate a PGP public key attachment. */
3899 BODY *crypt_pgp_make_key_attachment (char *tempf)
3900 {
3901     return NULL;
3902 }
3903
3904 /* S/MIME */
3905
3906 /* fixme: Needs documentation. */
3907 void crypt_smime_getkeys (ENVELOPE * env)
3908 {
3909 }
3910
3911 /***************************************************************************/
3912
3913 void crypt_invoke_message (int type)
3914 {
3915     if (type & APPLICATION_PGP) {
3916         mutt_message _("Invoking PGP...");
3917     }
3918     else if (type & APPLICATION_SMIME) {
3919         mutt_message _("Invoking S/MIME...");
3920     }
3921 }
3922
3923 int mutt_protect (HEADER * msg, char *keylist)
3924 {
3925   BODY *pbody = NULL, *tmp_pbody = NULL;
3926   BODY *tmp_smime_pbody = NULL;
3927   BODY *tmp_pgp_pbody = NULL;
3928   int flags = msg->security;
3929
3930   if (!isendwin ())
3931     mutt_endwin (NULL);
3932
3933   tmp_smime_pbody = msg->content;
3934   tmp_pgp_pbody = msg->content;
3935
3936   if (msg->security & SIGN) {
3937     if (msg->security & APPLICATION_SMIME) {
3938       if (!(tmp_pbody = crypt_smime_sign_message (msg->content)))
3939         return -1;
3940       pbody = tmp_smime_pbody = tmp_pbody;
3941     }
3942
3943     if ((msg->security & APPLICATION_PGP)
3944         && (!(flags & ENCRYPT) || option (OPTPGPRETAINABLESIG))) {
3945       if (!(tmp_pbody = crypt_pgp_sign_message (msg->content)))
3946         return -1;
3947
3948       flags &= ~SIGN;
3949       pbody = tmp_pgp_pbody = tmp_pbody;
3950     }
3951
3952     if ((msg->security & APPLICATION_SMIME)
3953         && (msg->security & APPLICATION_PGP)) {
3954       /* here comes the draft ;-) */
3955     }
3956   }
3957
3958
3959   if (msg->security & ENCRYPT) {
3960     if ((msg->security & APPLICATION_SMIME)) {
3961       if (!(tmp_pbody = crypt_smime_build_smime_entity (tmp_smime_pbody,
3962                                                         keylist))) {
3963         /* signed ? free it! */
3964         return (-1);
3965       }
3966       /* free tmp_body if messages was signed AND encrypted ... */
3967       if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody) {
3968         /* detatch and dont't delete msg->content,
3969            which tmp_smime_pbody->parts after signing. */
3970         tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
3971         msg->content->next = NULL;
3972         body_list_wipe(&tmp_smime_pbody);
3973       }
3974       pbody = tmp_pbody;
3975     }
3976
3977     if ((msg->security & APPLICATION_PGP)) {
3978       if (!(pbody = crypt_pgp_encrypt_message (tmp_pgp_pbody, keylist,
3979                                                flags & SIGN))) {
3980
3981         /* did we perform a retainable signature? */
3982         if (flags != msg->security) {
3983           /* remove the outer multipart layer */
3984           tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3985           /* get rid of the signature */
3986           body_list_wipe(&tmp_pgp_pbody->next);
3987         }
3988
3989         return (-1);
3990       }
3991
3992       /* destroy temporary signature envelope when doing retainable 
3993        * signatures.
3994
3995        */
3996       if (flags != msg->security) {
3997         tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
3998         body_list_wipe(&tmp_pgp_pbody->next);
3999       }
4000     }
4001   }
4002
4003   if (pbody)
4004     msg->content = pbody;
4005
4006   return 0;
4007 }
4008
4009
4010 int crypt_query (BODY * m)
4011 {
4012   int t = 0;
4013
4014   if (!m)
4015     return 0;
4016
4017   if (m->type == TYPEAPPLICATION) {
4018     t |= mutt_is_application_pgp (m);
4019
4020     t |= mutt_is_application_smime (m);
4021     if (t && m->goodsig)
4022       t |= GOODSIGN;
4023     if (t && m->badsig)
4024       t |= BADSIGN;
4025   }
4026   else if (m->type == TYPETEXT) {
4027     t |= mutt_is_application_pgp (m);
4028     if (t && m->goodsig)
4029       t |= GOODSIGN;
4030   }
4031
4032   if (m->type == TYPEMULTIPART) {
4033     t |= mutt_is_multipart_encrypted (m);
4034     t |= mutt_is_multipart_signed (m);
4035
4036     if (t && m->goodsig)
4037       t |= GOODSIGN;
4038   }
4039
4040   if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE) {
4041     BODY *p;
4042     int u, v, w;
4043
4044     u = m->parts ? ~0 : 0;      /* Bits set in all parts */
4045     w = 0;                      /* Bits set in any part  */
4046
4047     for (p = m->parts; p; p = p->next) {
4048       v = crypt_query (p);
4049       u &= v;
4050       w |= v;
4051     }
4052     t |= u | (w & ~GOODSIGN);
4053
4054     if ((w & GOODSIGN) && !(u & GOODSIGN))
4055       t |= PARTSIGN;
4056   }
4057
4058   return t;
4059 }
4060
4061
4062 static void crypt_write_signed(BODY * a, STATE * s, FILE *fp)
4063 {
4064     int c;
4065     short hadcr;
4066     size_t bytes;
4067
4068     fseeko (s->fpin, a->hdr_offset, 0);
4069     bytes = a->length + a->offset - a->hdr_offset;
4070     hadcr = 0;
4071     while (bytes > 0) {
4072         if ((c = fgetc (s->fpin)) == EOF)
4073             break;
4074
4075         bytes--;
4076
4077         if (c == '\r')
4078             hadcr = 1;
4079         else {
4080             if (c == '\n' && !hadcr)
4081                 fputc ('\r', fp);
4082
4083             hadcr = 0;
4084         }
4085         fputc (c, fp);
4086     }
4087 }
4088
4089
4090
4091 void convert_to_7bit (BODY * a)
4092 {
4093   while (a) {
4094     if (a->type == TYPEMULTIPART) {
4095       if (a->encoding != ENC7BIT) {
4096         a->encoding = ENC7BIT;
4097         convert_to_7bit (a->parts);
4098       } else {
4099         convert_to_7bit (a->parts);
4100       }
4101     }
4102     else if (a->type == TYPEMESSAGE &&
4103              m_strcasecmp(a->subtype, "delivery-status")) {
4104       if (a->encoding != ENC7BIT)
4105         mutt_message_to_7bit (a, NULL);
4106     }
4107     else if (a->encoding == ENC8BIT)
4108       a->encoding = ENCQUOTEDPRINTABLE;
4109     else if (a->encoding == ENCBINARY)
4110       a->encoding = ENCBASE64;
4111     else if (a->content && a->encoding != ENCBASE64 &&
4112              (a->content->from || a->content->space))
4113       a->encoding = ENCQUOTEDPRINTABLE;
4114     a = a->next;
4115   }
4116 }
4117
4118
4119 static void extract_keys_aux(FILE *fpout, HEADER *h)
4120 {
4121     mutt_parse_mime_message (Context, h);
4122
4123     rewind(fpout);
4124     if (h->security & APPLICATION_PGP) {
4125         mutt_copy_message(fpout, Context, h, M_CM_DECODE | M_CM_CHARCONV, 0);
4126         fflush (fpout);
4127
4128         mutt_endwin (_("Trying to extract PGP keys...\n"));
4129     }
4130
4131     if (h->security & APPLICATION_SMIME) {
4132         if (h->security & ENCRYPT)
4133             mutt_copy_message (fpout, Context, h, M_CM_NOHEADER
4134                                | M_CM_DECODE_CRYPT | M_CM_DECODE_SMIME, 0);
4135         else
4136             mutt_copy_message(fpout, Context, h, 0, 0);
4137         fflush (fpout);
4138
4139         mutt_message (_("Trying to extract S/MIME certificates...\n"));
4140     }
4141
4142     rewind(fpout);
4143     crypt_invoke_import(fpout, h->security & APPLICATION_SMIME);
4144 }
4145
4146 void crypt_extract_keys_from_messages(HEADER * h)
4147 {
4148     FILE *tmpfp = tmpfile();
4149     if (!tmpfp) {
4150         mutt_error(_("Could not create temporary file"));
4151         return;
4152     }
4153
4154     set_option(OPTDONTHANDLEPGPKEYS);
4155     if (!h) {
4156         int i;
4157         for (i = 0; i < Context->vcount; i++) {
4158             if (!Context->hdrs[Context->v2r[i]]->tagged)
4159                 continue;
4160             extract_keys_aux(tmpfp, Context->hdrs[Context->v2r[i]]);
4161         }
4162     } else {
4163         extract_keys_aux(tmpfp, h);
4164     }
4165     unset_option(OPTDONTHANDLEPGPKEYS);
4166     m_fclose(&tmpfp);
4167
4168     if (isendwin())
4169         mutt_any_key_to_continue(NULL);
4170 }
4171
4172
4173
4174 static void crypt_fetch_signatures (BODY ***signatures, BODY * a, int *n)
4175 {
4176   for (; a; a = a->next) {
4177     if (a->type == TYPEMULTIPART)
4178       crypt_fetch_signatures (signatures, a->parts, n);
4179     else {
4180       if ((*n % 5) == 0)
4181         p_realloc(signatures, *n + 6);
4182
4183       (*signatures)[(*n)++] = a;
4184     }
4185   }
4186 }
4187
4188
4189 /*
4190  * This routine verifies a  "multipart/signed"  body.
4191  */
4192
4193 int mutt_signed_handler (BODY * a, STATE * s)
4194 {
4195   unsigned major, minor;
4196   char *protocol;
4197   int rc, i, goodsig = 1, sigcnt = 0;
4198   BODY *b = a;
4199
4200   protocol = parameter_getval(a->parameter, "protocol");
4201   a = a->parts;
4202
4203   switch (mime_which_token(protocol, -1)) {
4204     case MIME_APPLICATION_PGP_SIGNATURE:
4205       major = TYPEAPPLICATION;
4206       minor = MIME_PGP_SIGNATURE;
4207       break;
4208     case MIME_APPLICATION_X_PKCS7_SIGNATURE:
4209       major = TYPEAPPLICATION;
4210       minor = MIME_X_PKCS7_SIGNATURE;
4211       break;
4212     case MIME_APPLICATION_PKCS7_SIGNATURE:
4213       major = TYPEAPPLICATION;
4214       minor = MIME_PKCS7_SIGNATURE;
4215       break;
4216     case MIME_MULTIPART_MIXED:
4217       major = TYPEMULTIPART;
4218       minor = MIME_MIXED;
4219       break;
4220
4221     default:
4222       state_printf(s, _("[-- Error: "
4223                         "Unknown multipart/signed protocol %s! --]\n\n"),
4224                     protocol);
4225       return mutt_body_handler (a, s);
4226   }
4227
4228   /* consistency check */
4229   if (!(a && a->next && a->next->type == major &&
4230         mime_which_token(a->next->subtype, -1) == minor))
4231   {
4232     state_attach_puts(_("[-- Error: "
4233                         "Inconsistent multipart/signed structure! --]\n\n"),
4234                       s);
4235     return mutt_body_handler (a, s);
4236   }
4237
4238   if (s->flags & M_DISPLAY) {
4239     BODY **sigs = NULL;
4240
4241     crypt_fetch_signatures (&sigs, a->next, &sigcnt);
4242     if (sigcnt) {
4243       FILE *tmpfp = tmpfile();
4244
4245       if (!tmpfp) {
4246           mutt_error(_("Could not create temporary file"));
4247       } else {
4248         crypt_write_signed(a, s, tmpfp);
4249         rewind(tmpfp);
4250         for (i = 0; i < sigcnt; i++) {
4251           if (sigs[i]->type == TYPEAPPLICATION) {
4252             int subtype;
4253
4254             switch ((subtype = mime_which_token(sigs[i]->subtype, -1))) {
4255               case MIME_PGP_SIGNATURE:
4256               case MIME_X_PKCS7_SIGNATURE:
4257               case MIME_PKCS7_SIGNATURE:
4258                 if (crypt_verify_one(sigs[i], s, tmpfp, subtype != MIME_PGP_SIGNATURE) != 0)
4259                   goodsig = 0;
4260
4261                 m_fclose(&tmpfp);
4262                 continue;
4263
4264               default:
4265                 break;
4266             }
4267           }
4268
4269           state_printf(s, _("[-- Warning: "
4270                             "We can't verify %s/%s signatures. --]\n\n"),
4271                        TYPE (sigs[i]), sigs[i]->subtype);
4272         }
4273       }
4274
4275       b->goodsig = goodsig;
4276       b->badsig  = !goodsig;
4277
4278       /* Now display the signed body */
4279       state_attach_puts(_("[-- The following data is signed --]\n\n"), s);
4280
4281       p_delete(&sigs);
4282     } else {
4283       state_attach_puts(_("[-- Warning: Can't find any signatures. --]\n\n"),
4284                         s);
4285     }
4286   }
4287
4288   rc = mutt_body_handler (a, s);
4289
4290   if (s->flags & M_DISPLAY && sigcnt)
4291     state_attach_puts (_("\n[-- End of signed data --]\n"), s);
4292
4293   return (rc);
4294 }