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