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