NeoMutt  2025-12-11-58-g09398d
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
hook.c
Go to the documentation of this file.
1
30
36
37#include "config.h"
38#include <limits.h>
39#include <stdbool.h>
40#include <string.h>
41#include <unistd.h>
42#include "mutt/lib.h"
43#include "address/lib.h"
44#include "config/lib.h"
45#include "email/lib.h"
46#include "core/lib.h"
47#include "alias/lib.h"
48#include "hook.h"
49#include "attach/lib.h"
50#include "compmbox/lib.h"
51#include "expando/lib.h"
52#include "index/lib.h"
53#include "parse/lib.h"
54#include "pattern/lib.h"
55#include "commands.h"
56#include "globals.h"
57#include "muttlib.h"
58#include "mx.h"
59
60extern const struct ExpandoDefinition IndexFormatDef[];
61
65struct Hook
66{
68 struct Regex regex;
69 char *command;
71 struct PatternList *pattern;
72 struct Expando *expando;
73 TAILQ_ENTRY(Hook) entries;
74};
75TAILQ_HEAD(HookList, Hook);
76
78static struct HookList Hooks = TAILQ_HEAD_INITIALIZER(Hooks);
79
81static struct HashTable *IdxFmtHooks = NULL;
82
85
90static void hook_free(struct Hook **ptr)
91{
92 if (!ptr || !*ptr)
93 return;
94
95 struct Hook *h = *ptr;
96
97 FREE(&h->command);
98 FREE(&h->source_file);
99 FREE(&h->regex.pattern);
100 if (h->regex.regex)
101 {
102 regfree(h->regex.regex);
103 FREE(&h->regex.regex);
104 }
107 FREE(ptr);
108}
109
114static struct Hook *hook_new(void)
115{
116 return MUTT_MEM_CALLOC(1, struct Hook);
117}
118
127 struct Buffer *line, struct Buffer *err)
128{
129 if (!MoreArgs(line))
130 {
131 buf_printf(err, _("%s: too few arguments"), cmd->name);
132 return MUTT_CMD_WARNING;
133 }
134
135 struct Buffer *alias = buf_pool_get();
136 struct Buffer *charset = buf_pool_get();
138
139 if (parse_extract_token(alias, line, TOKEN_NO_FLAGS) < 0)
140 goto done;
141 if (parse_extract_token(charset, line, TOKEN_NO_FLAGS) < 0)
142 goto done;
143
144 const enum LookupType type = (cmd->data & MUTT_ICONV_HOOK) ? MUTT_LOOKUP_ICONV :
146
147 if (buf_is_empty(alias) || buf_is_empty(charset))
148 {
149 buf_printf(err, _("%s: too few arguments"), cmd->name);
150 rc = MUTT_CMD_WARNING;
151 }
152 else if (MoreArgs(line))
153 {
154 buf_printf(err, _("%s: too many arguments"), cmd->name);
155 buf_reset(line); // clean up buffer to avoid a mess with further rcfile processing
156 rc = MUTT_CMD_WARNING;
157 }
158 else if (mutt_ch_lookup_add(type, buf_string(alias), buf_string(charset), err))
159 {
160 rc = MUTT_CMD_SUCCESS;
161 }
162
163done:
164 buf_pool_release(&alias);
165 buf_pool_release(&charset);
166
167 return rc;
168}
169
179 struct Buffer *line, struct Buffer *err)
180{
181 struct Hook *hook = NULL;
183
184 struct Buffer *command = buf_pool_get();
185
186 // TOKEN_SPACE allows the command to contain whitespace, without quoting
187 parse_extract_token(command, line, TOKEN_SPACE);
188
189 if (buf_is_empty(command))
190 {
191 buf_printf(err, _("%s: too few arguments"), cmd->name);
192 rc = MUTT_CMD_WARNING;
193 goto cleanup;
194 }
195
196 if (MoreArgs(line))
197 {
198 buf_printf(err, _("%s: too many arguments"), cmd->name);
199 rc = MUTT_CMD_WARNING;
200 goto cleanup;
201 }
202
203 /* check to make sure that a matching hook doesn't already exist */
204 TAILQ_FOREACH(hook, &Hooks, entries)
205 {
206 /* Ignore duplicate global hooks */
207 if ((hook->type == cmd->data) && mutt_str_equal(hook->command, buf_string(command)))
208 {
209 rc = MUTT_CMD_SUCCESS;
210 goto cleanup;
211 }
212 }
213
214 hook = hook_new();
215 hook->type = cmd->data;
216 hook->command = buf_strdup(command);
218 hook->pattern = NULL;
219 hook->regex.pattern = NULL;
220 hook->regex.regex = NULL;
221 hook->regex.pat_not = false;
222 hook->expando = NULL;
223
224 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
225 rc = MUTT_CMD_SUCCESS;
226
227cleanup:
228 buf_pool_release(&command);
229 return rc;
230}
231
242 struct Buffer *line, struct Buffer *err)
243{
244 struct Hook *hook = NULL;
246 bool pat_not = false;
247 struct PatternList *pat = NULL;
248
249 struct Buffer *command = buf_pool_get();
250 struct Buffer *pattern = buf_pool_get();
251
252 if (*line->dptr == '!')
253 {
254 line->dptr++;
255 SKIPWS(line->dptr);
256 pat_not = true;
257 }
258
259 parse_extract_token(pattern, line, TOKEN_NO_FLAGS);
260
261 if (!MoreArgs(line))
262 {
263 buf_printf(err, _("%s: too few arguments"), cmd->name);
264 rc = MUTT_CMD_WARNING;
265 goto cleanup;
266 }
267
268 // TOKEN_SPACE allows the command to contain whitespace, without quoting
269 parse_extract_token(command, line, TOKEN_SPACE);
270
271 if (buf_is_empty(command))
272 {
273 buf_printf(err, _("%s: too few arguments"), cmd->name);
274 rc = MUTT_CMD_WARNING;
275 goto cleanup;
276 }
277
278 if (MoreArgs(line))
279 {
280 buf_printf(err, _("%s: too many arguments"), cmd->name);
281 rc = MUTT_CMD_WARNING;
282 goto cleanup;
283 }
284
285 const char *const c_default_hook = cs_subset_string(NeoMutt->sub, "default_hook");
286 if (c_default_hook)
287 {
288 mutt_check_simple(pattern, c_default_hook);
289 }
290
291 /* check to make sure that a matching hook doesn't already exist */
292 TAILQ_FOREACH(hook, &Hooks, entries)
293 {
294 if ((hook->type == cmd->data) && (hook->regex.pat_not == pat_not) &&
295 mutt_str_equal(buf_string(pattern), hook->regex.pattern))
296 {
297 /* these hooks allow multiple commands with the same pattern,
298 * so if we've already seen this pattern/command pair,
299 * just ignore it instead of creating a duplicate */
300 if (mutt_str_equal(hook->command, buf_string(command)))
301 {
302 rc = MUTT_CMD_SUCCESS;
303 goto cleanup;
304 }
305 }
306 }
307
308 PatternCompFlags comp_flags;
309 if (cmd->data & MUTT_SEND2_HOOK)
310 comp_flags = MUTT_PC_SEND_MODE_SEARCH;
311 else if (cmd->data & (MUTT_SEND_HOOK))
312 comp_flags = MUTT_PC_NO_FLAGS;
313 else
314 comp_flags = MUTT_PC_FULL_MSG;
315
316 struct MailboxView *mv_cur = get_current_mailbox_view();
317 pat = mutt_pattern_comp(mv_cur, buf_string(pattern), comp_flags, err);
318 if (!pat)
319 goto cleanup;
320
321 hook = hook_new();
322 hook->type = cmd->data;
323 hook->command = buf_strdup(command);
325 hook->pattern = pat;
327 hook->regex.regex = NULL;
328 hook->regex.pat_not = pat_not;
329 hook->expando = NULL;
330
331 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
332 rc = MUTT_CMD_SUCCESS;
333
334cleanup:
335 buf_pool_release(&command);
337 return rc;
338}
339
349 struct Buffer *line, struct Buffer *err)
350{
351 struct Hook *hook = NULL;
353 bool pat_not = false;
354 struct PatternList *pat = NULL;
355
356 struct Buffer *pattern = buf_pool_get();
357 struct Buffer *mailbox = buf_pool_get();
358
359 if (*line->dptr == '!')
360 {
361 line->dptr++;
362 SKIPWS(line->dptr);
363 pat_not = true;
364 }
365
366 parse_extract_token(pattern, line, TOKEN_NO_FLAGS);
367
368 if (!MoreArgs(line))
369 {
370 buf_printf(err, _("%s: too few arguments"), cmd->name);
371 rc = MUTT_CMD_WARNING;
372 goto cleanup;
373 }
374
375 parse_extract_token(mailbox, line, TOKEN_NO_FLAGS);
376
377 if (buf_is_empty(mailbox))
378 {
379 buf_printf(err, _("%s: too few arguments"), cmd->name);
380 rc = MUTT_CMD_WARNING;
381 goto cleanup;
382 }
383
384 if (MoreArgs(line))
385 {
386 buf_printf(err, _("%s: too many arguments"), cmd->name);
387 rc = MUTT_CMD_WARNING;
388 goto cleanup;
389 }
390
391 const char *const c_default_hook = cs_subset_string(NeoMutt->sub, "default_hook");
392 if (c_default_hook)
393 {
394 mutt_check_simple(pattern, c_default_hook);
395 }
396
397 buf_expand_path(mailbox);
398
399 /* check to make sure that a matching hook doesn't already exist */
400 TAILQ_FOREACH(hook, &Hooks, entries)
401 {
402 if ((hook->type == cmd->data) && (hook->regex.pat_not == pat_not) &&
403 mutt_str_equal(buf_string(pattern), hook->regex.pattern))
404 {
405 // Update an existing hook
406 FREE(&hook->command);
407 hook->command = buf_strdup(mailbox);
408 FREE(&hook->source_file);
410
411 expando_free(&hook->expando);
412 hook->expando = expando_parse(buf_string(mailbox), IndexFormatDef, err);
413
414 rc = MUTT_CMD_SUCCESS;
415 goto cleanup;
416 }
417 }
418
419 PatternCompFlags comp_flags;
420 if (cmd->data & MUTT_FCC_HOOK)
421 comp_flags = MUTT_PC_NO_FLAGS;
422 else
423 comp_flags = MUTT_PC_FULL_MSG;
424
425 struct MailboxView *mv_cur = get_current_mailbox_view();
426 pat = mutt_pattern_comp(mv_cur, buf_string(pattern), comp_flags, err);
427 if (!pat)
428 goto cleanup;
429
430 struct Expando *exp = expando_parse(buf_string(mailbox), IndexFormatDef, err);
431
432 hook = hook_new();
433 hook->type = cmd->data;
434 hook->command = buf_strdup(mailbox);
436 hook->pattern = pat;
437 hook->regex.pattern = buf_strdup(pattern);
438 hook->regex.regex = NULL;
439 hook->regex.pat_not = pat_not;
440 hook->expando = exp;
441
442 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
443 rc = MUTT_CMD_SUCCESS;
444
445cleanup:
446 buf_pool_release(&pattern);
447 buf_pool_release(&mailbox);
448 return rc;
449}
450
458 struct Buffer *line, struct Buffer *err)
459{
460 struct Hook *hook = NULL;
462 bool pat_not = false;
463 regex_t *rx = NULL;
464
465 struct Buffer *regex = buf_pool_get();
466 struct Buffer *command = buf_pool_get();
467
468 if (*line->dptr == '!')
469 {
470 line->dptr++;
471 SKIPWS(line->dptr);
472 pat_not = true;
473 }
474
476
477 if (!MoreArgs(line))
478 {
479 buf_printf(err, _("%s: too few arguments"), cmd->name);
480 rc = MUTT_CMD_WARNING;
481 goto cleanup;
482 }
483
484 parse_extract_token(command, line, TOKEN_SPACE);
485
486 if (buf_is_empty(command))
487 {
488 buf_printf(err, _("%s: too few arguments"), cmd->name);
489 rc = MUTT_CMD_WARNING;
490 goto cleanup;
491 }
492
493 if (MoreArgs(line))
494 {
495 buf_printf(err, _("%s: too many arguments"), cmd->name);
496 rc = MUTT_CMD_WARNING;
497 goto cleanup;
498 }
499
500 /* check to make sure that a matching hook doesn't already exist */
501 TAILQ_FOREACH(hook, &Hooks, entries)
502 {
503 if ((hook->type == cmd->data) && (hook->regex.pat_not == pat_not) &&
504 mutt_str_equal(buf_string(regex), hook->regex.pattern))
505 {
506 // Ignore duplicate hooks
507 if (mutt_str_equal(hook->command, buf_string(command)))
508 {
509 rc = MUTT_CMD_SUCCESS;
510 goto cleanup;
511 }
512 }
513 }
514
515 /* Hooks not allowing full patterns: Check syntax of regex */
516 rx = MUTT_MEM_CALLOC(1, regex_t);
517 int rc2 = REG_COMP(rx, buf_string(regex), 0);
518 if (rc2 != 0)
519 {
520 regerror(rc2, rx, err->data, err->dsize);
521 FREE(&rx);
522 goto cleanup;
523 }
524
525 hook = hook_new();
526 hook->type = cmd->data;
527 hook->command = buf_strdup(command);
529 hook->pattern = NULL;
530 hook->regex.pattern = buf_strdup(regex);
531 hook->regex.regex = rx;
532 hook->regex.pat_not = pat_not;
533 hook->expando = NULL;
534
535 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
536 rc = MUTT_CMD_SUCCESS;
537
538cleanup:
539 buf_pool_release(&regex);
540 buf_pool_release(&command);
541 return rc;
542}
543
551 struct Buffer *line, struct Buffer *err)
552{
553 struct Hook *hook = NULL;
555 bool pat_not = false;
556 bool use_regex = true;
557 regex_t *rx = NULL;
558
559 struct Buffer *regex = buf_pool_get();
560 struct Buffer *command = buf_pool_get();
561
562 if (*line->dptr == '!')
563 {
564 line->dptr++;
565 SKIPWS(line->dptr);
566 pat_not = true;
567 }
568
570 if (mutt_str_equal(buf_string(regex), "-noregex"))
571 {
572 use_regex = false;
573 if (!MoreArgs(line))
574 {
575 buf_printf(err, _("%s: too few arguments"), cmd->name);
576 rc = MUTT_CMD_WARNING;
577 goto cleanup;
578 }
580 }
581
582 if (!MoreArgs(line))
583 {
584 buf_printf(err, _("%s: too few arguments"), cmd->name);
585 rc = MUTT_CMD_WARNING;
586 goto cleanup;
587 }
588
589 parse_extract_token(command, line, TOKEN_SPACE);
590
591 if (buf_is_empty(command))
592 {
593 buf_printf(err, _("%s: too few arguments"), cmd->name);
594 rc = MUTT_CMD_WARNING;
595 goto cleanup;
596 }
597
598 if (MoreArgs(line))
599 {
600 buf_printf(err, _("%s: too many arguments"), cmd->name);
601 rc = MUTT_CMD_WARNING;
602 goto cleanup;
603 }
604
605 /* Accidentally using the ^ mailbox shortcut in the .neomuttrc is a
606 * common mistake */
607 if ((buf_at(regex, 0) == '^') && !CurrentFolder)
608 {
609 buf_strcpy(err, _("current mailbox shortcut '^' is unset"));
610 goto cleanup;
611 }
612
613 struct Buffer *tmp = buf_pool_get();
614 buf_copy(tmp, regex);
615 buf_expand_path_regex(tmp, use_regex);
616
617 /* Check for other mailbox shortcuts that expand to the empty string.
618 * This is likely a mistake too */
619 if (buf_is_empty(tmp) && !buf_is_empty(regex))
620 {
621 buf_strcpy(err, _("mailbox shortcut expanded to empty regex"));
622 buf_pool_release(&tmp);
623 goto cleanup;
624 }
625
626 if (use_regex)
627 {
628 buf_copy(regex, tmp);
629 }
630 else
631 {
633 }
634 buf_pool_release(&tmp);
635
636 /* check to make sure that a matching hook doesn't already exist */
637 TAILQ_FOREACH(hook, &Hooks, entries)
638 {
639 if ((hook->type == cmd->data) && (hook->regex.pat_not == pat_not) &&
640 mutt_str_equal(buf_string(regex), hook->regex.pattern))
641 {
642 // Ignore duplicate hooks
643 if (mutt_str_equal(hook->command, buf_string(command)))
644 {
645 rc = MUTT_CMD_SUCCESS;
646 goto cleanup;
647 }
648 }
649 }
650
651 /* Hooks not allowing full patterns: Check syntax of regex */
652 rx = MUTT_MEM_CALLOC(1, regex_t);
653 int rc2 = REG_COMP(rx, buf_string(regex), 0);
654 if (rc2 != 0)
655 {
656 regerror(rc2, rx, err->data, err->dsize);
657 FREE(&rx);
658 goto cleanup;
659 }
660
661 hook = hook_new();
662 hook->type = cmd->data;
663 hook->command = buf_strdup(command);
665 hook->pattern = NULL;
666 hook->regex.pattern = buf_strdup(regex);
667 hook->regex.regex = rx;
668 hook->regex.pat_not = pat_not;
669 hook->expando = NULL;
670
671 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
672 rc = MUTT_CMD_SUCCESS;
673
674cleanup:
675 buf_pool_release(&regex);
676 buf_pool_release(&command);
677 return rc;
678}
679
688 struct Buffer *line, struct Buffer *err)
689{
690 struct Hook *hook = NULL;
692 bool pat_not = false;
693 regex_t *rx = NULL;
694
695 struct Buffer *regex = buf_pool_get();
696 struct Buffer *keyid = buf_pool_get();
697
698 if (*line->dptr == '!')
699 {
700 line->dptr++;
701 SKIPWS(line->dptr);
702 pat_not = true;
703 }
704
706
707 if (!MoreArgs(line))
708 {
709 buf_printf(err, _("%s: too few arguments"), cmd->name);
710 rc = MUTT_CMD_WARNING;
711 goto cleanup;
712 }
713
715
716 if (buf_is_empty(keyid))
717 {
718 buf_printf(err, _("%s: too few arguments"), cmd->name);
719 rc = MUTT_CMD_WARNING;
720 goto cleanup;
721 }
722
723 if (MoreArgs(line))
724 {
725 buf_printf(err, _("%s: too many arguments"), cmd->name);
726 rc = MUTT_CMD_WARNING;
727 goto cleanup;
728 }
729
730 /* check to make sure that a matching hook doesn't already exist */
731 TAILQ_FOREACH(hook, &Hooks, entries)
732 {
733 if ((hook->type == cmd->data) && (hook->regex.pat_not == pat_not) &&
734 mutt_str_equal(buf_string(regex), hook->regex.pattern))
735 {
736 // Ignore duplicate hooks
737 if (mutt_str_equal(hook->command, buf_string(keyid)))
738 {
739 rc = MUTT_CMD_SUCCESS;
740 goto cleanup;
741 }
742 }
743 }
744
745 /* Hooks not allowing full patterns: Check syntax of regex */
746 rx = MUTT_MEM_CALLOC(1, regex_t);
747 int rc2 = REG_COMP(rx, buf_string(regex), REG_ICASE);
748 if (rc2 != 0)
749 {
750 regerror(rc2, rx, err->data, err->dsize);
751 FREE(&rx);
752 goto cleanup;
753 }
754
755 hook = hook_new();
756 hook->type = cmd->data;
757 hook->command = buf_strdup(keyid);
759 hook->pattern = NULL;
760 hook->regex.pattern = buf_strdup(regex);
761 hook->regex.regex = rx;
762 hook->regex.pat_not = pat_not;
763 hook->expando = NULL;
764
765 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
766 rc = MUTT_CMD_SUCCESS;
767
768cleanup:
769 buf_pool_release(&regex);
770 buf_pool_release(&keyid);
771 return rc;
772}
773
780enum CommandResult parse_hook_mbox(const struct Command *cmd,
781 struct Buffer *line, struct Buffer *err)
782{
783 struct Hook *hook = NULL;
785 bool pat_not = false;
786 bool use_regex = true;
787 regex_t *rx = NULL;
788
789 struct Buffer *regex = buf_pool_get();
790 struct Buffer *command = buf_pool_get();
791
792 if (*line->dptr == '!')
793 {
794 line->dptr++;
795 SKIPWS(line->dptr);
796 pat_not = true;
797 }
798
800 if (mutt_str_equal(buf_string(regex), "-noregex"))
801 {
802 use_regex = false;
803 if (!MoreArgs(line))
804 {
805 buf_printf(err, _("%s: too few arguments"), cmd->name);
806 rc = MUTT_CMD_WARNING;
807 goto cleanup;
808 }
810 }
811
812 if (!MoreArgs(line))
813 {
814 buf_printf(err, _("%s: too few arguments"), cmd->name);
815 rc = MUTT_CMD_WARNING;
816 goto cleanup;
817 }
818
819 parse_extract_token(command, line, TOKEN_NO_FLAGS);
820
821 if (buf_is_empty(command))
822 {
823 buf_printf(err, _("%s: too few arguments"), cmd->name);
824 rc = MUTT_CMD_WARNING;
825 goto cleanup;
826 }
827
828 if (MoreArgs(line))
829 {
830 buf_printf(err, _("%s: too many arguments"), cmd->name);
831 rc = MUTT_CMD_WARNING;
832 goto cleanup;
833 }
834
835 /* Accidentally using the ^ mailbox shortcut in the .neomuttrc is a
836 * common mistake */
837 if ((buf_at(regex, 0) == '^') && !CurrentFolder)
838 {
839 buf_strcpy(err, _("current mailbox shortcut '^' is unset"));
840 goto cleanup;
841 }
842
843 struct Buffer *tmp = buf_pool_get();
844 buf_copy(tmp, regex);
845 buf_expand_path_regex(tmp, use_regex);
846
847 /* Check for other mailbox shortcuts that expand to the empty string.
848 * This is likely a mistake too */
849 if (buf_is_empty(tmp) && !buf_is_empty(regex))
850 {
851 buf_strcpy(err, _("mailbox shortcut expanded to empty regex"));
852 buf_pool_release(&tmp);
853 goto cleanup;
854 }
855
856 if (use_regex)
857 {
858 buf_copy(regex, tmp);
859 }
860 else
861 {
863 }
864 buf_pool_release(&tmp);
865
866 buf_expand_path(command);
867
868 /* check to make sure that a matching hook doesn't already exist */
869 TAILQ_FOREACH(hook, &Hooks, entries)
870 {
871 if ((hook->type == cmd->data) && (hook->regex.pat_not == pat_not) &&
872 mutt_str_equal(buf_string(regex), hook->regex.pattern))
873 {
874 // Update an existing hook
875 FREE(&hook->command);
876 hook->command = buf_strdup(command);
877 FREE(&hook->source_file);
879
880 expando_free(&hook->expando);
881 hook->expando = expando_parse(buf_string(command), IndexFormatDef, err);
882
883 rc = MUTT_CMD_SUCCESS;
884 goto cleanup;
885 }
886 }
887
888 /* Hooks not allowing full patterns: Check syntax of regex */
889 rx = MUTT_MEM_CALLOC(1, regex_t);
890 int rc2 = REG_COMP(rx, buf_string(regex), 0);
891 if (rc2 != 0)
892 {
893 regerror(rc2, rx, err->data, err->dsize);
894 FREE(&rx);
895 goto cleanup;
896 }
897
898 struct Expando *exp = expando_parse(buf_string(command), IndexFormatDef, err);
899
900 hook = hook_new();
901 hook->type = cmd->data;
902 hook->command = buf_strdup(command);
904 hook->pattern = NULL;
905 hook->regex.pattern = buf_strdup(regex);
906 hook->regex.regex = rx;
907 hook->regex.pat_not = pat_not;
908 hook->expando = exp;
909
910 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
911 rc = MUTT_CMD_SUCCESS;
912
913cleanup:
914 buf_pool_release(&regex);
915 buf_pool_release(&command);
916 return rc;
917}
918
928 struct Buffer *line, struct Buffer *err)
929{
930 struct Hook *hook = NULL;
932 bool pat_not = false;
933 regex_t *rx = NULL;
934
935 struct Buffer *regex = buf_pool_get();
936 struct Buffer *command = buf_pool_get();
937
938 if (*line->dptr == '!')
939 {
940 line->dptr++;
941 SKIPWS(line->dptr);
942 pat_not = true;
943 }
944
946
947 if (!MoreArgs(line))
948 {
949 buf_printf(err, _("%s: too few arguments"), cmd->name);
950 rc = MUTT_CMD_WARNING;
951 goto cleanup;
952 }
953
954 // TOKEN_SPACE allows the command to contain whitespace, without quoting
955 parse_extract_token(command, line, TOKEN_SPACE);
956
957 if (buf_is_empty(command))
958 {
959 buf_printf(err, _("%s: too few arguments"), cmd->name);
960 rc = MUTT_CMD_WARNING;
961 goto cleanup;
962 }
963
964 if (MoreArgs(line))
965 {
966 buf_printf(err, _("%s: too many arguments"), cmd->name);
967 rc = MUTT_CMD_WARNING;
968 goto cleanup;
969 }
970
971 if (mutt_comp_valid_command(buf_string(command)) == 0)
972 {
973 buf_strcpy(err, _("badly formatted command string"));
974 goto cleanup;
975 }
976
977 /* check to make sure that a matching hook doesn't already exist */
978 TAILQ_FOREACH(hook, &Hooks, entries)
979 {
980 if ((hook->type == cmd->data) && (hook->regex.pat_not == pat_not) &&
981 mutt_str_equal(buf_string(regex), hook->regex.pattern))
982 {
983 // Update an existing hook
984 FREE(&hook->command);
985 hook->command = buf_strdup(command);
986 FREE(&hook->source_file);
988
989 rc = MUTT_CMD_SUCCESS;
990 goto cleanup;
991 }
992 }
993
994 /* Hooks not allowing full patterns: Check syntax of regex */
995 rx = MUTT_MEM_CALLOC(1, regex_t);
996 int rc2 = REG_COMP(rx, buf_string(regex), 0);
997 if (rc2 != 0)
998 {
999 regerror(rc2, rx, err->data, err->dsize);
1000 FREE(&rx);
1001 goto cleanup;
1002 }
1003
1004 hook = hook_new();
1005 hook->type = cmd->data;
1006 hook->command = buf_strdup(command);
1008 hook->pattern = NULL;
1009 hook->regex.pattern = buf_strdup(regex);
1010 hook->regex.regex = rx;
1011 hook->regex.pat_not = pat_not;
1012 hook->expando = NULL;
1013
1014 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
1015 rc = MUTT_CMD_SUCCESS;
1016
1017cleanup:
1018 buf_pool_release(&regex);
1019 buf_pool_release(&command);
1020 return rc;
1021}
1022
1030{
1031 struct Hook *h = NULL;
1032 struct Hook *tmp = NULL;
1033
1034 TAILQ_FOREACH_SAFE(h, &Hooks, entries, tmp)
1035 {
1036 if ((type == MUTT_HOOK_NO_FLAGS) || (type == h->type))
1037 {
1038 TAILQ_REMOVE(&Hooks, h, entries);
1039 hook_free(&h);
1040 }
1041 }
1042}
1043
1047static void idxfmt_hashelem_free(int type, void *obj, intptr_t data)
1048{
1049 struct HookList *hl = obj;
1050 struct Hook *h = NULL;
1051 struct Hook *tmp = NULL;
1052
1053 TAILQ_FOREACH_SAFE(h, hl, entries, tmp)
1054 {
1055 TAILQ_REMOVE(hl, h, entries);
1056 hook_free(&h);
1057 }
1058
1059 FREE(&hl);
1060}
1061
1065static void delete_idxfmt_hooks(void)
1066{
1068}
1069
1077 struct Buffer *line, struct Buffer *err)
1078{
1079 if (!MoreArgs(line))
1080 {
1081 buf_printf(err, _("%s: too few arguments"), cmd->name);
1082 return MUTT_CMD_WARNING;
1083 }
1084
1085 enum CommandResult rc = MUTT_CMD_ERROR;
1086 bool pat_not = false;
1087
1088 struct Buffer *name = buf_pool_get();
1089 struct Buffer *pattern = buf_pool_get();
1090 struct Buffer *fmt = buf_pool_get();
1091 struct Expando *exp = NULL;
1092
1093 if (!IdxFmtHooks)
1094 {
1097 }
1098
1100 struct HookList *hl = mutt_hash_find(IdxFmtHooks, buf_string(name));
1101
1102 if (*line->dptr == '!')
1103 {
1104 line->dptr++;
1105 SKIPWS(line->dptr);
1106 pat_not = true;
1107 }
1108 parse_extract_token(pattern, line, TOKEN_NO_FLAGS);
1109
1110 if (!MoreArgs(line))
1111 {
1112 buf_printf(err, _("%s: too few arguments"), cmd->name);
1113 goto out;
1114 }
1116
1117 exp = expando_parse(buf_string(fmt), IndexFormatDef, err);
1118 if (!exp)
1119 goto out;
1120
1121 if (MoreArgs(line))
1122 {
1123 buf_printf(err, _("%s: too many arguments"), cmd->name);
1124 goto out;
1125 }
1126
1127 const char *const c_default_hook = cs_subset_string(NeoMutt->sub, "default_hook");
1128 if (c_default_hook)
1129 mutt_check_simple(pattern, c_default_hook);
1130
1131 /* check to make sure that a matching hook doesn't already exist */
1132 struct Hook *hook = NULL;
1133 if (hl)
1134 {
1135 TAILQ_FOREACH(hook, hl, entries)
1136 {
1137 // Update an existing hook
1138 if ((hook->regex.pat_not == pat_not) &&
1140 {
1141 expando_free(&hook->expando);
1142 hook->expando = exp;
1143 exp = NULL;
1144 rc = MUTT_CMD_SUCCESS;
1145 goto out;
1146 }
1147 }
1148 }
1149
1150 /* MUTT_PC_PATTERN_DYNAMIC sets so that date ranges are regenerated during
1151 * matching. This of course is slower, but index-format-hook is commonly
1152 * used for date ranges, and they need to be evaluated relative to "now", not
1153 * the hook compilation time. */
1154 struct MailboxView *mv_cur = get_current_mailbox_view();
1155 struct PatternList *pat = mutt_pattern_comp(mv_cur, buf_string(pattern),
1157 err);
1158 if (!pat)
1159 goto out;
1160
1161 hook = hook_new();
1162 hook->type = MUTT_IDXFMTHOOK;
1163 hook->command = NULL;
1165 hook->pattern = pat;
1166 hook->regex.pattern = buf_strdup(pattern);
1167 hook->regex.regex = NULL;
1168 hook->regex.pat_not = pat_not;
1169 hook->expando = exp;
1170 exp = NULL;
1171
1172 if (!hl)
1173 {
1174 hl = MUTT_MEM_CALLOC(1, struct HookList);
1175 TAILQ_INIT(hl);
1177 }
1178
1179 TAILQ_INSERT_TAIL(hl, hook, entries);
1180 rc = MUTT_CMD_SUCCESS;
1181
1182out:
1183 buf_pool_release(&name);
1184 buf_pool_release(&pattern);
1185 buf_pool_release(&fmt);
1186 expando_free(&exp);
1187
1188 return rc;
1189}
1190
1197static HookFlags mutt_get_hook_type(const char *name)
1198{
1199 const struct Command **cp = NULL;
1201 {
1202 const struct Command *cmd = *cp;
1203
1204 if (((cmd->parse == parse_hook_index) || (cmd->parse == parse_hook_global) ||
1205 (cmd->parse == parse_hook_pattern) ||
1206 (cmd->parse == parse_hook_mailbox) || (cmd->parse == parse_hook_regex) ||
1207 (cmd->parse == parse_hook_folder) || (cmd->parse == parse_hook_crypt) ||
1208 (cmd->parse == parse_hook_mbox) || (cmd->parse == parse_hook_compress)) &&
1209 mutt_istr_equal(cmd->name, name))
1210 {
1211 return cmd->data;
1212 }
1213 }
1214 return MUTT_HOOK_NO_FLAGS;
1215}
1216
1223enum CommandResult parse_unhook(const struct Command *cmd, struct Buffer *line,
1224 struct Buffer *err)
1225{
1226 if (!MoreArgs(line))
1227 {
1228 buf_printf(err, _("%s: too few arguments"), cmd->name);
1229 return MUTT_CMD_WARNING;
1230 }
1231
1232 struct Buffer *token = buf_pool_get();
1234
1235 while (MoreArgs(line))
1236 {
1237 parse_extract_token(token, line, TOKEN_NO_FLAGS);
1238 if (mutt_str_equal("*", buf_string(token)))
1239 {
1241 {
1242 buf_addstr(err, _("unhook: Can't do unhook * from within a hook"));
1243 goto done;
1244 }
1248 }
1249 else
1250 {
1252
1253 if (type == MUTT_HOOK_NO_FLAGS)
1254 {
1255 buf_printf(err, _("unhook: unknown hook type: %s"), buf_string(token));
1256 rc = MUTT_CMD_ERROR;
1257 goto done;
1258 }
1259 if (type & (MUTT_CHARSET_HOOK | MUTT_ICONV_HOOK))
1260 {
1262 rc = MUTT_CMD_SUCCESS;
1263 goto done;
1264 }
1265 if (CurrentHookType == type)
1266 {
1267 buf_printf(err, _("unhook: Can't delete a %s from within a %s"),
1268 buf_string(token), buf_string(token));
1269 rc = MUTT_CMD_WARNING;
1270 goto done;
1271 }
1272 if (type == MUTT_IDXFMTHOOK)
1274 else
1275 mutt_delete_hooks(type);
1276 }
1277 }
1278
1279 rc = MUTT_CMD_SUCCESS;
1280
1281done:
1282 buf_pool_release(&token);
1283 return rc;
1284}
1285
1291void mutt_folder_hook(const char *path, const char *desc)
1292{
1293 if (!path && !desc)
1294 return;
1295
1296 struct Hook *hook = NULL;
1297 struct Buffer *err = buf_pool_get();
1298
1300
1301 TAILQ_FOREACH(hook, &Hooks, entries)
1302 {
1303 if (!hook->command)
1304 continue;
1305
1306 if (!(hook->type & MUTT_FOLDER_HOOK))
1307 continue;
1308
1309 const char *match = NULL;
1310 if (mutt_regex_match(&hook->regex, path))
1311 match = path;
1312 else if (mutt_regex_match(&hook->regex, desc))
1313 match = desc;
1314
1315 if (match)
1316 {
1317 mutt_debug(LL_DEBUG1, "folder-hook '%s' matches '%s'\n", hook->regex.pattern, match);
1318 mutt_debug(LL_DEBUG5, " %s\n", hook->command);
1319 if (parse_rc_line_cwd(hook->command, hook->source_file, err) == MUTT_CMD_ERROR)
1320 {
1321 mutt_error("%s", buf_string(err));
1322 break;
1323 }
1324 }
1325 }
1326 buf_pool_release(&err);
1327
1329}
1330
1339char *mutt_find_hook(HookFlags type, const char *pat)
1340{
1341 struct Hook *tmp = NULL;
1342
1343 TAILQ_FOREACH(tmp, &Hooks, entries)
1344 {
1345 if (tmp->type & type)
1346 {
1347 if (mutt_regex_match(&tmp->regex, pat))
1348 return tmp->command;
1349 }
1350 }
1351 return NULL;
1352}
1353
1360void mutt_message_hook(struct Mailbox *m, struct Email *e, HookFlags type)
1361{
1362 struct Hook *hook = NULL;
1363 struct PatternCache cache = { 0 };
1364 struct Buffer *err = buf_pool_get();
1365
1366 CurrentHookType = type;
1367
1368 TAILQ_FOREACH(hook, &Hooks, entries)
1369 {
1370 if (!hook->command)
1371 continue;
1372
1373 if (hook->type & type)
1374 {
1375 if ((mutt_pattern_exec(SLIST_FIRST(hook->pattern), 0, m, e, &cache) > 0) ^
1376 hook->regex.pat_not)
1377 {
1378 if (parse_rc_line_cwd(hook->command, hook->source_file, err) == MUTT_CMD_ERROR)
1379 {
1380 mutt_error("%s", buf_string(err));
1382 buf_pool_release(&err);
1383
1384 return;
1385 }
1386 /* Executing arbitrary commands could affect the pattern results,
1387 * so the cache has to be wiped */
1388 memset(&cache, 0, sizeof(cache));
1389 }
1390 }
1391 }
1392 buf_pool_release(&err);
1393
1395}
1396
1406static int addr_hook(struct Buffer *path, HookFlags type, struct Mailbox *m, struct Email *e)
1407{
1408 struct Hook *hook = NULL;
1409 struct PatternCache cache = { 0 };
1410
1411 /* determine if a matching hook exists */
1412 TAILQ_FOREACH(hook, &Hooks, entries)
1413 {
1414 if (!hook->command)
1415 continue;
1416
1417 if (hook->type & type)
1418 {
1419 if ((mutt_pattern_exec(SLIST_FIRST(hook->pattern), 0, m, e, &cache) > 0) ^
1420 hook->regex.pat_not)
1421 {
1422 buf_alloc(path, PATH_MAX);
1423 mutt_make_string(path, -1, hook->expando, m, -1, e, MUTT_FORMAT_PLAIN, NULL);
1424 buf_fix_dptr(path);
1425 return 0;
1426 }
1427 }
1428 }
1429
1430 return -1;
1431}
1432
1438void mutt_default_save(struct Buffer *path, struct Email *e)
1439{
1440 struct Mailbox *m_cur = get_current_mailbox();
1441 if (addr_hook(path, MUTT_SAVE_HOOK, m_cur, e) == 0)
1442 return;
1443
1444 struct Envelope *env = e->env;
1445 const struct Address *from = TAILQ_FIRST(&env->from);
1446 const struct Address *reply_to = TAILQ_FIRST(&env->reply_to);
1447 const struct Address *to = TAILQ_FIRST(&env->to);
1448 const struct Address *cc = TAILQ_FIRST(&env->cc);
1449 const struct Address *addr = NULL;
1450 bool from_me = mutt_addr_is_user(from);
1451
1452 if (!from_me && reply_to && reply_to->mailbox)
1453 addr = reply_to;
1454 else if (!from_me && from && from->mailbox)
1455 addr = from;
1456 else if (to && to->mailbox)
1457 addr = to;
1458 else if (cc && cc->mailbox)
1459 addr = cc;
1460 else
1461 addr = NULL;
1462 if (addr)
1463 {
1464 struct Buffer *tmp = buf_pool_get();
1465 mutt_safe_path(tmp, addr);
1466 buf_add_printf(path, "=%s", buf_string(tmp));
1467 buf_pool_release(&tmp);
1468 }
1469}
1470
1476void mutt_select_fcc(struct Buffer *path, struct Email *e)
1477{
1478 buf_alloc(path, PATH_MAX);
1479
1480 if (addr_hook(path, MUTT_FCC_HOOK, NULL, e) != 0)
1481 {
1482 const struct Address *to = TAILQ_FIRST(&e->env->to);
1483 const struct Address *cc = TAILQ_FIRST(&e->env->cc);
1484 const struct Address *bcc = TAILQ_FIRST(&e->env->bcc);
1485 const bool c_save_name = cs_subset_bool(NeoMutt->sub, "save_name");
1486 const bool c_force_name = cs_subset_bool(NeoMutt->sub, "force_name");
1487 const char *const c_record = cs_subset_string(NeoMutt->sub, "record");
1488 if ((c_save_name || c_force_name) && (to || cc || bcc))
1489 {
1490 const struct Address *addr = to ? to : (cc ? cc : bcc);
1491 struct Buffer *buf = buf_pool_get();
1492 mutt_safe_path(buf, addr);
1493 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
1494 buf_concat_path(path, NONULL(c_folder), buf_string(buf));
1495 buf_pool_release(&buf);
1496 if (!c_force_name && (mx_access(buf_string(path), W_OK) != 0))
1497 buf_strcpy(path, c_record);
1498 }
1499 else
1500 {
1501 buf_strcpy(path, c_record);
1502 }
1503 }
1504 else
1505 {
1506 buf_fix_dptr(path);
1507 }
1508
1509 buf_pretty_mailbox(path);
1510}
1511
1518static void list_hook(struct ListHead *matches, const char *match, HookFlags type)
1519{
1520 struct Hook *tmp = NULL;
1521
1522 TAILQ_FOREACH(tmp, &Hooks, entries)
1523 {
1524 if ((tmp->type & type) && mutt_regex_match(&tmp->regex, match))
1525 {
1527 }
1528 }
1529}
1530
1538void mutt_crypt_hook(struct ListHead *list, struct Address *addr)
1539{
1541}
1542
1547void mutt_account_hook(const char *url)
1548{
1549 /* parsing commands with URLs in an account hook can cause a recursive
1550 * call. We just skip processing if this occurs. Typically such commands
1551 * belong in a folder-hook -- perhaps we should warn the user. */
1552 static bool inhook = false;
1553 if (inhook)
1554 return;
1555
1556 struct Hook *hook = NULL;
1557 struct Buffer *err = buf_pool_get();
1558
1559 TAILQ_FOREACH(hook, &Hooks, entries)
1560 {
1561 if (!(hook->command && (hook->type & MUTT_ACCOUNT_HOOK)))
1562 continue;
1563
1564 if (mutt_regex_match(&hook->regex, url))
1565 {
1566 inhook = true;
1567 mutt_debug(LL_DEBUG1, "account-hook '%s' matches '%s'\n", hook->regex.pattern, url);
1568 mutt_debug(LL_DEBUG5, " %s\n", hook->command);
1569
1570 if (parse_rc_line_cwd(hook->command, hook->source_file, err) == MUTT_CMD_ERROR)
1571 {
1572 mutt_error("%s", buf_string(err));
1573 buf_pool_release(&err);
1574
1575 inhook = false;
1576 goto done;
1577 }
1578
1579 inhook = false;
1580 }
1581 }
1582done:
1583 buf_pool_release(&err);
1584}
1585
1593{
1594 struct Hook *hook = NULL;
1595 struct Buffer *err = buf_pool_get();
1596
1597 TAILQ_FOREACH(hook, &Hooks, entries)
1598 {
1599 if (!(hook->command && (hook->type & MUTT_TIMEOUT_HOOK)))
1600 continue;
1601
1602 if (parse_rc_line_cwd(hook->command, hook->source_file, err) == MUTT_CMD_ERROR)
1603 {
1604 mutt_error("%s", buf_string(err));
1605 buf_reset(err);
1606
1607 /* The hooks should be independent of each other, so even though this on
1608 * failed, we'll carry on with the others. */
1609 }
1610 }
1611 buf_pool_release(&err);
1612
1613 /* Delete temporary attachment files */
1615}
1616
1625{
1626 struct Hook *hook = NULL;
1627 struct Buffer *err = buf_pool_get();
1628
1629 TAILQ_FOREACH(hook, &Hooks, entries)
1630 {
1631 if (!(hook->command && (hook->type & type)))
1632 continue;
1633
1634 if (parse_rc_line_cwd(hook->command, hook->source_file, err) == MUTT_CMD_ERROR)
1635 {
1636 mutt_error("%s", buf_string(err));
1637 buf_reset(err);
1638 }
1639 }
1640 buf_pool_release(&err);
1641}
1642
1651const struct Expando *mutt_idxfmt_hook(const char *name, struct Mailbox *m, struct Email *e)
1652{
1653 if (!IdxFmtHooks)
1654 return NULL;
1655
1656 struct HookList *hl = mutt_hash_find(IdxFmtHooks, name);
1657 if (!hl)
1658 return NULL;
1659
1661
1662 struct PatternCache cache = { 0 };
1663 const struct Expando *exp = NULL;
1664 struct Hook *hook = NULL;
1665
1666 TAILQ_FOREACH(hook, hl, entries)
1667 {
1668 struct Pattern *pat = SLIST_FIRST(hook->pattern);
1669 if ((mutt_pattern_exec(pat, 0, m, e, &cache) > 0) ^ hook->regex.pat_not)
1670 {
1671 exp = hook->expando;
1672 break;
1673 }
1674 }
1675
1677
1678 return exp;
1679}
1680
1684static const struct Command HookCommands[] = {
1685 // clang-format off
1686 { "account-hook", parse_hook_regex, MUTT_ACCOUNT_HOOK,
1687 N_("Run a command when switching to a matching account"),
1688 N_("account-hook <regex> <command>"),
1689 "optionalfeatures.html#account-hook" },
1690 { "charset-hook", parse_hook_charset, MUTT_CHARSET_HOOK,
1691 N_("Define charset alias for languages"),
1692 N_("charset-hook <alias> <charset>"),
1693 "configuration.html#charset-hook" },
1694 { "crypt-hook", parse_hook_crypt, MUTT_CRYPT_HOOK,
1695 N_("Specify which keyid to use for recipients matching regex"),
1696 N_("crypt-hook <regex> <keyid>"),
1697 "configuration.html#crypt-hook" },
1698 { "fcc-hook", parse_hook_mailbox, MUTT_FCC_HOOK,
1699 N_("Pattern rule to set the save location for outgoing mail"),
1700 N_("fcc-hook <pattern> <mailbox>"),
1701 "configuration.html#default-save-mailbox" },
1702 { "fcc-save-hook", parse_hook_mailbox, MUTT_FCC_HOOK | MUTT_SAVE_HOOK,
1703 N_("Equivalent to both `fcc-hook` and `save-hook`"),
1704 N_("fcc-save-hook <pattern> <mailbox>"),
1705 "configuration.html#default-save-mailbox" },
1706 { "folder-hook", parse_hook_folder, MUTT_FOLDER_HOOK,
1707 N_("Run a command upon entering a folder matching regex"),
1708 N_("folder-hook [ -noregex ] <regex> <command>"),
1709 "configuration.html#folder-hook" },
1710 { "iconv-hook", parse_hook_charset, MUTT_ICONV_HOOK,
1711 N_("Define a system-specific alias for a character set"),
1712 N_("iconv-hook <charset> <local-charset>"),
1713 "configuration.html#charset-hook" },
1714 { "index-format-hook", parse_hook_index, MUTT_IDXFMTHOOK,
1715 N_("Create dynamic index format strings"),
1716 N_("index-format-hook <name> [ ! ]<pattern> <format-string>"),
1717 "configuration.html#index-format-hook" },
1718 { "mbox-hook", parse_hook_mbox, MUTT_MBOX_HOOK,
1719 N_("On leaving a mailbox, move read messages matching a regex regex"),
1720 N_("mbox-hook [ -noregex ] <regex> <mailbox>"),
1721 "configuration.html#mbox-hook" },
1722 { "message-hook", parse_hook_pattern, MUTT_MESSAGE_HOOK,
1723 N_("Run a command when viewing a message matching patterns"),
1724 N_("message-hook <pattern> <command>"),
1725 "configuration.html#message-hook" },
1726 { "reply-hook", parse_hook_pattern, MUTT_REPLY_HOOK,
1727 N_("Run a command when replying to messages matching a pattern"),
1728 N_("reply-hook <pattern> <command>"),
1729 "configuration.html#send-hook" },
1730 { "save-hook", parse_hook_mailbox, MUTT_SAVE_HOOK,
1731 N_("Set default save folder for messages"),
1732 N_("save-hook <pattern> <mailbox>"),
1733 "configuration.html#default-save-mailbox" },
1734 { "send-hook", parse_hook_pattern, MUTT_SEND_HOOK,
1735 N_("Run a command when sending a message, new or reply, matching a pattern"),
1736 N_("send-hook <pattern> <command>"),
1737 "configuration.html#send-hook" },
1738 { "send2-hook", parse_hook_pattern, MUTT_SEND2_HOOK,
1739 N_("Run command whenever a composed message is edited"),
1740 N_("send2-hook <pattern> <command>"),
1741 "configuration.html#send-hook" },
1742 { "shutdown-hook", parse_hook_global, MUTT_SHUTDOWN_HOOK,
1743 N_("Run a command before NeoMutt exits"),
1744 N_("shutdown-hook <command>"),
1745 "optionalfeatures.html#global-hooks" },
1746 { "startup-hook", parse_hook_global, MUTT_STARTUP_HOOK,
1747 N_("Run a command when NeoMutt starts up"),
1748 N_("startup-hook <command>"),
1749 "optionalfeatures.html#global-hooks" },
1750 { "timeout-hook", parse_hook_global, MUTT_TIMEOUT_HOOK,
1751 N_("Run a command after a specified timeout or idle period"),
1752 N_("timeout-hook <command>"),
1753 "optionalfeatures.html#global-hooks" },
1754 { "unhook", parse_unhook, 0,
1755 N_("Remove hooks of a given type"),
1756 N_("unhook { * | <hook-type> }"),
1757 "configuration.html#unhook" },
1758
1759 // Deprecated
1760 { "pgp-hook", parse_hook_crypt, MUTT_CRYPT_HOOK, NULL, NULL, NULL, CF_SYNONYM },
1761
1762 { NULL, NULL, 0, NULL, NULL, NULL, CF_NO_FLAGS },
1763 // clang-format on
1764};
1765
Email Address Handling.
Email Aliases.
bool mutt_addr_is_user(const struct Address *addr)
Does the address belong to the user.
Definition alias.c:595
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:214
GUI display the mailboxes in a side panel.
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:204
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition buffer.c:182
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition buffer.c:668
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition buffer.c:601
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
size_t buf_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition buffer.c:509
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition buffer.c:337
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
#define CF_SYNONYM
Command is a synonym for another command.
Definition command.h:47
#define CF_NO_FLAGS
No flags are set.
Definition command.h:46
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
enum CommandResult parse_rc_line_cwd(const char *line, char *cwd, struct Buffer *err)
Parse and run a muttrc line in a relative directory.
Definition commands.c:179
char * mutt_get_sourced_cwd(void)
Get the current file path that is being parsed.
Definition commands.c:202
Functions to parse commands in a config file.
struct PatternList * mutt_pattern_comp(struct MailboxView *mv, const char *s, PatternCompFlags flags, struct Buffer *err)
Create a Pattern.
Definition compile.c:902
void mutt_pattern_free(struct PatternList **pat)
Free a Pattern.
Definition compile.c:774
int mutt_comp_valid_command(const char *cmd)
Is this command string allowed?
Definition compress.c:391
Compressed mbox local mailbox type.
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
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.
bool commands_register(struct CommandArray *ca, const struct Command *cmds)
Add commands to Commands array.
Definition command.c:51
Convenience wrapper for the core headers.
int mutt_make_string(struct Buffer *buf, size_t max_cols, const struct Expando *exp, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition dlg_index.c:802
Structs that make up an email.
bool mutt_pattern_exec(struct Pattern *pat, PatternExecFlags flags, struct Mailbox *m, struct Email *e, struct PatternCache *cache)
Match a pattern against an email header.
Definition exec.c:1148
struct Expando * expando_parse(const char *str, const struct ExpandoDefinition *defs, struct Buffer *err)
Parse an Expando string.
Definition expando.c:81
void expando_free(struct Expando **ptr)
Free an Expando object.
Definition expando.c:61
Parse Expando string.
int parse_extract_token(struct Buffer *dest, struct Buffer *line, TokenFlags flags)
Extract one token from a string.
Definition extract.c:48
#define TOKEN_SPACE
Don't treat whitespace as a term.
Definition extract.h:47
#define MoreArgs(buf)
Definition extract.h:30
#define TOKEN_NO_FLAGS
No flags are set.
Definition extract.h:44
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition file.c:631
char * CurrentFolder
Currently selected mailbox.
Definition globals.c:39
Global variables.
enum CommandResult parse_hook_folder(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse folder hook command - Implements Command::parse() -.
Definition hook.c:550
enum CommandResult parse_unhook(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse the unhook command - Implements Command::parse() -.
Definition hook.c:1223
enum CommandResult parse_hook_pattern(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse pattern-based hook commands - Implements Command::parse() -.
Definition hook.c:241
enum CommandResult parse_hook_regex(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse regex-based hook command - Implements Command::parse() -.
Definition hook.c:457
enum CommandResult parse_hook_compress(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse compress hook commands - Implements Command::parse() -.
Definition hook.c:927
enum CommandResult parse_hook_index(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse the index format hook command - Implements Command::parse() -.
Definition hook.c:1076
enum CommandResult parse_hook_mailbox(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse mailbox pattern hook commands - Implements Command::parse() -.
Definition hook.c:348
enum CommandResult parse_hook_global(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse global hook commands - Implements Command::parse() -.
Definition hook.c:178
enum CommandResult parse_hook_charset(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse charset hook commands - Implements Command::parse() -.
Definition hook.c:126
enum CommandResult parse_hook_crypt(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse crypt hook commands - Implements Command::parse() -.
Definition hook.c:687
enum CommandResult parse_hook_mbox(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse mbox hook command - Implements Command::parse() -.
Definition hook.c:780
static void idxfmt_hashelem_free(int type, void *obj, intptr_t data)
Free our hash table data - Implements hash_hdata_free_t -.
Definition hook.c:1047
#define mutt_error(...)
Definition logging2.h:93
#define mutt_debug(LEVEL,...)
Definition logging2.h:90
struct HashElem * mutt_hash_insert(struct HashTable *table, const char *strkey, void *data)
Add a new element to the Hash Table (with string keys)
Definition hash.c:335
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition hash.c:362
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition hash.c:259
void mutt_hash_set_destructor(struct HashTable *table, hash_hdata_free_t fn, intptr_t fn_data)
Set the destructor for a Hash Table.
Definition hash.c:301
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition hash.c:457
#define MUTT_HASH_STRDUP_KEYS
make a copy of the keys
Definition hash.h:113
void mutt_default_save(struct Buffer *path, struct Email *e)
Find the default save path for an email.
Definition hook.c:1438
void mutt_timeout_hook(void)
Execute any timeout hooks.
Definition hook.c:1592
static struct HashTable * IdxFmtHooks
All Index Format hooks.
Definition hook.c:81
char * mutt_find_hook(HookFlags type, const char *pat)
Find a matching hook.
Definition hook.c:1339
void mutt_startup_shutdown_hook(HookFlags type)
Execute any startup/shutdown hooks.
Definition hook.c:1624
void mutt_delete_hooks(HookFlags type)
Delete matching hooks.
Definition hook.c:1029
static void list_hook(struct ListHead *matches, const char *match, HookFlags type)
Find hook strings matching.
Definition hook.c:1518
void mutt_account_hook(const char *url)
Perform an account hook.
Definition hook.c:1547
static void delete_idxfmt_hooks(void)
Delete all the index-format-hooks.
Definition hook.c:1065
static void hook_free(struct Hook **ptr)
Free a Hook.
Definition hook.c:90
void mutt_folder_hook(const char *path, const char *desc)
Perform a folder hook.
Definition hook.c:1291
void mutt_select_fcc(struct Buffer *path, struct Email *e)
Select the FCC path for an email.
Definition hook.c:1476
const struct Expando * mutt_idxfmt_hook(const char *name, struct Mailbox *m, struct Email *e)
Get index-format-hook format string.
Definition hook.c:1651
static int addr_hook(struct Buffer *path, HookFlags type, struct Mailbox *m, struct Email *e)
Perform an address hook (get a path)
Definition hook.c:1406
const struct ExpandoDefinition IndexFormatDef[]
Expando definitions.
void hooks_init(void)
Setup feature commands.
Definition hook.c:1769
static struct HookList Hooks
All simple hooks, e.g. MUTT_FOLDER_HOOK.
Definition hook.c:78
static HookFlags mutt_get_hook_type(const char *name)
Find a hook by name.
Definition hook.c:1197
static HookFlags CurrentHookType
The type of the hook currently being executed, e.g. MUTT_SAVE_HOOK.
Definition hook.c:84
static const struct Command HookCommands[]
Hook Commands.
Definition hook.c:1684
void mutt_crypt_hook(struct ListHead *list, struct Address *addr)
Find crypto hooks for an Address.
Definition hook.c:1538
static struct Hook * hook_new(void)
Create a Hook.
Definition hook.c:114
void mutt_message_hook(struct Mailbox *m, struct Email *e, HookFlags type)
Perform a message hook.
Definition hook.c:1360
Parse and execute user-defined hooks.
#define MUTT_ICONV_HOOK
iconv-hook: create a system charset alias
Definition hook.h:43
#define MUTT_FOLDER_HOOK
folder-hook: when entering a mailbox
Definition hook.h:37
#define MUTT_SAVE_HOOK
save-hook: set a default folder when saving an email
Definition hook.h:41
#define MUTT_SEND_HOOK
send-hook: when composing a new email
Definition hook.h:39
uint32_t HookFlags
Flags for parse_hook(), e.g. MUTT_FOLDER_HOOK.
Definition hook.h:35
#define MUTT_FCC_HOOK
fcc-hook: to save outgoing email
Definition hook.h:40
#define MUTT_ACCOUNT_HOOK
account-hook: when changing between accounts
Definition hook.h:46
#define MUTT_STARTUP_HOOK
startup-hook: run when starting NeoMutt
Definition hook.h:54
#define MUTT_SEND2_HOOK
send2-hook: when changing fields in the compose menu
Definition hook.h:48
#define MUTT_CRYPT_HOOK
crypt-hook: automatically select a PGP/SMIME key
Definition hook.h:45
#define MUTT_MBOX_HOOK
mbox-hook: move messages after reading them
Definition hook.h:38
#define MUTT_REPLY_HOOK
reply-hook: when replying to an email
Definition hook.h:47
#define MUTT_TIMEOUT_HOOK
timeout-hook: run a command periodically
Definition hook.h:53
#define MUTT_MESSAGE_HOOK
message-hook: run before displaying a message
Definition hook.h:44
#define MUTT_SHUTDOWN_HOOK
shutdown-hook: run when leaving NeoMutt
Definition hook.h:55
#define MUTT_IDXFMTHOOK
index-format-hook: customise the format of the index
Definition hook.h:52
#define MUTT_CHARSET_HOOK
charset-hook: create a charset alias for malformed emails
Definition hook.h:42
#define MUTT_HOOK_NO_FLAGS
No flags are set.
Definition hook.h:36
GUI manage the main index (list of emails)
struct MailboxView * get_current_mailbox_view(void)
Get the current Mailbox view.
Definition index.c:689
struct Mailbox * get_current_mailbox(void)
Get the current Mailbox.
Definition index.c:721
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition list.c:65
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:48
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:44
#define FREE(x)
Definition memory.h:62
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:47
void mutt_ch_lookup_remove(void)
Remove all the character set lookups.
Definition charset.c:541
bool mutt_ch_lookup_add(enum LookupType type, const char *pat, const char *replace, struct Buffer *err)
Add a new character set lookup.
Definition charset.c:509
LookupType
Types of character set lookups.
Definition charset.h:59
@ MUTT_LOOKUP_ICONV
Character set conversion.
Definition charset.h:61
@ MUTT_LOOKUP_CHARSET
Alias for another character set.
Definition charset.h:60
Convenience wrapper for the library headers.
#define N_(a)
Definition message.h:32
#define _(a)
Definition message.h:28
bool mutt_regex_match(const struct Regex *regex, const char *str)
Shorthand to mutt_regex_capture()
Definition regex.c:613
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
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:660
#define PATH_MAX
Definition mutt.h:42
void mutt_temp_attachments_cleanup(void)
Delete all temporary attachments.
void buf_expand_path_regex(struct Buffer *buf, bool regex)
Create the canonical path (with regex char escaping)
Definition muttlib.c:121
void buf_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition muttlib.c:518
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition muttlib.c:314
void mutt_safe_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition muttlib.c:681
Some miscellaneous functions.
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition mx.c:170
API for mailboxes.
Text parsing functions.
Match patterns to emails.
#define MUTT_PC_SEND_MODE_SEARCH
Allow send-mode body searching.
Definition lib.h:71
uint8_t PatternCompFlags
Flags for mutt_pattern_comp(), e.g. MUTT_PC_FULL_MSG.
Definition lib.h:67
#define MUTT_PC_FULL_MSG
Enable body and header matching.
Definition lib.h:69
void mutt_check_simple(struct Buffer *s, const char *simple)
Convert a simple search into a real request.
Definition pattern.c:109
#define MUTT_PC_NO_FLAGS
No flags are set.
Definition lib.h:68
#define MUTT_PC_PATTERN_DYNAMIC
Enable runtime date range evaluation.
Definition lib.h:70
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 TAILQ_FOREACH(var, head, field)
Definition queue.h:782
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition queue.h:792
#define TAILQ_INIT(head)
Definition queue.h:822
#define TAILQ_HEAD(name, type)
Definition queue.h:680
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition queue.h:866
#define TAILQ_FIRST(head)
Definition queue.h:780
#define SLIST_FIRST(head)
Definition queue.h:227
#define TAILQ_REMOVE(head, elm, field)
Definition queue.h:901
#define TAILQ_HEAD_INITIALIZER(head)
Definition queue.h:694
#define TAILQ_ENTRY(type)
Definition queue.h:697
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition regex3.h:50
#define MUTT_FORMAT_PLAIN
Do not prepend DISP_TO, DISP_CC ...
Definition render.h:39
#define NONULL(x)
Definition string2.h:43
#define SKIPWS(ch)
Definition string2.h:51
An email address.
Definition address.h:35
struct Buffer * mailbox
Mailbox and host address.
Definition address.h:37
String manipulation buffer.
Definition buffer.h:36
char * dptr
Current read/write position.
Definition buffer.h:38
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
enum CommandResult(* parse)(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Definition command.h:75
intptr_t data
Data or flags to pass to the command.
Definition command.h:77
const char * name
Name of the command.
Definition command.h:59
The envelope/body of an email.
Definition email.h:39
struct Envelope * env
Envelope information.
Definition email.h:68
The header of an Email.
Definition envelope.h:57
struct AddressList to
Email's 'To' list.
Definition envelope.h:60
struct AddressList reply_to
Email's 'reply-to'.
Definition envelope.h:64
struct AddressList cc
Email's 'Cc' list.
Definition envelope.h:61
struct AddressList bcc
Email's 'Bcc' list.
Definition envelope.h:62
struct AddressList from
Email's 'From' list.
Definition envelope.h:59
Definition of a format string.
Definition definition.h:43
Parsed Expando trees.
Definition expando.h:41
A Hash Table.
Definition hash.h:99
A list of user hooks.
Definition hook.c:66
HookFlags type
Hook type.
Definition hook.c:67
struct PatternList * pattern
Used for fcc,save,send-hook.
Definition hook.c:71
struct Regex regex
Regular expression.
Definition hook.c:68
char * command
Filename, command or pattern to execute.
Definition hook.c:69
struct Expando * expando
Used for format hooks.
Definition hook.c:72
char * source_file
Used for relative-directory source.
Definition hook.c:70
View of a Mailbox.
Definition mview.h:40
char * pattern
Limit pattern string.
Definition mview.h:42
A mailbox.
Definition mailbox.h:79
Container for Accounts, Notifications.
Definition neomutt.h:43
struct CommandArray commands
NeoMutt commands.
Definition neomutt.h:51
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:47
Cache commonly-used patterns.
Definition lib.h:117
A simple (non-regex) pattern.
Definition lib.h:77
Cached regular expression.
Definition regex3.h:86
char * pattern
printable version
Definition regex3.h:87
bool pat_not
do not match
Definition regex3.h:89
regex_t * regex
compiled expression
Definition regex3.h:88