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