NeoMutt  2025-12-11-980-ge38c27
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/types.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.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 "module_data.h"
#include "muttlib.h"
#include "mx.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 (struct ListHead *list)
 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 68 of file mutt_attach.c.

69{
70 char type[256] = { 0 };
71
72 if (b->unlink)
73 return 0;
74
75 struct Buffer *tempfile = buf_pool_get();
76 struct MailcapEntry *entry = mailcap_entry_new();
77 snprintf(type, sizeof(type), "%s/%s", BODY_TYPE(b), b->subtype);
78 mailcap_lookup(b, type, sizeof(type), entry, MUTT_MC_NONE);
79 mailcap_expand_filename(entry->nametemplate, b->filename, tempfile);
80
81 mailcap_entry_free(&entry);
82
83 FILE *fp_in = NULL;
84 FILE *fp_out = NULL;
85 if ((fp_in = mutt_file_fopen(b->filename, "r")) &&
86 (fp_out = mutt_file_fopen(buf_string(tempfile), "w")))
87 {
88 mutt_file_copy_stream(fp_in, fp_out);
89 mutt_str_replace(&b->filename, buf_string(tempfile));
90 b->unlink = true;
91
92 struct stat st = { 0 };
93 if ((fstat(fileno(fp_in), &st) == 0) && (b->stamp >= st.st_mtime))
94 {
96 }
97 }
98 else
99 {
100 mutt_perror("%s", fp_in ? buf_string(tempfile) : b->filename);
101 }
102
103 mutt_file_fclose(&fp_in);
104 mutt_file_fclose(&fp_out);
105
106 buf_pool_release(&tempfile);
107
108 return b->unlink ? 0 : -1;
109}
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition file.c:224
#define mutt_file_fclose(FP)
Definition file.h:144
#define mutt_file_fopen(PATH, MODE)
Definition file.h:143
#define mutt_perror(...)
Definition logging2.h:95
void mailcap_entry_free(struct MailcapEntry **ptr)
Deallocate an struct MailcapEntry.
Definition mailcap.c:455
struct MailcapEntry * mailcap_entry_new(void)
Allocate memory for a new rfc1524 entry.
Definition mailcap.c:446
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:568
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:484
@ MUTT_MC_NONE
No flags set.
Definition mailcap.h:57
#define BODY_TYPE(body)
Get the type name of a body part.
Definition mime.h:93
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
void mutt_stamp_attachment(struct Body *b)
Timestamp an Attachment.
Definition sendlib.c:412
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition body.h:68
time_t stamp
Time stamp of last encoding update.
Definition body.h:77
char * subtype
content-type subtype
Definition body.h:61
char * filename
When sending a message, this is the file to which this structure refers.
Definition body.h:59
String manipulation buffer.
Definition buffer.h:36
A mailcap entry.
Definition mailcap.h:37
char * nametemplate
Filename template.
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 117 of file mutt_attach.c.

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

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

