mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-2000,2002,2013 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 1999-2004,2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#if HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include "mutt.h"
25#include "mutt_menu.h"
26#include "attach.h"
27#include "mutt_curses.h"
28#include "keymap.h"
29#include "rfc1524.h"
30#include "mime.h"
31#include "pager.h"
32#include "mailbox.h"
33#include "copy.h"
34#include "mx.h"
35#include "mutt_crypt.h"
36
37#include <ctype.h>
38#include <stdlib.h>
39#include <unistd.h>
40#include <sys/wait.h>
41#include <sys/stat.h>
42#include <fcntl.h>
43#include <string.h>
44#include <errno.h>
45
46int mutt_get_tmp_attachment (BODY *a)
47{
48 char type[STRING];
49 BUFFER *tempfile = NULL;
50 rfc1524_entry *entry = NULL;
51 FILE *fpin = NULL, *fpout = NULL;
52 struct stat st;
53
54 if (a->unlink)
55 return 0;
56
57 tempfile = mutt_buffer_pool_get ();
58 entry = rfc1524_new_entry();
59
60 snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
61 rfc1524_mailcap_lookup(a, type, sizeof(type), entry, 0);
62 mutt_rfc1524_expand_filename (entry->nametemplate, a->filename, tempfile);
63
64 rfc1524_free_entry(&entry);
65
66 if (stat(a->filename, &st) == -1)
67 {
68 mutt_buffer_pool_release (&tempfile);
69 return -1;
70 }
71
72 if ((fpin = fopen(a->filename, "r")) && /* __FOPEN_CHECKED__ */
73 (fpout = safe_fopen(mutt_b2s (tempfile), "w")))
74 {
75 mutt_copy_stream (fpin, fpout);
76 mutt_str_replace (&a->filename, mutt_b2s (tempfile));
77 a->unlink = 1;
78
79 if (a->stamp >= st.st_mtime)
80 mutt_stamp_attachment(a);
81 }
82 else
83 mutt_perror(fpin ? mutt_b2s (tempfile) : a->filename);
84
85 if (fpin) safe_fclose (&fpin);
86 if (fpout) safe_fclose (&fpout);
87
88 mutt_buffer_pool_release (&tempfile);
89
90 return a->unlink ? 0 : -1;
91}
92
93
94/* return 1 if require full screen redraw, 0 otherwise */
95int mutt_compose_attachment (BODY *a)
96{
97 char type[STRING];
98 BUFFER *command = mutt_buffer_pool_get ();
99 BUFFER *newfile = mutt_buffer_pool_get ();
100 BUFFER *tempfile = mutt_buffer_pool_get ();
101 rfc1524_entry *entry = rfc1524_new_entry ();
102 short unlink_newfile = 0;
103 int rc = 0;
104
105 snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
106 if (rfc1524_mailcap_lookup (a, type, sizeof(type), entry, MUTT_COMPOSE))
107 {
108 if (entry->composecommand || entry->composetypecommand)
109 {
110 if (entry->composetypecommand)
111 mutt_buffer_strcpy (command, entry->composetypecommand);
112 else
113 mutt_buffer_strcpy (command, entry->composecommand);
114
115 mutt_rfc1524_expand_filename (entry->nametemplate,
116 a->filename, newfile);
117 dprint(1, (debugfile, "oldfile: %s\t newfile: %s\n",
118 a->filename, mutt_b2s (newfile)));
119
120 if (safe_symlink (a->filename, mutt_b2s (newfile)) == -1)
121 {
122 if (mutt_yesorno (_("Can't match nametemplate, continue?"), MUTT_YES) != MUTT_YES)
123 goto bailout;
124 mutt_buffer_strcpy (newfile, a->filename);
125 }
126 else
127 unlink_newfile = 1;
128
129 if (mutt_rfc1524_expand_command (a, mutt_b2s (newfile), type, command))
130 {
131 /* For now, editing requires a file, no piping */
132 mutt_error _("Mailcap compose entry requires %%s");
133 }
134 else
135 {
136 int r;
137
138 mutt_endwin (NULL);
139 if ((r = mutt_system (mutt_b2s (command))) == -1)
140 mutt_error (_("Error running \"%s\"!"), mutt_b2s (command));
141
142 if (r != -1 && entry->composetypecommand)
143 {
144 BODY *b;
145 FILE *fp, *tfp;
146
147 if ((fp = safe_fopen (a->filename, "r")) == NULL)
148 {
149 mutt_perror _("Failure to open file to parse headers.");
150 goto bailout;
151 }
152
153 b = mutt_read_mime_header (fp, 0);
154 if (b)
155 {
156 if (b->parameter)
157 {
158 mutt_free_parameter (&a->parameter);
159 a->parameter = b->parameter;
160 b->parameter = NULL;
161 }
162 if (b->description)
163 {
164 FREE (&a->description);
165 a->description = b->description;
166 b->description = NULL;
167 }
168 if (b->form_name)
169 {
170 FREE (&a->form_name);
171 a->form_name = b->form_name;
172 b->form_name = NULL;
173 }
174
175 /* Remove headers by copying out data to another file, then
176 * copying the file back */
177 fseeko (fp, b->offset, 0);
178 mutt_buffer_mktemp (tempfile);
179 if ((tfp = safe_fopen (mutt_b2s (tempfile), "w")) == NULL)
180 {
181 mutt_perror _("Failure to open file to strip headers.");
182 goto bailout;
183 }
184 mutt_copy_stream (fp, tfp);
185 safe_fclose (&fp);
186 safe_fclose (&tfp);
187 mutt_unlink (a->filename);
188 if (mutt_rename_file (mutt_b2s (tempfile), a->filename) != 0)
189 {
190 mutt_perror _("Failure to rename file.");
191 goto bailout;
192 }
193
194 mutt_free_body (&b);
195 }
196 }
197 }
198 }
199 }
200 else
201 {
202 mutt_message (_("No mailcap compose entry for %s, creating empty file."),
203 type);
204 rc = 1;
205 goto bailout;
206 }
207
208 rc = 1;
209
210bailout:
211
212 if (unlink_newfile)
213 unlink(mutt_b2s (newfile));
214
215 mutt_buffer_pool_release (&command);
216 mutt_buffer_pool_release (&newfile);
217 mutt_buffer_pool_release (&tempfile);
218
219 rfc1524_free_entry (&entry);
220 return rc;
221}
222
223/*
224 * Currently, this only works for send mode, as it assumes that the
225 * BODY->filename actually contains the information. I'm not sure
226 * we want to deal with editing attachments we've already received,
227 * so this should be ok.
228 *
229 * Returns 1 if editor found, 0 if not (useful to tell calling menu to
230 * redraw)
231 */
232int mutt_edit_attachment (BODY *a)
233{
234 char type[STRING];
235 BUFFER *command = mutt_buffer_pool_get ();
236 BUFFER *newfile = mutt_buffer_pool_get ();
237 rfc1524_entry *entry = rfc1524_new_entry ();
238 short unlink_newfile = 0;
239 int rc = 0;
240
241 snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
242 if (rfc1524_mailcap_lookup (a, type, sizeof(type), entry, MUTT_EDIT))
243 {
244 if (entry->editcommand)
245 {
246
247 mutt_buffer_strcpy (command, entry->editcommand);
248 mutt_rfc1524_expand_filename (entry->nametemplate,
249 a->filename, newfile);
250 dprint(1, (debugfile, "oldfile: %s\t newfile: %s\n",
251 a->filename, mutt_b2s (newfile)));
252
253 if (safe_symlink (a->filename, mutt_b2s (newfile)) == -1)
254 {
255 if (mutt_yesorno (_("Can't match nametemplate, continue?"), MUTT_YES) != MUTT_YES)
256 goto bailout;
257 mutt_buffer_strcpy (newfile, a->filename);
258 }
259 else
260 unlink_newfile = 1;
261
262 if (mutt_rfc1524_expand_command (a, mutt_b2s (newfile), type, command))
263 {
264 /* For now, editing requires a file, no piping */
265 mutt_error _("Mailcap Edit entry requires %%s");
266 goto bailout;
267 }
268 else
269 {
270 mutt_endwin (NULL);
271 if (mutt_system (mutt_b2s (command)) == -1)
272 {
273 mutt_error (_("Error running \"%s\"!"), mutt_b2s (command));
274 goto bailout;
275 }
276 }
277 }
278 }
279 else if (a->type == TYPETEXT)
280 {
281 /* On text, default to editor */
282 mutt_edit_file (NONULL (Editor), a->filename);
283 }
284 else
285 {
286 mutt_error (_("No mailcap edit entry for %s"),type);
287 rc = 0;
288 goto bailout;
289 }
290
291 rc = 1;
292
293bailout:
294
295 if (unlink_newfile)
296 unlink(mutt_b2s (newfile));
297
298 mutt_buffer_pool_release (&command);
299 mutt_buffer_pool_release (&newfile);
300
301 rfc1524_free_entry (&entry);
302 return rc;
303}
304
305
306void mutt_check_lookup_list (BODY *b, char *type, size_t len)
307{
308 LIST *t = MimeLookupList;
309 int i;
310
311 for (; t; t = t->next)
312 {
313 i = mutt_strlen (t->data) - 1;
314 if ((i > 0 && t->data[i-1] == '/' && t->data[i] == '*' &&
315 ascii_strncasecmp (type, t->data, i) == 0) ||
316 ascii_strcasecmp (type, t->data) == 0)
317 {
318 BODY tmp = {0};
319 int n;
320 if ((n = mutt_lookup_mime_type (&tmp, b->filename)) != TYPEOTHER)
321 {
322 snprintf (type, len, "%s/%s",
323 n == TYPEAUDIO ? "audio" :
324 n == TYPEAPPLICATION ? "application" :
325 n == TYPEIMAGE ? "image" :
326 n == TYPEMESSAGE ? "message" :
327 n == TYPEMODEL ? "model" :
328 n == TYPEMULTIPART ? "multipart" :
329 n == TYPETEXT ? "text" :
330 n == TYPEVIDEO ? "video" : "other",
331 tmp.subtype);
332 dprint(1, (debugfile, "mutt_check_lookup_list: \"%s\" -> %s\n",
333 b->filename, type));
334 }
335 if (tmp.subtype)
336 FREE (&tmp.subtype);
337 if (tmp.xtype)
338 FREE (&tmp.xtype);
339 }
340 }
341}
342
343/* returns -1 on error, 0 or the return code from mutt_do_pager() on success */
344int mutt_view_attachment (FILE *fp, BODY *a, int flag, HEADER *hdr,
345 ATTACH_CONTEXT *actx)
346{
347 BUFFER *tempfile = NULL;
348 BUFFER *pagerfile = NULL;
349 BUFFER *command = NULL;
350 int is_message;
351 int use_mailcap;
352 int use_pipe = 0;
353 int use_pager = 1;
354 char type[STRING];
355 char descrip[STRING];
356 char *fname;
357 rfc1524_entry *entry = NULL;
358 int rc = -1;
359 int unlink_tempfile = 0;
360
361 is_message = mutt_is_message_type(a->type, a->subtype);
362 if (WithCrypto && is_message && a->hdr && (a->hdr->security & ENCRYPT) &&
363 !crypt_valid_passphrase(a->hdr->security))
364 return (rc);
365
366 tempfile = mutt_buffer_pool_get ();
367 pagerfile = mutt_buffer_pool_get ();
368 command = mutt_buffer_pool_get ();
369
370 use_mailcap = (flag == MUTT_MAILCAP ||
371 (flag == MUTT_REGULAR && mutt_needs_mailcap (a)));
372 snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
373
374 if (use_mailcap)
375 {
376 entry = rfc1524_new_entry ();
377 if (!rfc1524_mailcap_lookup (a, type, sizeof(type), entry, 0))
378 {
379 if (flag == MUTT_REGULAR)
380 {
381 /* fallback to view as text */
382 rfc1524_free_entry (&entry);
383 mutt_error _("No matching mailcap entry found. Viewing as text.");
384 flag = MUTT_AS_TEXT;
385 use_mailcap = 0;
386 }
387 else
388 goto return_error;
389 }
390 }
391
392 if (use_mailcap)
393 {
394 if (!entry->command)
395 {
396 mutt_error _("MIME type not defined. Cannot view attachment.");
397 goto return_error;
398 }
399 mutt_buffer_strcpy (command, entry->command);
400
401 if (fp)
402 {
403 fname = safe_strdup (a->filename);
404 mutt_sanitize_filename (fname, 1);
405 }
406 else
407 fname = a->filename;
408
409 mutt_rfc1524_expand_filename (entry->nametemplate, fname,
410 tempfile);
411
412 /* send case: the file is already there; symlink to it */
413 if (fp == NULL)
414 {
415 if (safe_symlink (a->filename, mutt_b2s (tempfile)) == -1)
416 {
417 if (mutt_yesorno (_("Can't match nametemplate, continue?"), MUTT_YES) != MUTT_YES)
418 goto return_error;
419 mutt_buffer_strcpy (tempfile, a->filename);
420 }
421 else
422 unlink_tempfile = 1;
423 }
424 /* recv case: we need to save the attachment to a file */
425 else
426 {
427 FREE (&fname);
428 if (mutt_save_attachment (fp, a, mutt_b2s (tempfile), 0, NULL) == -1)
429 goto return_error;
430 }
431
432 use_pipe = mutt_rfc1524_expand_command (a, mutt_b2s (tempfile), type,
433 command);
434 use_pager = entry->copiousoutput;
435 }
436
437 if (use_pager)
438 {
439 if (fp && !use_mailcap && a->filename)
440 {
441 /* recv case */
442 mutt_buffer_strcpy (pagerfile, a->filename);
443 mutt_adv_mktemp (pagerfile);
444 }
445 else
446 mutt_buffer_mktemp (pagerfile);
447 }
448
449 if (use_mailcap)
450 {
451 pid_t thepid = 0;
452 int tempfd = -1, pagerfd = -1;
453
454 if (!use_pager)
455 mutt_endwin (NULL);
456
457 if (use_pager || use_pipe)
458 {
459 if (use_pager && ((pagerfd = safe_open (mutt_b2s (pagerfile), O_CREAT | O_EXCL | O_WRONLY)) == -1))
460 {
461 mutt_perror ("open");
462 goto return_error;
463 }
464 if (use_pipe && ((tempfd = open (mutt_b2s (tempfile), 0)) == -1))
465 {
466 if (pagerfd != -1)
467 close(pagerfd);
468 mutt_perror ("open");
469 goto return_error;
470 }
471
472 if ((thepid = mutt_create_filter_fd (mutt_b2s (command), NULL, NULL, NULL,
473 use_pipe ? tempfd : -1, use_pager ? pagerfd : -1, -1)) == -1)
474 {
475 if (pagerfd != -1)
476 close(pagerfd);
477
478 if (tempfd != -1)
479 close(tempfd);
480
481 mutt_error _("Cannot create filter");
482 goto return_error;
483 }
484
485 if (use_pager)
486 {
487 if (a->description)
488 snprintf (descrip, sizeof (descrip),
489 _("---Command: %-20.20s Description: %s"),
490 mutt_b2s (command), a->description);
491 else
492 snprintf (descrip, sizeof (descrip),
493 _("---Command: %-30.30s Attachment: %s"), mutt_b2s (command), type);
494
495 mutt_wait_filter (thepid);
496 }
497 else
498 {
499 if (mutt_wait_interactive_filter (thepid) ||
500 (entry->needsterminal && option (OPTWAITKEY)))
501 mutt_any_key_to_continue (NULL);
502 }
503
504 if (tempfd != -1)
505 close (tempfd);
506 if (pagerfd != -1)
507 close (pagerfd);
508 }
509 else
510 {
511 /* interactive command */
512 if (mutt_system (mutt_b2s (command)) ||
513 (entry->needsterminal && option (OPTWAITKEY)))
514 mutt_any_key_to_continue (NULL);
515 }
516 }
517 else
518 {
519 /* Don't use mailcap; the attachment is viewed in the pager */
520
521 if (flag == MUTT_AS_TEXT)
522 {
523 /* just let me see the raw data */
524 if (fp)
525 {
526 /* Viewing from a received message.
527 *
528 * Don't use mutt_save_attachment() because we want to perform charset
529 * conversion since this will be displayed by the internal pager.
530 */
531 STATE decode_state;
532
533 memset(&decode_state, 0, sizeof(decode_state));
534 decode_state.fpout = safe_fopen(mutt_b2s (pagerfile), "w");
535 if (!decode_state.fpout)
536 {
537 dprint(1, (debugfile, "mutt_view_attachment:%d safe_fopen(%s) errno=%d %s\n",
538 __LINE__, mutt_b2s (pagerfile), errno, strerror(errno)));
539 mutt_perror(mutt_b2s (pagerfile));
540 mutt_sleep(1);
541 goto return_error;
542 }
543 decode_state.fpin = fp;
544 decode_state.flags = MUTT_CHARCONV;
545 mutt_decode_attachment(a, &decode_state);
546 if (fclose(decode_state.fpout) == EOF)
547 dprint(1, (debugfile, "mutt_view_attachment:%d fclose errno=%d %s\n",
548 __LINE__, mutt_b2s (pagerfile), errno, strerror(errno)));
549 }
550 else
551 {
552 /* in compose mode, just copy the file. we can't use
553 * mutt_decode_attachment() since it assumes the content-encoding has
554 * already been applied
555 */
556 if (mutt_save_attachment(fp, a, mutt_b2s (pagerfile), 0, NULL))
557 goto return_error;
558 }
559 }
560 else
561 {
562 /* Use built-in handler */
563 set_option (OPTVIEWATTACH); /* disable the "use 'v' to view this part"
564 * message in case of error */
565 if (mutt_decode_save_attachment (fp, a, mutt_b2s (pagerfile), MUTT_DISPLAY, 0))
566 {
567 unset_option (OPTVIEWATTACH);
568 goto return_error;
569 }
570 unset_option (OPTVIEWATTACH);
571 }
572
573 if (a->description)
574 strfcpy (descrip, a->description, sizeof (descrip));
575 else if (a->filename)
576 snprintf (descrip, sizeof (descrip), _("---Attachment: %s: %s"),
577 a->filename, type);
578 else
579 snprintf (descrip, sizeof (descrip), _("---Attachment: %s"), type);
580 }
581
582 /* We only reach this point if there have been no errors */
583
584 if (use_pager)
585 {
586 pager_t info;
587
588 memset (&info, 0, sizeof (info));
589 info.fp = fp;
590 info.bdy = a;
591 info.ctx = Context;
592 info.actx = actx;
593 info.hdr = hdr;
594
595 rc = mutt_do_pager (descrip, mutt_b2s (pagerfile),
596 MUTT_PAGER_ATTACHMENT | (is_message ? MUTT_PAGER_MESSAGE : 0), &info);
597 mutt_buffer_clear (pagerfile);
598 }
599 else
600 rc = 0;
601
602return_error:
603
604 rfc1524_free_entry (&entry);
605 if (fp && (mutt_b2s (tempfile))[0])
606 mutt_unlink (mutt_b2s (tempfile));
607 else if (unlink_tempfile)
608 unlink (mutt_b2s (tempfile));
609
610 if ((mutt_b2s (pagerfile))[0])
611 mutt_unlink (mutt_b2s (pagerfile));
612
613 mutt_buffer_pool_release (&tempfile);
614 mutt_buffer_pool_release (&pagerfile);
615 mutt_buffer_pool_release (&command);
616
617 return rc;
618}
619
620/* returns 1 on success, 0 on error */
621int mutt_pipe_attachment (FILE *fp, BODY *b, const char *path, const char *outfile)
622{
623 pid_t thepid;
624 int out = -1;
625 int rv = 0;
626
627 if (outfile && *outfile)
628 if ((out = safe_open (outfile, O_CREAT | O_EXCL | O_WRONLY)) < 0)
629 {
630 mutt_perror ("open");
631 return 0;
632 }
633
634 mutt_endwin (NULL);
635
636 if (fp)
637 {
638 /* recv case */
639
640 STATE s;
641
642 memset (&s, 0, sizeof (STATE));
643 /* perform charset conversion on text attachments when piping */
644 s.flags = MUTT_CHARCONV;
645
646 if (outfile && *outfile)
647 thepid = mutt_create_filter_fd (path, &s.fpout, NULL, NULL, -1, out, -1);
648 else
649 thepid = mutt_create_filter (path, &s.fpout, NULL, NULL);
650
651 if (thepid < 0)
652 {
653 mutt_perror _("Can't create filter");
654 goto bail;
655 }
656
657 s.fpin = fp;
658 mutt_decode_attachment (b, &s);
659 safe_fclose (&s.fpout);
660 }
661 else
662 {
663 /* send case */
664
665 FILE *ifp, *ofp;
666
667 if ((ifp = fopen (b->filename, "r")) == NULL)
668 {
669 mutt_perror ("fopen");
670 if (outfile && *outfile)
671 {
672 close (out);
673 unlink (outfile);
674 }
675 return 0;
676 }
677
678 if (outfile && *outfile)
679 thepid = mutt_create_filter_fd (path, &ofp, NULL, NULL, -1, out, -1);
680 else
681 thepid = mutt_create_filter (path, &ofp, NULL, NULL);
682
683 if (thepid < 0)
684 {
685 mutt_perror _("Can't create filter");
686 safe_fclose (&ifp);
687 goto bail;
688 }
689
690 mutt_copy_stream (ifp, ofp);
691 safe_fclose (&ofp);
692 safe_fclose (&ifp);
693 }
694
695 rv = 1;
696
697bail:
698
699 if (outfile && *outfile)
700 close (out);
701
702 /*
703 * check for error exit from child process
704 */
705 if (mutt_wait_filter (thepid) != 0)
706 rv = 0;
707
708 if (rv == 0 || option (OPTWAITKEY))
709 mutt_any_key_to_continue (NULL);
710 return rv;
711}
712
713static FILE *
714mutt_save_attachment_open (const char *path, int flags)
715{
716 if (flags == MUTT_SAVE_APPEND)
717 return fopen (path, "a");
718 if (flags == MUTT_SAVE_OVERWRITE)
719 return fopen (path, "w"); /* __FOPEN_CHECKED__ */
720
721 return safe_fopen (path, "w");
722}
723
724/* returns 0 on success, -1 on error */
725int mutt_save_attachment (FILE *fp, BODY *m, const char *path, int flags, HEADER *hdr)
726{
727 if (fp)
728 {
729
730 /* recv mode */
731
732 if (hdr &&
733 m->hdr &&
734 m->encoding != ENCBASE64 &&
735 m->encoding != ENCQUOTEDPRINTABLE &&
736 mutt_is_message_type(m->type, m->subtype))
737 {
738 /* message type attachments are written to mail folders. */
739
740 char buf[HUGE_STRING];
741 HEADER *hn;
742 CONTEXT ctx;
743 MESSAGE *msg;
744 int chflags = 0;
745 int r = -1;
746
747 hn = m->hdr;
748 hn->msgno = hdr->msgno; /* required for MH/maildir */
749 hn->read = 1;
750
751 fseeko (fp, m->offset, 0);
752 if (fgets (buf, sizeof (buf), fp) == NULL)
753 return -1;
754 if (mx_open_mailbox(path, MUTT_APPEND | MUTT_QUIET, &ctx) == NULL)
755 return -1;
756 if ((msg = mx_open_new_message (&ctx, hn, is_from (buf, NULL, 0, NULL) ? 0 : MUTT_ADD_FROM)) == NULL)
757 {
758 mx_close_mailbox(&ctx, NULL);
759 return -1;
760 }
761 if (ctx.magic == MUTT_MBOX || ctx.magic == MUTT_MMDF)
762 chflags = CH_FROM | CH_UPDATE_LEN;
763 chflags |= (ctx.magic == MUTT_MAILDIR ? CH_NOSTATUS : CH_UPDATE);
764 if (_mutt_copy_message (msg->fp, fp, hn, hn->content, 0, chflags) == 0
765 && mx_commit_message (msg, &ctx) == 0)
766 r = 0;
767 else
768 r = -1;
769
770 mx_close_message (&ctx, &msg);
771 mx_close_mailbox (&ctx, NULL);
772 return r;
773 }
774 else
775 {
776 /* In recv mode, extract from folder and decode */
777
778 STATE s;
779
780 memset (&s, 0, sizeof (s));
781 if ((s.fpout = mutt_save_attachment_open (path, flags)) == NULL)
782 {
783 mutt_perror ("fopen");
784 mutt_sleep (2);
785 return (-1);
786 }
787 fseeko ((s.fpin = fp), m->offset, 0);
788 mutt_decode_attachment (m, &s);
789
790 if (fclose (s.fpout) != 0)
791 {
792 mutt_perror ("fclose");
793 mutt_sleep (2);
794 return (-1);
795 }
796 }
797 }
798 else
799 {
800 /* In send mode, just copy file */
801
802 FILE *ofp, *nfp;
803
804 if ((ofp = fopen (m->filename, "r")) == NULL)
805 {
806 mutt_perror ("fopen");
807 return (-1);
808 }
809
810 if ((nfp = mutt_save_attachment_open (path, flags)) == NULL)
811 {
812 mutt_perror ("fopen");
813 safe_fclose (&ofp);
814 return (-1);
815 }
816
817 if (mutt_copy_stream (ofp, nfp) == -1)
818 {
819 mutt_error _("Write fault!");
820 safe_fclose (&ofp);
821 safe_fclose (&nfp);
822 return (-1);
823 }
824 safe_fclose (&ofp);
825 safe_fclose (&nfp);
826 }
827
828 return 0;
829}
830
831/* returns 0 on success, -1 on error */
832int mutt_decode_save_attachment (FILE *fp, BODY *m, const char *path,
833 int displaying, int flags)
834{
835 STATE s;
836 unsigned int saved_encoding = 0;
837 BODY *saved_parts = NULL;
838 HEADER *saved_hdr = NULL;
839
840 memset (&s, 0, sizeof (s));
841 s.flags = displaying;
842
843 if (flags == MUTT_SAVE_APPEND)
844 s.fpout = fopen (path, "a");
845 else if (flags == MUTT_SAVE_OVERWRITE)
846 s.fpout = fopen (path, "w"); /* __FOPEN_CHECKED__ */
847 else
848 s.fpout = safe_fopen (path, "w");
849
850 if (s.fpout == NULL)
851 {
852 mutt_perror ("fopen");
853 return (-1);
854 }
855
856 if (fp == NULL)
857 {
858 /* When called from the compose menu, the attachment isn't parsed,
859 * so we need to do it here. */
860 struct stat st;
861
862 if (stat (m->filename, &st) == -1)
863 {
864 mutt_perror ("stat");
865 safe_fclose (&s.fpout);
866 return (-1);
867 }
868
869 if ((s.fpin = fopen (m->filename, "r")) == NULL)
870 {
871 mutt_perror ("fopen");
872 return (-1);
873 }
874
875 saved_encoding = m->encoding;
876 if (!is_multipart (m))
877 m->encoding = ENC8BIT;
878
879 m->length = st.st_size;
880 m->offset = 0;
881 saved_parts = m->parts;
882 saved_hdr = m->hdr;
883 mutt_parse_part (s.fpin, m);
884
885 if (m->noconv || is_multipart (m))
886 s.flags |= MUTT_CHARCONV;
887 }
888 else
889 {
890 s.fpin = fp;
891 s.flags |= MUTT_CHARCONV;
892 }
893
894 mutt_body_handler (m, &s);
895
896 safe_fclose (&s.fpout);
897 if (fp == NULL)
898 {
899 m->length = 0;
900 m->encoding = saved_encoding;
901 if (saved_parts)
902 {
903 mutt_free_header (&m->hdr);
904 m->parts = saved_parts;
905 m->hdr = saved_hdr;
906 }
907 safe_fclose (&s.fpin);
908 }
909
910 return (0);
911}
912
913/* Ok, the difference between send and receive:
914 * recv: BODY->filename is a suggested name, and Context|HEADER points
915 * to the attachment in mailbox which is encoded
916 * send: BODY->filename points to the un-encoded file which contains the
917 * attachment
918 */
919
920int mutt_print_attachment (FILE *fp, BODY *a)
921{
922 BUFFER *newfile = mutt_buffer_pool_get ();
923 BUFFER *command = mutt_buffer_pool_get ();
924 char type[STRING];
925 pid_t thepid;
926 FILE *ifp, *fpout;
927 short unlink_newfile = 0;
928 int rc = 0;
929
930 snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype);
931
932 if (rfc1524_mailcap_lookup (a, type, sizeof(type), NULL, MUTT_PRINT))
933 {
934 rfc1524_entry *entry = NULL;
935 int piped = FALSE;
936
937 dprint (2, (debugfile, "Using mailcap...\n"));
938
939 entry = rfc1524_new_entry ();
940 rfc1524_mailcap_lookup (a, type, sizeof(type), entry, MUTT_PRINT);
941 mutt_rfc1524_expand_filename (entry->nametemplate, a->filename,
942 newfile);
943
944 /* send mode: symlink from existing file to the newfile */
945 if (!fp)
946 {
947 if (safe_symlink (a->filename, mutt_b2s (newfile)) == -1)
948 {
949 if (mutt_yesorno (_("Can't match nametemplate, continue?"), MUTT_YES) != MUTT_YES)
950 goto mailcap_cleanup;
951 mutt_buffer_strcpy (newfile, a->filename);
952 }
953 else
954 unlink_newfile = 1;
955 }
956 /* in recv mode, save file to newfile first */
957 else
958 {
959 if (mutt_save_attachment (fp, a, mutt_b2s (newfile), 0, NULL) == -1)
960 goto mailcap_cleanup;
961 }
962
963 mutt_buffer_strcpy (command, entry->printcommand);
964 piped = mutt_rfc1524_expand_command (a, mutt_b2s (newfile), type, command);
965
966 mutt_endwin (NULL);
967
968 /* interactive program */
969 if (piped)
970 {
971 if ((ifp = fopen (mutt_b2s (newfile), "r")) == NULL)
972 {
973 mutt_perror ("fopen");
974 goto mailcap_cleanup;
975 }
976
977 if ((thepid = mutt_create_filter (mutt_b2s (command), &fpout, NULL, NULL)) < 0)
978 {
979 mutt_perror _("Can't create filter");
980 safe_fclose (&ifp);
981 goto mailcap_cleanup;
982 }
983 mutt_copy_stream (ifp, fpout);
984 safe_fclose (&fpout);
985 safe_fclose (&ifp);
986 if (mutt_wait_filter (thepid) || option (OPTWAITKEY))
987 mutt_any_key_to_continue (NULL);
988 }
989 else
990 {
991 if (mutt_system (mutt_b2s (command)) || option (OPTWAITKEY))
992 mutt_any_key_to_continue (NULL);
993 }
994
995 rc = 1;
996
997 mailcap_cleanup:
998 if (fp)
999 mutt_unlink (mutt_b2s (newfile));
1000 else if (unlink_newfile)
1001 unlink(mutt_b2s (newfile));
1002
1003 rfc1524_free_entry (&entry);
1004 goto out;
1005 }
1006
1007 if (!ascii_strcasecmp ("text/plain", type) ||
1008 !ascii_strcasecmp ("application/postscript", type))
1009 {
1010 rc = mutt_pipe_attachment (fp, a, NONULL(PrintCmd), NULL);
1011 goto out;
1012 }
1013 else if (mutt_can_decode (a))
1014 {
1015 /* decode and print */
1016
1017 ifp = NULL;
1018 fpout = NULL;
1019
1020 mutt_buffer_mktemp (newfile);
1021 if (mutt_decode_save_attachment (fp, a, mutt_b2s (newfile), MUTT_PRINTING, 0) == 0)
1022 {
1023
1024 dprint (2, (debugfile, "successfully decoded %s type attachment to %s\n",
1025 type, mutt_b2s (newfile)));
1026
1027 if ((ifp = fopen (mutt_b2s (newfile), "r")) == NULL)
1028 {
1029 mutt_perror ("fopen");
1030 goto decode_cleanup;
1031 }
1032
1033 dprint (2, (debugfile, "successfully opened %s read-only\n", mutt_b2s (newfile)));
1034
1035 mutt_endwin (NULL);
1036 if ((thepid = mutt_create_filter (NONULL(PrintCmd), &fpout, NULL, NULL)) < 0)
1037 {
1038 mutt_perror _("Can't create filter");
1039 goto decode_cleanup;
1040 }
1041
1042 dprint (2, (debugfile, "Filter created.\n"));
1043
1044 mutt_copy_stream (ifp, fpout);
1045
1046 safe_fclose (&fpout);
1047 safe_fclose (&ifp);
1048
1049 if (mutt_wait_filter (thepid) != 0 || option (OPTWAITKEY))
1050 mutt_any_key_to_continue (NULL);
1051 rc = 1;
1052 }
1053 decode_cleanup:
1054 safe_fclose (&ifp);
1055 safe_fclose (&fpout);
1056 mutt_unlink (mutt_b2s (newfile));
1057 }
1058 else
1059 {
1060 mutt_error _("I don't know how to print that!");
1061 rc = 0;
1062 }
1063
1064out:
1065 mutt_buffer_pool_release (&newfile);
1066 mutt_buffer_pool_release (&command);
1067
1068 return rc;
1069}
1070
1071void mutt_actx_add_attach (ATTACH_CONTEXT *actx, ATTACHPTR *attach)
1072{
1073 int i;
1074
1075 if (actx->idxlen == actx->idxmax)
1076 {
1077 actx->idxmax += 5;
1078 safe_realloc (&actx->idx, sizeof (ATTACHPTR *) * actx->idxmax);
1079 safe_realloc (&actx->v2r, sizeof (short) * actx->idxmax);
1080 for (i = actx->idxlen; i < actx->idxmax; i++)
1081 actx->idx[i] = NULL;
1082 }
1083
1084 actx->idx[actx->idxlen++] = attach;
1085}
1086
1087void mutt_actx_add_fp (ATTACH_CONTEXT *actx, FILE *new_fp)
1088{
1089 int i;
1090
1091 if (actx->fp_len == actx->fp_max)
1092 {
1093 actx->fp_max += 5;
1094 safe_realloc (&actx->fp_idx, sizeof (FILE *) * actx->fp_max);
1095 for (i = actx->fp_len; i < actx->fp_max; i++)
1096 actx->fp_idx[i] = NULL;
1097 }
1098
1099 actx->fp_idx[actx->fp_len++] = new_fp;
1100}
1101
1102void mutt_actx_add_body (ATTACH_CONTEXT *actx, BODY *new_body)
1103{
1104 int i;
1105
1106 if (actx->body_len == actx->body_max)
1107 {
1108 actx->body_max += 5;
1109 safe_realloc (&actx->body_idx, sizeof (BODY *) * actx->body_max);
1110 for (i = actx->body_len; i < actx->body_max; i++)
1111 actx->body_idx[i] = NULL;
1112 }
1113
1114 actx->body_idx[actx->body_len++] = new_body;
1115}
1116
1117void mutt_actx_free_entries (ATTACH_CONTEXT *actx)
1118{
1119 int i;
1120
1121 for (i = 0; i < actx->idxlen; i++)
1122 {
1123 if (actx->idx[i]->content)
1124 actx->idx[i]->content->aptr = NULL;
1125 FREE (&actx->idx[i]->tree);
1126 FREE (&actx->idx[i]);
1127 }
1128 actx->idxlen = 0;
1129 actx->vcount = 0;
1130
1131 for (i = 0; i < actx->fp_len; i++)
1132 safe_fclose (&actx->fp_idx[i]);
1133 actx->fp_len = 0;
1134
1135 for (i = 0; i < actx->body_len; i++)
1136 mutt_free_body (&actx->body_idx[i]);
1137 actx->body_len = 0;
1138}
1139
1140void mutt_free_attach_context (ATTACH_CONTEXT **pactx)
1141{
1142 ATTACH_CONTEXT *actx;
1143
1144 if (!pactx || !*pactx)
1145 return;
1146
1147 actx = *pactx;
1148 mutt_actx_free_entries (actx);
1149 FREE (&actx->idx);
1150 FREE (&actx->v2r);
1151 FREE (&actx->fp_idx);
1152 FREE (&actx->body_idx);
1153 FREE (pactx); /* __FREE_CHECKED__ */
1154}