NeoMutt  2024-04-16-36-g75b6fb
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
attachments.c
Go to the documentation of this file.
1
30#include "config.h"
31#include <stdbool.h>
32#include <stdint.h>
33#include <stdio.h>
34#include <string.h>
35#include "mutt/lib.h"
36#include "config/lib.h"
37#include "email/lib.h"
38#include "core/lib.h"
39#include "gui/lib.h"
40#include "attachments.h"
41#include "ncrypt/lib.h"
42#include "parse/lib.h"
43#include "mview.h"
44
49{
50 const char *major;
52 const char *minor;
53 regex_t minor_regex;
54};
55
60static struct Notify *AttachmentsNotify = NULL;
61
69static void attachmatch_free(struct AttachMatch **ptr)
70{
71 if (!ptr || !*ptr)
72 return;
73
74 struct AttachMatch *am = *ptr;
75 regfree(&am->minor_regex);
76 FREE(&am->major);
77 FREE(ptr);
78}
79
84static struct AttachMatch *attachmatch_new(void)
85{
86 return mutt_mem_calloc(1, sizeof(struct AttachMatch));
87}
88
93{
95
96 /* Lists of AttachMatch */
101}
102
106void attach_init(void)
107{
109 return;
110
113}
114
122static bool count_body_parts_check(struct ListHead *checklist, struct Body *b, bool dflt)
123{
124 /* If list is null, use default behavior. */
125 if (!checklist || STAILQ_EMPTY(checklist))
126 {
127 return false;
128 }
129
130 struct AttachMatch *a = NULL;
131 struct ListNode *np = NULL;
132 STAILQ_FOREACH(np, checklist, entries)
133 {
134 a = (struct AttachMatch *) np->data;
135 mutt_debug(LL_DEBUG3, "%s %d/%s ?? %s/%s [%d]... ", dflt ? "[OK] " : "[EXCL] ",
136 b->type, b->subtype ? b->subtype : "*", a->major, a->minor, a->major_int);
137 if (((a->major_int == TYPE_ANY) || (a->major_int == b->type)) &&
138 (!b->subtype || (regexec(&a->minor_regex, b->subtype, 0, NULL, 0) == 0)))
139 {
140 mutt_debug(LL_DEBUG3, "yes\n");
141 return true;
142 }
143 else
144 {
145 mutt_debug(LL_DEBUG3, "no\n");
146 }
147 }
148
149 return false;
150}
151
157static int count_body_parts(struct Body *b)
158{
159 if (!b)
160 return 0;
161
162 int count = 0;
163
164 for (struct Body *bp = b; bp; bp = bp->next)
165 {
166 /* Initial disposition is to count and not to recurse this part. */
167 bool shallcount = true; /* default */
168 bool shallrecurse = false;
169
170 mutt_debug(LL_DEBUG5, "desc=\"%s\"; fn=\"%s\", type=\"%d/%s\"\n",
171 bp->description ? bp->description : ("none"),
172 bp->filename ? bp->filename :
173 bp->d_filename ? bp->d_filename :
174 "(none)",
175 bp->type, bp->subtype ? bp->subtype : "*");
176
177 if (bp->type == TYPE_MESSAGE)
178 {
179 shallrecurse = true;
180
181 /* If it's an external body pointer, don't recurse it. */
182 if (mutt_istr_equal(bp->subtype, "external-body"))
183 shallrecurse = false;
184 }
185 else if (bp->type == TYPE_MULTIPART)
186 {
187 /* Always recurse multiparts, except multipart/alternative. */
188 shallrecurse = true;
189 if (mutt_istr_equal(bp->subtype, "alternative"))
190 {
191 const bool c_count_alternatives = cs_subset_bool(NeoMutt->sub, "count_alternatives");
192 shallrecurse = c_count_alternatives;
193 }
194 }
195
196 if ((bp->disposition == DISP_INLINE) && (bp->type != TYPE_MULTIPART) &&
197 (bp->type != TYPE_MESSAGE) && (bp == b))
198 {
199 shallcount = false; /* ignore fundamental inlines */
200 }
201
202 /* If this body isn't scheduled for enumeration already, don't bother
203 * profiling it further. */
204 if (shallcount)
205 {
206 /* Turn off shallcount if message type is not in ok list,
207 * or if it is in except list. Check is done separately for
208 * inlines vs. attachments. */
209
210 if (bp->disposition == DISP_ATTACH)
211 {
212 if (!count_body_parts_check(&AttachAllow, bp, true))
213 shallcount = false; /* attach not allowed */
214 if (count_body_parts_check(&AttachExclude, bp, false))
215 shallcount = false; /* attach excluded */
216 }
217 else
218 {
219 if (!count_body_parts_check(&InlineAllow, bp, true))
220 shallcount = false; /* inline not allowed */
221 if (count_body_parts_check(&InlineExclude, bp, false))
222 shallcount = false; /* excluded */
223 }
224 }
225
226 if (shallcount)
227 count++;
228 bp->attach_qualifies = shallcount;
229
230 mutt_debug(LL_DEBUG3, "%p shallcount = %d\n", (void *) bp, shallcount);
231
232 if (shallrecurse)
233 {
234 mutt_debug(LL_DEBUG3, "%p pre count = %d\n", (void *) bp, count);
235 bp->attach_count = count_body_parts(bp->parts);
236 count += bp->attach_count;
237 mutt_debug(LL_DEBUG3, "%p post count = %d\n", (void *) bp, count);
238 }
239 }
240
241 mutt_debug(LL_DEBUG3, "return %d\n", (count < 0) ? 0 : count);
242 return (count < 0) ? 0 : count;
243}
244
252int mutt_count_body_parts(const struct Mailbox *m, struct Email *e, FILE *fp)
253{
254 if (!m || !e)
255 return 0;
256
257 bool keep_parts = false;
258
259 if (e->attach_valid)
260 return e->attach_total;
261
262 if (e->body->parts)
263 keep_parts = true;
264 else
266
269 {
271 }
272 else
273 {
274 e->attach_total = 0;
275 }
276
277 e->attach_valid = true;
278
279 if (!keep_parts)
281
282 return e->attach_total;
283}
284
290{
291 if (!mv || !mv->mailbox)
292 return;
293
294 struct Mailbox *m = mv->mailbox;
295
296 for (int i = 0; i < m->msg_count; i++)
297 {
298 struct Email *e = m->emails[i];
299 if (!e)
300 break;
301 e->attach_valid = false;
302 e->attach_total = 0;
303 }
304}
305
314static enum CommandResult parse_attach_list(struct Buffer *buf, struct Buffer *s,
315 struct ListHead *head, struct Buffer *err)
316{
317 struct AttachMatch *a = NULL;
318 char *p = NULL;
319 char *tmpminor = NULL;
320 size_t len;
321 int rc;
322
323 do
324 {
326
327 if (!buf->data || (*buf->data == '\0'))
328 continue;
329
330 a = attachmatch_new();
331
332 /* some cheap hacks that I expect to remove */
333 if (mutt_istr_equal(buf->data, "any"))
334 a->major = mutt_str_dup("*/.*");
335 else if (mutt_istr_equal(buf->data, "none"))
336 a->major = mutt_str_dup("cheap_hack/this_should_never_match");
337 else
338 a->major = mutt_str_dup(buf->data);
339
340 p = strchr(a->major, '/');
341 if (p)
342 {
343 *p = '\0';
344 p++;
345 a->minor = p;
346 }
347 else
348 {
349 a->minor = "unknown";
350 }
351
352 len = strlen(a->minor);
353 tmpminor = mutt_mem_malloc(len + 3);
354 strcpy(&tmpminor[1], a->minor);
355 tmpminor[0] = '^';
356 tmpminor[len + 1] = '$';
357 tmpminor[len + 2] = '\0';
358
360 rc = REG_COMP(&a->minor_regex, tmpminor, REG_ICASE);
361
362 FREE(&tmpminor);
363
364 if (rc != 0)
365 {
366 regerror(rc, &a->minor_regex, err->data, err->dsize);
367 FREE(&a->major);
368 FREE(&a);
369 return MUTT_CMD_ERROR;
370 }
371
372 mutt_debug(LL_DEBUG3, "added %s/%s [%d]\n", a->major, a->minor, a->major_int);
373
374 mutt_list_insert_tail(head, (char *) a);
375 } while (MoreArgs(s));
376
377 if (!a)
378 return MUTT_CMD_ERROR;
379
380 mutt_debug(LL_NOTIFY, "NT_ATTACH_ADD: %s/%s\n", a->major, a->minor);
382
383 return MUTT_CMD_SUCCESS;
384}
385
394static enum CommandResult parse_unattach_list(struct Buffer *buf, struct Buffer *s,
395 struct ListHead *head, struct Buffer *err)
396{
397 struct AttachMatch *a = NULL;
398 char *tmp = NULL;
399 char *minor = NULL;
400
401 do
402 {
404 FREE(&tmp);
405
406 if (mutt_istr_equal(buf->data, "any"))
407 tmp = mutt_str_dup("*/.*");
408 else if (mutt_istr_equal(buf->data, "none"))
409 tmp = mutt_str_dup("cheap_hack/this_should_never_match");
410 else
411 tmp = mutt_str_dup(buf->data);
412
413 minor = strchr(tmp, '/');
414 if (minor)
415 {
416 *minor = '\0';
417 minor++;
418 }
419 else
420 {
421 minor = "unknown";
422 }
423 const enum ContentType major = mutt_check_mime_type(tmp);
424
425 struct ListNode *np = NULL, *tmp2 = NULL;
426 STAILQ_FOREACH_SAFE(np, head, entries, tmp2)
427 {
428 a = (struct AttachMatch *) np->data;
429 mutt_debug(LL_DEBUG3, "check %s/%s [%d] : %s/%s [%d]\n", a->major,
430 a->minor, a->major_int, tmp, minor, major);
431 if ((a->major_int == major) && mutt_istr_equal(minor, a->minor))
432 {
433 mutt_debug(LL_DEBUG3, "removed %s/%s [%d]\n", a->major, a->minor, a->major_int);
434 mutt_debug(LL_NOTIFY, "NT_ATTACH_DELETE: %s/%s\n", a->major, a->minor);
435
436 regfree(&a->minor_regex);
437 FREE(&a->major);
438 STAILQ_REMOVE(head, np, ListNode, entries);
439 FREE(&np->data);
440 FREE(&np);
441 }
442 }
443
444 } while (MoreArgs(s));
445
446 FREE(&tmp);
447
449
450 return MUTT_CMD_SUCCESS;
451}
452
460static int print_attach_list(struct ListHead *h, const char op, const char *name)
461{
462 struct ListNode *np = NULL;
463 STAILQ_FOREACH(np, h, entries)
464 {
465 printf("attachments %c%s %s/%s\n", op, name,
466 ((struct AttachMatch *) np->data)->major,
467 ((struct AttachMatch *) np->data)->minor);
468 }
469
470 return 0;
471}
472
476enum CommandResult parse_attachments(struct Buffer *buf, struct Buffer *s,
477 intptr_t data, struct Buffer *err)
478{
480 if (!buf->data || (*buf->data == '\0'))
481 {
482 buf_strcpy(err, _("attachments: no disposition"));
483 return MUTT_CMD_WARNING;
484 }
485
486 char *category = buf->data;
487 char op = *category++;
488
489 if (op == '?')
490 {
491 mutt_endwin();
492 fflush(stdout);
493 printf("\n%s\n\n", _("Current attachments settings:"));
494 print_attach_list(&AttachAllow, '+', "A");
496 print_attach_list(&InlineAllow, '+', "I");
499 return MUTT_CMD_SUCCESS;
500 }
501
502 if ((op != '+') && (op != '-'))
503 {
504 op = '+';
505 category--;
506 }
507
508 struct ListHead *head = NULL;
509 if (mutt_istr_startswith("attachment", category))
510 {
511 if (op == '+')
512 head = &AttachAllow;
513 else
514 head = &AttachExclude;
515 }
516 else if (mutt_istr_startswith("inline", category))
517 {
518 if (op == '+')
519 head = &InlineAllow;
520 else
521 head = &InlineExclude;
522 }
523 else
524 {
525 buf_strcpy(err, _("attachments: invalid disposition"));
526 return MUTT_CMD_ERROR;
527 }
528
529 return parse_attach_list(buf, s, head, err);
530}
531
535enum CommandResult parse_unattachments(struct Buffer *buf, struct Buffer *s,
536 intptr_t data, struct Buffer *err)
537{
538 char op;
539 char *p = NULL;
540 struct ListHead *head = NULL;
541
543 if (!buf->data || (*buf->data == '\0'))
544 {
545 buf_strcpy(err, _("unattachments: no disposition"));
546 return MUTT_CMD_WARNING;
547 }
548
549 p = buf->data;
550 op = *p++;
551
552 if (op == '*')
553 {
558
559 mutt_debug(LL_NOTIFY, "NT_ATTACH_DELETE_ALL\n");
561 return 0;
562 }
563
564 if ((op != '+') && (op != '-'))
565 {
566 op = '+';
567 p--;
568 }
569 if (mutt_istr_startswith("attachment", p))
570 {
571 if (op == '+')
572 head = &AttachAllow;
573 else
574 head = &AttachExclude;
575 }
576 else if (mutt_istr_startswith("inline", p))
577 {
578 if (op == '+')
579 head = &InlineAllow;
580 else
581 head = &InlineExclude;
582 }
583 else
584 {
585 buf_strcpy(err, _("unattachments: invalid disposition"));
586 return MUTT_CMD_ERROR;
587 }
588
589 return parse_unattach_list(buf, s, head, err);
590}
591
597void mutt_parse_mime_message(struct Email *e, FILE *fp)
598{
599 const bool right_type = (e->body->type == TYPE_MESSAGE) ||
600 (e->body->type == TYPE_MULTIPART);
601 const bool not_parsed = (e->body->parts == NULL);
602
603 if (right_type && fp && not_parsed)
604 {
605 mutt_parse_part(fp, e->body);
606 if (WithCrypto)
607 {
608 e->security = crypt_query(e->body);
609 }
610 }
611
612 e->attach_valid = false;
613}
static struct ListHead AttachAllow
List of attachment types to be counted.
Definition: attachments.c:56
static int print_attach_list(struct ListHead *h, const char op, const char *name)
Print a list of attachments.
Definition: attachments.c:460
static int count_body_parts(struct Body *b)
Count the MIME Body parts.
Definition: attachments.c:157
static struct ListHead InlineExclude
List of inline types to ignore.
Definition: attachments.c:59
void mutt_parse_mime_message(struct Email *e, FILE *fp)
Parse a MIME email.
Definition: attachments.c:597
void mutt_attachments_reset(struct MailboxView *mv)
Reset the attachment count for all Emails.
Definition: attachments.c:289
static struct ListHead AttachExclude
List of attachment types to be ignored.
Definition: attachments.c:57
static struct AttachMatch * attachmatch_new(void)
Create a new AttachMatch.
Definition: attachments.c:84
int mutt_count_body_parts(const struct Mailbox *m, struct Email *e, FILE *fp)
Count the MIME Body parts.
Definition: attachments.c:252
void attach_init(void)
Set up the attachments lists.
Definition: attachments.c:106
void attach_cleanup(void)
Free the attachments lists.
Definition: attachments.c:92
static struct Notify * AttachmentsNotify
Notifications: NotifyAttach.
Definition: attachments.c:60
static enum CommandResult parse_unattach_list(struct Buffer *buf, struct Buffer *s, struct ListHead *head, struct Buffer *err)
Parse the "unattachments" command.
Definition: attachments.c:394
static bool count_body_parts_check(struct ListHead *checklist, struct Body *b, bool dflt)
Compares mime types to the ok and except lists.
Definition: attachments.c:122
static enum CommandResult parse_attach_list(struct Buffer *buf, struct Buffer *s, struct ListHead *head, struct Buffer *err)
Parse the "attachments" command.
Definition: attachments.c:314
static struct ListHead InlineAllow
List of inline types to counted.
Definition: attachments.c:58
Miscellaneous email parsing routines.
@ NT_ATTACH_DELETE
Attachment regex has been deleted.
Definition: attachments.h:42
@ NT_ATTACH_DELETE_ALL
All Attachment regexes have been deleted.
Definition: attachments.h:43
@ NT_ATTACH_ADD
Attachment regex has been added.
Definition: attachments.h:41
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:394
CommandResult
Error codes for command_t parse functions.
Definition: command.h:36
@ MUTT_CMD_SUCCESS
Success: Command worked.
Definition: command.h:39
@ MUTT_CMD_ERROR
Error: Can't help the user.
Definition: command.h:37
@ MUTT_CMD_WARNING
Warning: Help given to the user.
Definition: command.h:38
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
Convenience wrapper for the config headers.
Convenience wrapper for the core headers.
SecurityFlags crypt_query(struct Body *b)
Check out the type of encryption used.
Definition: crypt.c:673
int mutt_any_key_to_continue(const char *s)
Prompt the user to 'press any key' and wait.
Definition: curs_lib.c:173
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:151
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:58
Structs that make up an email.
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition: parse.c:1817
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition: parse.c:366
int parse_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: extract.c:50
#define MoreArgs(buf)
Definition: extract.h:32
#define TOKEN_NO_FLAGS
No flags are set.
Definition: extract.h:46
enum CommandResult parse_unattachments(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'unattachments' command - Implements Command::parse() -.
Definition: attachments.c:535
enum CommandResult parse_attachments(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'attachments' command - Implements Command::parse() -.
Definition: attachments.c:476
static void attachmatch_free(struct AttachMatch **ptr)
Free an AttachMatch - Implements list_free_t -.
Definition: attachments.c:69
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
Convenience wrapper for the gui headers.
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
void mutt_list_free_type(struct ListHead *h, list_free_t fn)
Free a List of type.
Definition: list.c:144
void(* list_free_t)(void **ptr)
Definition: list.h:48
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:45
@ LL_DEBUG5
Log at debug level 5.
Definition: logging2.h:47
@ LL_NOTIFY
Log of notifications.
Definition: logging2.h:48
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define FREE(x)
Definition: memory.h:45
ContentType
Content-Type.
Definition: mime.h:30
@ TYPE_MESSAGE
Type: 'message/*'.
Definition: mime.h:35
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition: mime.h:37
@ TYPE_ANY
Type: '*' or '.*'.
Definition: mime.h:40
@ DISP_ATTACH
Content is attached.
Definition: mime.h:63
@ DISP_INLINE
Content is inline.
Definition: mime.h:62
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
struct Notify * notify_new(void)
Create a new notifications handler.
Definition: notify.c:62
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition: notify.c:173
void notify_set_parent(struct Notify *notify, struct Notify *parent)
Set the parent notification handler.
Definition: notify.c:95
void notify_free(struct Notify **ptr)
Free a notification handler.
Definition: notify.c:75
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:666
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:242
View of a Mailbox.
API for encryption/signing of emails.
#define WithCrypto
Definition: lib.h:116
@ NT_ATTACH
Attachment command changed, NotifyAttach.
Definition: notify_type.h:39
Text parsing functions.
#define STAILQ_REMOVE(head, elm, type, field)
Definition: queue.h:402
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define STAILQ_EMPTY(head)
Definition: queue.h:348
#define STAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:362
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:49
An attachment matching a regex for attachment counter.
Definition: attachments.c:49
const char * minor
Minor mime type, e.g. "html".
Definition: attachments.c:52
regex_t minor_regex
Minor mime type regex.
Definition: attachments.c:53
const char * major
Major mime type, e.g. "text".
Definition: attachments.c:50
enum ContentType major_int
Major mime type, e.g. TYPE_TEXT.
Definition: attachments.c:51
The body of an email.
Definition: body.h:36
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:72
struct Body * next
next attachment in the list
Definition: body.h:71
char * subtype
content-type subtype
Definition: body.h:60
unsigned int type
content-type primary type, ContentType
Definition: body.h:40
String manipulation buffer.
Definition: buffer.h:36
size_t dsize
Length of data.
Definition: buffer.h:39
char * data
Pointer to data.
Definition: buffer.h:37
The envelope/body of an email.
Definition: email.h:39
bool attach_valid
true when the attachment count is valid
Definition: email.h:103
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
short attach_total
Number of qualifying attachments in message, if attach_valid.
Definition: email.h:118
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
View of a Mailbox.
Definition: mview.h:40
struct Mailbox * mailbox
Current Mailbox.
Definition: mview.h:51
A mailbox.
Definition: mailbox.h:79
int msg_count
Total number of messages.
Definition: mailbox.h:88
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct Notify * notify
Notifications handler.
Definition: neomutt.h:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
Notification API.
Definition: notify.c:53