341{
343 ASSERT(mod_data);
344
345 struct ListNode *np = NULL;
346 STAILQ_FOREACH(np, &mod_data->mime_lookup, entries)
347 {
348 const int i = (int) mutt_str_len(np->data) - 1;
349 if (((i > 0) && (np->data[i - 1] == '/') && (np->data[i] == '*') &&
350 mutt_istrn_equal(type, np->data, i)) ||
351 mutt_istr_equal(type, np->data))
352 {
353 struct Body tmp = { 0 };
354 enum ContentType n;
355 if ((n = mutt_lookup_mime_type(&tmp, b->filename)) != TYPE_OTHER ||
357 {
358 snprintf(type, len, "%s/%s",
359 (n == TYPE_AUDIO) ? "audio" :
360 (n == TYPE_APPLICATION) ? "application" :
361 (n == TYPE_IMAGE) ? "image" :
362 (n == TYPE_MESSAGE) ? "message" :
363 (n == TYPE_MODEL) ? "model" :
364 (n == TYPE_MULTIPART) ? "multipart" :
365 (n == TYPE_TEXT) ? "text" :
366 (n == TYPE_VIDEO) ? "video" :
367 "other",
368 tmp.subtype);
369 mutt_debug(LL_DEBUG1, "\"%s\" -> %s\n", b->filename, type);
370 }
371 FREE(&tmp.subtype);
372 FREE(&tmp.xtype);
373 }
374 }
375}
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
@ MODULE_ID_ATTACH
ModuleAttach, Attachments
Definition module_api.h:49
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:678
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:503
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:457
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:666
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
enum ContentType mutt_lookup_mime_type(struct Body *b, const char *path)
Find the MIME type for an attachment.
Definition sendlib.c:75
#define ASSERT(COND)
Definition signal2.h:59
Attach private Module data.
Definition module_data.h:32
struct ListHead mime_lookup
List of mime types that that shouldn't use the mailcap entry.
Definition module_data.h:42
char * xtype
content-type if x-unknown
Definition body.h:62
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
+ 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 390 of file mutt_attach.c.

391{
392 int rc;
393
394 rc = imap_wait_keep_alive(pid);
396 rc = WIFEXITED(rc) ? WEXITSTATUS(rc) : -1;
397
398 return rc;
399}
int imap_wait_keep_alive(pid_t pid)
Wait for a process to change state.
Definition util.c:1034
void mutt_sig_unblock_system(bool restore)
Restore previously blocked signals.
Definition signal.c:284
+ 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 419 of file mutt_attach.c.

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

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

891{
892 if (opt == MUTT_SAVE_APPEND)
893 return mutt_file_fopen_masked(path, "a");
894 else
895 return mutt_file_fopen_masked(path, "w");
896}
@ MUTT_SAVE_APPEND
Append to existing file.
Definition mutt_attach.h:60
#define mutt_file_fopen_masked(PATH, MODE)
Open a file with proper permissions, tracked for debugging.
Definition neomutt.h:92
+ 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 908 of file mutt_attach.c.

910{
911 if (!b)
912 return -1;
913
914 if (fp)
915 {
916 /* recv mode */
917
918 if (e && b->email && (b->encoding != ENC_BASE64) &&
920 {
921 /* message type attachments are written to mail folders. */
922
923 char buf[8192] = { 0 };
924 struct Message *msg = NULL;
925 CopyHeaderFlags chflags = CH_NONE;
926 int rc = -1;
927
928 struct Email *e_new = b->email;
929 e_new->msgno = e->msgno; /* required for MH/maildir */
930 e_new->read = true;
931
932 if (!mutt_file_seek(fp, b->offset, SEEK_SET))
933 return -1;
934 if (!fgets(buf, sizeof(buf), fp))
935 return -1;
936 struct Mailbox *m_att = mx_path_resolve(path);
937 if (!mx_mbox_open(m_att, MUTT_APPEND | MUTT_QUIET))
938 {
939 mailbox_free(&m_att);
940 return -1;
941 }
942 msg = mx_msg_open_new(m_att, e_new, is_from(buf, NULL, 0, NULL) ? MUTT_MSG_NONE : MUTT_ADD_FROM);
943 if (!msg)
944 {
945 mx_mbox_close(m_att);
946 return -1;
947 }
948 if ((m_att->type == MUTT_MBOX) || (m_att->type == MUTT_MMDF))
949 chflags = CH_FROM | CH_UPDATE_LEN;
950 chflags |= ((m_att->type == MUTT_MAILDIR) ? CH_NOSTATUS : CH_UPDATE);
951 if ((mutt_copy_message_fp(msg->fp, fp, e_new, MUTT_CM_NONE, chflags, 0) == 0) &&
952 (mx_msg_commit(m_att, msg) == 0))
953 {
954 rc = 0;
955 }
956 else
957 {
958 rc = -1;
959 }
960
961 mx_msg_close(m_att, &msg);
962 mx_mbox_close(m_att);
963 return rc;
964 }
965 else
966 {
967 /* In recv mode, extract from folder and decode */
968
969 struct State state = { 0 };
970
971 state.fp_out = save_attachment_open(path, opt);
972 if (!state.fp_out)
973 {
974 mutt_perror("fopen");
975 return -1;
976 }
977 if (!mutt_file_seek((state.fp_in = fp), b->offset, SEEK_SET))
978 {
979 mutt_file_fclose(&state.fp_out);
980 return -1;
981 }
982 mutt_decode_attachment(b, &state);
983
984 if (mutt_file_fsync_close(&state.fp_out) != 0)
985 {
986 mutt_perror("fclose");
987 return -1;
988 }
989 }
990 }
991 else
992 {
993 if (!b->filename)
994 return -1;
995
996 /* In send mode, just copy file */
997
998 FILE *fp_old = mutt_file_fopen(b->filename, "r");
999 if (!fp_old)
1000 {
1001 mutt_perror("fopen");
1002 return -1;
1003 }
1004
1005 FILE *fp_new = save_attachment_open(path, opt);
1006 if (!fp_new)
1007 {
1008 mutt_perror("fopen");
1009 mutt_file_fclose(&fp_old);
1010 return -1;
1011 }
1012
1013 if (mutt_file_copy_stream(fp_old, fp_new) == -1)
1014 {
1015 mutt_error(_("Write fault"));
1016 mutt_file_fclose(&fp_old);
1017 mutt_file_fclose(&fp_new);
1018 return -1;
1019 }
1020 mutt_file_fclose(&fp_old);
1021 if (mutt_file_fsync_close(&fp_new) != 0)
1022 {
1023 mutt_error(_("Write fault"));
1024 return -1;
1025 }
1026 }
1027
1028 return 0;
1029}
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_email.c:663
@ MUTT_CM_NONE
No flags are set.
Definition copy_email.h:41
uint32_t CopyHeaderFlags
Definition copy_email.h:89
@ CH_UPDATE
Update the status and x-status fields?
Definition copy_email.h:66
@ CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition copy_email.h:76
@ CH_FROM
Retain the "From " message separator?
Definition copy_email.h:70
@ CH_NOSTATUS
Suppress the status and x-status fields.
Definition copy_email.h:72
@ CH_NONE
No flags are set.
Definition copy_email.h:65
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition mailbox.c:90
@ MUTT_MMDF
'mmdf' Mailbox type
Definition mailbox.h:45
@ MUTT_MBOX
'mbox' Mailbox type
Definition mailbox.h:44
@ MUTT_MAILDIR
'Maildir' Mailbox type
Definition mailbox.h:47
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition file.c:128
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.
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition mx.c:1185
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition mx.c:285
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition mx.c:1044
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition mx.c:1164
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition mx.c:1650
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition mx.c:595
@ MUTT_ADD_FROM
add a From_ line
Definition mx.h:43
@ MUTT_MSG_NONE
No flags are set.
Definition mx.h:42
@ MUTT_QUIET
Do not print any messages.
Definition mxapi.h:46
@ MUTT_APPEND
Open mailbox for appending 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:111
A mailbox.
Definition mailbox.h:81
enum MailboxType type
Mailbox type.
Definition mailbox.h:104
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 1041 of file mutt_attach.c.

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

1301{
1303 ASSERT(mod_data);
1304
1306}
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition list.c:65
struct ListHead temp_attachments
List of temporary files for displaying attachments.
Definition module_data.h:43
+ 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 ( struct ListHead * list)

Delete all temporary attachments.

Parameters
listList of temporary attachment files

Definition at line 1312 of file mutt_attach.c.

1313{
1314 if (!list)
1315 return;
1316
1317 struct ListNode *np = NULL;
1318
1319 STAILQ_FOREACH(np, list, entries)
1320 {
1321 (void) mutt_file_chmod_add(np->data, S_IWUSR);
1323 }
1324
1325 mutt_list_free(list);
1326}
int mutt_file_chmod_add(const char *path, mode_t mode)
Add permissions to a file.
Definition file.c:1002
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition list.c:123
+ Here is the call graph for this function:
+ Here is the caller graph for this function: