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