Nico Golde:
[apps/madmutt.git] / commands.c
1 /*
2  * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
3  * Copyright (C) 2000 Thomas Roessler <roessler@does-not-exist.org>
4  * 
5  *     This program is free software; you can redistribute it and/or modify
6  *     it under the terms of the GNU General Public License as published by
7  *     the Free Software Foundation; either version 2 of the License, or
8  *     (at your option) any later version.
9  * 
10  *     This program is distributed in the hope that it will be useful,
11  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *     GNU General Public License for more details.
14  * 
15  *     You should have received a copy of the GNU General Public License
16  *     along with this program; if not, write to the Free Software
17  *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
18  */ 
19
20 #if HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include "mutt.h"
25 #include "mutt_curses.h"
26 #include "mutt_menu.h"
27 #include "mime.h"
28 #include "sort.h"
29 #include "mailbox.h"
30 #include "copy.h"
31 #include "mx.h"
32 #include "pager.h"
33 #include "mutt_crypt.h"
34 #include "mutt_idna.h"
35 #include "rfc1524.h"
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39
40 #ifdef USE_IMAP
41 #include "imap.h"
42 #endif
43
44 #ifdef BUFFY_SIZE
45 #include "buffy.h"
46 #endif
47
48 #include <errno.h>
49 #include <unistd.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <sys/wait.h>
53 #include <sys/stat.h>
54 #include <sys/types.h>
55 #include <utime.h>
56
57 extern char *ReleaseDate;
58
59 /* The folder the user last saved to.  Used by ci_save_message() */
60 static char LastSaveFolder[_POSIX_PATH_MAX] = "";
61
62 int mutt_display_message (HEADER *cur)
63 {
64   char tempfile[_POSIX_PATH_MAX], buf[LONG_STRING];
65   int rc = 0, builtin = 0;
66   int cmflags = M_CM_DECODE | M_CM_DISPLAY | M_CM_CHARCONV;
67   FILE *fpout = NULL;
68   FILE *fpfilterout = NULL;
69   pid_t filterpid = -1;
70   int res;
71
72   snprintf (buf, sizeof (buf), "%s/%s", TYPE (cur->content),
73             cur->content->subtype);
74
75   mutt_parse_mime_message (Context, cur);
76   mutt_message_hook (Context, cur, M_MESSAGEHOOK);
77
78   /* see if crytpo is needed for this message.  if so, we should exit curses */
79   if (WithCrypto && cur->security)
80   {
81     if (cur->security & ENCRYPT)
82     {
83       if (cur->security & APPLICATION_SMIME)
84         crypt_smime_getkeys (cur->env);
85       if(!crypt_valid_passphrase(cur->security))
86         return 0;
87
88       cmflags |= M_CM_VERIFY;
89     }
90     else if (cur->security & SIGN)
91     {
92       /* find out whether or not the verify signature */
93       if (query_quadoption (OPT_VERIFYSIG, _("Verify PGP signature?")) == M_YES)
94       {
95         cmflags |= M_CM_VERIFY;
96       }
97     }
98   }
99   
100   if (cmflags & M_CM_VERIFY || cur->security & ENCRYPT)
101   {
102     if (cur->security & APPLICATION_PGP)
103     {
104       if (cur->env->from)
105         crypt_pgp_invoke_getkeys (cur->env->from);
106       
107       crypt_invoke_message (APPLICATION_PGP);
108     }
109
110     if (cur->security & APPLICATION_SMIME)
111       crypt_invoke_message (APPLICATION_SMIME);
112   }
113
114
115   mutt_mktemp (tempfile);
116   if ((fpout = safe_fopen (tempfile, "w")) == NULL)
117   {
118     mutt_error _("Could not create temporary file!");
119     return (0);
120   }
121
122   if (DisplayFilter && *DisplayFilter) 
123   {
124     fpfilterout = fpout;
125     fpout = NULL;
126     /* mutt_endwin (NULL); */
127     filterpid = mutt_create_filter_fd (DisplayFilter, &fpout, NULL, NULL,
128                                        -1, fileno(fpfilterout), -1);
129     if (filterpid < 0)
130     {
131       mutt_error (_("Cannot create display filter"));
132       safe_fclose (&fpfilterout);
133       unlink (tempfile);
134       return 0;
135     }
136   }
137
138   if (!Pager || mutt_strcmp (Pager, "builtin") == 0)
139     builtin = 1;
140   else
141   {
142     mutt_make_string (buf, sizeof (buf), NONULL(PagerFmt), Context, cur);
143     fputs (buf, fpout);
144     fputs ("\n\n", fpout);
145   }
146
147   res = mutt_copy_message (fpout, Context, cur, cmflags,
148         (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) | CH_DECODE | CH_FROM);
149   if ((safe_fclose (&fpout) != 0 && errno != EPIPE) || res == -1)
150   {
151     mutt_error (_("Could not copy message"));
152     if (fpfilterout != NULL)
153     {
154       mutt_wait_filter (filterpid);
155       safe_fclose (&fpfilterout);
156     }
157     mutt_unlink (tempfile);
158     return 0;
159   }
160
161   if (fpfilterout != NULL && mutt_wait_filter (filterpid) != 0)
162     mutt_any_key_to_continue (NULL);
163
164   safe_fclose (&fpfilterout);   /* XXX - check result? */
165
166   
167   if (WithCrypto)
168   {
169     /* update crypto information for this message */
170     cur->security |= crypt_query (cur->content);
171   
172     /* Remove color cache for this message, in case there
173        are color patterns for both ~g and ~V */
174     cur->pair = 0;
175   }
176
177   if (builtin)
178   {
179     pager_t info;
180
181     if (WithCrypto 
182         && (cur->security & APPLICATION_SMIME) && (cmflags & M_CM_VERIFY))
183     {
184       if (cur->security & GOODSIGN)
185       {
186         if (!crypt_smime_verify_sender(cur))
187           mutt_message ( _("S/MIME signature successfully verified."));
188         else
189           mutt_error ( _("S/MIME certificate owner does not match sender."));
190       }
191       else if (cur->security & PARTSIGN)
192         mutt_message (_("Warning: Part of this message has not been signed."));
193       else if (cur->security & SIGN || cur->security & BADSIGN)
194         mutt_error ( _("S/MIME signature could NOT be verified."));
195     }
196
197     if (WithCrypto 
198         && (cur->security & APPLICATION_PGP) && (cmflags & M_CM_VERIFY))
199     {
200       if (cur->security & GOODSIGN)
201         mutt_message (_("PGP signature successfully verified."));
202       else if (cur->security & PARTSIGN)
203         mutt_message (_("Warning: Part of this message has not been signed."));
204       else
205         mutt_message (_("PGP signature could NOT be verified."));
206     }
207
208     /* Invoke the builtin pager */
209     memset (&info, 0, sizeof (pager_t));
210     info.hdr = cur;
211     info.ctx = Context;
212     rc = mutt_pager (NULL, tempfile, M_PAGER_MESSAGE, &info);
213   }
214   else
215   {
216     int r;
217
218     mutt_endwin (NULL);
219     snprintf (buf, sizeof (buf), "%s %s", NONULL(Pager), tempfile);
220     if ((r = mutt_system (buf)) == -1)
221       mutt_error (_("Error running \"%s\"!"), buf);
222     unlink (tempfile);
223     keypad (stdscr, TRUE);
224     if (r != -1)
225       mutt_set_flag (Context, cur, M_READ, 1);
226     if (r != -1 && option (OPTPROMPTAFTER))
227     {
228       mutt_ungetch (mutt_any_key_to_continue _("Command: "), 0);
229       rc = km_dokey (MENU_PAGER);
230     }
231     else
232       rc = 0;
233   }
234
235   return rc;
236 }
237
238 void ci_bounce_message (HEADER *h, int *redraw)
239 {
240   char prompt[SHORT_STRING];
241   char buf[HUGE_STRING] = { 0 };
242   ADDRESS *adr = NULL;
243   char *err = NULL;
244   int rc;
245
246   if(h)
247     strfcpy(prompt, _("Bounce message to: "), sizeof(prompt));
248   else
249     strfcpy(prompt, _("Bounce tagged messages to: "), sizeof(prompt));
250   
251   rc = mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS);
252
253   if (option (OPTNEEDREDRAW))
254   {
255     unset_option (OPTNEEDREDRAW);
256     *redraw = REDRAW_FULL;
257   }
258
259   if (rc || !buf[0])
260     return;
261
262   if (!(adr = rfc822_parse_adrlist (adr, buf)))
263   {
264     mutt_error _("Error parsing address!");
265     return;
266   }
267
268   adr = mutt_expand_aliases (adr);
269
270   if (mutt_addrlist_to_idna (adr, &err) < 0)
271   {
272     mutt_error (_("Bad IDN: '%s'"), err);
273     FREE (&err);
274     rfc822_free_address (&adr);
275     return;
276   }
277
278   buf[0] = 0;
279   rfc822_write_address (buf, sizeof (buf), adr, 1);
280
281 #define extra_space (15 + 7 + 2)
282   snprintf (prompt, sizeof (prompt),
283            (h ? _("Bounce message to %s") : _("Bounce messages to %s")), buf);
284
285   if (mutt_strwidth (prompt) > COLS - extra_space)
286   {
287     mutt_format_string (prompt, sizeof (prompt),
288                         0, COLS-extra_space, 0, 0,
289                         prompt, sizeof (prompt), 0);
290     safe_strcat (prompt, sizeof (prompt), "...?");
291   }
292   else
293     safe_strcat (prompt, sizeof (prompt), "?");
294
295   if (query_quadoption (OPT_BOUNCE, prompt) != M_YES)
296   {
297     rfc822_free_address (&adr);
298     CLEARLINE (LINES - 1);
299     mutt_message (h ? _("Message not bounced.") : _("Messages not bounced."));
300     return;
301   }
302
303   CLEARLINE (LINES - 1);
304   
305   rc = mutt_bounce_message (NULL, h, adr);
306   rfc822_free_address (&adr);
307   /* If no error, or background, display message. */
308   if ((rc == 0) || (rc == S_BKG))
309     mutt_message (h ? _("Message bounced.") : _("Messages bounced."));
310 }
311
312 static void pipe_set_flags (int decode, int print, int *cmflags, int *chflags)
313 {
314   if (decode)
315   {
316     *cmflags |= M_CM_DECODE | M_CM_CHARCONV;
317     *chflags |= CH_DECODE | CH_REORDER;
318     
319     if (option (OPTWEED))
320     {
321       *chflags |= CH_WEED;
322       *cmflags |= M_CM_WEED;
323     }
324   }
325   
326   if (print)
327     *cmflags |= M_CM_PRINTING;
328   
329 }
330
331 static void pipe_msg (HEADER *h, FILE *fp, int decode, int print)
332 {
333   int cmflags = 0;
334   int chflags = CH_FROM;
335   
336   pipe_set_flags (decode, print, &cmflags, &chflags);
337
338   if (WithCrypto && decode && h->security & ENCRYPT)
339   {
340     if(!crypt_valid_passphrase(h->security))
341       return;
342     endwin ();
343   }
344
345   if (decode)
346     mutt_parse_mime_message (Context, h);
347
348   mutt_copy_message (fp, Context, h, cmflags, chflags);
349 }
350
351
352 /* the following code is shared between printing and piping */
353
354 static int _mutt_pipe_message (HEADER *h, char *cmd,
355                                int decode,
356                                int print,
357                                int split,
358                                char *sep)
359 {
360   
361   int i, rc = 0;
362   pid_t thepid;
363   FILE *fpout;
364   
365 /*   mutt_endwin (NULL); 
366
367      is this really needed here ? 
368      it makes the screen flicker on pgp and s/mime messages,
369      before asking for a passphrase...
370                                      Oliver Ehli */
371   if (h)
372   {
373
374     mutt_message_hook (Context, h, M_MESSAGEHOOK);
375
376     if (WithCrypto && decode)
377     {
378       mutt_parse_mime_message (Context, h);
379       if(h->security & ENCRYPT && !crypt_valid_passphrase(h->security))
380         return 1;
381     }
382     mutt_endwin (NULL);
383
384     if ((thepid = mutt_create_filter (cmd, &fpout, NULL, NULL)) < 0)
385     {
386       mutt_perror _("Can't create filter process");
387       return 1;
388     }
389       
390     pipe_msg (h, fpout, decode, print);
391     fclose (fpout);
392     rc = mutt_wait_filter (thepid);
393   }
394   else
395   { /* handle tagged messages */
396
397     if (WithCrypto && decode)
398     {
399       for (i = 0; i < Context->vcount; i++)
400         if(Context->hdrs[Context->v2r[i]]->tagged)
401         {
402           mutt_message_hook (Context, Context->hdrs[Context->v2r[i]], M_MESSAGEHOOK);
403           mutt_parse_mime_message(Context, Context->hdrs[Context->v2r[i]]);
404           if (Context->hdrs[Context->v2r[i]]->security & ENCRYPT &&
405               !crypt_valid_passphrase(Context->hdrs[Context->v2r[i]]->security))
406             return 1;
407         }
408     }
409     
410     if (split)
411     {
412       for (i = 0; i < Context->vcount; i++)
413       {
414         if (Context->hdrs[Context->v2r[i]]->tagged)
415         {
416           mutt_message_hook (Context, Context->hdrs[Context->v2r[i]], M_MESSAGEHOOK);
417           mutt_endwin (NULL);
418           if ((thepid = mutt_create_filter (cmd, &fpout, NULL, NULL)) < 0)
419           {
420             mutt_perror _("Can't create filter process");
421             return 1;
422           }
423           pipe_msg (Context->hdrs[Context->v2r[i]], fpout, decode, print);
424           /* add the message separator */
425           if (sep)  fputs (sep, fpout);
426           safe_fclose (&fpout);
427           if (mutt_wait_filter (thepid) != 0)
428             rc = 1;
429         }
430       }
431     }
432     else
433     {
434       mutt_endwin (NULL);
435       if ((thepid = mutt_create_filter (cmd, &fpout, NULL, NULL)) < 0)
436       {
437         mutt_perror _("Can't create filter process");
438         return 1;
439       }
440       for (i = 0; i < Context->vcount; i++)
441       {
442         if (Context->hdrs[Context->v2r[i]]->tagged)
443         {
444           mutt_message_hook (Context, Context->hdrs[Context->v2r[i]], M_MESSAGEHOOK);
445           pipe_msg (Context->hdrs[Context->v2r[i]], fpout, decode, print);
446           /* add the message separator */
447           if (sep) fputs (sep, fpout);
448         }
449       }
450       safe_fclose (&fpout);
451       if (mutt_wait_filter (thepid) != 0)
452         rc = 1;
453     }
454   }
455
456   if (rc || option (OPTWAITKEY))
457     mutt_any_key_to_continue (NULL);
458   return rc;
459 }
460
461 void mutt_pipe_message (HEADER *h)
462 {
463   char buffer[LONG_STRING];
464
465   buffer[0] = 0;
466   if (mutt_get_field (_("Pipe to command: "), buffer, sizeof (buffer), M_CMD)
467       != 0 || !buffer[0])
468     return;
469
470   mutt_expand_path (buffer, sizeof (buffer));
471   _mutt_pipe_message (h, buffer,
472                       option (OPTPIPEDECODE),
473                       0, 
474                       option (OPTPIPESPLIT),
475                       PipeSep);
476 }
477
478 void mutt_print_message (HEADER *h)
479 {
480
481   if (quadoption (OPT_PRINT) && (!PrintCmd || !*PrintCmd))
482   {
483     mutt_message (_("No printing command has been defined."));
484     return;
485   }
486   
487   if (query_quadoption (OPT_PRINT,
488                         h ? _("Print message?") : _("Print tagged messages?"))
489                         != M_YES)
490     return;
491
492   if (_mutt_pipe_message (h, PrintCmd,
493                           option (OPTPRINTDECODE),
494                           1,
495                           option (OPTPRINTSPLIT),
496                           "\f") == 0)
497     mutt_message (h ? _("Message printed") : _("Messages printed"));
498   else
499     mutt_message (h ? _("Message could not be printed") :
500                   _("Messages could not be printed"));
501 }
502
503
504 int mutt_select_sort (int reverse)
505 {
506   int method = Sort; /* save the current method in case of abort */
507
508   switch (mutt_multi_choice (reverse ?
509                              _("Rev-Sort (d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/(u)nsort/si(z)e/s(c)ore/s(p)am?: ") :
510                              _("Sort (d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/(u)nsort/si(z)e/s(c)ore/s(p)am?: "),
511                              _("dfrsotuzcp")))
512   {
513   case -1: /* abort - don't resort */
514     return -1;
515
516   case 1: /* (d)ate */
517     Sort = SORT_DATE;
518     break;
519
520   case 2: /* (f)rm */
521     Sort = SORT_FROM;
522     break;
523   
524   case 3: /* (r)ecv */
525     Sort = SORT_RECEIVED;
526     break;
527   
528   case 4: /* (s)ubj */
529     Sort = SORT_SUBJECT;
530     break;
531   
532   case 5: /* t(o) */
533     Sort = SORT_TO;
534     break;
535   
536   case 6: /* (t)hread */
537     Sort = SORT_THREADS;
538     break;
539   
540   case 7: /* (u)nsort */
541     Sort = SORT_ORDER;
542     break;
543   
544   case 8: /* si(z)e */
545     Sort = SORT_SIZE;
546     break;
547   
548   case 9: /* s(c)ore */ 
549     Sort = SORT_SCORE;
550     break;
551
552   case 10: /* s(p)am */
553     Sort = SORT_SPAM;
554     break;
555   }
556   if (reverse)
557     Sort |= SORT_REVERSE;
558
559   return (Sort != method ? 0 : -1); /* no need to resort if it's the same */
560 }
561
562 /* invoke a command in a subshell */
563 void mutt_shell_escape (void)
564 {
565   char buf[LONG_STRING];
566
567   buf[0] = 0;
568   if (mutt_get_field (_("Shell command: "), buf, sizeof (buf), M_CMD) == 0)
569   {
570     if (!buf[0] && Shell)
571       strfcpy (buf, Shell, sizeof (buf));
572     if(buf[0])
573     {
574       CLEARLINE (LINES-1);
575       mutt_endwin (NULL);
576       fflush (stdout);
577       if (mutt_system (buf) != 0 || option (OPTWAITKEY))
578         mutt_any_key_to_continue (NULL);
579     }
580   }
581 }
582
583 /* enter a mutt command */
584 void mutt_enter_command (void)
585 {
586   BUFFER err, token;
587   char buffer[LONG_STRING], errbuf[SHORT_STRING];
588   int r;
589
590   buffer[0] = 0;
591   if (mutt_get_field (":", buffer, sizeof (buffer), M_COMMAND) != 0 || !buffer[0])
592     return;
593   err.data = errbuf;
594   err.dsize = sizeof (errbuf);
595   memset (&token, 0, sizeof (token));
596   r = mutt_parse_rc_line (buffer, &token, &err);
597   FREE (&token.data);
598   if (errbuf[0])
599   {
600     /* since errbuf could potentially contain printf() sequences in it,
601        we must call mutt_error() in this fashion so that vsprintf()
602        doesn't expect more arguments that we passed */
603     if (r == 0)
604       mutt_message ("%s", errbuf);
605     else
606       mutt_error ("%s", errbuf);
607   }
608 }
609
610 void mutt_display_address (ENVELOPE *env)
611 {
612   char *pfx = NULL;
613   char buf[SHORT_STRING];
614   ADDRESS *adr = NULL;
615
616   adr = mutt_get_address (env, &pfx);
617
618   if (!adr) return;
619   
620   /* 
621    * Note: We don't convert IDNA to local representation this time.
622    * That is intentional, so the user has an opportunity to copy &
623    * paste the on-the-wire form of the address to other, IDN-unable
624    * software. 
625    */
626   
627   buf[0] = 0;
628   rfc822_write_address (buf, sizeof (buf), adr, 0);
629   mutt_message ("%s: %s", pfx, buf);
630 }
631
632 static void set_copy_flags (HEADER *hdr, int decode, int decrypt, int *cmflags, int *chflags)
633 {
634   *cmflags = 0;
635   *chflags = CH_UPDATE_LEN;
636   
637   if (WithCrypto && !decode && decrypt && (hdr->security & ENCRYPT))
638   {
639     if ((WithCrypto & APPLICATION_PGP)
640         && mutt_is_multipart_encrypted(hdr->content))
641     {
642       *chflags = CH_NONEWLINE | CH_XMIT | CH_MIME;
643       *cmflags = M_CM_DECODE_PGP;
644     }
645     else if ((WithCrypto & APPLICATION_PGP)
646               && mutt_is_application_pgp (hdr->content) & ENCRYPT)
647       decode = 1;
648     else if ((WithCrypto & APPLICATION_SMIME)
649              && mutt_is_application_smime(hdr->content) & ENCRYPT)
650     {
651       *chflags = CH_NONEWLINE | CH_XMIT | CH_MIME;
652       *cmflags = M_CM_DECODE_SMIME;
653     }
654   }
655
656   if (decode)
657   {
658     *chflags = CH_XMIT | CH_MIME | CH_TXTPLAIN;
659     *cmflags = M_CM_DECODE | M_CM_CHARCONV;
660
661     if (!decrypt)       /* If decode doesn't kick in for decrypt, */
662     {
663       *chflags |= CH_DECODE;    /* then decode RFC 2047 headers, */
664
665       if (option (OPTWEED))
666       {
667         *chflags |= CH_WEED;    /* and respect $weed. */
668         *cmflags |= M_CM_WEED;
669       }
670     }
671   }
672 }
673
674 void _mutt_save_message (HEADER *h, CONTEXT *ctx, int delete, int decode, int decrypt)
675 {
676   int cmflags, chflags;
677   
678   set_copy_flags (h, decode, decrypt, &cmflags, &chflags);
679
680   if (decode || decrypt)
681     mutt_parse_mime_message (Context, h);
682
683   if (mutt_append_message (ctx, Context, h, cmflags, chflags) == 0)
684   {
685     if (delete)
686     {
687       mutt_set_flag (Context, h, M_DELETE, 1);
688       if (option (OPTDELETEUNTAG))
689         mutt_set_flag (Context, h, M_TAG, 0);
690     }
691     mutt_set_flag (Context, h, M_APPENDED, 1);
692   }
693 }
694
695 /* returns 0 if the copy/save was successful, or -1 on error/abort */
696 int mutt_save_message (HEADER *h, int delete, 
697                        int decode, int decrypt, int *redraw)
698 {
699   int i, need_buffy_cleanup;
700   int need_passphrase = 0, app=0;
701   char prompt[SHORT_STRING], buf[_POSIX_PATH_MAX];
702   CONTEXT ctx;
703   struct stat st;
704 #ifdef BUFFY_SIZE
705   BUFFY *tmp = NULL;
706 #else
707   struct utimbuf ut;
708 #endif
709
710   *redraw = 0;
711
712   
713   snprintf (prompt, sizeof (prompt),
714             decode  ? (delete ? _("Decode-save%s to mailbox") :
715                        _("Decode-copy%s to mailbox")) :
716             (decrypt ? (delete ? _("Decrypt-save%s to mailbox") :
717                         _("Decrypt-copy%s to mailbox")) :
718              (delete ? _("Save%s to mailbox") : _("Copy%s to mailbox"))),
719             h ? "" : _(" tagged"));
720   
721
722   if (h)
723   {
724     if (WithCrypto)
725     {
726       need_passphrase = h->security & ENCRYPT;
727       app = h->security;
728     }
729     mutt_message_hook (Context, h, M_MESSAGEHOOK);
730     mutt_default_save (buf, sizeof (buf), h);
731   }
732   else
733   {
734     /* look for the first tagged message */
735
736     for (i = 0; i < Context->vcount; i++)
737     {
738       if (Context->hdrs[Context->v2r[i]]->tagged)
739       {
740         h = Context->hdrs[Context->v2r[i]];
741         break;
742       }
743     }
744
745
746     if (h)
747     {
748       mutt_message_hook (Context, h, M_MESSAGEHOOK);
749       mutt_default_save (buf, sizeof (buf), h);
750       if (WithCrypto)
751       {
752         need_passphrase = h->security & ENCRYPT;
753         app = h->security;
754       }
755       h = NULL;
756     }
757   }
758
759   mutt_pretty_mailbox (buf);
760   if (mutt_enter_fname (prompt, buf, sizeof (buf), redraw, 0) == -1)
761     return (-1);
762
763   if (*redraw != REDRAW_FULL)
764   {
765     if (!h)
766       *redraw = REDRAW_INDEX | REDRAW_STATUS;
767     else
768       *redraw = REDRAW_STATUS;
769   }
770
771   if (!buf[0])
772     return (-1);
773  
774   /* This is an undocumented feature of ELM pointed out to me by Felix von
775    * Leitner <leitner@prz.fu-berlin.de>
776    */
777   if (mutt_strcmp (buf, ".") == 0)
778     strfcpy (buf, LastSaveFolder, sizeof (buf));
779   else
780     strfcpy (LastSaveFolder, buf, sizeof (LastSaveFolder));
781
782   mutt_expand_path (buf, sizeof (buf));
783
784   /* check to make sure that this file is really the one the user wants */
785   if (mutt_save_confirm (buf, &st) != 0)
786     return -1;
787
788   if (WithCrypto && need_passphrase && (decode || decrypt)
789       && !crypt_valid_passphrase(app))
790     return -1;
791   
792   mutt_message (_("Copying to %s..."), buf);
793   
794 #ifdef USE_IMAP
795   if (Context->magic == M_IMAP && 
796       !(decode || decrypt) && mx_is_imap (buf))
797   {
798     switch (imap_copy_messages (Context, h, buf, delete))
799     {
800       /* success */
801       case 0: mutt_clear_error (); return 0;
802       /* non-fatal error: fall through to fetch/append */
803       case 1: break;
804       /* fatal error, abort */
805       case -1: return -1;
806     }
807   }
808 #endif
809
810   if (mx_open_mailbox (buf, M_APPEND, &ctx) != NULL)
811   {
812     if (h)
813       _mutt_save_message(h, &ctx, delete, decode, decrypt);
814     else
815     {
816       for (i = 0; i < Context->vcount; i++)
817       {
818         if (Context->hdrs[Context->v2r[i]]->tagged)
819         {
820           mutt_message_hook (Context, Context->hdrs[Context->v2r[i]], M_MESSAGEHOOK);
821           _mutt_save_message(Context->hdrs[Context->v2r[i]],
822                              &ctx, delete, decode, decrypt);
823         }
824       }
825     }
826
827     need_buffy_cleanup = (ctx.magic == M_MBOX || ctx.magic == M_MMDF);
828
829     mx_close_mailbox (&ctx, NULL);
830
831     if (need_buffy_cleanup)
832     {
833 #ifdef BUFFY_SIZE
834       tmp = mutt_find_mailbox (buf);
835       if (tmp && !tmp->new)
836         mutt_update_mailbox (tmp);
837 #else
838       /* fix up the times so buffy won't get confused */
839       if (st.st_mtime > st.st_atime)
840       {
841         ut.actime = st.st_atime;
842         ut.modtime = time (NULL);
843         utime (buf, &ut); 
844       }
845       else
846         utime (buf, NULL);
847 #endif
848     }
849
850     mutt_clear_error ();
851     return (0);
852   }
853   
854   return -1;
855 }
856
857 int mutt_update_list_file (char *filename, char *section, char *key, char *line)
858 {
859   FILE *ifp;
860   FILE *ofp;
861   char buf[HUGE_STRING];
862   char oldfile[_POSIX_PATH_MAX];
863   char *c;
864   int ext = 0, done = 0, r = 0;
865
866   snprintf (oldfile, sizeof(oldfile), "%s.bak", filename);
867   dprint (1, (debugfile, "Renaming %s to %s\n", filename, oldfile));
868
869   /* if file not exist, create it */
870   if ((ifp = safe_fopen (filename, "a")))
871     fclose (ifp);
872   if (_mutt_rename_file (filename, oldfile, 1))
873   {
874     mutt_perror _("Unable to create backup file");
875     return (-1);
876   }
877   dprint (1, (debugfile, "Opening %s\n", oldfile));
878   if (!(ifp = safe_fopen (oldfile, "r")))
879   {
880     mutt_perror _("Unable to open backup file for reading");
881     return (-1);
882   }
883   dprint (1, (debugfile, "Opening %s\n", filename));
884   if (!(ofp = safe_fopen (filename, "w")))
885   {
886     fclose (ifp);
887     mutt_perror _("Unable to open new file for writing");
888     return (-1);
889   }
890   if (mx_lock_file (filename, fileno (ofp), 1, 0, 1))
891   {
892     fclose (ofp);
893     fclose (ifp);
894     mutt_error (_("Unable to lock %s, old file saved as %s"), filename, oldfile);
895     return (-1);
896   }
897
898   if (section)
899   {
900     while (r != EOF && !done && fgets (buf, sizeof (buf), ifp))
901     {
902       r = fputs (buf, ofp);
903       c = buf;
904       while (*c && *c != '\n') c++;
905       c[0] = 0; /* strip EOL */
906       if (!strncmp (buf, "#: ", 3) && !mutt_strcasecmp (buf+3, section))
907         done++;
908     }
909     if (r != EOF && !done)
910     {
911       snprintf (buf, sizeof(buf), "#: %s\n", section);
912       r = fputs (buf, ofp);
913     }
914     done = 0;
915   }
916
917   while (r != EOF && fgets (buf, sizeof (buf), ifp))
918   {
919     if (ext)
920     {
921       c = buf;
922       while (*c && (*c != '\r') && (*c != '\n')) c++;
923       c--;
924       if (*c != '\\') ext = 0;
925     }
926     else if ((section && !strncmp (buf, "#: ", 3)))
927     {
928       if (!done && line)
929       {
930         fputs (line, ofp);
931         fputc ('\n', ofp);
932       }
933       r = fputs (buf, ofp);
934       done++;
935       break;
936     }
937     else if (key && !strncmp (buf, key, strlen(key)) &&
938             (!*key || buf[strlen(key)] == ' '))
939     {
940       c = buf;
941       ext = 0;
942       while (*c && (*c != '\r') && (*c != '\n')) c++;
943       c--;
944       if (*c == '\\') ext = 1;
945       if (!done && line)
946       {
947         r = fputs (line, ofp);
948         if (*key)
949           r = fputc ('\n', ofp);
950         done++;
951       }
952     }
953     else
954     {
955       r = fputs (buf, ofp);
956     }
957   }
958
959   while (r != EOF && fgets (buf, sizeof (buf), ifp))
960     r = fputs (buf, ofp);
961
962   /* If there wasn't a line to replace, put it on the end of the file */
963   if (r != EOF && !done && line)
964   {
965     fputs (line, ofp);
966     r = fputc ('\n', ofp);
967   }
968   mx_unlock_file (filename, fileno (ofp), 0);
969   fclose (ofp);
970   fclose (ifp);
971   if (r != EOF)
972   {
973     unlink (oldfile);
974     return 0;
975   }
976   unlink (filename);
977   mutt_error (_("Cannot write new %s, old file saved as %s"), filename,
978         oldfile);
979   return (-1);
980 }
981
982
983 void mutt_version (void)
984 {
985   mutt_message ("Mutt-ng %s (%s)", MUTT_VERSION, ReleaseDate);
986 }
987
988 void mutt_edit_content_type (HEADER *h, BODY *b, FILE *fp)
989 {
990   char buf[LONG_STRING];
991   char obuf[LONG_STRING];
992   char tmp[STRING];
993   PARAMETER *p;
994
995   char charset[STRING];
996   char *cp;
997
998   short charset_changed = 0;
999   short type_changed = 0;
1000   
1001   cp = mutt_get_parameter ("charset", b->parameter);
1002   strfcpy (charset, NONULL (cp), sizeof (charset));
1003
1004   snprintf (buf, sizeof (buf), "%s/%s", TYPE (b), b->subtype);
1005   strfcpy (obuf, buf, sizeof (obuf));
1006   if (b->parameter)
1007   {
1008     size_t l;
1009     
1010     for (p = b->parameter; p; p = p->next)
1011     {
1012       l = strlen (buf);
1013
1014       rfc822_cat (tmp, sizeof (tmp), p->value, MimeSpecials);
1015       snprintf (buf + l, sizeof (buf) - l, "; %s=%s", p->attribute, tmp);
1016     }
1017   }
1018   
1019   if (mutt_get_field ("Content-Type: ", buf, sizeof (buf), 0) != 0 ||
1020       buf[0] == 0)
1021     return;
1022   
1023   /* clean up previous junk */
1024   mutt_free_parameter (&b->parameter);
1025   FREE (&b->subtype);
1026   
1027   mutt_parse_content_type (buf, b);
1028
1029   
1030   snprintf (tmp, sizeof (tmp), "%s/%s", TYPE (b), NONULL (b->subtype));
1031   type_changed = ascii_strcasecmp (tmp, obuf);
1032   charset_changed = ascii_strcasecmp (charset, mutt_get_parameter ("charset", b->parameter));
1033
1034   /* if in send mode, check for conversion - current setting is default. */
1035
1036   if (!h && b->type == TYPETEXT && charset_changed)
1037   {
1038     int r;
1039     snprintf (tmp, sizeof (tmp), _("Convert to %s upon sending?"),
1040               mutt_get_parameter ("charset", b->parameter));
1041     if ((r = mutt_yesorno (tmp, !b->noconv)) != -1)
1042       b->noconv = (r == M_NO);
1043   }
1044
1045   /* inform the user */
1046   
1047   snprintf (tmp, sizeof (tmp), "%s/%s", TYPE (b), NONULL (b->subtype));
1048   if (type_changed)
1049     mutt_message (_("Content-Type changed to %s."), tmp);
1050   if (b->type == TYPETEXT && charset_changed)
1051   {
1052     if (type_changed)
1053       mutt_sleep (1);
1054     mutt_message (_("Character set changed to %s; %s."),
1055                   mutt_get_parameter ("charset", b->parameter),
1056                   b->noconv ? _("not converting") : _("converting"));
1057   }
1058
1059   b->force_charset |= charset_changed ? 1 : 0;
1060
1061   if (!is_multipart (b) && b->parts)
1062     mutt_free_body (&b->parts);
1063   if (!mutt_is_message_type (b->type, b->subtype) && b->hdr)
1064   {
1065     b->hdr->content = NULL;
1066     mutt_free_header (&b->hdr);
1067   }
1068
1069   if (fp && (is_multipart (b) || mutt_is_message_type (b->type, b->subtype)))
1070     mutt_parse_part (fp, b);
1071   
1072   if (WithCrypto && h)
1073   {
1074     if (h->content == b)
1075       h->security  = 0;
1076
1077     h->security |= crypt_query (b);
1078   }
1079 }
1080
1081
1082 static int _mutt_check_traditional_pgp (HEADER *h, int *redraw)
1083 {
1084   MESSAGE *msg;
1085   int rv = 0;
1086   
1087   h->security |= PGP_TRADITIONAL_CHECKED;
1088   
1089   mutt_parse_mime_message (Context, h);
1090   if ((msg = mx_open_message (Context, h->msgno)) == NULL)
1091     return 0;
1092   if (crypt_pgp_check_traditional (msg->fp, h->content, 0))
1093   {
1094     h->security = crypt_query (h->content);
1095     *redraw |= REDRAW_FULL;
1096     rv = 1;
1097   }
1098   
1099   h->security |= PGP_TRADITIONAL_CHECKED;
1100   mx_close_message (&msg);
1101   return rv;
1102 }
1103
1104 int mutt_check_traditional_pgp (HEADER *h, int *redraw)
1105 {
1106   int i;
1107   int rv = 0;
1108   if (h && !(h->security & PGP_TRADITIONAL_CHECKED))
1109     rv = _mutt_check_traditional_pgp (h, redraw);
1110   else
1111   {
1112     for (i = 0; i < Context->vcount; i++)
1113       if (Context->hdrs[Context->v2r[i]]->tagged && 
1114           !(Context->hdrs[Context->v2r[i]]->security & PGP_TRADITIONAL_CHECKED))
1115         rv = _mutt_check_traditional_pgp (Context->hdrs[Context->v2r[i]], redraw)
1116           || rv;
1117   }
1118   return rv;
1119 }
1120
1121