NeoMutt  2025-12-11-58-g09398d
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
commands.c
Go to the documentation of this file.
1
23
29
30#include "config.h"
31#include <stdbool.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 "commands.h"
40#include "ncrypt/lib.h"
41#include "parse/lib.h"
42#include "mview.h"
43
48{
49 const char *major;
51 const char *minor;
52 regex_t minor_regex;
53};
54
59static struct Notify *AttachmentsNotify = NULL;
60
68static void attachmatch_free(struct AttachMatch **ptr)
69{
70 if (!ptr || !*ptr)
71 return;
72
73 struct AttachMatch *am = *ptr;
74 regfree(&am->minor_regex);
75 FREE(&am->major);
76 FREE(ptr);
77}
78
83static struct AttachMatch *attachmatch_new(void)
84{
85 return MUTT_MEM_CALLOC(1, struct AttachMatch);
86}
87
101
105void attach_init(void)
106{
108 return;
109
112}
113
121static bool count_body_parts_check(struct ListHead *checklist, struct Body *b, bool dflt)
122{
123 /* If list is null, use default behavior. */
124 if (!checklist || STAILQ_EMPTY(checklist))
125 {
126 return false;
127 }
128
129 struct AttachMatch *a = NULL;
130 struct ListNode *np = NULL;
131 STAILQ_FOREACH(np, checklist, entries)
132 {
133 a = (struct AttachMatch *) np->data;
134 mutt_debug(LL_DEBUG3, "%s %d/%s ?? %s/%s [%d]... ", dflt ? "[OK] " : "[EXCL] ",
135 b->type, b->subtype ? b->subtype : "*", a->major, a->minor, a->major_int);
136 if (((a->major_int == TYPE_ANY) || (a->major_int == b->type)) &&
137 (!b->subtype || (regexec(&a->minor_regex, b->subtype, 0, NULL, 0) == 0)))
138 {
139 mutt_debug(LL_DEBUG3, "yes\n");
140 return true;
141 }
142 else
143 {
144 mutt_debug(LL_DEBUG3, "no\n");
145 }
146 }
147
148 return false;
149}
150
156static int count_body_parts(struct Body *b)
157{
158 if (!b)
159 return 0;
160
161 int count = 0;
162
163 for (struct Body *bp = b; bp; bp = bp->next)
164 {
165 /* Initial disposition is to count and not to recurse this part. */
166 bool shallcount = true; /* default */
167 bool shallrecurse = false;
168
169 mutt_debug(LL_DEBUG5, "desc=\"%s\"; fn=\"%s\", type=\"%d/%s\"\n",
170 bp->description ? bp->description : ("none"),
171 bp->filename ? bp->filename :
172 bp->d_filename ? bp->d_filename :
173 "(none)",
174 bp->type, bp->subtype ? bp->subtype : "*");
175
176 if (bp->type == TYPE_MESSAGE)
177 {
178 shallrecurse = true;
179
180 /* If it's an external body pointer, don't recurse it. */
181 if (mutt_istr_equal(bp->subtype, "external-body"))
182 shallrecurse = false;
183 }
184 else if (bp->type == TYPE_MULTIPART)
185 {
186 /* Always recurse multiparts, except multipart/alternative. */
187 shallrecurse = true;
188 if (mutt_istr_equal(bp->subtype, "alternative"))
189 {
190 const bool c_count_alternatives = cs_subset_bool(NeoMutt->sub, "count_alternatives");
191 shallrecurse = c_count_alternatives;
192 }
193 }
194
195 if ((bp->disposition == DISP_INLINE) && (bp->type != TYPE_MULTIPART) &&
196 (bp->type != TYPE_MESSAGE) && (bp == b))
197 {
198 shallcount = false; /* ignore fundamental inlines */
199 }
200
201 /* If this body isn't scheduled for enumeration already, don't bother
202 * profiling it further. */
203 if (shallcount)
204 {
205 /* Turn off shallcount if message type is not in ok list,
206 * or if it is in except list. Check is done separately for
207 * inlines vs. attachments. */
208
209 if (bp->disposition == DISP_ATTACH)
210 {
211 if (!count_body_parts_check(&AttachAllow, bp, true))
212 shallcount = false; /* attach not allowed */
213 if (count_body_parts_check(&AttachExclude, bp, false))
214 shallcount = false; /* attach excluded */
215 }
216 else
217 {
218 if (!count_body_parts_check(&InlineAllow, bp, true))
219 shallcount = false; /* inline not allowed */
220 if (count_body_parts_check(&InlineExclude, bp, false))
221 shallcount = false; /* excluded */
222 }
223 }
224
225 if (shallcount)
226 count++;
227 bp->attach_qualifies = shallcount;
228
229 mutt_debug(LL_DEBUG3, "%p shallcount = %d\n", (void *) bp, shallcount);
230
231 if (shallrecurse)
232 {
233 mutt_debug(LL_DEBUG3, "%p pre count = %d\n", (void *) bp, count);
234 bp->attach_count = count_body_parts(bp->parts);
235 count += bp->attach_count;
236 mutt_debug(LL_DEBUG3, "%p post count = %d\n", (void *) bp, count);
237 }
238 }
239
240 mutt_debug(LL_DEBUG3, "return %d\n", (count < 0) ? 0 : count);
241 return (count < 0) ? 0 : count;
242}
243
250int mutt_count_body_parts(struct Email *e, FILE *fp)
251{
252 if (!e)
253 return 0;
254
255 bool keep_parts = false;
256
257 if (e->attach_valid)
258 return e->attach_total;
259
260 if (e->body->parts)
261 keep_parts = true;
262 else
264
267 {
269 }
270 else
271 {
272 e->attach_total = 0;
273 }
274
275 e->attach_valid = true;
276
277 if (!keep_parts)
279
280 return e->attach_total;
281}
282
288{
289 if (!mv || !mv->mailbox)
290 return;
291
292 struct Mailbox *m = mv->mailbox;
293
294 for (int i = 0; i < m->msg_count; i++)
295 {
296 struct Email *e = m->emails[i];
297 if (!e)
298 break;
299 e->attach_valid = false;
300 e->attach_total = 0;
301 }
302}
303
312static enum CommandResult parse_attach_list(const struct Command *cmd, struct Buffer *line,
313 struct ListHead *head, struct Buffer *err)
314{
315 struct AttachMatch *a = NULL;
316 char *p = NULL;
317 char *tmpminor = NULL;
318 size_t len;
319 struct Buffer *token = buf_pool_get();
321
322 do
323 {
325
326 if (buf_is_empty(token))
327 continue;
328
329 a = attachmatch_new();
330
331 /* some cheap hacks that I expect to remove */
332 if (mutt_istr_equal(token->data, "any"))
333 a->major = mutt_str_dup("*/.*");
334 else if (mutt_istr_equal(token->data, "none"))
335 a->major = mutt_str_dup("cheap_hack/this_should_never_match");
336 else
337 a->major = buf_strdup(token);
338
339 p = strchr(a->major, '/');
340 if (p)
341 {
342 *p = '\0';
343 p++;
344 a->minor = p;
345 }
346 else
347 {
348 a->minor = "unknown";
349 }
350
351 len = strlen(a->minor);
352 tmpminor = MUTT_MEM_MALLOC(len + 3, char);
353 strcpy(&tmpminor[1], a->minor);
354 tmpminor[0] = '^';
355 tmpminor[len + 1] = '$';
356 tmpminor[len + 2] = '\0';
357
359 int rc_regex = REG_COMP(&a->minor_regex, tmpminor, REG_ICASE);
360
361 FREE(&tmpminor);
362
363 if (rc_regex != 0)
364 {
365 regerror(rc_regex, &a->minor_regex, err->data, err->dsize);
366 FREE(&a->major);
367 FREE(&a);
368 goto done;
369 }
370
371 mutt_debug(LL_DEBUG3, "added %s/%s [%d]\n", a->major, a->minor, a->major_int);
372
373 mutt_list_insert_tail(head, (char *) a);
374 } while (MoreArgs(line));
375
376 if (!a)
377 goto done;
378
379 mutt_debug(LL_NOTIFY, "NT_ATTACH_ADD: %s/%s\n", a->major, a->minor);
381
382 rc = MUTT_CMD_SUCCESS;
383
384done:
385 buf_pool_release(&token);
386 return rc;
387}
388
397static enum CommandResult parse_unattach_list(const struct Command *cmd, struct Buffer *line,
398 struct ListHead *head, struct Buffer *err)
399{
400 struct Buffer *token = buf_pool_get();
401
402 struct AttachMatch *a = NULL;
403 char *tmp = NULL;
404 char *minor = NULL;
405
406 do
407 {
409 FREE(&tmp);
410
411 if (mutt_istr_equal(token->data, "any"))
412 tmp = mutt_str_dup("*/.*");
413 else if (mutt_istr_equal(token->data, "none"))
414 tmp = mutt_str_dup("cheap_hack/this_should_never_match");
415 else
416 tmp = buf_strdup(token);
417
418 minor = strchr(tmp, '/');
419 if (minor)
420 {
421 *minor = '\0';
422 minor++;
423 }
424 else
425 {
426 minor = "unknown";
427 }
428 const enum ContentType major = mutt_check_mime_type(tmp);
429
430 struct ListNode *np = NULL, *tmp2 = NULL;
431 STAILQ_FOREACH_SAFE(np, head, entries, tmp2)
432 {
433 a = (struct AttachMatch *) np->data;
434 mutt_debug(LL_DEBUG3, "check %s/%s [%d] : %s/%s [%d]\n", a->major,
435 a->minor, a->major_int, tmp, minor, major);
436 if ((a->major_int == major) && mutt_istr_equal(minor, a->minor))
437 {
438 mutt_debug(LL_DEBUG3, "removed %s/%s [%d]\n", a->major, a->minor, a->major_int);
439 mutt_debug(LL_NOTIFY, "NT_ATTACH_DELETE: %s/%s\n", a->major, a->minor);
440
441 regfree(&a->minor_regex);
442 FREE(&a->major);
443 STAILQ_REMOVE(head, np, ListNode, entries);
444 FREE(&np->data);
445 FREE(&np);
446 }
447 }
448
449 } while (MoreArgs(line));
450
451 FREE(&tmp);
452
454
455 buf_pool_release(&token);
456 return MUTT_CMD_SUCCESS;
457}
458
466static int print_attach_list(struct ListHead *h, const char op, const char *name)
467{
468 struct ListNode *np = NULL;
469 STAILQ_FOREACH(np, h, entries)
470 {
471 printf("attachments %c%s %s/%s\n", op, name,
472 ((struct AttachMatch *) np->data)->major,
473 ((struct AttachMatch *) np->data)->minor);
474 }
475
476 return 0;
477}
478
486 struct Buffer *line, struct Buffer *err)
487{
488 if (!MoreArgs(line))
489 {
490 buf_printf(err, _("%s: too few arguments"), cmd->name);
491 return MUTT_CMD_WARNING;
492 }
493
494 struct Buffer *token = buf_pool_get();
496
498
499 char *category = token->data;
500 char op = *category++;
501
502 if (op == '?')
503 {
504 mutt_endwin();
505 fflush(stdout);
506 printf("\n%s\n\n", _("Current attachments settings:"));
507 print_attach_list(&AttachAllow, '+', "A");
509 print_attach_list(&InlineAllow, '+', "I");
512
513 rc = MUTT_CMD_SUCCESS;
514 goto done;
515 }
516
517 if ((op != '+') && (op != '-'))
518 {
519 op = '+';
520 category--;
521 }
522
523 struct ListHead *head = NULL;
524 if (mutt_istr_startswith("attachment", category))
525 {
526 if (op == '+')
527 head = &AttachAllow;
528 else
529 head = &AttachExclude;
530 }
531 else if (mutt_istr_startswith("inline", category))
532 {
533 if (op == '+')
534 head = &InlineAllow;
535 else
536 head = &InlineExclude;
537 }
538 else
539 {
540 buf_strcpy(err, _("attachments: invalid disposition"));
541 goto done;
542 }
543
544 rc = parse_attach_list(cmd, line, head, err);
545
546done:
547 buf_pool_release(&token);
548 return rc;
549}
550
558 struct Buffer *line, struct Buffer *err)
559{
560 if (!MoreArgs(line))
561 {
562 buf_printf(err, _("%s: too few arguments"), cmd->name);
563 return MUTT_CMD_WARNING;
564 }
565
566 struct Buffer *token = buf_pool_get();
568
569 char op;
570 const char *p = NULL;
571 struct ListHead *head = NULL;
572
574
575 p = buf_string(token);
576 op = *p++;
577
578 if (op == '*')
579 {
584
585 mutt_debug(LL_NOTIFY, "NT_ATTACH_DELETE_ALL\n");
587
588 rc = MUTT_CMD_SUCCESS;
589 goto done;
590 }
591
592 if ((op != '+') && (op != '-'))
593 {
594 op = '+';
595 p--;
596 }
597 if (mutt_istr_startswith("attachment", p))
598 {
599 if (op == '+')
600 head = &AttachAllow;
601 else
602 head = &AttachExclude;
603 }
604 else if (mutt_istr_startswith("inline", p))
605 {
606 if (op == '+')
607 head = &InlineAllow;
608 else
609 head = &InlineExclude;
610 }
611 else
612 {
613 buf_strcpy(err, _("unattachments: invalid disposition"));
614 goto done;
615 }
616
617 rc = parse_unattach_list(cmd, line, head, err);
618
619done:
620 buf_pool_release(&token);
621 return rc;
622}
623
629void mutt_parse_mime_message(struct Email *e, FILE *fp)
630{
631 const bool right_type = (e->body->type == TYPE_MESSAGE) ||
632 (e->body->type == TYPE_MULTIPART);
633 const bool not_parsed = (e->body->parts == NULL);
634
635 if (right_type && fp && not_parsed)
636 {
637 mutt_parse_part(fp, e->body);
638 if (WithCrypto)
639 {
640 e->security = crypt_query(e->body);
641 }
642 }
643
644 e->attach_valid = false;
645}
static struct ListHead AttachAllow
List of attachment types to be counted.
Definition commands.c:55
static int print_attach_list(struct ListHead *h, const char op, const char *name)
Print a list of attachments.
Definition commands.c:466
static int count_body_parts(struct Body *b)
Count the MIME Body parts.
Definition commands.c:156
static struct ListHead InlineExclude
List of inline types to ignore.
Definition commands.c:58
static enum CommandResult parse_unattach_list(const struct Command *cmd, struct Buffer *line, struct ListHead *head, struct Buffer *err)
Parse the "unattachments" command.
Definition commands.c:397
void mutt_parse_mime_message(struct Email *e, FILE *fp)
Parse a MIME email.
Definition commands.c:629
void mutt_attachments_reset(struct MailboxView *mv)
Reset the attachment count for all Emails.
Definition commands.c:287
static struct ListHead AttachExclude
List of attachment types to be ignored.
Definition commands.c:56
static struct AttachMatch * attachmatch_new(void)
Create a new AttachMatch.
Definition commands.c:83
void attach_init(void)
Set up the attachments lists.
Definition commands.c:105
static enum CommandResult parse_attach_list(const struct Command *cmd, struct Buffer *line, struct ListHead *head, struct Buffer *err)
Parse the "attachments" command.
Definition commands.c:312
void attach_cleanup(void)
Free the attachments lists.
Definition commands.c:91
static struct Notify * AttachmentsNotify
Notifications: NotifyAttach.
Definition commands.c:59
int mutt_count_body_parts(struct Email *e, FILE *fp)
Count the MIME Body parts.
Definition commands.c:250
static bool count_body_parts_check(struct ListHead *checklist, struct Body *b, bool dflt)
Compares mime types to the ok and except lists.
Definition commands.c:121
static struct ListHead InlineAllow
List of inline types to counted.
Definition commands.c:57
Handle the attachments command.
@ NT_ATTACH_DELETE
Attachment regex has been deleted.
Definition commands.h:41
@ NT_ATTACH_DELETE_ALL
All Attachment regexes have been deleted.
Definition commands.h:42
@ NT_ATTACH_ADD
Attachment regex has been added.
Definition commands.h:40
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
CommandResult
Error codes for command_t parse functions.
Definition command.h:35
@ MUTT_CMD_SUCCESS
Success: Command worked.
Definition command.h:38
@ MUTT_CMD_ERROR
Error: Can't help the user.
Definition command.h:36
@ MUTT_CMD_WARNING
Warning: Help given to the user.
Definition command.h:37
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
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:687
int mutt_any_key_to_continue(const char *s)
Prompt the user to 'press any key' and wait.
Definition curs_lib.c:174
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
Structs that make up an email.
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition parse.c:1821
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition parse.c:365
int parse_extract_token(struct Buffer *dest, struct Buffer *line, TokenFlags flags)
Extract one token from a string.
Definition extract.c:48
#define MoreArgs(buf)
Definition extract.h:30
#define TOKEN_NO_FLAGS
No flags are set.
Definition extract.h:44
enum CommandResult parse_attachments(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse the 'attachments' command - Implements Command::parse() -.
Definition commands.c:485
enum CommandResult parse_unattachments(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse the 'unattachments' command - Implements Command::parse() -.
Definition commands.c:557
static void attachmatch_free(struct AttachMatch **ptr)
Free an AttachMatch - Implements list_free_t -.
Definition commands.c:68
#define mutt_debug(LEVEL,...)
Definition logging2.h:90
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:65
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:50
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:46
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:48
@ LL_NOTIFY
Log of notifications.
Definition logging2.h:49
#define FREE(x)
Definition memory.h:62
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:47
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:48
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:672
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:255
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:244
View of a Mailbox.
API for encryption/signing of emails.
#define WithCrypto
Definition lib.h:122
@ NT_ATTACH
Attachment command changed, NotifyAttach.
Definition notify_type.h:39
Text parsing functions.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:96
#define STAILQ_REMOVE(head, elm, type, field)
Definition queue.h:441
#define STAILQ_HEAD_INITIALIZER(head)
Definition queue.h:324
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
#define STAILQ_EMPTY(head)
Definition queue.h:382
#define STAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition queue.h:400
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition regex3.h:50
An attachment matching a regex for attachment counter.
Definition commands.c:48
const char * minor
Minor mime type, e.g. "html".
Definition commands.c:51
regex_t minor_regex
Minor mime type regex.
Definition commands.c:52
const char * major
Major mime type, e.g. "text".
Definition commands.c:49
enum ContentType major_int
Major mime type, e.g. TYPE_TEXT.
Definition commands.c:50
The body of an email.
Definition body.h:36
struct Body * parts
parts of a multipart or message/rfc822
Definition body.h:73
struct Body * next
next attachment in the list
Definition body.h:72
char * subtype
content-type subtype
Definition body.h:61
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
const char * name
Name of the command.
Definition command.h:59
The envelope/body of an email.
Definition email.h:39
bool attach_valid
true when the attachment count is valid
Definition email.h:100
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:115
A List node for strings.
Definition list.h:37
char * data
String.
Definition list.h:38
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:43
struct Notify * notify
Notifications handler.
Definition neomutt.h:44
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:47
Notification API.
Definition notify.c:53