NeoMutt  2022-04-29-145-g9b6a0e
Teaching an old dog new tricks
DOXYGEN
mutt_attach.c File Reference

Handling of email attachments. More...

#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "gui/lib.h"
#include "mutt_attach.h"
#include "lib.h"
#include "ncrypt/lib.h"
#include "pager/lib.h"
#include "question/lib.h"
#include "send/lib.h"
#include "cid.h"
#include "copy.h"
#include "handler.h"
#include "mailcap.h"
#include "mutt_globals.h"
#include "muttlib.h"
#include "mx.h"
#include "protos.h"
#include "rfc3676.h"
#include "imap/lib.h"
+ Include dependency graph for mutt_attach.c:

Go to the source code of this file.

Functions

int mutt_get_tmp_attachment (struct Body *a)
 Get a temporary copy of an attachment. More...
 
int mutt_compose_attachment (struct Body *a)
 Create an attachment. More...
 
int mutt_edit_attachment (struct Body *a)
 Edit an attachment. More...
 
void mutt_check_lookup_list (struct Body *b, char *type, size_t len)
 Update the mime type. More...
 
static int wait_interactive_filter (pid_t pid)
 Wait after an interactive filter. More...
 
int mutt_view_attachment (FILE *fp, struct Body *a, enum ViewAttachMode mode, struct Email *e, struct AttachCtx *actx, struct MuttWindow *win)
 View an attachment. More...
 
int mutt_pipe_attachment (FILE *fp, struct Body *b, const char *path, char *outfile)
 Pipe an attachment to a command. More...
 
static FILE * save_attachment_open (const char *path, enum SaveAttach opt)
 Open a file to write an attachment to. More...
 
int mutt_save_attachment (FILE *fp, struct Body *m, const char *path, enum SaveAttach opt, struct Email *e)
 Save an attachment. More...
 
int mutt_decode_save_attachment (FILE *fp, struct Body *m, const char *path, int displaying, enum SaveAttach opt)
 Decode, then save an attachment. More...
 
int mutt_print_attachment (FILE *fp, struct Body *a)
 Print out an attachment. More...
 
void mutt_add_temp_attachment (const char *filename)
 Add file to list of temporary attachments. More...
 
void mutt_unlink_temp_attachments (void)
 Delete all temporary attachments. More...
 

Detailed Description

Handling of email attachments.

Authors
  • Michael R. Elkins
  • Thomas Roessler

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file mutt_attach.c.

Function Documentation

◆ mutt_get_tmp_attachment()

int mutt_get_tmp_attachment ( struct Body a)

Get a temporary copy of an attachment.

Parameters
aAttachment to copy
Return values
0Success
-1Error

Definition at line 69 of file mutt_attach.c.

70 {
71  char type[256];
72 
73  if (a->unlink)
74  return 0;
75 
76  struct Buffer *tmpfile = mutt_buffer_pool_get();
77  struct MailcapEntry *entry = mailcap_entry_new();
78  snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
79  mailcap_lookup(a, type, sizeof(type), entry, MUTT_MC_NO_FLAGS);
80  mailcap_expand_filename(entry->nametemplate, a->filename, tmpfile);
81 
82  mailcap_entry_free(&entry);
83 
84  FILE *fp_in = NULL, *fp_out = NULL;
85  if ((fp_in = fopen(a->filename, "r")) &&
86  (fp_out = mutt_file_fopen(mutt_buffer_string(tmpfile), "w")))
87  {
88  mutt_file_copy_stream(fp_in, fp_out);
90  a->unlink = true;
91 
92  struct stat st = { 0 };
93  if ((fstat(fileno(fp_in), &st) == 0) && (a->stamp >= st.st_mtime))
94  {
96  }
97  }
98  else
99  mutt_perror(fp_in ? mutt_buffer_string(tmpfile) : a->filename);
100 
101  mutt_file_fclose(&fp_in);
102  mutt_file_fclose(&fp_out);
103 
104  mutt_buffer_pool_release(&tmpfile);
105 
106  return a->unlink ? 0 : -1;
107 }
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:260
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:618
#define mutt_perror(...)
Definition: logging.h:88
struct MailcapEntry * mailcap_entry_new(void)
Allocate memory for a new rfc1524 entry.
Definition: mailcap.c:435
bool mailcap_lookup(struct Body *a, char *type, size_t typelen, struct MailcapEntry *entry, enum MailcapLookup opt)
Find given type in the list of mailcap files.
Definition: mailcap.c:473
void mailcap_entry_free(struct MailcapEntry **ptr)
Deallocate an struct MailcapEntry.
Definition: mailcap.c:444
void mailcap_expand_filename(const char *nametemplate, const char *oldfile, struct Buffer *newfile)
Expand a new filename from a template or existing filename.
Definition: mailcap.c:542
@ MUTT_MC_NO_FLAGS
No flags set.
Definition: mailcap.h:56
#define TYPE(body)
Definition: mime.h:89
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
void mutt_stamp_attachment(struct Body *a)
Timestamp an Attachment.
Definition: sendlib.c:401
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition: body.h:67
time_t stamp
Time stamp of last encoding update.
Definition: body.h:76
char * subtype
content-type subtype
Definition: body.h:60
char * filename
When sending a message, this is the file to which this structure refers.
Definition: body.h:58
String manipulation buffer.
Definition: buffer.h:34
A mailcap entry.
Definition: mailcap.h:36
char * nametemplate
Definition: mailcap.h:43
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_compose_attachment()

int mutt_compose_attachment ( struct Body a)

Create an attachment.

Parameters
aBody of email
Return values
1Require full screen redraw
0Otherwise

Definition at line 115 of file mutt_attach.c.

