NeoMutt  2023-03-22
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 "globals.h"
#include "handler.h"
#include "mailcap.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...
 
bool 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, StateFlags flags, 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] = { 0 };
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);
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:78
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:259
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:634
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:151
#define mutt_perror(...)
Definition: logging.h:88
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
struct MailcapEntry * mailcap_entry_new(void)
Allocate memory for a new rfc1524 entry.
Definition: mailcap.c:435
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:399
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] = { 0 };
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
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;
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
234bailout:
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:365
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:353
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:706
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition: file.c:1419
int mutt_file_symlink(const char *oldpath, const char *newpath)
Create a symlink.
Definition: file.c:287
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:193
#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:1317
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()

bool mutt_edit_attachment ( struct Body a)

Edit an attachment.

Parameters
aEmail containing attachment
Return values
trueEditor found
falseEditor 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] = { 0 };
263 struct MailcapEntry *entry = mailcap_entry_new();
264 bool unlink_newfile = false;
265 bool rc = false;
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 goto bailout;
314 }
315
316 rc = true;
317
318bailout:
319
320 if (unlink_newfile)
321 unlink(mutt_buffer_string(newfile));
322
324 mutt_buffer_pool_release(&newfile);
325
326 mailcap_entry_free(&entry);
327 return rc;
328}
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:311
@ 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 336 of file mutt_attach.c.

337{
338 struct ListNode *np = NULL;
339 STAILQ_FOREACH(np, &MimeLookupList, entries)
340 {
341 const int i = mutt_str_len(np->data) - 1;
342 if (((i > 0) && (np->data[i - 1] == '/') && (np->data[i] == '*') &&
343 mutt_istrn_equal(type, np->data, i)) ||
344 mutt_istr_equal(type, np->data))
345 {
346 struct Body tmp = { 0 };
347 enum ContentType n;
348 if ((n = mutt_lookup_mime_type(&tmp, b->filename)) != TYPE_OTHER ||
350 {
351 snprintf(type, len, "%s/%s",
352 (n == TYPE_AUDIO) ? "audio" :
353 (n == TYPE_APPLICATION) ? "application" :
354 (n == TYPE_IMAGE) ? "image" :
355 (n == TYPE_MESSAGE) ? "message" :
356 (n == TYPE_MODEL) ? "model" :
357 (n == TYPE_MULTIPART) ? "multipart" :
358 (n == TYPE_TEXT) ? "text" :
359 (n == TYPE_VIDEO) ? "video" :
360 "other",
361 tmp.subtype);
362 mutt_debug(LL_DEBUG1, "\"%s\" -> %s\n", b->filename, type);
363 }
364 FREE(&tmp.subtype);
365 FREE(&tmp.xtype);
366 }
367 }
368}
struct ListHead MimeLookupList
List of mime types that that shouldn't use the mailcap entry.
Definition: globals.c:51
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:819
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
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:524
#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:69
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 383 of file mutt_attach.c.

384{
385 int rc;
386
387#ifdef USE_IMAP
388 rc = imap_wait_keepalive(pid);
389#else
390 waitpid(pid, &rc, 0);
391#endif
393 rc = WIFEXITED(rc) ? WEXITSTATUS(rc) : -1;
394
395 return rc;
396}
int imap_wait_keepalive(pid_t pid)
Wait for a process to change state.
Definition: util.c:961
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 416 of file mutt_attach.c.

418{
419 bool use_mailcap = false;
420 bool use_pipe = false;
421 bool use_pager = true;
422 char type[256] = { 0 };
423 char desc[256] = { 0 };
424 char *fname = NULL;
425 struct MailcapEntry *entry = NULL;
426 int rc = -1;
427 bool has_tempfile = false;
428 bool unlink_pagerfile = false;
429
430 bool is_message = mutt_is_message_type(a->type, a->subtype);
431 if ((WithCrypto != 0) && is_message && a->email &&
433 {
434 return rc;
435 }
436
437 struct Buffer *tmpfile = mutt_buffer_pool_get();
438 struct Buffer *pagerfile = mutt_buffer_pool_get();
439 struct Buffer *cmd = mutt_buffer_pool_get();
440
441 use_mailcap = ((mode == MUTT_VA_MAILCAP) ||
442 ((mode == MUTT_VA_REGULAR) && mutt_needs_mailcap(a)) ||
443 (mode == MUTT_VA_PAGER));
444 snprintf(type, sizeof(type), "%s/%s", TYPE(a), a->subtype);
445
446 char columns[16] = { 0 };
447 snprintf(columns, sizeof(columns), "%d", win->state.cols);
448 mutt_envlist_set("COLUMNS", columns, true);
449
450 if (use_mailcap)
451 {
452 entry = mailcap_entry_new();
453 enum MailcapLookup mailcap_opt = (mode == MUTT_VA_PAGER) ? MUTT_MC_AUTOVIEW : MUTT_MC_NO_FLAGS;
454 if (!mailcap_lookup(a, type, sizeof(type), entry, mailcap_opt))
455 {
456 if ((mode == MUTT_VA_REGULAR) || (mode == MUTT_VA_PAGER))
457 {
458 /* fallback to view as text */
459 mailcap_entry_free(&entry);
460 mutt_error(_("No matching mailcap entry found. Viewing as text."));
461 mode = MUTT_VA_AS_TEXT;
462 use_mailcap = false;
463 }
464 else
465 goto return_error;
466 }
467 }
468
469 if (use_mailcap)
470 {
471 if (!entry->command)
472 {
473 mutt_error(_("MIME type not defined. Can't view attachment."));
474 goto return_error;
475 }
476 mutt_buffer_strcpy(cmd, entry->command);
477
478 fname = mutt_str_dup(a->filename);
479 /* In send mode(!fp), we allow slashes because those are part of
480 * the tmpfile. The path will be removed in expand_filename */
481 mutt_file_sanitize_filename(fname, fp ? true : false);
482 mailcap_expand_filename(entry->nametemplate, fname, tmpfile);
483 FREE(&fname);
484
485 if (mutt_save_attachment(fp, a, mutt_buffer_string(tmpfile), 0, NULL) == -1)
486 goto return_error;
487 has_tempfile = true;
488
490
491 /* check for multipart/related and save attachments with a Content-ID */
492 if (mutt_str_equal(type, "text/html"))
493 {
494 struct Body *related_ancestor = NULL;
495 if (actx->body_idx && (WithCrypto != 0) && (e->security & SEC_ENCRYPT))
496 related_ancestor = attach_body_ancestor(actx->body_idx[0], a, "related");
497 else
498 related_ancestor = attach_body_ancestor(e->body, a, "related");
499 if (related_ancestor)
500 {
501 struct CidMapList cid_map_list = STAILQ_HEAD_INITIALIZER(cid_map_list);
502 mutt_debug(LL_DEBUG2, "viewing text/html attachment in multipart/related group\n");
503 /* save attachments and build cid_map_list Content-ID to filename mapping list */
504 cid_save_attachments(related_ancestor->parts, &cid_map_list);
505 /* replace Content-IDs with filenames */
506 cid_to_filename(tmpfile, &cid_map_list);
507 /* empty Content-ID to filename mapping list */
508 cid_map_list_clear(&cid_map_list);
509 }
510 }
511
512 use_pipe = mailcap_expand_command(a, mutt_buffer_string(tmpfile), type, cmd);
513 use_pager = entry->copiousoutput;
514 }
515
516 if (use_pager)
517 {
518 if (fp && !use_mailcap && a->filename)
519 {
520 /* recv case */
521 mutt_buffer_strcpy(pagerfile, a->filename);
522 mutt_adv_mktemp(pagerfile);
523 }
524 else
525 mutt_buffer_mktemp(pagerfile);
526 }
527
528 if (use_mailcap)
529 {
530 pid_t pid = 0;
531 int fd_temp = -1, fd_pager = -1;
532
533 if (!use_pager)
534 mutt_endwin();
535
536 const bool c_wait_key = cs_subset_bool(NeoMutt->sub, "wait_key");
537 if (use_pager || use_pipe)
538 {
539 if (use_pager && ((fd_pager = mutt_file_open(mutt_buffer_string(pagerfile),
540 O_CREAT | O_EXCL | O_WRONLY)) == -1))
541 {
542 mutt_perror("open");
543 goto return_error;
544 }
545 unlink_pagerfile = true;
546
547 if (use_pipe && ((fd_temp = open(mutt_buffer_string(tmpfile), 0)) == -1))
548 {
549 if (fd_pager != -1)
550 close(fd_pager);
551 mutt_perror("open");
552 goto return_error;
553 }
554 unlink_pagerfile = true;
555
556 pid = filter_create_fd(mutt_buffer_string(cmd), NULL, NULL, NULL,
557 use_pipe ? fd_temp : -1, use_pager ? fd_pager : -1, -1);
558
559 if (pid == -1)
560 {
561 if (fd_pager != -1)
562 close(fd_pager);
563
564 if (fd_temp != -1)
565 close(fd_temp);
566
567 mutt_error(_("Can't create filter"));
568 goto return_error;
569 }
570
571 if (use_pager)
572 {
573 if (a->description)
574 {
575 snprintf(desc, sizeof(desc), _("---Command: %-20.20s Description: %s"),
577 }
578 else
579 {
580 snprintf(desc, sizeof(desc), _("---Command: %-30.30s Attachment: %s"),
581 mutt_buffer_string(cmd), type);
582 }
583 filter_wait(pid);
584 }
585 else
586 {
587 if (wait_interactive_filter(pid) || (entry->needsterminal && c_wait_key))
589 }
590
591 if (fd_temp != -1)
592 close(fd_temp);
593 if (fd_pager != -1)
594 close(fd_pager);
595 }
596 else
597 {
598 /* interactive cmd */
599 int rv = mutt_system(mutt_buffer_string(cmd));
600 if (rv == -1)
601 mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd->data);
602
603 if ((rv != 0) || (entry->needsterminal && c_wait_key))
605 }
606 }
607 else
608 {
609 /* Don't use mailcap; the attachment is viewed in the pager */
610
611 if (mode == MUTT_VA_AS_TEXT)
612 {
613 /* just let me see the raw data */
614 if (fp)
615 {
616 /* Viewing from a received message.
617 *
618 * Don't use mutt_save_attachment() because we want to perform charset
619 * conversion since this will be displayed by the internal pager. */
620 struct State state = { 0 };
621
622 state.fp_out = mutt_file_fopen(mutt_buffer_string(pagerfile), "w");
623 if (!state.fp_out)
624 {
625 mutt_debug(LL_DEBUG1, "mutt_file_fopen(%s) errno=%d %s\n",
626 mutt_buffer_string(pagerfile), errno, strerror(errno));
628 goto return_error;
629 }
630 state.fp_in = fp;
631 state.flags = STATE_CHARCONV;
632 mutt_decode_attachment(a, &state);
633 if (mutt_file_fclose(&state.fp_out) == EOF)
634 {
635 mutt_debug(LL_DEBUG1, "fclose(%s) errno=%d %s\n",
636 mutt_buffer_string(pagerfile), errno, strerror(errno));
637 }
638 }
639 else
640 {
641 /* in compose mode, just copy the file. we can't use
642 * mutt_decode_attachment() since it assumes the content-encoding has
643 * already been applied */
644 if (mutt_save_attachment(fp, a, mutt_buffer_string(pagerfile), MUTT_SAVE_NO_FLAGS, NULL))
645 goto return_error;
646 unlink_pagerfile = true;
647 }
649 }
650 else
651 {
652 /* Use built-in handler */
655 {
656 goto return_error;
657 }
658 unlink_pagerfile = true;
659 }
660
661 if (a->description)
662 mutt_str_copy(desc, a->description, sizeof(desc));
663 else if (a->filename)
664 snprintf(desc, sizeof(desc), _("---Attachment: %s: %s"), a->filename, type);
665 else
666 snprintf(desc, sizeof(desc), _("---Attachment: %s"), type);
667 }
668
669 /* We only reach this point if there have been no errors */
670
671 if (use_pager)
672 {
673 struct PagerData pdata = { 0 };
674 struct PagerView pview = { &pdata };
675
676 pdata.actx = actx;
677 pdata.body = a;
678 pdata.fname = mutt_buffer_string(pagerfile);
679 pdata.fp = fp;
680
681 pview.banner = desc;
683 (is_message ? MUTT_PAGER_MESSAGE : MUTT_PAGER_NO_FLAGS) |
684 ((use_mailcap && entry->xneomuttnowrap) ? MUTT_PAGER_NOWRAP :
686 pview.mode = PAGER_MODE_ATTACH;
687
688 rc = mutt_do_pager(&pview, e);
689
690 mutt_buffer_reset(pagerfile);
691 unlink_pagerfile = false;
692 }
693 else
694 rc = 0;
695
696return_error:
697
698 if (!entry || !entry->xneomuttkeep)
699 {
700 if ((fp && !mutt_buffer_is_empty(tmpfile)) || has_tempfile)
701 {
702 /* add temporary file to TempAttachmentsList to be deleted on timeout hook */
704 }
705 }
706
707 mailcap_entry_free(&entry);
708
709 if (unlink_pagerfile)
711
712 mutt_buffer_pool_release(&tmpfile);
713 mutt_buffer_pool_release(&pagerfile);
715 mutt_envlist_unset("COLUMNS");
716
717 return rc;
718}
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:298
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
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:386
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:123
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:548
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:663
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 *state)
Decode an email's attachment.
Definition: handler.c:1871
@ 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 STATE_DISPLAY
Output is displayed to the user.
Definition: state.h:32
#define STATE_DISPLAY_ATTACH
We are displaying an attachment.
Definition: state.h:40
#define STATE_CHARCONV
Do character set conversions.
Definition: state.h:36
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:807
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:652
int mutt_decode_save_attachment(FILE *fp, struct Body *m, const char *path, StateFlags flags, enum SaveAttach opt)
Decode, then save an attachment.
Definition: mutt_attach.c:1030
static int wait_interactive_filter(pid_t pid)
Wait after an interactive filter.
Definition: mutt_attach.c:383
void mutt_add_temp_attachment(const char *filename)
Add file to list of temporary attachments.
Definition: mutt_attach.c:1292
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:896
@ 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:1441
#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:514
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. STATE_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 729 of file mutt_attach.c.

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

877{
878 if (opt == MUTT_SAVE_APPEND)
879 return fopen(path, "a");
880 if (opt == MUTT_SAVE_OVERWRITE)
881 return fopen(path, "w");
882
883 return mutt_file_fopen(path, "w");
884}
@ 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 896 of file mutt_attach.c.

898{
899 if (!m)
900 return -1;
901
902 if (fp)
903 {
904 /* recv mode */
905
906 if (e && m->email && (m->encoding != ENC_BASE64) &&
908 {
909 /* message type attachments are written to mail folders. */
910
911 char buf[8192] = { 0 };
912 struct Message *msg = NULL;
914 int rc = -1;
915
916 struct Email *e_new = m->email;
917 e_new->msgno = e->msgno; /* required for MH/maildir */
918 e_new->read = true;
919
920 if (!mutt_file_seek(fp, m->offset, SEEK_SET))
921 return -1;
922 if (!fgets(buf, sizeof(buf), fp))
923 return -1;
924 struct Mailbox *m_att = mx_path_resolve(path);
925 if (!mx_mbox_open(m_att, MUTT_APPEND | MUTT_QUIET))
926 {
927 mailbox_free(&m_att);
928 return -1;
929 }
930 msg = mx_msg_open_new(m_att, e_new,
931 is_from(buf, NULL, 0, NULL) ? MUTT_MSG_NO_FLAGS : MUTT_ADD_FROM);
932 if (!msg)
933 {
934 mx_mbox_close(m_att);
935 return -1;
936 }
937 if ((m_att->type == MUTT_MBOX) || (m_att->type == MUTT_MMDF))
938 chflags = CH_FROM | CH_UPDATE_LEN;
939 chflags |= ((m_att->type == MUTT_MAILDIR) ? CH_NOSTATUS : CH_UPDATE);
940 if ((mutt_copy_message_fp(msg->fp, fp, e_new, MUTT_CM_NO_FLAGS, chflags, 0) == 0) &&
941 (mx_msg_commit(m_att, msg) == 0))
942 {
943 rc = 0;
944 }
945 else
946 {
947 rc = -1;
948 }
949
950 mx_msg_close(m_att, &msg);
951 mx_mbox_close(m_att);
952 return rc;
953 }
954 else
955 {
956 /* In recv mode, extract from folder and decode */
957
958 struct State state = { 0 };
959
960 state.fp_out = save_attachment_open(path, opt);
961 if (!state.fp_out)
962 {
963 mutt_perror("fopen");
964 return -1;
965 }
966 if (!mutt_file_seek((state.fp_in = fp), m->offset, SEEK_SET))
967 {
968 mutt_file_fclose(&state.fp_out);
969 return -1;
970 }
971 mutt_decode_attachment(m, &state);
972
973 if (mutt_file_fsync_close(&state.fp_out) != 0)
974 {
975 mutt_perror("fclose");
976 return -1;
977 }
978 }
979 }
980 else
981 {
982 if (!m->filename)
983 return -1;
984
985 /* In send mode, just copy file */
986
987 FILE *fp_old = fopen(m->filename, "r");
988 if (!fp_old)
989 {
990 mutt_perror("fopen");
991 return -1;
992 }
993
994 FILE *fp_new = save_attachment_open(path, opt);
995 if (!fp_new)
996 {
997 mutt_perror("fopen");
998 mutt_file_fclose(&fp_old);
999 return -1;
1000 }
1001
1002 if (mutt_file_copy_stream(fp_old, fp_new) == -1)
1003 {
1004 mutt_error(_("Write fault"));
1005 mutt_file_fclose(&fp_old);
1006 mutt_file_fclose(&fp_new);
1007 return -1;
1008 }
1009 mutt_file_fclose(&fp_old);
1010 if (mutt_file_fsync_close(&fp_new) != 0)
1011 {
1012 mutt_error(_("Write fault"));
1013 return -1;
1014 }
1015 }
1016
1017 return 0;
1018}
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:641
#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:167
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:876
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1192
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:303
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1056
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition: mx.c:1171
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1676
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition: mx.c:614
#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:110
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,
StateFlags  flags,
enum SaveAttach  opt 
)

Decode, then save an attachment.

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

Definition at line 1030 of file mutt_attach.c.

1032{
1033 struct State state = { 0 };
1034 unsigned int saved_encoding = 0;
1035 struct Body *saved_parts = NULL;
1036 struct Email *e_saved = NULL;
1037 int rc = 0;
1038
1039 state.flags = flags;
1040
1041 if (opt == MUTT_SAVE_APPEND)
1042 state.fp_out = fopen(path, "a");
1043 else if (opt == MUTT_SAVE_OVERWRITE)
1044 state.fp_out = fopen(path, "w");
1045 else
1046 state.fp_out = mutt_file_fopen(path, "w");
1047
1048 if (!state.fp_out)
1049 {
1050 mutt_perror("fopen");
1051 return -1;
1052 }
1053
1054 if (!fp)
1055 {
1056 /* When called from the compose menu, the attachment isn't parsed,
1057 * so we need to do it here. */
1058 state.fp_in = fopen(m->filename, "r");
1059 if (!state.fp_in)
1060 {
1061 mutt_perror("fopen");
1062 mutt_file_fclose(&state.fp_out);
1063 return -1;
1064 }
1065
1066 struct stat st = { 0 };
1067 if (fstat(fileno(state.fp_in), &st) == -1)
1068 {
1069 mutt_perror("stat");
1070 mutt_file_fclose(&state.fp_in);
1071 mutt_file_fclose(&state.fp_out);
1072 return -1;
1073 }
1074
1075 saved_encoding = m->encoding;
1076 if (!is_multipart(m))
1077 m->encoding = ENC_8BIT;
1078
1079 m->length = st.st_size;
1080 m->offset = 0;
1081 saved_parts = m->parts;
1082 e_saved = m->email;
1083 mutt_parse_part(state.fp_in, m);
1084
1085 if (m->noconv || is_multipart(m))
1086 state.flags |= STATE_CHARCONV;
1087 }
1088 else
1089 {
1090 state.fp_in = fp;
1091 state.flags |= STATE_CHARCONV;
1092 }
1093
1094 mutt_body_handler(m, &state);
1095
1096 if (mutt_file_fsync_close(&state.fp_out) != 0)
1097 {
1098 mutt_perror("fclose");
1099 rc = -1;
1100 }
1101 if (!fp)
1102 {
1103 m->length = 0;
1104 m->encoding = saved_encoding;
1105 if (saved_parts)
1106 {
1107 email_free(&m->email);
1108 m->parts = saved_parts;
1109 m->email = e_saved;
1110 }
1111 mutt_file_fclose(&state.fp_in);
1112 }
1113
1114 return rc;
1115}
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition: handler.c:1601
@ 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:1736
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 1130 of file mutt_attach.c.

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

1293{
1295}
struct ListHead TempAttachmentsList
List of temporary files for displaying attachments.
Definition: globals.c:53
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
+ 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 1300 of file mutt_attach.c.

1301{
1302 struct ListNode *np = NULL;
1303
1304 STAILQ_FOREACH(np, &TempAttachmentsList, entries)
1305 {
1306 (void) mutt_file_chmod_add(np->data, S_IWUSR);
1308 }
1309
1311}
int mutt_file_chmod_add(const char *path, mode_t mode)
Add permissions to a file.
Definition: file.c:1139
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: