unlink file before writing it, and don't fail if it's not here
[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 #include <lib-lib/lib-lib.h>
12 #include <utime.h>
13
14 #include <lib-mime/mime.h>
15
16 #include <lib-sys/exit.h>
17 #include <lib-sys/unix.h>
18
19 #include <lib-lua/lib-lua.h>
20 #include <lib-ui/curses.h>
21 #include <lib-ui/enter.h>
22 #include <lib-ui/menu.h>
23 #include <lib-mx/mx.h>
24
25 #include "mutt.h"
26 #include "alias.h"
27 #include "recvattach.h"
28 #include "sort.h"
29 #include "copy.h"
30 #include "pager.h"
31 #include "crypt.h"
32 #include "mutt_idna.h"
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36
37 #include <imap/imap.h>
38
39 /* The folder the user last saved to.  Used by ci_save_message() */
40 static char LastSaveFolder[_POSIX_PATH_MAX] = "";
41
42 int mutt_display_message (HEADER * cur)
43 {
44   char tempfile[_POSIX_PATH_MAX], buf[LONG_STRING];
45   int cmflags = M_CM_DECODE | M_CM_DISPLAY | M_CM_CHARCONV;
46   FILE *fpout = NULL;
47   FILE *fpfilterout = NULL;
48   MESSAGE *msg = NULL;
49   pid_t filterpid = -1;
50   int res = 0;
51
52   snprintf (buf, sizeof (buf), "%s/%s", TYPE (cur->content),
53             cur->content->subtype);
54
55   mutt_parse_mime_message (Context, cur);
56   mutt_message_hook (Context, cur, M_MESSAGEHOOK);
57
58   fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
59   if (!fpout) {
60     mutt_error _("Could not create temporary file!");
61     return 0;
62   }
63
64   if (DisplayFilter && *DisplayFilter) {
65     fpfilterout = fpout;
66     fpout = NULL;
67     /* mutt_endwin (NULL); */
68     filterpid = mutt_create_filter_fd (DisplayFilter, &fpout, NULL, NULL,
69                                        -1, fileno (fpfilterout), -1);
70     if (filterpid < 0) {
71       mutt_error (_("Cannot create display filter"));
72       m_fclose(&fpfilterout);
73       unlink (tempfile);
74       return 0;
75     }
76   }
77
78   msg = mx_open_message (Context, cur->msgno);
79   if (msg == NULL) res = -1;
80
81   if (res != -1) {
82     /* see if crytpo is needed for this message.  if so, we should exit curses */
83     if (cur->security) {
84       if (cur->security & ENCRYPT) {
85         cmflags |= M_CM_VERIFY;
86       }
87       else if (cur->security & SIGN) {
88         /* find out whether or not the verify signature */
89         if (query_quadoption2(mod_crypt.verify_sig, _("Verify PGP signature?")) ==
90             M_YES) {
91           cmflags |= M_CM_VERIFY;
92         }
93       }
94     }
95
96     if (cmflags & M_CM_VERIFY || cur->security & ENCRYPT) {
97       crypt_invoke_message(cur->security);
98     }
99
100     res = _mutt_copy_message (fpout, msg->fp, cur, cur->content, cmflags,
101                              (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
102                              CH_DECODE | CH_FROM);
103     if (res == 0 && (ferror(fpout) || feof(fpout))) {
104       res = -1;
105     }
106
107     mx_close_message (&msg);
108   }
109
110   if ((m_fclose(&fpout) != 0 && errno != EPIPE) || res == -1) {
111     mutt_error (_("Could not copy message"));
112     if (fpfilterout != NULL) {
113       mutt_wait_filter (filterpid);
114       m_fclose(&fpfilterout);
115     }
116   }
117
118   if (fpfilterout != NULL && mutt_wait_filter (filterpid) != 0)
119     mutt_any_key_to_continue (NULL);
120
121   m_fclose(&fpfilterout);   /* XXX - check result? */
122
123
124   /* update crypto information for this message */
125   cur->security &= ~(GOODSIGN|BADSIGN);
126   cur->security |= crypt_query (cur->content);
127
128   /* Remove color cache for this message, in case there
129      are color patterns for both ~g and ~V */
130   cur->pair = 0;
131
132   if ((cur->security & APPLICATION_SMIME) && (cmflags & M_CM_VERIFY)) {
133     if (cur->security & GOODSIGN) {
134       if (!crypt_smime_verify_sender (cur))
135         mutt_message (_("S/MIME signature successfully verified."));
136       else
137         mutt_error (_("S/MIME certificate owner does not match sender."));
138     }
139     else if (cur->security & PARTSIGN)
140       mutt_message (_
141                     ("Warning: Part of this message has not been signed."));
142     else if (cur->security & SIGN || cur->security & BADSIGN)
143       mutt_error (_("S/MIME signature could NOT be verified."));
144   }
145
146   if ((cur->security & APPLICATION_PGP) && (cmflags & M_CM_VERIFY)) {
147     if (cur->security & GOODSIGN)
148       mutt_message (_("PGP signature successfully verified."));
149     else if (cur->security & PARTSIGN)
150       mutt_message (_
151                     ("Warning: Part of this message has not been signed."));
152     else if (cur->security & SIGN)
153       mutt_message (_("PGP signature could NOT be verified."));
154   }
155
156   {
157     pager_t info;
158
159     p_clear(&info, 1);
160     info.hdr = cur;
161     info.ctx = Context;
162     return mutt_pager (NULL, tempfile, M_PAGER_MESSAGE, &info);
163   }
164 }
165
166 void ci_bounce_message (HEADER * h, int *redraw)
167 {
168   char prompt[STRING];
169   char buf[HUGE_STRING] = { 0 };
170   address_t *adr = NULL;
171   char *err = NULL;
172   int rc;
173
174   if (h)
175     m_strcpy(prompt, sizeof(prompt), _("Bounce message to: "));
176   else
177     m_strcpy(prompt, sizeof(prompt), _("Bounce tagged messages to: "));
178
179   rc = mutt_get_field (prompt, buf, sizeof (buf), M_ALIAS);
180
181   if (option (OPTNEEDREDRAW)) {
182     unset_option (OPTNEEDREDRAW);
183     *redraw = REDRAW_FULL;
184   }
185
186   if (rc || !buf[0])
187     return;
188
189   if (!(adr = rfc822_parse_adrlist (adr, buf))) {
190     mutt_error _("Error parsing address!");
191
192     return;
193   }
194
195   adr = mutt_expand_aliases (adr);
196
197   if (mutt_addrlist_to_idna (adr, &err) < 0) {
198     mutt_error (_("Bad IDN: '%s'"), err);
199     p_delete(&err);
200     address_list_wipe(&adr);
201     return;
202   }
203
204   buf[0] = 0;
205   rfc822_addrcat(buf, sizeof (buf), adr, 1);
206
207   snprintf (prompt, sizeof (prompt),
208             (h ? _("Bounce message to %s") : _("Bounce messages to %s")),
209             buf);
210
211 #define extra_space (15 + 7 + 2)
212   if (m_strwidth(prompt) > COLS - extra_space) {
213     mutt_format_string(prompt, sizeof(prompt), 0, COLS - extra_space, 0, 0,
214                        prompt, sizeof(prompt), 0);
215     m_strcat(prompt, sizeof(prompt), "...?");
216   } else {
217     m_strcat(prompt, sizeof(prompt), "?");
218   }
219 #undef extra_space
220
221   if (query_quadoption (OPT_BOUNCE, prompt) != M_YES) {
222     address_list_wipe(&adr);
223     CLEARLINE (LINES - 1);
224     mutt_message (h ? _("Message not bounced.") : _("Messages not bounced."));
225     return;
226   }
227
228   CLEARLINE (LINES - 1);
229
230   rc = mutt_bounce_message (NULL, h, adr);
231   address_list_wipe(&adr);
232   /* If no error, or background, display message. */
233   if ((rc == 0) || (rc == S_BKG))
234     mutt_message (h ? _("Message bounced.") : _("Messages bounced."));
235 }
236
237 static void pipe_set_flags (int decode, int print, int *cmflags, int *chflags)
238 {
239   if (decode) {
240     *cmflags |= M_CM_DECODE | M_CM_CHARCONV;
241     *chflags |= CH_DECODE | CH_REORDER;
242
243     if (option (OPTWEED)) {
244       *chflags |= CH_WEED;
245       *cmflags |= M_CM_WEED;
246     }
247   }
248
249   if (print)
250     *cmflags |= M_CM_PRINTING;
251
252 }
253
254 static void pipe_msg (HEADER * h, FILE * fp, int decode, int print)
255 {
256   int cmflags = 0;
257   int chflags = CH_FROM;
258
259   pipe_set_flags (decode, print, &cmflags, &chflags);
260
261   if (decode && h->security & ENCRYPT) {
262     endwin ();
263   }
264
265   if (decode)
266     mutt_parse_mime_message (Context, h);
267
268   mutt_copy_message (fp, Context, h, cmflags, chflags);
269 }
270
271
272 /* the following code is shared between printing and piping */
273
274 static int _mutt_pipe_message(HEADER * h, char *cmd, int decode, int print,
275                               int split, const char *sep)
276 {
277
278   int i, rc = 0;
279   pid_t thepid;
280   FILE *fpout;
281
282 /*   mutt_endwin (NULL); 
283
284      is this really needed here ? 
285      it makes the screen flicker on pgp and s/mime messages,
286      before asking for a passphrase...
287                                      Oliver Ehli */
288   if (h) {
289
290     mutt_message_hook (Context, h, M_MESSAGEHOOK);
291
292     if (decode) {
293       mutt_parse_mime_message (Context, h);
294     }
295     mutt_endwin (NULL);
296
297     if ((thepid = mutt_create_filter (cmd, &fpout, NULL, NULL)) < 0) {
298       mutt_perror (_("Can't create filter process"));
299
300       return 1;
301     }
302
303     pipe_msg (h, fpout, decode, print);
304     m_fclose(&fpout);
305     rc = mutt_wait_filter (thepid);
306   }
307   else {                        /* handle tagged messages */
308
309     if (decode) {
310       for (i = 0; i < Context->vcount; i++)
311         if (Context->hdrs[Context->v2r[i]]->tagged) {
312           mutt_message_hook (Context, Context->hdrs[Context->v2r[i]],
313                              M_MESSAGEHOOK);
314           mutt_parse_mime_message (Context, Context->hdrs[Context->v2r[i]]);
315         }
316     }
317
318     if (split) {
319       for (i = 0; i < Context->vcount; i++) {
320         if (Context->hdrs[Context->v2r[i]]->tagged) {
321           mutt_message_hook (Context, Context->hdrs[Context->v2r[i]],
322                              M_MESSAGEHOOK);
323           mutt_endwin (NULL);
324           if ((thepid = mutt_create_filter (cmd, &fpout, NULL, NULL)) < 0) {
325             mutt_perror (_("Can't create filter process"));
326
327             return 1;
328           }
329           pipe_msg (Context->hdrs[Context->v2r[i]], fpout, decode, print);
330           /* add the message separator */
331           if (sep)
332             fputs (sep, fpout);
333           m_fclose(&fpout);
334           if (mutt_wait_filter (thepid) != 0)
335             rc = 1;
336         }
337       }
338     }
339     else {
340       mutt_endwin (NULL);
341       if ((thepid = mutt_create_filter (cmd, &fpout, NULL, NULL)) < 0) {
342         mutt_perror (_("Can't create filter process"));
343
344         return 1;
345       }
346       for (i = 0; i < Context->vcount; i++) {
347         if (Context->hdrs[Context->v2r[i]]->tagged) {
348           mutt_message_hook (Context, Context->hdrs[Context->v2r[i]],
349                              M_MESSAGEHOOK);
350           pipe_msg (Context->hdrs[Context->v2r[i]], fpout, decode, print);
351           /* add the message separator */
352           if (sep)
353             fputs (sep, fpout);
354         }
355       }
356       m_fclose(&fpout);
357       if (mutt_wait_filter (thepid) != 0)
358         rc = 1;
359     }
360   }
361
362   if (rc || option (OPTWAITKEY))
363     mutt_any_key_to_continue (NULL);
364   return rc;
365 }
366
367 void mutt_pipe_message (HEADER * h)
368 {
369   char buffer[LONG_STRING];
370
371   buffer[0] = 0;
372   if (mutt_get_field (_("Pipe to command: "), buffer, sizeof (buffer), M_CMD)
373       != 0 || !buffer[0])
374     return;
375
376   mutt_expand_path (buffer, sizeof (buffer));
377   _mutt_pipe_message (h, buffer,
378                       option (OPTPIPEDECODE),
379                       0, option (OPTPIPESPLIT), PipeSep);
380 }
381
382 void mutt_print_message (HEADER * h)
383 {
384
385   if (quadoption (OPT_PRINT) && m_strisempty(PrintCmd)) {
386     mutt_message (_("No printing command has been defined."));
387     return;
388   }
389
390   if (query_quadoption (OPT_PRINT,
391                         h ? _("Print message?") : _("Print tagged messages?"))
392       != M_YES)
393     return;
394
395   if (_mutt_pipe_message (h, PrintCmd,
396                           option (OPTPRINTDECODE),
397                           1, option (OPTPRINTSPLIT), "\f") == 0)
398     mutt_message (h ? _("Message printed") : _("Messages printed"));
399   else
400     mutt_message (h ? _("Message could not be printed") :
401                   _("Messages could not be printed"));
402 }
403
404
405 int mutt_select_sort (int reverse)
406 {
407   int method = Sort;            /* save the current method in case of abort */
408
409   switch (mutt_multi_choice (reverse ?
410                              _
411                              ("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?: ")
412                              :
413                              _
414                              ("Sort (d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/(u)nsort/si(z)e/s(c)ore/s(p)am?: "),
415                              _("dfrsotuzcp"))) {
416   case -1:                     /* abort - don't resort */
417     return -1;
418
419   case 1:                      /* (d)ate */
420     Sort = SORT_DATE;
421     break;
422
423   case 2:                      /* (f)rm */
424     Sort = SORT_FROM;
425     break;
426
427   case 3:                      /* (r)ecv */
428     Sort = SORT_RECEIVED;
429     break;
430
431   case 4:                      /* (s)ubj */
432     Sort = SORT_SUBJECT;
433     break;
434
435   case 5:                      /* t(o) */
436     Sort = SORT_TO;
437     break;
438
439   case 6:                      /* (t)hread */
440     Sort = SORT_THREADS;
441     break;
442
443   case 7:                      /* (u)nsort */
444     Sort = SORT_ORDER;
445     break;
446
447   case 8:                      /* si(z)e */
448     Sort = SORT_SIZE;
449     break;
450
451   case 9:                      /* s(c)ore */
452     Sort = SORT_SCORE;
453     break;
454
455   case 10:                     /* s(p)am */
456     Sort = SORT_SPAM;
457     break;
458   }
459   if (reverse)
460     Sort |= SORT_REVERSE;
461
462   return (Sort != method ? 0 : -1);     /* no need to resort if it's the same */
463 }
464
465 /* invoke a command in a subshell */
466 void mutt_shell_escape (void)
467 {
468   char buf[LONG_STRING];
469
470   buf[0] = 0;
471   if (mutt_get_field (_("Shell command: "), buf, sizeof (buf), M_CMD) == 0) {
472     if (!buf[0])
473       m_strcpy(buf, sizeof(buf), MCore.shell);
474     if (buf[0]) {
475       CLEARLINE (LINES - 1);
476       mutt_endwin (NULL);
477       fflush (stdout);
478       if (mutt_system (buf) != 0 || option (OPTWAITKEY))
479         mutt_any_key_to_continue (NULL);
480     }
481   }
482 }
483
484 /* enter a mutt command */
485 void mutt_enter_command (void)
486 {
487   BUFFER err, token;
488   char buffer[LONG_STRING], errbuf[STRING];
489   int r;
490
491   buffer[0] = 0;
492   if (mutt_get_field (":", buffer, sizeof (buffer), M_COMMAND) != 0
493       || !buffer[0])
494     return;
495   err.data = errbuf;
496   err.dsize = sizeof (errbuf);
497   p_clear(&token, 1);
498   r = mutt_parse_rc_line (buffer, &token, &err);
499   p_delete(&token.data);
500   if (errbuf[0]) {
501     /* since errbuf could potentially contain printf() sequences in it,
502        we must call mutt_error() in this fashion so that vsprintf()
503        doesn't expect more arguments that we passed */
504     if (r == 0)
505       mutt_message ("%s", errbuf);
506     else
507       mutt_error ("%s", errbuf);
508   }
509 }
510
511 void mutt_display_address (ENVELOPE * env)
512 {
513   const char *pfx = NULL;
514   char buf[STRING];
515   address_t *adr = NULL;
516
517   adr = mutt_get_address(env, &pfx);
518
519   if (!adr)
520     return;
521
522   /* 
523    * Note: We don't convert IDNA to local representation this time.
524    * That is intentional, so the user has an opportunity to copy &
525    * paste the on-the-wire form of the address to other, IDN-unable
526    * software. 
527    */
528
529   buf[0] = 0;
530   rfc822_addrcat(buf, sizeof (buf), adr, 0);
531   mutt_message ("%s: %s", pfx, buf);
532 }
533
534 static void set_copy_flags (HEADER * hdr, int decode, int decrypt,
535                             int *cmflags, int *chflags)
536 {
537   *cmflags = 0;
538   *chflags = CH_UPDATE_LEN;
539
540   if (!decode && decrypt && (hdr->security & ENCRYPT)) {
541     if (mutt_is_multipart_encrypted (hdr->content)) {
542       *chflags = CH_NONEWLINE | CH_XMIT | CH_MIME;
543       *cmflags = M_CM_DECODE_PGP;
544     }
545     else if (mutt_is_application_pgp (hdr->content) & ENCRYPT)
546       decode = 1;
547     else if (mutt_is_application_smime (hdr->content) & ENCRYPT) {
548       *chflags = CH_NONEWLINE | CH_XMIT | CH_MIME;
549       *cmflags = M_CM_DECODE_SMIME;
550     }
551   }
552
553   if (decode) {
554     *chflags = CH_XMIT | CH_MIME | CH_TXTPLAIN;
555     *cmflags = M_CM_DECODE | M_CM_CHARCONV;
556
557     if (!decrypt) {             /* If decode doesn't kick in for decrypt, */
558       *chflags |= CH_DECODE;    /* then decode RFC 2047 headers, */
559
560       if (option (OPTWEED)) {
561         *chflags |= CH_WEED;    /* and respect $weed. */
562         *cmflags |= M_CM_WEED;
563       }
564     }
565   }
566 }
567
568 int _mutt_save_message (HEADER * h, CONTEXT * ctx, int delete, int decode,
569                          int decrypt) {
570   int cmflags, chflags;
571   int rc;
572
573   set_copy_flags (h, decode, decrypt, &cmflags, &chflags);
574
575   if (decode || decrypt)
576     mutt_parse_mime_message (Context, h);
577
578   if ((rc = mutt_append_message (ctx, Context, h, cmflags, chflags)) != 0)
579     return rc;
580
581   if (delete) {
582     mutt_set_flag (Context, h, M_DELETE, 1);
583     if (option (OPTDELETEUNTAG))
584       mutt_set_flag (Context, h, M_TAG, 0);
585     mutt_set_flag (Context, h, M_APPENDED, 1);
586   }
587   return (0);
588 }
589
590 /* returns 0 if the copy/save was successful, or -1 on error/abort */
591 int mutt_save_message (HEADER * h, int delete,
592                        int decode, int decrypt, int *redraw) {
593   int i, need_buffy_cleanup;
594   int need_passphrase = 0, app = 0;
595   char prompt[STRING], buf[_POSIX_PATH_MAX];
596   CONTEXT ctx;
597   struct stat st;
598   struct utimbuf ut;
599
600   *redraw = 0;
601
602
603   snprintf (prompt, sizeof (prompt),
604             decode ? (delete ? _("Decode-save%s to mailbox") :
605                       _("Decode-copy%s to mailbox")) :
606             (decrypt ? (delete ? _("Decrypt-save%s to mailbox") :
607                         _("Decrypt-copy%s to mailbox")) :
608              (delete ? _("Save%s to mailbox") : _("Copy%s to mailbox"))),
609             h ? "" : _(" tagged"));
610
611
612   if (h) {
613     need_passphrase = h->security & ENCRYPT;
614     app = h->security;
615     mutt_message_hook (Context, h, M_MESSAGEHOOK);
616     mutt_default_save (buf, sizeof (buf), h);
617   }
618   else {
619     /* look for the first tagged message */
620
621     for (i = 0; i < Context->vcount; i++) {
622       if (Context->hdrs[Context->v2r[i]]->tagged) {
623         h = Context->hdrs[Context->v2r[i]];
624         break;
625       }
626     }
627
628
629     if (h) {
630       mutt_message_hook (Context, h, M_MESSAGEHOOK);
631       mutt_default_save (buf, sizeof (buf), h);
632       need_passphrase = h->security & ENCRYPT;
633       app = h->security;
634       h = NULL;
635     }
636   }
637
638   mutt_pretty_mailbox (buf);
639   if (mutt_enter_fname (prompt, buf, sizeof (buf), redraw, 0) == -1)
640     return (-1);
641
642   if (*redraw != REDRAW_FULL) {
643     if (!h)
644       *redraw = REDRAW_INDEX | REDRAW_STATUS;
645     else
646       *redraw = REDRAW_STATUS;
647   }
648
649   if (!buf[0])
650     return (-1);
651
652   /* This is an undocumented feature of ELM pointed out to me by Felix von
653    * Leitner <leitner@prz.fu-berlin.de>
654    */
655   if (m_strcmp(buf, ".") == 0)
656     m_strcpy(buf, sizeof(buf), LastSaveFolder);
657   else
658     m_strcpy(LastSaveFolder, sizeof(LastSaveFolder), buf);
659
660   mutt_expand_path (buf, sizeof (buf));
661
662   /* check to make sure that this file is really the one the user wants */
663   if (mutt_save_confirm (buf, &st) != 0)
664     return -1;
665
666   mutt_message (_("Copying to %s..."), buf);
667
668   if (Context->magic == M_IMAP && !(decode || decrypt) && mx_get_magic (buf) == M_IMAP) {
669     switch (imap_copy_messages (Context, h, buf, delete)) {
670       /* success */
671     case 0:
672       mutt_clear_error ();
673       return 0;
674       /* non-fatal error: fall through to fetch/append */
675     case 1:
676       break;
677       /* fatal error, abort */
678     case -1:
679       return -1;
680     }
681   }
682
683   if (mx_open_mailbox (buf, M_APPEND, &ctx) != NULL) {
684     if (h) {
685       if (_mutt_save_message (h, &ctx, delete, decode, decrypt) != 0) {
686         mx_close_mailbox (&ctx, NULL);
687         return (-1);
688       }
689     } else {
690       for (i = 0; i < Context->vcount; i++) {
691         if (Context->hdrs[Context->v2r[i]]->tagged) {
692           mutt_message_hook (Context, Context->hdrs[Context->v2r[i]],
693                              M_MESSAGEHOOK);
694           if (_mutt_save_message (Context->hdrs[Context->v2r[i]], &ctx, delete,
695                                   decode, decrypt) != 0) {
696             mx_close_mailbox (&ctx, NULL);
697             return (-1);
698           }
699         }
700       }
701     }
702
703     need_buffy_cleanup = (ctx.magic == M_MBOX);
704
705     mx_close_mailbox (&ctx, NULL);
706
707     if (need_buffy_cleanup) {
708       /* fix up the times so buffy won't get confused */
709       if (st.st_mtime > st.st_atime) {
710         ut.actime = st.st_atime;
711         ut.modtime = time (NULL);
712         utime (buf, &ut);
713       } else {
714         utime (buf, NULL);
715       }
716     }
717
718     mutt_clear_error ();
719     return (0);
720   }
721
722   return -1;
723 }
724
725 void mutt_version (void)
726 {
727   mutt_message (mutt_make_version());
728 }
729
730 void mutt_edit_content_type (HEADER * h, BODY * b, FILE * fp)
731 {
732   char buf[LONG_STRING];
733   char obuf[LONG_STRING];
734   char tmp[STRING];
735
736   char charset[STRING];
737   char *cp;
738
739   short charset_changed = 0;
740   short type_changed = 0;
741
742   cp = parameter_getval(b->parameter, "charset");
743   m_strcpy(charset, sizeof(charset), NONULL(cp));
744
745   snprintf (buf, sizeof (buf), "%s/%s", TYPE (b), b->subtype);
746   m_strcpy(obuf, sizeof(obuf), buf);
747   if (b->parameter) {
748     parameter_t *p;
749     ssize_t l;
750
751     for (p = b->parameter; p; p = p->next) {
752       l = m_strlen(buf);
753
754       rfc822_strcpy(tmp, sizeof(tmp), p->value, MimeSpecials);
755       snprintf (buf + l, sizeof (buf) - l, "; %s=%s", p->attribute, tmp);
756     }
757   }
758
759   if (mutt_get_field ("Content-Type: ", buf, sizeof (buf), 0) != 0 ||
760       buf[0] == 0)
761     return;
762
763   /* clean up previous junk */
764   parameter_list_wipe(&b->parameter);
765   p_delete(&b->subtype);
766
767   mutt_parse_content_type (buf, b);
768
769
770   snprintf (tmp, sizeof (tmp), "%s/%s", TYPE (b), NONULL (b->subtype));
771   type_changed = ascii_strcasecmp (tmp, obuf);
772   charset_changed =
773     ascii_strcasecmp (charset, parameter_getval(b->parameter, "charset"));
774
775   /* if in send mode, check for conversion - current setting is default. */
776
777   if (!h && b->type == TYPETEXT && charset_changed) {
778     int r;
779
780     snprintf (tmp, sizeof (tmp), _("Convert to %s upon sending?"),
781               parameter_getval(b->parameter, "charset"));
782     if ((r = mutt_yesorno (tmp, !b->noconv)) != -1)
783       b->noconv = (r == M_NO);
784   }
785
786   /* inform the user */
787
788   snprintf (tmp, sizeof (tmp), "%s/%s", TYPE (b), NONULL (b->subtype));
789   if (type_changed)
790     mutt_message (_("Content-Type changed to %s."), tmp);
791   if (b->type == TYPETEXT && charset_changed) {
792     if (type_changed)
793       mutt_sleep (1);
794     mutt_message (_("Character set changed to %s; %s."),
795                   parameter_getval(b->parameter, "charset"),
796                   b->noconv ? _("not converting") : _("converting"));
797   }
798
799   b->force_charset |= charset_changed ? 1 : 0;
800
801   if (!is_multipart(b) && b->parts)
802     body_list_wipe(&b->parts);
803   if (!mutt_is_message_type(b) && b->hdr) {
804     b->hdr->content = NULL;
805     header_delete(&b->hdr);
806   }
807
808   if (fp && (is_multipart(b) || mutt_is_message_type(b)))
809     mutt_parse_part (fp, b);
810
811   if (h) {
812     if (h->content == b)
813       h->security = 0;
814
815     h->security |= crypt_query (b);
816   }
817 }
818
819
820 static int _mutt_check_traditional_pgp (HEADER * h, int *redraw)
821 {
822   MESSAGE *msg;
823   int rv = 0;
824
825   h->security |= PGP_TRADITIONAL_CHECKED;
826
827   mutt_parse_mime_message (Context, h);
828   if ((msg = mx_open_message (Context, h->msgno)) == NULL)
829     return 0;
830   if (crypt_pgp_check_traditional (msg->fp, h->content, 0)) {
831     h->security = crypt_query (h->content);
832     *redraw |= REDRAW_FULL;
833     rv = 1;
834   }
835
836   h->security |= PGP_TRADITIONAL_CHECKED;
837   mx_close_message (&msg);
838   return rv;
839 }
840
841 int mutt_check_traditional_pgp (HEADER * h, int *redraw)
842 {
843   int i;
844   int rv = 0;
845
846   if (h && !(h->security & PGP_TRADITIONAL_CHECKED))
847     rv = _mutt_check_traditional_pgp (h, redraw);
848   else {
849     for (i = 0; i < Context->vcount; i++)
850       if (Context->hdrs[Context->v2r[i]]->tagged &&
851           !(Context->hdrs[Context->v2r[i]]->
852             security & PGP_TRADITIONAL_CHECKED))
853         rv =
854           _mutt_check_traditional_pgp (Context->hdrs[Context->v2r[i]], redraw)
855           || rv;
856   }
857   return rv;
858 }