NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
attachments.c
Go to the documentation of this file.
1 
29 #include "config.h"
30 #include <stdbool.h>
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include "mutt/lib.h"
35 #include "config/lib.h"
36 #include "email/lib.h"
37 #include "core/lib.h"
38 #include "gui/lib.h"
39 #include "mutt.h"
40 #include "attachments.h"
41 #include "ncrypt/lib.h"
42 #include "init.h"
43 #include "mutt_commands.h"
44 
49 {
50  const char *major;
52  const char *minor;
53  regex_t minor_regex;
54 };
55 
60 static struct Notify *AttachmentsNotify = NULL;
61 
69 static 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 
84 static struct AttachMatch *attachmatch_new(void)
85 {
86  return mutt_mem_calloc(1, sizeof(struct AttachMatch));
87 }
88 
92 void attach_free(void)
93 {
94  notify_free(&AttachmentsNotify);
95 
96  /* Lists of AttachMatch */
98  mutt_list_free_type(&AttachExclude, (list_free_t) attachmatch_free);
99  mutt_list_free_type(&InlineAllow, (list_free_t) attachmatch_free);
100  mutt_list_free_type(&InlineExclude, (list_free_t) attachmatch_free);
101 }
102 
106 void attach_init(void)
107 {
108  if (AttachmentsNotify)
109  return;
110 
111  AttachmentsNotify = notify_new();
112  notify_set_parent(AttachmentsNotify, NeoMutt->notify);
113 }
114 
122 static 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 
157 static int count_body_parts(struct Body *body)
158 {
159  if (!body)
160  return 0;
161 
162  int count = 0;
163 
164  for (struct Body *bp = body; 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 =
192  cs_subset_bool(NeoMutt->sub, "count_alternatives");
193  shallrecurse = c_count_alternatives;
194  }
195  }
196 
197  if ((bp->disposition == DISP_INLINE) && (bp->type != TYPE_MULTIPART) &&
198  (bp->type != TYPE_MESSAGE) && (bp == body))
199  {
200  shallcount = false; /* ignore fundamental inlines */
201  }
202 
203  /* If this body isn't scheduled for enumeration already, don't bother
204  * profiling it further. */
205  if (shallcount)
206  {
207  /* Turn off shallcount if message type is not in ok list,
208  * or if it is in except list. Check is done separately for
209  * inlines vs. attachments. */
210 
211  if (bp->disposition == DISP_ATTACH)
212  {
213  if (!count_body_parts_check(&AttachAllow, bp, true))
214  shallcount = false; /* attach not allowed */
215  if (count_body_parts_check(&AttachExclude, bp, false))
216  shallcount = false; /* attach excluded */
217  }
218  else
219  {
220  if (!count_body_parts_check(&InlineAllow, bp, true))
221  shallcount = false; /* inline not allowed */
222  if (count_body_parts_check(&InlineExclude, bp, false))
223  shallcount = false; /* excluded */
224  }
225  }
226 
227  if (shallcount)
228  count++;
229  bp->attach_qualifies = shallcount;
230 
231  mutt_debug(LL_DEBUG3, "%p shallcount = %d\n", (void *) bp, shallcount);
232 
233  if (shallrecurse)
234  {
235  mutt_debug(LL_DEBUG3, "%p pre count = %d\n", (void *) bp, count);
236  bp->attach_count = count_body_parts(bp->parts);
237  count += bp->attach_count;
238  mutt_debug(LL_DEBUG3, "%p post count = %d\n", (void *) bp, count);
239  }
240  }
241 
242  mutt_debug(LL_DEBUG3, "return %d\n", (count < 0) ? 0 : count);
243  return (count < 0) ? 0 : count;
244 }
245 
253 int mutt_count_body_parts(struct Mailbox *m, struct Email *e, FILE *fp)
254 {
255  if (!m || !e)
256  return 0;
257 
258  bool keep_parts = false;
259 
260  if (e->attach_valid)
261  return e->attach_total;
262 
263  if (e->body->parts)
264  keep_parts = true;
265  else
266  mutt_parse_mime_message(m, e, fp);
267 
270  {
272  }
273  else
274  e->attach_total = 0;
275 
276  e->attach_valid = true;
277 
278  if (!keep_parts)
279  mutt_body_free(&e->body->parts);
280 
281  return e->attach_total;
282 }
283 
288 {
289  if (!m)
290  return;
291 
292  for (int i = 0; i < m->msg_count; i++)
293  {
294  struct Email *e = m->emails[i];
295  if (!e)
296  break;
297  e->attach_valid = false;
298  e->attach_total = 0;
299  }
300 }
301 
310 static enum CommandResult parse_attach_list(struct Buffer *buf, struct Buffer *s,
311  struct ListHead *head, struct Buffer *err)
312 {
313  struct AttachMatch *a = NULL;
314  char *p = NULL;
315  char *tmpminor = NULL;
316  size_t len;
317  int ret;
318 
319  do
320  {
322 
323  if (!buf->data || (*buf->data == '\0'))
324  continue;
325 
326  a = attachmatch_new();
327 
328  /* some cheap hacks that I expect to remove */
329  if (mutt_istr_equal(buf->data, "any"))
330  a->major = mutt_str_dup("*/.*");
331  else if (mutt_istr_equal(buf->data, "none"))
332  a->major = mutt_str_dup("cheap_hack/this_should_never_match");
333  else
334  a->major = mutt_str_dup(buf->data);
335 
336  p = strchr(a->major, '/');
337  if (p)
338  {
339  *p = '\0';
340  p++;
341  a->minor = p;
342  }
343  else
344  {
345  a->minor = "unknown";
346  }
347 
348  len = strlen(a->minor);
349  tmpminor = mutt_mem_malloc(len + 3);
350  strcpy(&tmpminor[1], a->minor);
351  tmpminor[0] = '^';
352  tmpminor[len + 1] = '$';
353  tmpminor[len + 2] = '\0';
354 
356  ret = REG_COMP(&a->minor_regex, tmpminor, REG_ICASE);
357 
358  FREE(&tmpminor);
359 
360  if (ret != 0)
361  {
362  regerror(ret, &a->minor_regex, err->data, err->dsize);
363  FREE(&a->major);
364  FREE(&a);
365  return MUTT_CMD_ERROR;
366  }
367 
368  mutt_debug(LL_DEBUG3, "added %s/%s [%d]\n", a->major, a->minor, a->major_int);
369 
370  mutt_list_insert_tail(head, (char *) a);
371  } while (MoreArgs(s));
372 
373  mutt_debug(LL_NOTIFY, "NT_ATTACH_ADD: %s/%s\n", a->major, a->minor);
374  notify_send(AttachmentsNotify, NT_ATTACH, NT_ATTACH_ADD, NULL);
375 
376  return MUTT_CMD_SUCCESS;
377 }
378 
387 static enum CommandResult parse_unattach_list(struct Buffer *buf, struct Buffer *s,
388  struct ListHead *head, struct Buffer *err)
389 {
390  struct AttachMatch *a = NULL;
391  char *tmp = NULL;
392  char *minor = NULL;
393 
394  do
395  {
397  FREE(&tmp);
398 
399  if (mutt_istr_equal(buf->data, "any"))
400  tmp = mutt_str_dup("*/.*");
401  else if (mutt_istr_equal(buf->data, "none"))
402  tmp = mutt_str_dup("cheap_hack/this_should_never_match");
403  else
404  tmp = mutt_str_dup(buf->data);
405 
406  minor = strchr(tmp, '/');
407  if (minor)
408  {
409  *minor = '\0';
410  minor++;
411  }
412  else
413  {
414  minor = "unknown";
415  }
416  const enum ContentType major = mutt_check_mime_type(tmp);
417 
418  struct ListNode *np = NULL, *tmp2 = NULL;
419  STAILQ_FOREACH_SAFE(np, head, entries, tmp2)
420  {
421  a = (struct AttachMatch *) np->data;
422  mutt_debug(LL_DEBUG3, "check %s/%s [%d] : %s/%s [%d]\n", a->major,
423  a->minor, a->major_int, tmp, minor, major);
424  if ((a->major_int == major) && mutt_istr_equal(minor, a->minor))
425  {
426  mutt_debug(LL_DEBUG3, "removed %s/%s [%d]\n", a->major, a->minor, a->major_int);
427  mutt_debug(LL_NOTIFY, "NT_ATTACH_DELETE: %s/%s\n", a->major, a->minor);
428 
429  regfree(&a->minor_regex);
430  FREE(&a->major);
431  STAILQ_REMOVE(head, np, ListNode, entries);
432  FREE(&np->data);
433  FREE(&np);
434  }
435  }
436 
437  } while (MoreArgs(s));
438 
439  FREE(&tmp);
440 
441  notify_send(AttachmentsNotify, NT_ATTACH, NT_ATTACH_DELETE, NULL);
442 
443  return MUTT_CMD_SUCCESS;
444 }
445 
453 static int print_attach_list(struct ListHead *h, const char op, const char *name)
454 {
455  struct ListNode *np = NULL;
456  STAILQ_FOREACH(np, h, entries)
457  {
458  printf("attachments %c%s %s/%s\n", op, name,
459  ((struct AttachMatch *) np->data)->major,
460  ((struct AttachMatch *) np->data)->minor);
461  }
462 
463  return 0;
464 }
465 
469 enum CommandResult parse_attachments(struct Buffer *buf, struct Buffer *s,
470  intptr_t data, struct Buffer *err)
471 {
473  if (!buf->data || (*buf->data == '\0'))
474  {
475  mutt_buffer_strcpy(err, _("attachments: no disposition"));
476  return MUTT_CMD_WARNING;
477  }
478 
479  char *category = buf->data;
480  char op = *category++;
481 
482  if (op == '?')
483  {
484  mutt_endwin();
485  fflush(stdout);
486  printf("\n%s\n\n", _("Current attachments settings:"));
487  print_attach_list(&AttachAllow, '+', "A");
488  print_attach_list(&AttachExclude, '-', "A");
489  print_attach_list(&InlineAllow, '+', "I");
490  print_attach_list(&InlineExclude, '-', "I");
492  return MUTT_CMD_SUCCESS;
493  }
494 
495  if ((op != '+') && (op != '-'))
496  {
497  op = '+';
498  category--;
499  }
500 
501  struct ListHead *head = NULL;
502  if (mutt_istr_startswith("attachment", category))
503  {
504  if (op == '+')
505  head = &AttachAllow;
506  else
507  head = &AttachExclude;
508  }
509  else if (mutt_istr_startswith("inline", category))
510  {
511  if (op == '+')
512  head = &InlineAllow;
513  else
514  head = &InlineExclude;
515  }
516  else
517  {
518  mutt_buffer_strcpy(err, _("attachments: invalid disposition"));
519  return MUTT_CMD_ERROR;
520  }
521 
522  return parse_attach_list(buf, s, head, err);
523 }
524 
528 enum CommandResult parse_unattachments(struct Buffer *buf, struct Buffer *s,
529  intptr_t data, struct Buffer *err)
530 {
531  char op;
532  char *p = NULL;
533  struct ListHead *head = NULL;
534 
536  if (!buf->data || (*buf->data == '\0'))
537  {
538  mutt_buffer_strcpy(err, _("unattachments: no disposition"));
539  return MUTT_CMD_WARNING;
540  }
541 
542  p = buf->data;
543  op = *p++;
544 
545  if (op == '*')
546  {
548  mutt_list_free_type(&AttachExclude, (list_free_t) attachmatch_free);
549  mutt_list_free_type(&InlineAllow, (list_free_t) attachmatch_free);
550  mutt_list_free_type(&InlineExclude, (list_free_t) attachmatch_free);
551 
552  mutt_debug(LL_NOTIFY, "NT_ATTACH_DELETE_ALL\n");
553  notify_send(AttachmentsNotify, NT_ATTACH, NT_ATTACH_DELETE_ALL, NULL);
554  return 0;
555  }
556 
557  if ((op != '+') && (op != '-'))
558  {
559  op = '+';
560  p--;
561  }
562  if (mutt_istr_startswith("attachment", p))
563  {
564  if (op == '+')
565  head = &AttachAllow;
566  else
567  head = &AttachExclude;
568  }
569  else if (mutt_istr_startswith("inline", p))
570  {
571  if (op == '+')
572  head = &InlineAllow;
573  else
574  head = &InlineExclude;
575  }
576  else
577  {
578  mutt_buffer_strcpy(err, _("unattachments: invalid disposition"));
579  return MUTT_CMD_ERROR;
580  }
581 
582  return parse_unattach_list(buf, s, head, err);
583 }
584 
591 void mutt_parse_mime_message(struct Mailbox *m, struct Email *e, FILE *fp)
592 {
593  const bool right_type =
594  (e->body->type == TYPE_MESSAGE) || (e->body->type == TYPE_MULTIPART);
595  const bool not_parsed = (e->body->parts == NULL);
596 
597  if (right_type && fp && not_parsed)
598  {
599  mutt_parse_part(fp, e->body);
600  if (WithCrypto)
601  {
602  e->security = crypt_query(e->body);
603  }
604  }
605 
606  e->attach_valid = false;
607 }
void mutt_parse_mime_message(struct Mailbox *m, struct Email *e, FILE *fp)
Parse a MIME email.
Definition: attachments.c:591
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
Convenience wrapper for the gui headers.
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
struct ListHead InlineAllow
List of inline types to counted.
Definition: attachments.c:58
#define STAILQ_REMOVE(head, elm, type, field)
Definition: queue.h:402
int msg_count
Total number of messages.
Definition: mailbox.h:91
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
#define WithCrypto
Definition: lib.h:113
The envelope/body of an email.
Definition: email.h:37
CommandResult
Error codes for command_t parse functions.
Definition: mutt_commands.h:34
Config/command parsing.
struct Body * body
List of MIME parts.
Definition: email.h:91
static struct AttachMatch * attachmatch_new(void)
Create a new AttachMatch.
Definition: attachments.c:84
Error: Can&#39;t help the user.
Definition: mutt_commands.h:36
Log of notifications.
Definition: logging.h:45
Structs that make up an email.
All Attachment regexes have been deleted.
Definition: attachments.h:40
static void attachmatch_free(struct AttachMatch **ptr)
Free an AttachMatch - Implements list_free_t.
Definition: attachments.c:69
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:387
void notify_free(struct Notify **ptr)
Free a notification handler.
Definition: notify.c:73
Attachment command changed, NotifyAttach.
Definition: notify_type.h:37
void mutt_list_free_type(struct ListHead *h, list_free_t fn)
Free a List of type.
Definition: list.c:144
String manipulation buffer.
Definition: buffer.h:33
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
enum ContentType major_int
Major mime type, e.g. TYPE_TEXT.
Definition: attachments.c:51
#define _(a)
Definition: message.h:28
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:54
struct Body * next
next attachment in the list
Definition: body.h:53
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
Type: &#39;*&#39; or &#39;.*&#39;.
Definition: mime.h:40
Container for Accounts, Notifications.
Definition: neomutt.h:36
The body of an email.
Definition: body.h:34
Convenience wrapper for the config headers.
int mutt_count_body_parts(struct Mailbox *m, struct Email *e, FILE *fp)
Count the MIME Body parts.
Definition: attachments.c:253
size_t dsize
Length of data.
Definition: buffer.h:37
#define MoreArgs(buf)
Definition: buffer.h:40
Many unsorted constants and some structs.
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition: parse.c:326
Convenience wrapper for the core headers.
static int count_body_parts(struct Body *body)
Count the MIME Body parts.
Definition: attachments.c:157
Content is attached.
Definition: mime.h:63
const char * major
Major mime type, e.g. "text".
Definition: attachments.c:50
void mutt_attachments_reset(struct Mailbox *m)
Reset the attachment count for all Emails.
Definition: attachments.c:287
short attach_total
Number of qualifying attachments in message, if attach_valid.
Definition: email.h:97
#define STAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:362
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:916
char * subtype
content-type subtype
Definition: body.h:37
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
Attachment regex has been deleted.
Definition: attachments.h:39
struct Notify * notify_new(void)
Create a new notifications handler.
Definition: notify.c:60
void mutt_endwin(void)
Shutdown curses/slang.
Definition: curs_lib.c:422
A mailbox.
Definition: mailbox.h:81
int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: init.c:395
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
char * data
Pointer to data.
Definition: buffer.h:35
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:54
bool attach_valid
true when the attachment count is valid
Definition: email.h:70
Definitions of NeoMutt commands.
API for encryption/signing of emails.
Notification API.
Definition: notify.c:50
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
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:310
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
struct Notify * notify
Notifications handler.
Definition: neomutt.h:38
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib...
Definition: email.h:39
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
unsigned int type
content-type primary type, ContentType
Definition: body.h:65
enum CommandResult parse_unattachments(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the &#39;unattachments&#39; command - Implements Command::parse() -.
Definition: attachments.c:528
Type: &#39;message/*&#39;.
Definition: mime.h:35
int mutt_any_key_to_continue(const char *s)
Prompt the user to &#39;press any key&#39; and wait.
Definition: curs_lib.c:455
static int print_attach_list(struct ListHead *h, const char op, const char *name)
Print a list of attachments.
Definition: attachments.c:453
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition: parse.c:1678
Success: Command worked.
Definition: mutt_commands.h:38
char * data
String.
Definition: list.h:36
Type: &#39;multipart/*&#39;.
Definition: mime.h:37
Warning: Help given to the user.
Definition: mutt_commands.h:37
regex_t minor_regex
Minor mime type regex.
Definition: attachments.c:53
enum CommandResult parse_attachments(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the &#39;attachments&#39; command - Implements Command::parse() -.
Definition: attachments.c:469
Attachment regex has been added.
Definition: attachments.h:38
SecurityFlags crypt_query(struct Body *b)
Check out the type of encryption used.
Definition: crypt.c:698
#define FREE(x)
Definition: memory.h:40
struct ListHead InlineExclude
List of inline types to ignore.
Definition: attachments.c:59
#define STAILQ_EMPTY(head)
Definition: queue.h:348
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
void attach_free(void)
Free the attachments lists.
Definition: attachments.c:92
void notify_set_parent(struct Notify *notify, struct Notify *parent)
Set the parent notification handler.
Definition: notify.c:93
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
void attach_init(void)
Set up the attachments lists.
Definition: attachments.c:106
struct ListHead AttachExclude
List of attachment types to be ignored.
Definition: attachments.c:57
struct ListHead AttachAllow
List of attachment types to be counted.
Definition: attachments.c:56
An attachment matching a regex for attachment counter.
Definition: attachments.c:48
Log at debug level 5.
Definition: logging.h:44
Convenience wrapper for the library headers.
A List node for strings.
Definition: list.h:34
Miscellaneous email parsing routines.
Content is inline.
Definition: mime.h:62
const char * minor
Minor mime type, e.g. "html".
Definition: attachments.c:52
Log at debug level 3.
Definition: logging.h:42
#define MUTT_TOKEN_NO_FLAGS
No flags are set.
Definition: mutt.h:66
ContentType
Content-Type.
Definition: mime.h:29
void(* list_free_t)(void **ptr)
Prototype for a function to free List data.
Definition: list.h:45
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition: notify.c:171