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