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