116 {
117  char type[256];
118  struct MailcapEntry *entry = mailcap_entry_new();
119  bool unlink_newfile = false;
120  int rc = 0;
121  struct Buffer *cmd = mutt_buffer_pool_get();
122  struct Buffer *newfile = mutt_buffer_pool_get();
123  struct Buffer *tmpfile = mutt_buffer_pool_get();
124 
125  snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
126  if (mailcap_lookup(a, type, sizeof(type), entry, MUTT_MC_COMPOSE))
127  {
128  if (entry->composecommand || entry->composetypecommand)
129  {
130  if (entry->composetypecommand)
132  else
133  mutt_buffer_strcpy(cmd, entry->composecommand);
134 
135  mailcap_expand_filename(entry->nametemplate, a->filename, newfile);
136  mutt_debug(LL_DEBUG1, "oldfile: %s newfile: %s\n", a->filename,
137  mutt_buffer_string(newfile));
138  if (mutt_file_symlink(a->filename, mutt_buffer_string(newfile)) == -1)
139  {
140  if (mutt_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
141  goto bailout;
142  mutt_buffer_strcpy(newfile, a->filename);
143  }
144  else
145  unlink_newfile = true;
146 
147  if (mailcap_expand_command(a, mutt_buffer_string(newfile), type, cmd))
148  {
149  /* For now, editing requires a file, no piping */
150  mutt_error(_("Mailcap compose entry requires %%s"));
151  }
152  else
153  {
154  int r;
155 
156  mutt_endwin();
158  if (r == -1)
159  mutt_error(_("Error running \"%s\""), mutt_buffer_string(cmd));
160 
161  if ((r != -1) && entry->composetypecommand)
162  {
163  struct Body *b = NULL;
164 
165  FILE *fp = mutt_file_fopen(a->filename, "r");
166  if (!fp)
167  {
168  mutt_perror(_("Failure to open file to parse headers"));
169  goto bailout;
170  }
171 
172  b = mutt_read_mime_header(fp, 0);
173  if (b)
174  {
175  if (!TAILQ_EMPTY(&b->parameter))
176  {
178  a->parameter = b->parameter;
179  TAILQ_INIT(&b->parameter);
180  }
181  if (b->description)
182  {
183  FREE(&a->description);
184  a->description = b->description;
185  b->description = NULL;
186  }
187  if (b->form_name)
188  {
189  FREE(&a->form_name);
190  a->form_name = b->form_name;
191  b->form_name = NULL;
192  }
193 
194  /* Remove headers by copying out data to another file, then
195  * copying the file back */
196  const LOFF_T offset = b->offset;
197  mutt_body_free(&b);
198  if (!mutt_file_seek(fp, offset, SEEK_SET))
199  {
200  goto bailout;
201  }
202 
203  mutt_buffer_mktemp(tmpfile);
204  FILE *fp_tmp = mutt_file_fopen(mutt_buffer_string(tmpfile), "w");
205  if (!fp_tmp)
206  {
207  mutt_perror(_("Failure to open file to strip headers"));
208  mutt_file_fclose(&fp);
209  goto bailout;
210  }
211  mutt_file_copy_stream(fp, fp_tmp);
212  mutt_file_fclose(&fp);
213  mutt_file_fclose(&fp_tmp);
215  if (mutt_file_rename(mutt_buffer_string(tmpfile), a->filename) != 0)
216  {
217  mutt_perror(_("Failure to rename file"));
218  goto bailout;
219  }
220  }
221  }
222  }
223  }
224  }
225  else
226  {
227  mutt_message(_("No mailcap compose entry for %s, creating empty file"), type);
228  rc = 1;
229  goto bailout;
230  }
231 
232  rc = 1;
233 
234 bailout:
235 
236  if (unlink_newfile)
237  unlink(mutt_buffer_string(newfile));
238 
240  mutt_buffer_pool_release(&newfile);
241  mutt_buffer_pool_release(&tmpfile);
242 
243  mailcap_entry_free(&entry);
244  return rc;
245 }
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:310
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:355
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:690
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition: file.c:1403
int mutt_file_symlink(const char *oldpath, const char *newpath)
Create a symlink.
Definition: file.c:288
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:194
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
int mailcap_expand_command(struct Body *a, const char *filename, const char *type, struct Buffer *command)
Expand expandos in a command.
Definition: mailcap.c:67
@ MUTT_MC_COMPOSE
Mailcap compose field.
Definition: mailcap.h:58
#define FREE(x)
Definition: memory.h:43
#define _(a)
Definition: message.h:28
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:74
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition: parameter.c:61
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition: parse.c:1318
int mutt_system(const char *cmd)
Run an external command.
Definition: system.c:51
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:194
#define TAILQ_INIT(head)
Definition: queue.h:765
#define TAILQ_EMPTY(head)
Definition: queue.h:721
The body of an email.
Definition: body.h:36
LOFF_T offset
offset where the actual data begins
Definition: body.h:52
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:62
char * description
content-description
Definition: body.h:55
char * form_name
Content-Disposition form-data name param.
Definition: body.h:59
unsigned int type
content-type primary type, ContentType
Definition: body.h:40
char * composecommand
Definition: mailcap.h:39
char * composetypecommand
Definition: mailcap.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_edit_attachment()

int mutt_edit_attachment ( struct Body a)

Edit an attachment.

Parameters
aEmail containing attachment
Return values
1Editor found
0Editor not found

Currently, this only works for send mode, as it assumes that the Body->filename actually contains the information. I'm not sure we want to deal with editing attachments we've already received, so this should be ok.

Returning 0 is useful to tell the calling menu to redraw

Definition at line 260 of file mutt_attach.c.

261 {
262  char type[256];
263  struct MailcapEntry *entry = mailcap_entry_new();
264  bool unlink_newfile = false;
265  int rc = 0;
266  struct Buffer *cmd = mutt_buffer_pool_get();
267  struct Buffer *newfile = mutt_buffer_pool_get();
268 
269  snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
270  if (mailcap_lookup(a, type, sizeof(type), entry, MUTT_MC_EDIT))
271  {
272  if (entry->editcommand)
273  {
274  mutt_buffer_strcpy(cmd, entry->editcommand);
275  mailcap_expand_filename(entry->nametemplate, a->filename, newfile);
276  mutt_debug(LL_DEBUG1, "oldfile: %s newfile: %s\n", a->filename,
277  mutt_buffer_string(newfile));
278  if (mutt_file_symlink(a->filename, mutt_buffer_string(newfile)) == -1)
279  {
280  if (mutt_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
281  goto bailout;
282  mutt_buffer_strcpy(newfile, a->filename);
283  }
284  else
285  unlink_newfile = true;
286 
287  if (mailcap_expand_command(a, mutt_buffer_string(newfile), type, cmd))
288  {
289  /* For now, editing requires a file, no piping */
290  mutt_error(_("Mailcap Edit entry requires %%s"));
291  goto bailout;
292  }
293  else
294  {
295  mutt_endwin();
296  if (mutt_system(mutt_buffer_string(cmd)) == -1)
297  {
298  mutt_error(_("Error running \"%s\""), mutt_buffer_string(cmd));
299  goto bailout;
300  }
301  }
302  }
303  }
304  else if (a->type == TYPE_TEXT)
305  {
306  /* On text, default to editor */
307  const char *const c_editor = cs_subset_string(NeoMutt->sub, "editor");
308  mutt_edit_file(NONULL(c_editor), a->filename);
309  }
310  else
311  {
312  mutt_error(_("No mailcap edit entry for %s"), type);
313  rc = 0;
314  goto bailout;
315  }
316 
317  rc = 1;
318 
319 bailout:
320 
321  if (unlink_newfile)
322  unlink(mutt_buffer_string(newfile));
323 
325  mutt_buffer_pool_release(&newfile);
326 
327  mailcap_entry_free(&entry);
328  return rc;
329 }
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:313
@ MUTT_MC_EDIT
Mailcap edit field.
Definition: mailcap.h:57
@ TYPE_TEXT
Type: 'text/*'.
Definition: mime.h:38
#define NONULL(x)
Definition: string2.h:37
char * editcommand
Definition: mailcap.h:41
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_check_lookup_list()

void mutt_check_lookup_list ( struct Body b,
char *  type,
size_t  len 
)

Update the mime type.

Parameters
bMessage attachment body
typeBuffer with mime type of attachment in "type/subtype" format
lenBuffer length

Definition at line 337 of file mutt_attach.c.

338 {
339  struct ListNode *np = NULL;
340  STAILQ_FOREACH(np, &MimeLookupList, entries)
341  {
342  const int i = mutt_str_len(np->data) - 1;
343  if (((i > 0) && (np->data[i - 1] == '/') && (np->data[i] == '*') &&
344  mutt_istrn_equal(type, np->data, i)) ||
345  mutt_istr_equal(type, np->data))
346  {
347  struct Body tmp = { 0 };
348  enum ContentType n;
349  if ((n = mutt_lookup_mime_type(&tmp, b->filename)) != TYPE_OTHER ||
350  (n = mutt_lookup_mime_type(&tmp, b->description)) != TYPE_OTHER)
351  {
352  snprintf(type, len, "%s/%s",
353  (n == TYPE_AUDIO) ? "audio" :
354  (n == TYPE_APPLICATION) ? "application" :
355  (n == TYPE_IMAGE) ? "image" :
356  (n == TYPE_MESSAGE) ? "message" :
357  (n == TYPE_MODEL) ? "model" :
358  (n == TYPE_MULTIPART) ? "multipart" :
359  (n == TYPE_TEXT) ? "text" :
360  (n == TYPE_VIDEO) ? "video" :
361  "other",
362  tmp.subtype);
363  mutt_debug(LL_DEBUG1, "\"%s\" -> %s\n", b->filename, type);
364  }
365  FREE(&tmp.subtype);
366  FREE(&tmp.xtype);
367  }
368  }
369 }
ContentType
Content-Type.
Definition: mime.h:30
@ TYPE_AUDIO
Type: 'audio/*'.
Definition: mime.h:32
@ TYPE_IMAGE
Type: 'image/*'.
Definition: mime.h:34
@ TYPE_OTHER
Unknown Content-Type.
Definition: mime.h:31
@ TYPE_MESSAGE
Type: 'message/*'.
Definition: mime.h:35
@ TYPE_MODEL
Type: 'model/*'.
Definition: mime.h:36
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition: mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition: mime.h:33
@ TYPE_VIDEO
Type: 'video/*'.
Definition: mime.h:39
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:796
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:544
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:501
struct ListHead MimeLookupList
List of mime types that that shouldn't use the mailcap entry.
Definition: mutt_globals.h:63
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
enum ContentType mutt_lookup_mime_type(struct Body *att, const char *path)
Find the MIME type for an attachment.
Definition: sendlib.c:71
char * xtype
content-type if x-unknown
Definition: body.h:61
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ wait_interactive_filter()

static int wait_interactive_filter ( pid_t  pid)
static

Wait after an interactive filter.

Parameters
pidProcess id of the process to wait for
Return values
numExit status of the process identified by pid
-1Error

This is used for filters that are actually interactive commands with input piped in: e.g. in mutt_view_attachment(), a mailcap entry without copiousoutput and without a s.

For those cases, we treat it like a blocking system command, and poll IMAP to keep connections open.

Definition at line 384 of file mutt_attach.c.

385 {
386  int rc;
387 
388 #ifdef USE_IMAP
389  rc = imap_wait_keepalive(pid);
390 #else
391  waitpid(pid, &rc, 0);
392 #endif
394  rc = WIFEXITED(rc) ? WEXITSTATUS(rc) : -1;
395 
396  return rc;
397 }
int imap_wait_keepalive(pid_t pid)
Wait for a process to change state.
Definition: util.c:969
void mutt_sig_unblock_system(bool restore)
Restore previously blocked signals.
Definition: signal.c:207
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_view_attachment()

int mutt_view_attachment ( FILE *  fp,
struct Body a,
enum ViewAttachMode  mode,
struct Email e,
struct AttachCtx actx,
struct MuttWindow win 
)

View an attachment.

Parameters
fpSource file stream. Can be NULL
aThe message body containing the attachment
modeHow the attachment should be viewed, see ViewAttachMode
eCurrent Email. Can be NULL
actxAttachment context
winWindow
Return values
0The viewer is run and exited successfully
-1Error
numReturn value of mutt_do_pager() when it is used

Display a message attachment using the viewer program configured in mailcap. If there is no mailcap entry for a file type, view the image as text. Viewer processes are opened and waited on synchronously so viewing an attachment this way will block the main neomutt process until the viewer process exits.

Definition at line 417 of file mutt_attach.c.

419 {
420  bool use_mailcap = false;
421  bool use_pipe = false;
422  bool use_pager = true;
423  char type[256];
424  char desc[256];
425  char *fname = NULL;
426  struct MailcapEntry *entry = NULL;
427  int rc = -1;
428  bool has_tempfile = false;
429  bool unlink_pagerfile = false;
430 
431  bool is_message = mutt_is_message_type(a->type, a->subtype);
432  if ((WithCrypto != 0) && is_message && a->email &&
434  {
435  return rc;
436  }
437 
438  struct Buffer *tmpfile = mutt_buffer_pool_get();
439  struct Buffer *pagerfile = mutt_buffer_pool_get();
440  struct Buffer *cmd = mutt_buffer_pool_get();
441 
442  use_mailcap = ((mode == MUTT_VA_MAILCAP) ||
443  ((mode == MUTT_VA_REGULAR) && mutt_needs_mailcap(a)) ||
444  (mode == MUTT_VA_PAGER));
445  snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
446 
447  char columns[16];
448  snprintf(columns, sizeof(columns), "%d", win->state.cols);
449  mutt_envlist_set("COLUMNS", columns, true);
450 
451  if (use_mailcap)
452  {
453  entry = mailcap_entry_new();
454  enum MailcapLookup mailcap_opt = (mode == MUTT_VA_PAGER) ? MUTT_MC_AUTOVIEW : MUTT_MC_NO_FLAGS;
455  if (!mailcap_lookup(a, type, sizeof(type), entry, mailcap_opt))
456  {
457  if ((mode == MUTT_VA_REGULAR) || (mode == MUTT_VA_PAGER))
458  {
459  /* fallback to view as text */
460  mailcap_entry_free(&entry);
461  mutt_error(_("No matching mailcap entry found. Viewing as text."));
462  mode = MUTT_VA_AS_TEXT;
463  use_mailcap = false;
464  }
465  else
466  goto return_error;
467  }
468  }
469 
470  if (use_mailcap)
471  {
472  if (!entry->command)
473  {
474  mutt_error(_("MIME type not defined. Can't view attachment."));
475  goto return_error;
476  }
477  mutt_buffer_strcpy(cmd, entry->command);
478 
479  fname = mutt_str_dup(a->filename);
480  /* In send mode(!fp), we allow slashes because those are part of
481  * the tmpfile. The path will be removed in expand_filename */
482  mutt_file_sanitize_filename(fname, fp ? true : false);
483  mailcap_expand_filename(entry->nametemplate, fname, tmpfile);
484  FREE(&fname);
485 
486  if (mutt_save_attachment(fp, a, mutt_buffer_string(tmpfile), 0, NULL) == -1)
487  goto return_error;
488  has_tempfile = true;
489 
491 
492  /* check for multipart/related and save attachments with a Content-ID */
493  if (mutt_str_equal(type, "text/html"))
494  {
495  struct Body *related_ancestor = NULL;
496  if (actx->body_idx && (WithCrypto != 0) && (e->security & SEC_ENCRYPT))
497  related_ancestor = attach_body_ancestor(actx->body_idx[0], a, "related");
498  else
499  related_ancestor = attach_body_ancestor(e->body, a, "related");
500  if (related_ancestor)
501  {
502  struct CidMapList cid_map_list = STAILQ_HEAD_INITIALIZER(cid_map_list);
503  mutt_debug(LL_DEBUG2, "viewing text/html attachment in multipart/related group\n");
504  /* save attachments and build cid_map_list Content-ID to filename mapping list */
505  cid_save_attachments(related_ancestor->parts, &cid_map_list);
506  /* replace Content-IDs with filenames */
507  cid_to_filename(tmpfile, &cid_map_list);
508  /* empty Content-ID to filename mapping list */
509  cid_map_list_clear(&cid_map_list);
510  }
511  }
512 
513  use_pipe = mailcap_expand_command(a, mutt_buffer_string(tmpfile), type, cmd);
514  use_pager = entry->copiousoutput;
515  }
516 
517  if (use_pager)
518  {
519  if (fp && !use_mailcap && a->filename)
520  {
521  /* recv case */
522  mutt_buffer_strcpy(pagerfile, a->filename);
523  mutt_adv_mktemp(pagerfile);
524  }
525  else
526  mutt_buffer_mktemp(pagerfile);
527  }
528 
529  if (use_mailcap)
530  {
531  pid_t pid = 0;
532  int fd_temp = -1, fd_pager = -1;
533 
534  if (!use_pager)
535  mutt_endwin();
536 
537  const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
538  if (use_pager || use_pipe)
539  {
540  if (use_pager && ((fd_pager = mutt_file_open(mutt_buffer_string(pagerfile),
541  O_CREAT | O_EXCL | O_WRONLY)) == -1))
542  {
543  mutt_perror("open");
544  goto return_error;
545  }
546  unlink_pagerfile = true;
547 
548  if (use_pipe && ((fd_temp = open(mutt_buffer_string(tmpfile), 0)) == -1))
549  {
550  if (fd_pager != -1)
551  close(fd_pager);
552  mutt_perror("open");
553  goto return_error;
554  }
555  unlink_pagerfile = true;
556 
557  pid = filter_create_fd(mutt_buffer_string(cmd), NULL, NULL, NULL,
558  use_pipe ? fd_temp : -1, use_pager ? fd_pager : -1, -1);
559 
560  if (pid == -1)
561  {
562  if (fd_pager != -1)
563  close(fd_pager);
564 
565  if (fd_temp != -1)
566  close(fd_temp);
567 
568  mutt_error(_("Can't create filter"));
569  goto return_error;
570  }
571 
572  if (use_pager)
573  {
574  if (a->description)
575  {
576  snprintf(desc, sizeof(desc), _("---Command: %-20.20s Description: %s"),
578  }
579  else
580  {
581  snprintf(desc, sizeof(desc), _("---Command: %-30.30s Attachment: %s"),
582  mutt_buffer_string(cmd), type);
583  }
584  filter_wait(pid);
585  }
586  else
587  {
588  if (wait_interactive_filter(pid) || (entry->needsterminal && c_wait_key))
590  }
591 
592  if (fd_temp != -1)
593  close(fd_temp);
594  if (fd_pager != -1)
595  close(fd_pager);
596  }
597  else
598  {
599  /* interactive cmd */
600  int rv = mutt_system(mutt_buffer_string(cmd));
601  if (rv == -1)
602  mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd->data);
603 
604  if ((rv != 0) || (entry->needsterminal && c_wait_key))
606  }
607  }
608  else
609  {
610  /* Don't use mailcap; the attachment is viewed in the pager */
611 
612  if (mode == MUTT_VA_AS_TEXT)
613  {
614  /* just let me see the raw data */
615  if (fp)
616  {
617  /* Viewing from a received message.
618  *
619  * Don't use mutt_save_attachment() because we want to perform charset
620  * conversion since this will be displayed by the internal pager. */
621  struct State decode_state = { 0 };
622 
623  decode_state.fp_out = mutt_file_fopen(mutt_buffer_string(pagerfile), "w");
624  if (!decode_state.fp_out)
625  {
626  mutt_debug(LL_DEBUG1, "mutt_file_fopen(%s) errno=%d %s\n",
627  mutt_buffer_string(pagerfile), errno, strerror(errno));
628  mutt_perror(mutt_buffer_string(pagerfile));
629  goto return_error;
630  }
631  decode_state.fp_in = fp;
632  decode_state.flags = MUTT_CHARCONV;
633  mutt_decode_attachment(a, &decode_state);
634  if (mutt_file_fclose(&decode_state.fp_out) == EOF)
635  {
636  mutt_debug(LL_DEBUG1, "fclose(%s) errno=%d %s\n",
637  mutt_buffer_string(pagerfile), errno, strerror(errno));
638  }
639  }
640  else
641  {
642  /* in compose mode, just copy the file. we can't use
643  * mutt_decode_attachment() since it assumes the content-encoding has
644  * already been applied */
645  if (mutt_save_attachment(fp, a, mutt_buffer_string(pagerfile), MUTT_SAVE_NO_FLAGS, NULL))
646  goto return_error;
647  unlink_pagerfile = true;
648  }
650  }
651  else
652  {
653  /* Use built-in handler */
654  if (mutt_decode_save_attachment(fp, a, mutt_buffer_string(pagerfile),
656  {
657  goto return_error;
658  }
659  unlink_pagerfile = true;
660  }
661 
662  if (a->description)
663  mutt_str_copy(desc, a->description, sizeof(desc));
664  else if (a->filename)
665  snprintf(desc, sizeof(desc), _("---Attachment: %s: %s"), a->filename, type);
666  else
667  snprintf(desc, sizeof(desc), _("---Attachment: %s"), type);
668  }
669 
670  /* We only reach this point if there have been no errors */
671 
672  if (use_pager)
673  {
674  struct PagerData pdata = { 0 };
675  struct PagerView pview = { &pdata };
676 
677  pdata.actx = actx;
678  pdata.body = a;
679  pdata.fname = mutt_buffer_string(pagerfile);
680  pdata.fp = fp;
681 
682  pview.banner = desc;
683  pview.flags = MUTT_PAGER_ATTACHMENT |
684  (is_message ? MUTT_PAGER_MESSAGE : MUTT_PAGER_NO_FLAGS) |
685  ((use_mailcap && entry->xneomuttnowrap) ? MUTT_PAGER_NOWRAP :
687  pview.mode = PAGER_MODE_ATTACH;
688 
689  rc = mutt_do_pager(&pview, e);
690 
691  mutt_buffer_reset(pagerfile);
692  unlink_pagerfile = false;
693  }
694  else
695  rc = 0;
696 
697 return_error:
698 
699  if (!entry || !entry->xneomuttkeep)
700  {
701  if ((fp && !mutt_buffer_is_empty(tmpfile)) || has_tempfile)
702  {
703  /* add temporary file to TempAttachmentsList to be deleted on timeout hook */
705  }
706  }
707 
708  mailcap_entry_free(&entry);
709 
710  if (unlink_pagerfile)
712 
713  mutt_buffer_pool_release(&tmpfile);
714  mutt_buffer_pool_release(&pagerfile);
716  mutt_envlist_unset("COLUMNS");
717 
718  return rc;
719 }
struct Body * attach_body_ancestor(struct Body *start, struct Body *body, const char *subtype)
Find the ancestor of a body with specified subtype.
Definition: lib.c:116
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:250
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:81
void cid_save_attachments(struct Body *body, struct CidMapList *cid_map_list)
Save all attachments in a "multipart/related" group with a Content-ID.
Definition: cid.c:150
void cid_to_filename(struct Buffer *filename, const struct CidMapList *cid_map_list)
Replace Content-IDs with filenames.
Definition: cid.c:169
void cid_map_list_clear(struct CidMapList *cid_map_list)
Empty a CidMapList.
Definition: cid.c:81
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
bool crypt_valid_passphrase(SecurityFlags flags)
Check that we have a usable passphrase, ask if not.
Definition: crypt.c:134
int mutt_any_key_to_continue(const char *s)
Prompt the user to 'press any key' and wait.
Definition: curs_lib.c:388
int mutt_do_pager(struct PagerView *pview, struct Email *e)
Display some page-able text to the user (help or attachment)
Definition: do_pager.c:120
bool mutt_envlist_unset(const char *name)
Unset an environment variable.
Definition: envlist.c:132
bool mutt_envlist_set(const char *name, const char *value, bool overwrite)
Set an environment variable.
Definition: envlist.c:85
int mutt_file_open(const char *path, uint32_t flags)
Open a file.
Definition: file.c:549
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:647
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
pid_t filter_create_fd(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, int fdin, int fdout, int fderr)
Run a command on a pipe (optionally connect stdin/stdout)
Definition: filter.c:61
void mutt_decode_attachment(struct Body *b, struct State *s)
Decode an email's attachment.
Definition: handler.c:1867
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
MailcapLookup
Mailcap actions.
Definition: mailcap.h:55
@ MUTT_MC_AUTOVIEW
Mailcap autoview field.
Definition: mailcap.h:60
#define MUTT_CHARCONV
Do character set conversions.
Definition: state.h:36
#define MUTT_DISPLAY
Output is displayed to the user.
Definition: state.h:32
#define MUTT_DISPLAY_ATTACH
We are displaying an attachment.
Definition: state.h:40
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:784
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:629
int mutt_decode_save_attachment(FILE *fp, struct Body *m, const char *path, int displaying, enum SaveAttach opt)
Decode, then save an attachment.
Definition: mutt_attach.c:1031
static int wait_interactive_filter(pid_t pid)
Wait after an interactive filter.
Definition: mutt_attach.c:384
void mutt_add_temp_attachment(const char *filename)
Add file to list of temporary attachments.
Definition: mutt_attach.c:1293
int mutt_save_attachment(FILE *fp, struct Body *m, const char *path, enum SaveAttach opt, struct Email *e)
Save an attachment.
Definition: mutt_attach.c:897
@ MUTT_SAVE_NO_FLAGS
No flags set.
Definition: mutt_attach.h:57
@ MUTT_VA_MAILCAP
Force viewing using mailcap entry.
Definition: mutt_attach.h:44
@ MUTT_VA_REGULAR
View using default method.
Definition: mutt_attach.h:43
@ MUTT_VA_PAGER
View attachment in pager using copiousoutput mailcap.
Definition: mutt_attach.h:46
@ MUTT_VA_AS_TEXT
Force viewing as text.
Definition: mutt_attach.h:45
bool mutt_needs_mailcap(struct Body *m)
Does this type need a mailcap entry do display.
Definition: muttlib.c:405
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition: muttlib.c:84
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:78
#define WithCrypto
Definition: lib.h:116
#define MUTT_PAGER_NO_FLAGS
No flags are set.
Definition: lib.h:58
#define MUTT_PAGER_NOWRAP
Format for term width, ignore $wrap.
Definition: lib.h:71
@ PAGER_MODE_ATTACH
Pager is invoked via 2nd path. A user-selected attachment (mime part or a nested email) will be shown...
Definition: lib.h:136
#define MUTT_PAGER_MESSAGE
Definition: lib.h:74
#define MUTT_PAGER_ATTACHMENT
Attachments may exist.
Definition: lib.h:70
bool mutt_is_message_type(int type, const char *subtype)
Determine if a mime type matches a message or not.
Definition: parse.c:1442
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
void mutt_rfc3676_space_unstuff_attachment(struct Body *b, const char *filename)
Unstuff attachments.
Definition: rfc3676.c:513
struct Body ** body_idx
Extra struct Body* used for decryption.
Definition: attach.h:66
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:72
struct Email * email
header information for message/rfc822
Definition: body.h:73
char * data
Pointer to data.
Definition: buffer.h:35
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib....
Definition: email.h:41
struct Body * body
List of MIME parts.
Definition: email.h:67
bool needsterminal
endwin() and system
Definition: mailcap.h:45
char * command
Definition: mailcap.h:37
bool copiousoutput
needs pager, basically
Definition: mailcap.h:46
bool xneomuttkeep
do not remove the file on command exit
Definition: mailcap.h:47
bool xneomuttnowrap
do not wrap the output in the pager
Definition: mailcap.h:48
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
Data to be displayed by PagerView.
Definition: lib.h:158
const char * fname
Name of the file to read.
Definition: lib.h:162
FILE * fp
Source stream.
Definition: lib.h:160
struct Body * body
Current attachment.
Definition: lib.h:159
struct AttachCtx * actx
Attachment information.
Definition: lib.h:161
Paged view into some data.
Definition: lib.h:169
struct PagerData * pdata
Data that pager displays. NOTNULL.
Definition: lib.h:170
enum PagerMode mode
Pager mode.
Definition: lib.h:171
PagerFlags flags
Additional settings to tweak pager's function.
Definition: lib.h:172
const char * banner
Title to display in status bar.
Definition: lib.h:173
Keep track when processing files.
Definition: state.h:46
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:50
FILE * fp_out
File to write to.
Definition: state.h:48
FILE * fp_in
File to read from.
Definition: state.h:47
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:60
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_pipe_attachment()

int mutt_pipe_attachment ( FILE *  fp,
struct Body b,
const char *  path,
char *  outfile 
)

Pipe an attachment to a command.

Parameters
fpFile to pipe into the command
bAttachment
pathPath to command
outfileFile to save output to
Return values
1Success
0Error

Definition at line 730 of file mutt_attach.c.

731 {
732  pid_t pid = 0;
733  int out = -1, rc = 0;
734  bool is_flowed = false;
735  bool unlink_unstuff = false;
736  FILE *fp_filter = NULL, *fp_unstuff = NULL, *fp_in = NULL;
737  struct Buffer *unstuff_tempfile = NULL;
738 
739  if (outfile && *outfile)
740  {
741  out = mutt_file_open(outfile, O_CREAT | O_EXCL | O_WRONLY);
742  if (out < 0)
743  {
744  mutt_perror("open");
745  return 0;
746  }
747  }
748 
750  {
751  is_flowed = true;
752  unstuff_tempfile = mutt_buffer_pool_get();
753  mutt_buffer_mktemp(unstuff_tempfile);
754  }
755 
756  mutt_endwin();
757 
758  if (outfile && *outfile)
759  pid = filter_create_fd(path, &fp_filter, NULL, NULL, -1, out, -1);
760  else
761  pid = filter_create(path, &fp_filter, NULL, NULL);
762  if (pid < 0)
763  {
764  mutt_perror(_("Can't create filter"));
765  goto bail;
766  }
767 
768  /* recv case */
769  if (fp)
770  {
771  struct State s = { 0 };
772 
773  /* perform charset conversion on text attachments when piping */
774  s.flags = MUTT_CHARCONV;
775 
776  if (is_flowed)
777  {
778  fp_unstuff = mutt_file_fopen(mutt_buffer_string(unstuff_tempfile), "w");
779  if (fp_unstuff == NULL)
780  {
781  mutt_perror("mutt_file_fopen");
782  goto bail;
783  }
784  unlink_unstuff = true;
785 
786  s.fp_in = fp;
787  s.fp_out = fp_unstuff;
788  mutt_decode_attachment(b, &s);
789  mutt_file_fclose(&fp_unstuff);
790 
792 
793  fp_unstuff = mutt_file_fopen(mutt_buffer_string(unstuff_tempfile), "r");
794  if (fp_unstuff == NULL)
795  {
796  mutt_perror("mutt_file_fopen");
797  goto bail;
798  }
799  mutt_file_copy_stream(fp_unstuff, fp_filter);
800  mutt_file_fclose(&fp_unstuff);
801  }
802  else
803  {
804  s.fp_in = fp;
805  s.fp_out = fp_filter;
806  mutt_decode_attachment(b, &s);
807  }
808  }
809 
810  /* send case */
811  else
812  {
813  const char *infile = NULL;
814 
815  if (is_flowed)
816  {
817  if (mutt_save_attachment(fp, b, mutt_buffer_string(unstuff_tempfile),
818  MUTT_SAVE_NO_FLAGS, NULL) == -1)
819  {
820  goto bail;
821  }
822  unlink_unstuff = true;
824  infile = mutt_buffer_string(unstuff_tempfile);
825  }
826  else
827  infile = b->filename;
828 
829  fp_in = fopen(infile, "r");
830  if (!fp_in)
831  {
832  mutt_perror("fopen");
833  goto bail;
834  }
835 
836  mutt_file_copy_stream(fp_in, fp_filter);
838  }
839 
840  mutt_file_fclose(&fp_filter);
841  rc = 1;
842 
843 bail:
844  if (outfile && *outfile)
845  {
846  close(out);
847  if (rc == 0)
848  unlink(outfile);
849  else if (is_flowed)
851  }
852 
853  mutt_file_fclose(&fp_unstuff);
854  mutt_file_fclose(&fp_filter);
856 
857  if (unlink_unstuff)
858  mutt_file_unlink(mutt_buffer_string(unstuff_tempfile));
859  mutt_buffer_pool_release(&unstuff_tempfile);
860 
861  /* check for error exit from child process */
862  if ((pid > 0) && (filter_wait(pid) != 0))
863  rc = 0;
864 
865  const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
866  if ((rc == 0) || c_wait_key)
868  return rc;
869 }
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:206
void mutt_rfc3676_space_stuff_attachment(struct Body *b, const char *filename)
Stuff attachments.
Definition: rfc3676.c:534
bool mutt_rfc3676_is_format_flowed(struct Body *b)
Is the Email "format-flowed"?
Definition: rfc3676.c:389
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ save_attachment_open()

static FILE* save_attachment_open ( const char *  path,
enum SaveAttach  opt 
)
static

Open a file to write an attachment to.

Parameters
pathPath to file to open
optSave option, see SaveAttach
Return values
ptrFile handle to attachment file

Definition at line 877 of file mutt_attach.c.

878 {
879  if (opt == MUTT_SAVE_APPEND)
880  return fopen(path, "a");
881  if (opt == MUTT_SAVE_OVERWRITE)
882  return fopen(path, "w");
883 
884  return mutt_file_fopen(path, "w");
885 }
@ MUTT_SAVE_APPEND
Append to existing file.
Definition: mutt_attach.h:58
@ MUTT_SAVE_OVERWRITE
Overwrite existing file.
Definition: mutt_attach.h:59
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_save_attachment()

int mutt_save_attachment ( FILE *  fp,
struct Body m,
const char *  path,
enum SaveAttach  opt,
struct Email e 
)

Save an attachment.

Parameters
fpSource file stream. Can be NULL
mEmail Body
pathWhere to save the attachment
optSave option, see SaveAttach
eCurrent Email. Can be NULL
Return values
0Success
-1Error

Definition at line 897 of file mutt_attach.c.

899 {
900  if (!m)
901  return -1;
902 
903  if (fp)
904  {
905  /* recv mode */
906 
907  if (e && m->email && (m->encoding != ENC_BASE64) &&
909  {
910  /* message type attachments are written to mail folders. */
911 
912  char buf[8192];
913  struct Message *msg = NULL;
914  CopyHeaderFlags chflags = CH_NO_FLAGS;
915  int rc = -1;
916 
917  struct Email *e_new = m->email;
918  e_new->msgno = e->msgno; /* required for MH/maildir */
919  e_new->read = true;
920 
921  if (!mutt_file_seek(fp, m->offset, SEEK_SET))
922  return -1;
923  if (!fgets(buf, sizeof(buf), fp))
924  return -1;
925  struct Mailbox *m_att = mx_path_resolve(path);
926  if (!mx_mbox_open(m_att, MUTT_APPEND | MUTT_QUIET))
927  {
928  mailbox_free(&m_att);
929  return -1;
930  }
931  msg = mx_msg_open_new(m_att, e_new,
932  is_from(buf, NULL, 0, NULL) ? MUTT_MSG_NO_FLAGS : MUTT_ADD_FROM);
933  if (!msg)
934  {
935  mx_mbox_close(m_att);
936  return -1;
937  }
938  if ((m_att->type == MUTT_MBOX) || (m_att->type == MUTT_MMDF))
939  chflags = CH_FROM | CH_UPDATE_LEN;
940  chflags |= ((m_att->type == MUTT_MAILDIR) ? CH_NOSTATUS : CH_UPDATE);
941  if ((mutt_copy_message_fp(msg->fp, fp, e_new, MUTT_CM_NO_FLAGS, chflags, 0) == 0) &&
942  (mx_msg_commit(m_att, msg) == 0))
943  {
944  rc = 0;
945  }
946  else
947  {
948  rc = -1;
949  }
950 
951  mx_msg_close(m_att, &msg);
952  mx_mbox_close(m_att);
953  return rc;
954  }
955  else
956  {
957  /* In recv mode, extract from folder and decode */
958 
959  struct State s = { 0 };
960 
961  s.fp_out = save_attachment_open(path, opt);
962  if (!s.fp_out)
963  {
964  mutt_perror("fopen");
965  return -1;
966  }
967  if (!mutt_file_seek((s.fp_in = fp), m->offset, SEEK_SET))
968  {
970  return -1;
971  }
972  mutt_decode_attachment(m, &s);
973 
974  if (mutt_file_fsync_close(&s.fp_out) != 0)
975  {
976  mutt_perror("fclose");
977  return -1;
978  }
979  }
980  }
981  else
982  {
983  if (!m->filename)
984  return -1;
985 
986  /* In send mode, just copy file */
987 
988  FILE *fp_old = fopen(m->filename, "r");
989  if (!fp_old)
990  {
991  mutt_perror("fopen");
992  return -1;
993  }
994 
995  FILE *fp_new = save_attachment_open(path, opt);
996  if (!fp_new)
997  {
998  mutt_perror("fopen");
999  mutt_file_fclose(&fp_old);
1000  return -1;
1001  }
1002 
1003  if (mutt_file_copy_stream(fp_old, fp_new) == -1)
1004  {
1005  mutt_error(_("Write fault"));
1006  mutt_file_fclose(&fp_old);
1007  mutt_file_fclose(&fp_new);
1008  return -1;
1009  }
1010  mutt_file_fclose(&fp_old);
1011  if (mutt_file_fsync_close(&fp_new) != 0)
1012  {
1013  mutt_error(_("Write fault"));
1014  return -1;
1015  }
1016  }
1017 
1018  return 0;
1019 }
int mutt_copy_message_fp(FILE *fp_out, FILE *fp_in, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Make a copy of a message from a FILE pointer.
Definition: copy.c:640
#define CH_UPDATE
Update the status and x-status fields?
Definition: copy.h:52
#define CH_NOSTATUS
Suppress the status and x-status fields.
Definition: copy.h:58
#define CH_FROM
Retain the "From " message separator?
Definition: copy.h:56
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:50
#define CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition: copy.h:62
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:35
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:51
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition: file.c:168
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition: from.c:48
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:87
@ MUTT_MMDF
'mmdf' Mailbox type
Definition: mailbox.h:46
@ MUTT_MBOX
'mbox' Mailbox type
Definition: mailbox.h:45
@ MUTT_MAILDIR
'Maildir' Mailbox type
Definition: mailbox.h:48
@ ENC_BASE64
Base-64 encoded text.
Definition: mime.h:52
@ ENC_QUOTED_PRINTABLE
Quoted-printable text.
Definition: mime.h:51
static FILE * save_attachment_open(const char *path, enum SaveAttach opt)
Open a file to write an attachment to.
Definition: mutt_attach.c:877
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1193
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:304
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1677
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition: mx.c:1172
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1057
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition: mx.c:615
#define MUTT_ADD_FROM
add a From_ line
Definition: mx.h:43
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition: mx.h:42
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mxapi.h:63
#define MUTT_QUIET
Do not print any messages.
Definition: mxapi.h:65
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:41
The envelope/body of an email.
Definition: email.h:37
bool read
Email is read.
Definition: email.h:48
int msgno
Number displayed to the user.
Definition: email.h:111
A mailbox.
Definition: mailbox.h:79
enum MailboxType type
Mailbox type.
Definition: mailbox.h:102
A local copy of an email.
Definition: mxapi.h:43
FILE * fp
pointer to the message data
Definition: mxapi.h:44
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_decode_save_attachment()

int mutt_decode_save_attachment ( FILE *  fp,
struct Body m,
const char *  path,
int  displaying,
enum SaveAttach  opt 
)

Decode, then save an attachment.

Parameters
fpFile to read from (OPTIONAL)
mAttachment
pathPath to save the Attachment to
displayingFlags, e.g. MUTT_DISPLAY
optSave option, see SaveAttach
Return values
0Success
-1Error

Definition at line 1031 of file mutt_attach.c.

1033 {
1034  struct State s = { 0 };
1035  unsigned int saved_encoding = 0;
1036  struct Body *saved_parts = NULL;
1037  struct Email *e_saved = NULL;
1038  int rc = 0;
1039 
1040  s.flags = displaying;
1041 
1042  if (opt == MUTT_SAVE_APPEND)
1043  s.fp_out = fopen(path, "a");
1044  else if (opt == MUTT_SAVE_OVERWRITE)
1045  s.fp_out = fopen(path, "w");
1046  else
1047  s.fp_out = mutt_file_fopen(path, "w");
1048 
1049  if (!s.fp_out)
1050  {
1051  mutt_perror("fopen");
1052  return -1;
1053  }
1054 
1055  if (!fp)
1056  {
1057  /* When called from the compose menu, the attachment isn't parsed,
1058  * so we need to do it here. */
1059  s.fp_in = fopen(m->filename, "r");
1060  if (!s.fp_in)
1061  {
1062  mutt_perror("fopen");
1064  return -1;
1065  }
1066 
1067  struct stat st = { 0 };
1068  if (fstat(fileno(s.fp_in), &st) == -1)
1069  {
1070  mutt_perror("stat");
1071  mutt_file_fclose(&s.fp_in);
1073  return -1;
1074  }
1075 
1076  saved_encoding = m->encoding;
1077  if (!is_multipart(m))
1078  m->encoding = ENC_8BIT;
1079 
1080  m->length = st.st_size;
1081  m->offset = 0;
1082  saved_parts = m->parts;
1083  e_saved = m->email;
1084  mutt_parse_part(s.fp_in, m);
1085 
1086  if (m->noconv || is_multipart(m))
1087  s.flags |= MUTT_CHARCONV;
1088  }
1089  else
1090  {
1091  s.fp_in = fp;
1092  s.flags |= MUTT_CHARCONV;
1093  }
1094 
1095  mutt_body_handler(m, &s);
1096 
1097  if (mutt_file_fsync_close(&s.fp_out) != 0)
1098  {
1099  mutt_perror("fclose");
1100  rc = -1;
1101  }
1102  if (!fp)
1103  {
1104  m->length = 0;
1105  m->encoding = saved_encoding;
1106  if (saved_parts)
1107  {
1108  email_free(&m->email);
1109  m->parts = saved_parts;
1110  m->email = e_saved;
1111  }
1112  mutt_file_fclose(&s.fp_in);
1113  }
1114 
1115  return rc;
1116 }
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
int mutt_body_handler(struct Body *b, struct State *s)
Handler for the Body of an email.
Definition: handler.c:1597
@ ENC_8BIT
8-bit text
Definition: mime.h:50
#define is_multipart(body)
Definition: mime.h:82
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition: parse.c:1737
bool noconv
Don't do character set conversion.
Definition: body.h:46
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
char * path
Path of Email (for local Mailboxes)
Definition: email.h:68
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_print_attachment()

int mutt_print_attachment ( FILE *  fp,
struct Body a 
)

Print out an attachment.

Parameters
fpFile to write to
aAttachment
Return values
1Success
0Error

Ok, the difference between send and receive: recv: Body->filename is a suggested name, and Mailbox|Email points to the attachment in mailbox which is encoded send: Body->filename points to the un-encoded file which contains the attachment

Definition at line 1131 of file mutt_attach.c.

1132 {
1133  char type[256];
1134  pid_t pid;
1135  FILE *fp_in = NULL, *fp_out = NULL;
1136  bool unlink_newfile = false;
1137  struct Buffer *newfile = mutt_buffer_pool_get();
1138  struct Buffer *cmd = mutt_buffer_pool_get();
1139 
1140  int rc = 0;
1141 
1142  snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
1143 
1144  if (mailcap_lookup(a, type, sizeof(type), NULL, MUTT_MC_PRINT))
1145  {
1146  mutt_debug(LL_DEBUG2, "Using mailcap\n");
1147 
1148  struct MailcapEntry *entry = mailcap_entry_new();
1149  mailcap_lookup(a, type, sizeof(type), entry, MUTT_MC_PRINT);
1150 
1151  char *sanitized_fname = mutt_str_dup(a->filename);
1152  /* In send mode (!fp), we allow slashes because those are part of
1153  * the tempfile. The path will be removed in expand_filename */
1154  mutt_file_sanitize_filename(sanitized_fname, fp ? true : false);
1155  mailcap_expand_filename(entry->nametemplate, sanitized_fname, newfile);
1156  FREE(&sanitized_fname);
1157 
1158  if (mutt_save_attachment(fp, a, mutt_buffer_string(newfile),
1159  MUTT_SAVE_NO_FLAGS, NULL) == -1)
1160  {
1161  goto mailcap_cleanup;
1162  }
1163  unlink_newfile = 1;
1164 
1166 
1167  mutt_buffer_strcpy(cmd, entry->printcommand);
1168 
1169  bool piped = mailcap_expand_command(a, mutt_buffer_string(newfile), type, cmd);
1170 
1171  mutt_endwin();
1172 
1173  const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1174  /* interactive program */
1175  if (piped)
1176  {
1177  fp_in = fopen(mutt_buffer_string(newfile), "r");
1178  if (!fp_in)
1179  {
1180  mutt_perror("fopen");
1181  mailcap_entry_free(&entry);
1182  goto mailcap_cleanup;
1183  }
1184 
1185  pid = filter_create(mutt_buffer_string(cmd), &fp_out, NULL, NULL);
1186  if (pid < 0)
1187  {
1188  mutt_perror(_("Can't create filter"));
1189  mailcap_entry_free(&entry);
1190  mutt_file_fclose(&fp_in);
1191  goto mailcap_cleanup;
1192  }
1193  mutt_file_copy_stream(fp_in, fp_out);
1194  mutt_file_fclose(&fp_out);
1195  mutt_file_fclose(&fp_in);
1196  if (filter_wait(pid) || c_wait_key)
1198  }
1199  else
1200  {
1201  int rc2 = mutt_system(mutt_buffer_string(cmd));
1202  if (rc2 == -1)
1203  mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd->data);
1204 
1205  if ((rc2 != 0) || c_wait_key)
1207  }
1208 
1209  rc = 1;
1210 
1211  mailcap_cleanup:
1212  if (unlink_newfile)
1214 
1215  mailcap_entry_free(&entry);
1216  goto out;
1217  }
1218 
1219  const char *const c_print_command = cs_subset_string(NeoMutt->sub, "print_command");
1220  if (mutt_istr_equal("text/plain", type) || mutt_istr_equal("application/postscript", type))
1221  {
1222  rc = (mutt_pipe_attachment(fp, a, NONULL(c_print_command), NULL));
1223  goto out;
1224  }
1225  else if (mutt_can_decode(a))
1226  {
1227  /* decode and print */
1228 
1229  fp_in = NULL;
1230  fp_out = NULL;
1231 
1232  mutt_buffer_mktemp(newfile);
1233  if (mutt_decode_save_attachment(fp, a, mutt_buffer_string(newfile),
1235  {
1236  unlink_newfile = true;
1237  mutt_debug(LL_DEBUG2, "successfully decoded %s type attachment to %s\n",
1238  type, mutt_buffer_string(newfile));
1239 
1240  fp_in = fopen(mutt_buffer_string(newfile), "r");
1241  if (!fp_in)
1242  {
1243  mutt_perror("fopen");
1244  goto decode_cleanup;
1245  }
1246 
1247  mutt_debug(LL_DEBUG2, "successfully opened %s read-only\n",
1248  mutt_buffer_string(newfile));
1249 
1250  mutt_endwin();
1251  pid = filter_create(NONULL(c_print_command), &fp_out, NULL, NULL);
1252  if (pid < 0)
1253  {
1254  mutt_perror(_("Can't create filter"));
1255  goto decode_cleanup;
1256  }
1257 
1258  mutt_debug(LL_DEBUG2, "Filter created\n");
1259 
1260  mutt_file_copy_stream(fp_in, fp_out);
1261 
1262  mutt_file_fclose(&fp_out);
1263  mutt_file_fclose(&fp_in);
1264 
1265  const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
1266  if ((filter_wait(pid) != 0) || c_wait_key)
1268  rc = 1;
1269  }
1270  decode_cleanup:
1271  mutt_file_fclose(&fp_in);
1272  mutt_file_fclose(&fp_out);
1273  if (unlink_newfile)
1275  }
1276  else
1277  {
1278  mutt_error(_("I don't know how to print that"));
1279  rc = 0;
1280  }
1281 
1282 out:
1283  mutt_buffer_pool_release(&newfile);
1285 
1286  return rc;
1287 }
bool mutt_can_decode(struct Body *b)
Will decoding the attachment produce any output.
Definition: handler.c:1827
@ MUTT_MC_PRINT
Mailcap print field.
Definition: mailcap.h:59
#define MUTT_PRINTING
Are we printing? - MUTT_DISPLAY "light".
Definition: state.h:37
int mutt_pipe_attachment(FILE *fp, struct Body *b, const char *path, char *outfile)
Pipe an attachment to a command.
Definition: mutt_attach.c:730
char * printcommand
Definition: mailcap.h:42
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_add_temp_attachment()

void mutt_add_temp_attachment ( const char *  filename)

Add file to list of temporary attachments.

Parameters
filenamefilename with full path

Definition at line 1293 of file mutt_attach.c.

1294 {
1296 }
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
struct ListHead TempAttachmentsList
List of temporary files for displaying attachments.
Definition: mutt_globals.h:65
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_unlink_temp_attachments()

void mutt_unlink_temp_attachments ( void  )

Delete all temporary attachments.

Definition at line 1301 of file mutt_attach.c.

1302 {
1303  struct ListNode *np = NULL;
1304 
1305  STAILQ_FOREACH(np, &TempAttachmentsList, entries)
1306  {
1307  (void) mutt_file_chmod_add(np->data, S_IWUSR);
1308  mutt_file_unlink(np->data);
1309  }
1310 
1312 }
int mutt_file_chmod_add(const char *path, mode_t mode)
Add permissions to a file.
Definition: file.c:1123
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
+ Here is the call graph for this function:
+ Here is the caller graph for this function: