NeoMutt  2024-02-01-35-geee02f
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
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 "imap/lib.h"
#include "ncrypt/lib.h"
#include "pager/lib.h"
#include "question/lib.h"
#include "send/lib.h"
#include "attach.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 dependency graph for mutt_attach.c:

Go to the source code of this file.

Functions

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

Detailed Description

Handling of email attachments.

Authors
  • Pietro Cerutti
  • Richard Russon
  • Ihor Antonov
  • David Purton
  • Dennis Schön

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 b)

Get a temporary copy of an attachment.

Parameters
bAttachment to copy
Return values
0Success
-1Error

Definition at line 71 of file mutt_attach.c.

72{
73 char type[256] = { 0 };
74
75 if (b->unlink)
76 return 0;
77
78 struct Buffer *tmpfile = buf_pool_get();
79 struct MailcapEntry *entry = mailcap_entry_new();
80 snprintf(type, sizeof(type), "%s/%s", TYPE(b), b->subtype);
81 mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_NO_FLAGS);
83
84 mailcap_entry_free(&entry);
85
86 FILE *fp_in = NULL, *fp_out = NULL;
87 if ((fp_in = mutt_file_fopen(b->filename, "r")) &&
88 (fp_out = mutt_file_fopen(buf_string(tmpfile), "w")))
89 {
90 mutt_file_copy_stream(fp_in, fp_out);
92 b->unlink = true;
93
94 struct stat st = { 0 };
95 if ((fstat(fileno(fp_in), &st) == 0) && (b->stamp >= st.st_mtime))
96 {
98 }
99 }
100 else
101 {
102 mutt_perror("%s", fp_in ? buf_string(tmpfile) : b->filename);
103 }
104
105 mutt_file_fclose(&fp_in);
106 mutt_file_fclose(&fp_out);
107
108 buf_pool_release(&tmpfile);
109
110 return b->unlink ? 0 : -1;
111}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:97
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:282
#define mutt_file_fclose(FP)
Definition: file.h:148
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:147
#define mutt_perror(...)
Definition: logging2.h:93
void mailcap_entry_free(struct MailcapEntry **ptr)
Deallocate an struct MailcapEntry.
Definition: mailcap.c:454
struct MailcapEntry * mailcap_entry_new(void)
Allocate memory for a new rfc1524 entry.
Definition: mailcap.c:445
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:552
bool mailcap_lookup(struct Body *b, char *type, size_t typelen, struct MailcapEntry *entry, enum MailcapLookup opt)
Find given type in the list of mailcap files.
Definition: mailcap.c:483
@ MUTT_MC_NO_FLAGS
No flags set.
Definition: mailcap.h:57
#define TYPE(body)
Definition: mime.h:89
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:329
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
void mutt_stamp_attachment(struct Body *b)
Timestamp an Attachment.
Definition: sendlib.c:409
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:36
A mailcap entry.
Definition: mailcap.h:37
char * nametemplate
Definition: mailcap.h:44
+ 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 b)

Create an attachment.

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

Definition at line 119 of file mutt_attach.c.

120{
121 char type[256] = { 0 };
122 struct MailcapEntry *entry = mailcap_entry_new();
123 bool unlink_newfile = false;
124 int rc = 0;
125 struct Buffer *cmd = buf_pool_get();
126 struct Buffer *newfile = buf_pool_get();
127 struct Buffer *tmpfile = buf_pool_get();
128
129 snprintf(type, sizeof(type), "%s/%s", TYPE(b), b->subtype);
130 if (mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_COMPOSE))
131 {
132 if (entry->composecommand || entry->composetypecommand)
133 {
134 if (entry->composetypecommand)
135 buf_strcpy(cmd, entry->composetypecommand);
136 else
137 buf_strcpy(cmd, entry->composecommand);
138
139 mailcap_expand_filename(entry->nametemplate, b->filename, newfile);
140 mutt_debug(LL_DEBUG1, "oldfile: %s newfile: %s\n", b->filename,
141 buf_string(newfile));
142 if (mutt_file_symlink(b->filename, buf_string(newfile)) == -1)
143 {
144 if (query_yesorno(_("Can't match 'nametemplate', continue?"), MUTT_YES) != MUTT_YES)
145 goto bailout;
146 buf_strcpy(newfile, b->filename);
147 }
148 else
149 {
150 unlink_newfile = true;
151 }
152
153 if (mailcap_expand_command(b, buf_string(newfile), type, cmd))
154 {
155 /* For now, editing requires a file, no piping */
156 mutt_error(_("Mailcap compose entry requires %%s"));
157 }
158 else
159 {
160 int r;
161
162 mutt_endwin();
163 r = mutt_system(buf_string(cmd));
164 if (r == -1)
165 mutt_error(_("Error running \"%s\""), buf_string(cmd));
166
167 if ((r != -1) && entry->composetypecommand)
168 {
169 FILE *fp = mutt_file_fopen(b->filename, "r");
170 if (!fp)
171 {
172 mutt_perror(_("Failure to open file to parse headers"));
173 goto bailout;
174 }
175
176 struct Body *b_mime = mutt_read_mime_header(fp, 0);
177 if (b_mime)
178 {
179 if (!TAILQ_EMPTY(&b_mime->parameter))
180 {
182 b->parameter = b_mime->parameter;
183 TAILQ_INIT(&b_mime->parameter);
184 }
185 if (b_mime->description)
186 {
187 FREE(&b->description);
188 b->description = b_mime->description;
189 b_mime->description = NULL;
190 }
191 if (b_mime->form_name)
192 {
193 FREE(&b->form_name);
194 b->form_name = b_mime->form_name;
195 b_mime->form_name = NULL;
196 }
197
198 /* Remove headers by copying out data to another file, then
199 * copying the file back */
200 const LOFF_T offset = b_mime->offset;
201 mutt_body_free(&b_mime);
202 if (!mutt_file_seek(fp, offset, SEEK_SET))
203 {
204 goto bailout;
205 }
206
207 buf_mktemp(tmpfile);
208 FILE *fp_tmp = mutt_file_fopen(buf_string(tmpfile), "w");
209 if (!fp_tmp)
210 {
211 mutt_perror(_("Failure to open file to strip headers"));
212 mutt_file_fclose(&fp);
213 goto bailout;
214 }
215 mutt_file_copy_stream(fp, fp_tmp);
216 mutt_file_fclose(&fp);
217 mutt_file_fclose(&fp_tmp);
219 if (mutt_file_rename(buf_string(tmpfile), b->filename) != 0)
220 {
221 mutt_perror(_("Failure to rename file"));
222 goto bailout;
223 }
224 }
225 }
226 }
227 }
228 }
229 else
230 {
231 mutt_message(_("No mailcap compose entry for %s, creating empty file"), type);
232 rc = 1;
233 goto bailout;
234 }
235
236 rc = 1;
237
238bailout:
239
240 if (unlink_newfile)
241 unlink(buf_string(newfile));
242
243 buf_pool_release(&cmd);
244 buf_pool_release(&newfile);
245 buf_pool_release(&tmpfile);
246
247 mailcap_entry_free(&entry);
248 return rc;
249}
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:412
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:153
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:58
struct Body * mutt_read_mime_header(FILE *fp, bool digest)
Parse a MIME header.
Definition: parse.c:1318
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:771
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition: file.c:1412
int mutt_file_symlink(const char *oldpath, const char *newpath)
Create a symlink.
Definition: file.c:310
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:216
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_message(...)
Definition: logging2.h:91
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
int mailcap_expand_command(struct Body *b, const char *filename, const char *type, struct Buffer *command)
Expand expandos in a command.
Definition: mailcap.c:69
@ MUTT_MC_COMPOSE
Mailcap compose field.
Definition: mailcap.h:59
#define FREE(x)
Definition: memory.h:45
#define _(a)
Definition: message.h:28
void mutt_param_free(struct ParameterList *pl)
Free a ParameterList.
Definition: parameter.c:62
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 query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:334
#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:40
char * composetypecommand
Definition: mailcap.h:41
#define buf_mktemp(buf)
Definition: tmp.h:33
+ 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 b)

Edit an attachment.

Parameters
bEmail 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 264 of file mutt_attach.c.

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

343{
344 struct ListNode *np = NULL;
345 STAILQ_FOREACH(np, &MimeLookupList, entries)
346 {
347 const int i = mutt_str_len(np->data) - 1;
348 if (((i > 0) && (np->data[i - 1] == '/') && (np->data[i] == '*') &&
349 mutt_istrn_equal(type, np->data, i)) ||
350 mutt_istr_equal(type, np->data))
351 {
352 struct Body tmp = { 0 };
353 enum ContentType n;
354 if ((n = mutt_lookup_mime_type(&tmp, b->filename)) != TYPE_OTHER ||
356 {
357 snprintf(type, len, "%s/%s",
358 (n == TYPE_AUDIO) ? "audio" :
359 (n == TYPE_APPLICATION) ? "application" :
360 (n == TYPE_IMAGE) ? "image" :
361 (n == TYPE_MESSAGE) ? "message" :
362 (n == TYPE_MODEL) ? "model" :
363 (n == TYPE_MULTIPART) ? "multipart" :
364 (n == TYPE_TEXT) ? "text" :
365 (n == TYPE_VIDEO) ? "video" :
366 "other",
367 tmp.subtype);
368 mutt_debug(LL_DEBUG1, "\"%s\" -> %s\n", b->filename, type);
369 }
370 FREE(&tmp.subtype);
371 FREE(&tmp.xtype);
372 }
373 }
374}
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:721
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:545
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:502
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
enum ContentType mutt_lookup_mime_type(struct Body *b, const char *path)
Find the MIME type for an attachment.
Definition: sendlib.c:75
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 389 of file mutt_attach.c.

390{
391 int rc;
392
393 rc = imap_wait_keep_alive(pid);
395 rc = WIFEXITED(rc) ? WEXITSTATUS(rc) : -1;
396
397 return rc;
398}
int imap_wait_keep_alive(pid_t pid)
Wait for a process to change state.
Definition: util.c:979
void mutt_sig_unblock_system(bool restore)
Restore previously blocked signals.
Definition: signal.c:223
+ 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 b,
enum ViewAttachMode  mode,
struct Email e,
struct AttachCtx actx,
struct MuttWindow win 
)

View an attachment.

Parameters
fpSource file stream. Can be NULL
bThe 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 418 of file mutt_attach.c.

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

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

886{
887 if (opt == MUTT_SAVE_APPEND)
888 return mutt_file_fopen(path, "a");
889 if (opt == MUTT_SAVE_OVERWRITE)
890 return mutt_file_fopen(path, "w");
891
892 return mutt_file_fopen(path, "w");
893}
@ MUTT_SAVE_APPEND
Append to existing file.
Definition: mutt_attach.h:59
@ MUTT_SAVE_OVERWRITE
Overwrite existing file.
Definition: mutt_attach.h:60
+ Here is the caller graph for this function:

◆ mutt_save_attachment()

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

Save an attachment.

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

Definition at line 905 of file mutt_attach.c.

907{
908 if (!b)
909 return -1;
910
911 if (fp)
912 {
913 /* recv mode */
914
915 if (e && b->email && (b->encoding != ENC_BASE64) &&
917 {
918 /* message type attachments are written to mail folders. */
919
920 char buf[8192] = { 0 };
921 struct Message *msg = NULL;
923 int rc = -1;
924
925 struct Email *e_new = b->email;
926 e_new->msgno = e->msgno; /* required for MH/maildir */
927 e_new->read = true;
928
929 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
930 return -1;
931 if (!fgets(buf, sizeof(buf), fp))
932 return -1;
933 struct Mailbox *m_att = mx_path_resolve(path);
934 if (!mx_mbox_open(m_att, MUTT_APPEND | MUTT_QUIET))
935 {
936 mailbox_free(&m_att);
937 return -1;
938 }
939 msg = mx_msg_open_new(m_att, e_new,
940 is_from(buf, NULL, 0, NULL) ? MUTT_MSG_NO_FLAGS : MUTT_ADD_FROM);
941 if (!msg)
942 {
943 mx_mbox_close(m_att);
944 return -1;
945 }
946 if ((m_att->type == MUTT_MBOX) || (m_att->type == MUTT_MMDF))
947 chflags = CH_FROM | CH_UPDATE_LEN;
948 chflags |= ((m_att->type == MUTT_MAILDIR) ? CH_NOSTATUS : CH_UPDATE);
949 if ((mutt_copy_message_fp(msg->fp, fp, e_new, MUTT_CM_NO_FLAGS, chflags, 0) == 0) &&
950 (mx_msg_commit(m_att, msg) == 0))
951 {
952 rc = 0;
953 }
954 else
955 {
956 rc = -1;
957 }
958
959 mx_msg_close(m_att, &msg);
960 mx_mbox_close(m_att);
961 return rc;
962 }
963 else
964 {
965 /* In recv mode, extract from folder and decode */
966
967 struct State state = { 0 };
968
969 state.fp_out = save_attachment_open(path, opt);
970 if (!state.fp_out)
971 {
972 mutt_perror("fopen");
973 return -1;
974 }
975 if (!mutt_file_seek((state.fp_in = fp), b->offset, SEEK_SET))
976 {
977 mutt_file_fclose(&state.fp_out);
978 return -1;
979 }
980 mutt_decode_attachment(b, &state);
981
982 if (mutt_file_fsync_close(&state.fp_out) != 0)
983 {
984 mutt_perror("fclose");
985 return -1;
986 }
987 }
988 }
989 else
990 {
991 if (!b->filename)
992 return -1;
993
994 /* In send mode, just copy file */
995
996 FILE *fp_old = mutt_file_fopen(b->filename, "r");
997 if (!fp_old)
998 {
999 mutt_perror("fopen");
1000 return -1;
1001 }
1002
1003 FILE *fp_new = save_attachment_open(path, opt);
1004 if (!fp_new)
1005 {
1006 mutt_perror("fopen");
1007 mutt_file_fclose(&fp_old);
1008 return -1;
1009 }
1010
1011 if (mutt_file_copy_stream(fp_old, fp_new) == -1)
1012 {
1013 mutt_error(_("Write fault"));
1014 mutt_file_fclose(&fp_old);
1015 mutt_file_fclose(&fp_new);
1016 return -1;
1017 }
1018 mutt_file_fclose(&fp_old);
1019 if (mutt_file_fsync_close(&fp_new) != 0)
1020 {
1021 mutt_error(_("Write fault"));
1022 return -1;
1023 }
1024 }
1025
1026 return 0;
1027}
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:653
#define CH_UPDATE
Update the status and x-status fields?
Definition: copy.h:54
#define CH_NOSTATUS
Suppress the status and x-status fields.
Definition: copy.h:60
#define CH_FROM
Retain the "From " message separator?
Definition: copy.h:58
uint32_t CopyHeaderFlags
Flags for mutt_copy_header(), e.g. CH_UPDATE.
Definition: copy.h:52
#define CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition: copy.h:64
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:37
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:53
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:90
@ 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
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition: file.c:188
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition: from.c:49
@ 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:885
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition: mx.c:1178
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:286
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1038
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition: mx.c:1157
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1634
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition: mx.c:596
#define MUTT_ADD_FROM
add a From_ line
Definition: mx.h:40
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition: mx.h:39
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mxapi.h:42
#define MUTT_QUIET
Do not print any messages.
Definition: mxapi.h:44
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:41
The envelope/body of an email.
Definition: email.h:39
bool read
Email is read.
Definition: email.h:50
int msgno
Number displayed to the user.
Definition: email.h:114
A mailbox.
Definition: mailbox.h:79
enum MailboxType type
Mailbox type.
Definition: mailbox.h:102
A local copy of an email.
Definition: message.h:34
FILE * fp
pointer to the message data
Definition: message.h:35
+ 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 b,
const char *  path,
StateFlags  flags,
enum SaveAttach  opt 
)

