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