more constness.
[apps/madmutt.git] / crypt.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996,1997 Michael R. Elkins <me@mutt.org>
4  * Copyright (C) 1999-2000 Thomas Roessler <roessler@does-not-exist.org>
5  * Copyright (C) 2001  Thomas Roessler <roessler@does-not-exist.org>
6  *                     Oliver Ehli <elmy@acm.org>
7  * Copyright (C) 2003  Werner Koch <wk@gnupg.org>
8  * Copyright (C) 2004 g10code GmbH
9  *
10  * This file is part of mutt-ng, see http://www.muttng.org/.
11  * It's licensed under the GNU General Public License,
12  * please see the file GPL in the top level source directory.
13  */
14
15 #if HAVE_CONFIG_H
16 # include "config.h"
17 #endif
18
19 #include "mutt.h"
20 #include "ascii.h"
21 #include "handler.h"
22 #include "mutt_curses.h"
23 #include "mime.h"
24 #include "copy.h"
25 #include "mutt_crypt.h"
26 #include "pgp.h"
27
28 #include "lib/mem.h"
29 #include "lib/intl.h"
30 #include "lib/str.h"
31
32 #include <sys/wait.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <sys/stat.h>
37 #include <errno.h>
38 #include <ctype.h>
39
40 #ifdef HAVE_LOCALE_H
41 #include <locale.h>
42 #endif
43
44 #ifdef HAVE_SYS_TIME_H
45 # include <sys/time.h>
46 #endif
47
48 #ifdef HAVE_SYS_RESOURCE_H
49 # include <sys/resource.h>
50 #endif
51
52
53 /* print the current time to avoid spoofing of the signature output */
54 void crypt_current_time (STATE * s, const char *app_name)
55 {
56   time_t t;
57   char p[STRING], tmp[STRING];
58
59   if (!WithCrypto)
60     return;
61
62   if (option (OPTCRYPTTIMESTAMP)) {
63     t = time (NULL);
64     setlocale (LC_TIME, "");
65     strftime (p, sizeof (p), _(" (current time: %c)"), localtime (&t));
66     setlocale (LC_TIME, "C");
67   }
68   else
69     *p = '\0';
70
71   snprintf (tmp, sizeof (tmp), _("[-- %s output follows%s --]\n"),
72             NONULL (app_name), p);
73   state_attach_puts (tmp, s);
74 }
75
76
77
78 void crypt_forget_passphrase (void)
79 {
80   if ((WithCrypto & APPLICATION_PGP))
81     crypt_pgp_void_passphrase ();
82
83   if ((WithCrypto & APPLICATION_SMIME))
84     crypt_smime_void_passphrase ();
85
86   if (WithCrypto)
87     mutt_message _("Passphrase(s) forgotten.");
88 }
89
90
91 #if defined(HAVE_SETRLIMIT) && (!defined(DEBUG))
92
93 static void disable_coredumps (void)
94 {
95   struct rlimit rl = { 0, 0 };
96   static short done = 0;
97
98   if (!done) {
99     setrlimit (RLIMIT_CORE, &rl);
100     done = 1;
101   }
102 }
103
104 #endif /* HAVE_SETRLIMIT */
105
106
107 int crypt_valid_passphrase (int flags)
108 {
109   int ret = 0;
110
111 # if defined(HAVE_SETRLIMIT) &&(!defined(DEBUG))
112   disable_coredumps ();
113 # endif
114
115   if ((WithCrypto & APPLICATION_PGP) && (flags & APPLICATION_PGP))
116     ret = crypt_pgp_valid_passphrase ();
117
118   if ((WithCrypto & APPLICATION_SMIME) && (flags & APPLICATION_SMIME))
119     ret = crypt_smime_valid_passphrase ();
120
121   return ret;
122 }
123
124
125
126 int mutt_protect (HEADER * msg, char *keylist)
127 {
128   BODY *pbody = NULL, *tmp_pbody = NULL;
129   BODY *tmp_smime_pbody = NULL;
130   BODY *tmp_pgp_pbody = NULL;
131   int flags = (WithCrypto & APPLICATION_PGP) ? msg->security : 0;
132   int i;
133
134   if (!WithCrypto)
135     return -1;
136
137   if ((msg->security & SIGN) && !crypt_valid_passphrase (msg->security))
138     return (-1);
139
140   if ((WithCrypto & APPLICATION_PGP)
141       && ((msg->security & PGPINLINE) == PGPINLINE)) {
142     /* they really want to send it inline... go for it */
143     if (!isendwin ())
144       mutt_endwin _("Invoking PGP...");
145
146     pbody = crypt_pgp_traditional_encryptsign (msg->content, flags, keylist);
147     if (pbody) {
148       msg->content = pbody;
149       return 0;
150     }
151
152     /* otherwise inline won't work...ask for revert */
153     if ((i =
154          query_quadoption (OPT_PGPMIMEAUTO,
155                            _
156                            ("Message can't be sent inline.  Revert to using PGP/MIME?")))
157         != M_YES) {
158       mutt_error _("Mail not sent.");
159
160       return -1;
161     }
162
163     /* go ahead with PGP/MIME */
164   }
165
166   if (!isendwin ())
167     mutt_endwin (NULL);
168
169   if ((WithCrypto & APPLICATION_SMIME))
170     tmp_smime_pbody = msg->content;
171   if ((WithCrypto & APPLICATION_PGP))
172     tmp_pgp_pbody = msg->content;
173
174   if (msg->security & SIGN) {
175     if ((WithCrypto & APPLICATION_SMIME)
176         && (msg->security & APPLICATION_SMIME)) {
177       if (!(tmp_pbody = crypt_smime_sign_message (msg->content)))
178         return -1;
179       pbody = tmp_smime_pbody = tmp_pbody;
180     }
181
182     if ((WithCrypto & APPLICATION_PGP)
183         && (msg->security & APPLICATION_PGP)
184         && (!(flags & ENCRYPT) || option (OPTPGPRETAINABLESIG))) {
185       if (!(tmp_pbody = crypt_pgp_sign_message (msg->content)))
186         return -1;
187
188       flags &= ~SIGN;
189       pbody = tmp_pgp_pbody = tmp_pbody;
190     }
191
192     if (WithCrypto && (msg->security & APPLICATION_SMIME)
193         && (msg->security & APPLICATION_PGP)) {
194       /* here comes the draft ;-) */
195     }
196   }
197
198
199   if (msg->security & ENCRYPT) {
200     if ((WithCrypto & APPLICATION_SMIME)
201         && (msg->security & APPLICATION_SMIME)) {
202       if (!(tmp_pbody = crypt_smime_build_smime_entity (tmp_smime_pbody,
203                                                         keylist))) {
204         /* signed ? free it! */
205         return (-1);
206       }
207       /* free tmp_body if messages was signed AND encrypted ... */
208       if (tmp_smime_pbody != msg->content && tmp_smime_pbody != tmp_pbody) {
209         /* detatch and dont't delete msg->content,
210            which tmp_smime_pbody->parts after signing. */
211         tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
212         msg->content->next = NULL;
213         mutt_free_body (&tmp_smime_pbody);
214       }
215       pbody = tmp_pbody;
216     }
217
218     if ((WithCrypto & APPLICATION_PGP)
219         && (msg->security & APPLICATION_PGP)) {
220       if (!(pbody = crypt_pgp_encrypt_message (tmp_pgp_pbody, keylist,
221                                                flags & SIGN))) {
222
223         /* did we perform a retainable signature? */
224         if (flags != msg->security) {
225           /* remove the outer multipart layer */
226           tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
227           /* get rid of the signature */
228           mutt_free_body (&tmp_pgp_pbody->next);
229         }
230
231         return (-1);
232       }
233
234       /* destroy temporary signature envelope when doing retainable 
235        * signatures.
236
237        */
238       if (flags != msg->security) {
239         tmp_pgp_pbody = mutt_remove_multipart (tmp_pgp_pbody);
240         mutt_free_body (&tmp_pgp_pbody->next);
241       }
242     }
243   }
244
245   if (pbody)
246     msg->content = pbody;
247
248   return 0;
249 }
250
251
252
253
254 int mutt_is_multipart_signed (BODY * b)
255 {
256   char *p;
257
258   if (!b || !(b->type == TYPEMULTIPART) ||
259       !b->subtype || ascii_strcasecmp (b->subtype, "signed"))
260     return 0;
261
262   if (!(p = mutt_get_parameter ("protocol", b->parameter)))
263     return 0;
264
265   if (!(ascii_strcasecmp (p, "multipart/mixed")))
266     return SIGN;
267
268   if ((WithCrypto & APPLICATION_PGP)
269       && !(ascii_strcasecmp (p, "application/pgp-signature")))
270     return PGPSIGN;
271
272   if ((WithCrypto & APPLICATION_SMIME)
273       && !(ascii_strcasecmp (p, "application/x-pkcs7-signature")))
274     return SMIMESIGN;
275   if ((WithCrypto & APPLICATION_SMIME)
276       && !(ascii_strcasecmp (p, "application/pkcs7-signature")))
277     return SMIMESIGN;
278
279   return 0;
280 }
281
282
283 int mutt_is_multipart_encrypted (BODY * b)
284 {
285   if ((WithCrypto & APPLICATION_PGP)) {
286     char *p;
287
288     if (!b || b->type != TYPEMULTIPART ||
289         !b->subtype || ascii_strcasecmp (b->subtype, "encrypted") ||
290         !(p = mutt_get_parameter ("protocol", b->parameter)) ||
291         ascii_strcasecmp (p, "application/pgp-encrypted"))
292       return 0;
293
294     return PGPENCRYPT;
295   }
296
297   return 0;
298 }
299
300
301 int mutt_is_application_pgp (BODY * m)
302 {
303   int t = 0;
304   char *p;
305
306   if (m->type == TYPEAPPLICATION) {
307     if (!ascii_strcasecmp (m->subtype, "pgp")
308         || !ascii_strcasecmp (m->subtype, "x-pgp-message")) {
309       if ((p = mutt_get_parameter ("x-action", m->parameter))
310           && (!ascii_strcasecmp (p, "sign")
311               || !ascii_strcasecmp (p, "signclear")))
312         t |= PGPSIGN;
313
314       if ((p = mutt_get_parameter ("format", m->parameter)) &&
315           !ascii_strcasecmp (p, "keys-only"))
316         t |= PGPKEY;
317
318       if (!t)
319         t |= PGPENCRYPT;        /* not necessarily correct, but... */
320     }
321
322     if (!ascii_strcasecmp (m->subtype, "pgp-signed"))
323       t |= PGPSIGN;
324
325     if (!ascii_strcasecmp (m->subtype, "pgp-keys"))
326       t |= PGPKEY;
327   }
328   else if (m->type == TYPETEXT && ascii_strcasecmp ("plain", m->subtype) == 0) {
329     if (((p = mutt_get_parameter ("x-mutt-action", m->parameter))
330          || (p = mutt_get_parameter ("x-action", m->parameter))
331          || (p = mutt_get_parameter ("action", m->parameter)))
332         && !ascii_strncasecmp ("pgp-sign", p, 8))
333       t |= PGPSIGN;
334     else if (p && !ascii_strncasecmp ("pgp-encrypt", p, 11))
335       t |= PGPENCRYPT;
336     else if (p && !ascii_strncasecmp ("pgp-keys", p, 7))
337       t |= PGPKEY;
338   }
339   if (t)
340     t |= PGPINLINE;
341
342   return t;
343 }
344
345 int mutt_is_application_smime (BODY * m)
346 {
347   char *t = NULL;
348   int len, complain = 0;
349
350   if (!m)
351     return 0;
352
353   if ((m->type & TYPEAPPLICATION) && m->subtype) {
354     /* S/MIME MIME types don't need x- anymore, see RFC2311 */
355     if (!ascii_strcasecmp (m->subtype, "x-pkcs7-mime") ||
356         !ascii_strcasecmp (m->subtype, "pkcs7-mime")) {
357       if ((t = mutt_get_parameter ("smime-type", m->parameter))) {
358         if (!ascii_strcasecmp (t, "enveloped-data"))
359           return SMIMEENCRYPT;
360         else if (!ascii_strcasecmp (t, "signed-data"))
361           return (SMIMESIGN | SMIMEOPAQUE);
362         else
363           return 0;
364       }
365       /* Netscape 4.7 uses 
366        * Content-Description: S/MIME Encrypted Message
367        * instead of Content-Type parameter
368        */
369       if (!ascii_strcasecmp (m->description, "S/MIME Encrypted Message"))
370         return SMIMEENCRYPT;
371       complain = 1;
372     }
373     else if (ascii_strcasecmp (m->subtype, "octet-stream"))
374       return 0;
375
376     t = mutt_get_parameter ("name", m->parameter);
377
378     if (!t)
379       t = m->d_filename;
380     if (!t)
381       t = m->filename;
382     if (!t) {
383       if (complain)
384         mutt_message (_
385                       ("S/MIME messages with no hints on content are unsupported."));
386       return 0;
387     }
388
389     /* no .p7c, .p10 support yet. */
390
391     len = str_len (t) - 4;
392     if (len > 0 && *(t + len) == '.') {
393       len++;
394       if (!ascii_strcasecmp ((t + len), "p7m"))
395 #if 0
396         return SMIMEENCRYPT;
397 #else
398         /* Not sure if this is the correct thing to do, but 
399            it's required for compatibility with Outlook */
400         return (SMIMESIGN | SMIMEOPAQUE);
401 #endif
402       else if (!ascii_strcasecmp ((t + len), "p7s"))
403         return (SMIMESIGN | SMIMEOPAQUE);
404     }
405   }
406
407   return 0;
408 }
409
410
411
412
413
414
415 int crypt_query (BODY * m)
416 {
417   int t = 0;
418
419   if (!WithCrypto)
420     return 0;
421
422   if (!m)
423     return 0;
424
425   if (m->type == TYPEAPPLICATION) {
426     if ((WithCrypto & APPLICATION_PGP))
427       t |= mutt_is_application_pgp (m);
428
429     if ((WithCrypto & APPLICATION_SMIME)) {
430       t |= mutt_is_application_smime (m);
431       if (t && m->goodsig)
432         t |= GOODSIGN;
433       if (t && m->badsig)
434         t |= BADSIGN;
435     }
436   }
437   else if ((WithCrypto & APPLICATION_PGP) && m->type == TYPETEXT) {
438     t |= mutt_is_application_pgp (m);
439     if (t && m->goodsig)
440       t |= GOODSIGN;
441   }
442
443   if (m->type == TYPEMULTIPART) {
444     t |= mutt_is_multipart_encrypted (m);
445     t |= mutt_is_multipart_signed (m);
446
447     if (t && m->goodsig)
448       t |= GOODSIGN;
449   }
450
451   if (m->type == TYPEMULTIPART || m->type == TYPEMESSAGE) {
452     BODY *p;
453     int u, v, w;
454
455     u = m->parts ? 0xffffffff : 0;      /* Bits set in all parts */
456     w = 0;                      /* Bits set in any part  */
457
458     for (p = m->parts; p; p = p->next) {
459       v = crypt_query (p);
460       u &= v;
461       w |= v;
462     }
463     t |= u | (w & ~GOODSIGN);
464
465     if ((w & GOODSIGN) && !(u & GOODSIGN))
466       t |= PARTSIGN;
467   }
468
469   return t;
470 }
471
472
473
474
475 int crypt_write_signed (BODY * a, STATE * s, const char *tempfile)
476 {
477   FILE *fp;
478   int c;
479   short hadcr;
480   size_t bytes;
481
482   if (!WithCrypto)
483     return -1;
484
485   if (!(fp = safe_fopen (tempfile, "w"))) {
486     mutt_perror (tempfile);
487     return -1;
488   }
489
490   fseeko (s->fpin, a->hdr_offset, 0);
491   bytes = a->length + a->offset - a->hdr_offset;
492   hadcr = 0;
493   while (bytes > 0) {
494     if ((c = fgetc (s->fpin)) == EOF)
495       break;
496
497     bytes--;
498
499     if (c == '\r')
500       hadcr = 1;
501     else {
502       if (c == '\n' && !hadcr)
503         fputc ('\r', fp);
504
505       hadcr = 0;
506     }
507
508     fputc (c, fp);
509
510   }
511   fclose (fp);
512
513   return 0;
514 }
515
516
517
518 void convert_to_7bit (BODY * a)
519 {
520   if (!WithCrypto)
521     return;
522
523   while (a) {
524     if (a->type == TYPEMULTIPART) {
525       if (a->encoding != ENC7BIT) {
526         a->encoding = ENC7BIT;
527         convert_to_7bit (a->parts);
528       }
529       else if ((WithCrypto & APPLICATION_PGP) && option (OPTPGPSTRICTENC))
530         convert_to_7bit (a->parts);
531     }
532     else if (a->type == TYPEMESSAGE &&
533              str_casecmp (a->subtype, "delivery-status")) {
534       if (a->encoding != ENC7BIT)
535         mutt_message_to_7bit (a, NULL);
536     }
537     else if (a->encoding == ENC8BIT)
538       a->encoding = ENCQUOTEDPRINTABLE;
539     else if (a->encoding == ENCBINARY)
540       a->encoding = ENCBASE64;
541     else if (a->content && a->encoding != ENCBASE64 &&
542              (a->content->from || (a->content->space &&
543                                    option (OPTPGPSTRICTENC))))
544       a->encoding = ENCQUOTEDPRINTABLE;
545     a = a->next;
546   }
547 }
548
549
550
551
552 void crypt_extract_keys_from_messages (HEADER * h)
553 {
554   int i;
555   char tempfname[_POSIX_PATH_MAX], *mbox;
556   ADDRESS *tmp = NULL;
557   FILE *fpout;
558
559   if (!WithCrypto)
560     return;
561
562   mutt_mktemp (tempfname);
563   if (!(fpout = safe_fopen (tempfname, "w"))) {
564     mutt_perror (tempfname);
565     return;
566   }
567
568   if ((WithCrypto & APPLICATION_PGP))
569     set_option (OPTDONTHANDLEPGPKEYS);
570
571   if (!h) {
572     for (i = 0; i < Context->vcount; i++) {
573       if (Context->hdrs[Context->v2r[i]]->tagged) {
574         mutt_parse_mime_message (Context, Context->hdrs[Context->v2r[i]]);
575         if (Context->hdrs[Context->v2r[i]]->security & ENCRYPT &&
576             !crypt_valid_passphrase (Context->hdrs[Context->v2r[i]]->
577                                      security)) {
578           fclose (fpout);
579           break;
580         }
581
582         if ((WithCrypto & APPLICATION_PGP)
583             && (Context->hdrs[Context->v2r[i]]->security & APPLICATION_PGP)) {
584           mutt_copy_message (fpout, Context, Context->hdrs[Context->v2r[i]],
585                              M_CM_DECODE | M_CM_CHARCONV, 0);
586           fflush (fpout);
587
588           mutt_endwin (_("Trying to extract PGP keys...\n"));
589           crypt_pgp_invoke_import (tempfname);
590         }
591
592         if ((WithCrypto & APPLICATION_SMIME)
593             && (Context->hdrs[Context->v2r[i]]->security & APPLICATION_SMIME)) {
594           if (Context->hdrs[Context->v2r[i]]->security & ENCRYPT)
595             mutt_copy_message (fpout, Context, Context->hdrs[Context->v2r[i]],
596                                M_CM_NOHEADER | M_CM_DECODE_CRYPT
597                                | M_CM_DECODE_SMIME, 0);
598           else
599             mutt_copy_message (fpout, Context,
600                                Context->hdrs[Context->v2r[i]], 0, 0);
601           fflush (fpout);
602
603           if (Context->hdrs[Context->v2r[i]]->env->from)
604             tmp = mutt_expand_aliases (h->env->from);
605           else if (Context->hdrs[Context->v2r[i]]->env->sender)
606             tmp = mutt_expand_aliases (Context->hdrs[Context->v2r[i]]
607                                        ->env->sender);
608           mbox = tmp ? tmp->mailbox : NULL;
609           if (mbox) {
610             mutt_endwin (_("Trying to extract S/MIME certificates...\n"));
611             crypt_smime_invoke_import (tempfname, mbox);
612             tmp = NULL;
613           }
614         }
615
616         rewind (fpout);
617       }
618     }
619   }
620   else {
621     mutt_parse_mime_message (Context, h);
622     if (!(h->security & ENCRYPT && !crypt_valid_passphrase (h->security))) {
623       if ((WithCrypto & APPLICATION_PGP)
624           && (h->security & APPLICATION_PGP)) {
625         mutt_copy_message (fpout, Context, h, M_CM_DECODE | M_CM_CHARCONV, 0);
626         fflush (fpout);
627         mutt_endwin (_("Trying to extract PGP keys...\n"));
628         crypt_pgp_invoke_import (tempfname);
629       }
630
631       if ((WithCrypto & APPLICATION_SMIME)
632           && (h->security & APPLICATION_SMIME)) {
633         if (h->security & ENCRYPT)
634           mutt_copy_message (fpout, Context, h, M_CM_NOHEADER
635                              | M_CM_DECODE_CRYPT | M_CM_DECODE_SMIME, 0);
636         else
637           mutt_copy_message (fpout, Context, h, 0, 0);
638
639         fflush (fpout);
640         if (h->env->from)
641           tmp = mutt_expand_aliases (h->env->from);
642         else if (h->env->sender)
643           tmp = mutt_expand_aliases (h->env->sender);
644         mbox = tmp ? tmp->mailbox : NULL;
645         if (mbox) {             /* else ? */
646           mutt_message (_("Trying to extract S/MIME certificates...\n"));
647           crypt_smime_invoke_import (tempfname, mbox);
648         }
649       }
650     }
651   }
652
653   fclose (fpout);
654   if (isendwin ())
655     mutt_any_key_to_continue (NULL);
656
657   mutt_unlink (tempfname);
658
659   if ((WithCrypto & APPLICATION_PGP))
660     unset_option (OPTDONTHANDLEPGPKEYS);
661 }
662
663
664
665 int crypt_get_keys (HEADER * msg, char **keylist)
666 {
667   /* Do a quick check to make sure that we can find all of the encryption
668    * keys if the user has requested this service.
669    */
670
671   if (!WithCrypto)
672     return 0;
673
674   if ((WithCrypto & APPLICATION_PGP))
675     set_option (OPTPGPCHECKTRUST);
676
677   *keylist = NULL;
678
679   if (msg->security & ENCRYPT) {
680     if ((WithCrypto & APPLICATION_PGP)
681         && (msg->security & APPLICATION_PGP)) {
682       if ((*keylist = crypt_pgp_findkeys (msg->env->to, msg->env->cc,
683                                           msg->env->bcc)) == NULL)
684         return (-1);
685       unset_option (OPTPGPCHECKTRUST);
686     }
687     if ((WithCrypto & APPLICATION_SMIME)
688         && (msg->security & APPLICATION_SMIME)) {
689       if ((*keylist = crypt_smime_findkeys (msg->env->to, msg->env->cc,
690                                             msg->env->bcc)) == NULL)
691         return (-1);
692     }
693   }
694
695   return (0);
696 }
697
698
699
700 static void crypt_fetch_signatures (BODY *** signatures, BODY * a, int *n)
701 {
702   if (!WithCrypto)
703     return;
704
705   for (; a; a = a->next) {
706     if (a->type == TYPEMULTIPART)
707       crypt_fetch_signatures (signatures, a->parts, n);
708     else {
709       if ((*n % 5) == 0)
710         mem_realloc (signatures, (*n + 6) * sizeof (BODY **));
711
712       (*signatures)[(*n)++] = a;
713     }
714   }
715 }
716
717
718 /*
719  * This routine verifies a  "multipart/signed"  body.
720  */
721
722 int mutt_signed_handler (BODY * a, STATE * s)
723 {
724   char tempfile[_POSIX_PATH_MAX];
725   char *protocol;
726   int protocol_major = TYPEOTHER;
727   char *protocol_minor = NULL;
728
729   BODY *b = a;
730   BODY **signatures = NULL;
731   int sigcnt = 0;
732   int i;
733   short goodsig = 1;
734   int rc = 0;
735
736   if (!WithCrypto)
737     return (-1);
738
739   protocol = mutt_get_parameter ("protocol", a->parameter);
740   a = a->parts;
741
742   /* extract the protocol information */
743
744   if (protocol) {
745     char major[STRING];
746     char *t;
747
748     if ((protocol_minor = strchr (protocol, '/')))
749       protocol_minor++;
750
751     strfcpy (major, protocol, sizeof (major));
752     if ((t = strchr (major, '/')))
753       *t = '\0';
754
755     protocol_major = mutt_check_mime_type (major);
756   }
757
758   /* consistency check */
759
760   if (!(a && a->next && a->next->type == protocol_major &&
761         !str_casecmp (a->next->subtype, protocol_minor))) {
762     state_attach_puts (_("[-- Error: "
763                          "Inconsistent multipart/signed structure! --]\n\n"),
764                        s);
765     return mutt_body_handler (a, s);
766   }
767
768
769   if ((WithCrypto & APPLICATION_PGP)
770       && protocol_major == TYPEAPPLICATION
771       && !str_casecmp (protocol_minor, "pgp-signature"));
772   else if ((WithCrypto & APPLICATION_SMIME)
773            && protocol_major == TYPEAPPLICATION
774            && !(str_casecmp (protocol_minor, "x-pkcs7-signature")
775                 && str_casecmp (protocol_minor, "pkcs7-signature")));
776   else if (protocol_major == TYPEMULTIPART
777            && !str_casecmp (protocol_minor, "mixed"));
778   else {
779     state_printf (s, _("[-- Error: "
780                        "Unknown multipart/signed protocol %s! --]\n\n"),
781                   protocol);
782     return mutt_body_handler (a, s);
783   }
784
785   if (s->flags & M_DISPLAY) {
786
787     crypt_fetch_signatures (&signatures, a->next, &sigcnt);
788
789     if (sigcnt) {
790       mutt_mktemp (tempfile);
791       if (crypt_write_signed (a, s, tempfile) == 0) {
792         for (i = 0; i < sigcnt; i++) {
793           if ((WithCrypto & APPLICATION_PGP)
794               && signatures[i]->type == TYPEAPPLICATION
795               && !str_casecmp (signatures[i]->subtype, "pgp-signature")) {
796             if (crypt_pgp_verify_one (signatures[i], s, tempfile) != 0)
797               goodsig = 0;
798
799             continue;
800           }
801
802           if ((WithCrypto & APPLICATION_SMIME)
803               && signatures[i]->type == TYPEAPPLICATION
804               &&
805               (!str_casecmp (signatures[i]->subtype, "x-pkcs7-signature")
806                || !str_casecmp (signatures[i]->subtype,
807                                     "pkcs7-signature"))) {
808             if (crypt_smime_verify_one (signatures[i], s, tempfile) != 0)
809               goodsig = 0;
810
811             continue;
812           }
813
814           state_printf (s, _("[-- Warning: "
815                              "We can't verify %s/%s signatures. --]\n\n"),
816                         TYPE (signatures[i]), signatures[i]->subtype);
817         }
818       }
819
820       mutt_unlink (tempfile);
821
822       b->goodsig = goodsig;
823       b->badsig = !goodsig;
824
825       /* Now display the signed body */
826       state_attach_puts (_("[-- The following data is signed --]\n\n"), s);
827
828
829       mem_free (&signatures);
830     }
831     else
832       state_attach_puts (_("[-- Warning: Can't find any signatures. --]\n\n"),
833                          s);
834   }
835
836   rc = mutt_body_handler (a, s);
837
838   if (s->flags & M_DISPLAY && sigcnt)
839     state_attach_puts (_("\n[-- End of signed data --]\n"), s);
840
841   return (rc);
842 }