Decode, then save an attachment.

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

Definition at line 1039 of file mutt_attach.c.

1041{
1042 struct State state = { 0 };
1043 unsigned int saved_encoding = 0;
1044 struct Body *saved_parts = NULL;
1045 struct Email *e_saved = NULL;
1046 int rc = 0;
1047
1048 state.flags = flags;
1049
1050 if (opt == MUTT_SAVE_APPEND)
1051 state.fp_out = mutt_file_fopen(path, "a");
1052 else if (opt == MUTT_SAVE_OVERWRITE)
1053 state.fp_out = mutt_file_fopen(path, "w");
1054 else
1055 state.fp_out = mutt_file_fopen(path, "w");
1056
1057 if (!state.fp_out)
1058 {
1059 mutt_perror("fopen");
1060 return -1;
1061 }
1062
1063 if (fp)
1064 {
1065 state.fp_in = fp;
1066 state.flags |= STATE_CHARCONV;
1067 }
1068 else
1069 {
1070 /* When called from the compose menu, the attachment isn't parsed,
1071 * so we need to do it here. */
1072 state.fp_in = mutt_file_fopen(b->filename, "r");
1073 if (!state.fp_in)
1074 {
1075 mutt_perror("fopen");
1076 mutt_file_fclose(&state.fp_out);
1077 return -1;
1078 }
1079
1080 struct stat st = { 0 };
1081 if (fstat(fileno(state.fp_in), &st) == -1)
1082 {
1083 mutt_perror("stat");
1084 mutt_file_fclose(&state.fp_in);
1085 mutt_file_fclose(&state.fp_out);
1086 return -1;
1087 }
1088
1089 saved_encoding = b->encoding;
1090 if (!is_multipart(b))
1091 b->encoding = ENC_8BIT;
1092
1093 b->length = st.st_size;
1094 b->offset = 0;
1095 saved_parts = b->parts;
1096 e_saved = b->email;
1097 mutt_parse_part(state.fp_in, b);
1098
1099 if (b->noconv || is_multipart(b))
1100 state.flags |= STATE_CHARCONV;
1101 }
1102
1103 mutt_body_handler(b, &state);
1104
1105 if (mutt_file_fsync_close(&state.fp_out) != 0)
1106 {
1107 mutt_perror("fclose");
1108 rc = -1;
1109 }
1110 if (!fp)
1111 {
1112 b->length = 0;
1113 b->encoding = saved_encoding;
1114 if (saved_parts)
1115 {
1116 email_free(&b->email);
1117 b->parts = saved_parts;
1118 b->email = e_saved;
1119 }
1120 mutt_file_fclose(&state.fp_in);
1121 }
1122
1123 return rc;
1124}
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:46
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition: parse.c:1748
int mutt_body_handler(struct Body *b, struct State *state)
Handler for the Body of an email.
Definition: handler.c:1631
@ ENC_8BIT
8-bit text
Definition: mime.h:50
#define is_multipart(body)
Definition: mime.h:82
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:70
+ 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 b 
)

Print out an attachment.

Parameters
fpFile to write to
bAttachment
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 1139 of file mutt_attach.c.

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

1300{
1302}
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_temp_attachments_cleanup()

void mutt_temp_attachments_cleanup ( void  )

Delete all temporary attachments.

Definition at line 1307 of file mutt_attach.c.

1308{
1309 struct ListNode *np = NULL;
1310
1311 STAILQ_FOREACH(np, &TempAttachmentsList, entries)
1312 {
1313 (void) mutt_file_chmod_add(np->data, S_IWUSR);
1315 }
1316
1318}
int mutt_file_chmod_add(const char *path, mode_t mode)
Add permissions to a file.
Definition: file.c:1157
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: