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