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