NeoMutt  2025-01-09-117-gace867
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
hook.c
Go to the documentation of this file.
1
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 "ncrypt/lib.h"
54#include "parse/lib.h"
55#include "pattern/lib.h"
56#include "commands.h"
57#include "globals.h"
58#include "muttlib.h"
59#include "mx.h"
60
61extern const struct ExpandoDefinition IndexFormatDef[];
62
66struct Hook
67{
69 struct Regex regex;
70 char *command;
72 struct PatternList *pattern;
73 struct Expando *expando;
74 TAILQ_ENTRY(Hook) entries;
75};
76TAILQ_HEAD(HookList, Hook);
77
79static struct HookList Hooks = TAILQ_HEAD_INITIALIZER(Hooks);
80
82static struct HashTable *IdxFmtHooks = NULL;
83
86
91static void hook_free(struct Hook **ptr)
92{
93 if (!ptr || !*ptr)
94 return;
95
96 struct Hook *h = *ptr;
97
98 FREE(&h->command);
99 FREE(&h->source_file);
100 FREE(&h->regex.pattern);
101 if (h->regex.regex)
102 {
103 regfree(h->regex.regex);
104 FREE(&h->regex.regex);
105 }
108 FREE(ptr);
109}
110
115static struct Hook *hook_new(void)
116{
117 return MUTT_MEM_CALLOC(1, struct Hook);
118}
119
124 intptr_t data, struct Buffer *err)
125{
126 struct Buffer *alias = buf_pool_get();
127 struct Buffer *charset = buf_pool_get();
128
129 int rc = MUTT_CMD_ERROR;
130
131 if (parse_extract_token(alias, s, TOKEN_NO_FLAGS) < 0)
132 goto done;
133 if (parse_extract_token(charset, s, TOKEN_NO_FLAGS) < 0)
134 goto done;
135
137
138 if (buf_is_empty(alias) || buf_is_empty(charset))
139 {
140 buf_printf(err, _("%s: too few arguments"), buf->data);
141 rc = MUTT_CMD_WARNING;
142 }
143 else if (MoreArgs(s))
144 {
145 buf_printf(err, _("%s: too many arguments"), buf->data);
146 buf_reset(s); // clean up buffer to avoid a mess with further rcfile processing
147 rc = MUTT_CMD_WARNING;
148 }
149 else if (mutt_ch_lookup_add(type, buf_string(alias), buf_string(charset), err))
150 {
151 rc = MUTT_CMD_SUCCESS;
152 }
153
154done:
155 buf_pool_release(&alias);
156 buf_pool_release(&charset);
157
158 return rc;
159}
160
166enum CommandResult mutt_parse_hook(struct Buffer *buf, struct Buffer *s,
167 intptr_t data, struct Buffer *err)
168{
169 struct Hook *hook = NULL;
170 int rc = MUTT_CMD_ERROR;
171 bool pat_not = false;
172 bool use_regex = true;
173 regex_t *rx = NULL;
174 struct PatternList *pat = NULL;
175 const bool folder_or_mbox = (data & (MUTT_FOLDER_HOOK | MUTT_MBOX_HOOK));
176
177 struct Buffer *cmd = buf_pool_get();
178 struct Buffer *pattern = buf_pool_get();
179
180 if (~data & MUTT_GLOBAL_HOOK) /* NOT a global hook */
181 {
182 if (*s->dptr == '!')
183 {
184 s->dptr++;
185 SKIPWS(s->dptr);
186 pat_not = true;
187 }
188
190 if (folder_or_mbox && mutt_str_equal(buf_string(pattern), "-noregex"))
191 {
192 use_regex = false;
193 if (!MoreArgs(s))
194 {
195 buf_printf(err, _("%s: too few arguments"), buf->data);
196 rc = MUTT_CMD_WARNING;
197 goto cleanup;
198 }
200 }
201
202 if (!MoreArgs(s))
203 {
204 buf_printf(err, _("%s: too few arguments"), buf->data);
205 rc = MUTT_CMD_WARNING;
206 goto cleanup;
207 }
208 }
209
210 parse_extract_token(cmd, s,
215
216 if (buf_is_empty(cmd))
217 {
218 buf_printf(err, _("%s: too few arguments"), buf->data);
219 rc = MUTT_CMD_WARNING;
220 goto cleanup;
221 }
222
223 if (MoreArgs(s))
224 {
225 buf_printf(err, _("%s: too many arguments"), buf->data);
226 rc = MUTT_CMD_WARNING;
227 goto cleanup;
228 }
229
230 const char *const c_default_hook = cs_subset_string(NeoMutt->sub, "default_hook");
231 if (folder_or_mbox)
232 {
233 /* Accidentally using the ^ mailbox shortcut in the .neomuttrc is a
234 * common mistake */
235 if ((pattern->data[0] == '^') && !CurrentFolder)
236 {
237 buf_strcpy(err, _("current mailbox shortcut '^' is unset"));
238 goto cleanup;
239 }
240
241 struct Buffer *tmp = buf_pool_get();
242 buf_copy(tmp, pattern);
243 buf_expand_path_regex(tmp, use_regex);
244
245 /* Check for other mailbox shortcuts that expand to the empty string.
246 * This is likely a mistake too */
247 if (buf_is_empty(tmp) && !buf_is_empty(pattern))
248 {
249 buf_strcpy(err, _("mailbox shortcut expanded to empty regex"));
250 buf_pool_release(&tmp);
251 goto cleanup;
252 }
253
254 if (use_regex)
255 {
256 buf_copy(pattern, tmp);
257 }
258 else
259 {
261 }
262 buf_pool_release(&tmp);
263 }
265 {
266 if (mutt_comp_valid_command(buf_string(cmd)) == 0)
267 {
268 buf_strcpy(err, _("badly formatted command string"));
269 goto cleanup;
270 }
271 }
272 else if (c_default_hook && (~data & MUTT_GLOBAL_HOOK) &&
274 {
275 /* At this stage only these hooks remain:
276 * fcc-, fcc-save-, index-format-, message-, reply-, save-, send- and send2-hook
277 * If given a plain string, or regex, we expand it using $default_hook. */
278 mutt_check_simple(pattern, c_default_hook);
279 }
280
282 {
283 buf_expand_path(cmd);
284 }
285
286 /* check to make sure that a matching hook doesn't already exist */
287 TAILQ_FOREACH(hook, &Hooks, entries)
288 {
290 {
291 /* Ignore duplicate global hooks */
292 if (mutt_str_equal(hook->command, buf_string(cmd)))
293 {
294 rc = MUTT_CMD_SUCCESS;
295 goto cleanup;
296 }
297 }
298 else if ((hook->type == data) && (hook->regex.pat_not == pat_not) &&
299 mutt_str_equal(buf_string(pattern), hook->regex.pattern))
300 {
304 {
305 /* these hooks allow multiple commands with the same
306 * pattern, so if we've already seen this pattern/command pair, just
307 * ignore it instead of creating a duplicate */
308 if (mutt_str_equal(hook->command, buf_string(cmd)))
309 {
310 rc = MUTT_CMD_SUCCESS;
311 goto cleanup;
312 }
313 }
314 else
315 {
316 /* other hooks only allow one command per pattern, so update the
317 * entry with the new command. this currently does not change the
318 * order of execution of the hooks, which i think is desirable since
319 * a common action to perform is to change the default (.) entry
320 * based upon some other information. */
321 FREE(&hook->command);
322 hook->command = buf_strdup(cmd);
323 FREE(&hook->source_file);
325
327 {
328 expando_free(&hook->expando);
330 }
331
332 rc = MUTT_CMD_SUCCESS;
333 goto cleanup;
334 }
335 }
336 }
337
340 {
341 PatternCompFlags comp_flags;
342
343 if (data & (MUTT_SEND2_HOOK))
344 comp_flags = MUTT_PC_SEND_MODE_SEARCH;
345 else if (data & (MUTT_SEND_HOOK | MUTT_FCC_HOOK))
346 comp_flags = MUTT_PC_NO_FLAGS;
347 else
348 comp_flags = MUTT_PC_FULL_MSG;
349
350 struct MailboxView *mv_cur = get_current_mailbox_view();
351 struct Menu *menu = get_current_menu();
352 pat = mutt_pattern_comp(mv_cur, menu, buf_string(pattern), comp_flags, err);
353 if (!pat)
354 goto cleanup;
355 }
356 else if (~data & MUTT_GLOBAL_HOOK) /* NOT a global hook */
357 {
358 /* Hooks not allowing full patterns: Check syntax of regex */
359 rx = MUTT_MEM_CALLOC(1, regex_t);
360 int rc2 = REG_COMP(rx, buf_string(pattern), ((data & MUTT_CRYPT_HOOK) ? REG_ICASE : 0));
361 if (rc2 != 0)
362 {
363 regerror(rc2, rx, err->data, err->dsize);
364 FREE(&rx);
365 goto cleanup;
366 }
367 }
368
369 struct Expando *exp = NULL;
371 exp = expando_parse(buf_string(cmd), IndexFormatDef, err);
372
373 hook = hook_new();
374 hook->type = data;
375 hook->command = buf_strdup(cmd);
377 hook->pattern = pat;
378 hook->regex.pattern = buf_strdup(pattern);
379 hook->regex.regex = rx;
380 hook->regex.pat_not = pat_not;
381 hook->expando = exp;
382
383 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
384 rc = MUTT_CMD_SUCCESS;
385
386cleanup:
387 buf_pool_release(&cmd);
388 buf_pool_release(&pattern);
389 return rc;
390}
391
399{
400 struct Hook *h = NULL;
401 struct Hook *tmp = NULL;
402
403 TAILQ_FOREACH_SAFE(h, &Hooks, entries, tmp)
404 {
405 if ((type == MUTT_HOOK_NO_FLAGS) || (type == h->type))
406 {
407 TAILQ_REMOVE(&Hooks, h, entries);
408 hook_free(&h);
409 }
410 }
411}
412
416static void idxfmt_hashelem_free(int type, void *obj, intptr_t data)
417{
418 struct HookList *hl = obj;
419 struct Hook *h = NULL;
420 struct Hook *tmp = NULL;
421
422 TAILQ_FOREACH_SAFE(h, hl, entries, tmp)
423 {
424 TAILQ_REMOVE(hl, h, entries);
425 hook_free(&h);
426 }
427
428 FREE(&hl);
429}
430
434static void delete_idxfmt_hooks(void)
435{
437}
438
442static enum CommandResult mutt_parse_idxfmt_hook(struct Buffer *buf, struct Buffer *s,
443 intptr_t data, struct Buffer *err)
444{
446 bool pat_not = false;
447
448 struct Buffer *name = buf_pool_get();
449 struct Buffer *pattern = buf_pool_get();
450 struct Buffer *fmtstring = buf_pool_get();
451 struct Expando *exp = NULL;
452
453 if (!IdxFmtHooks)
454 {
457 }
458
459 if (!MoreArgs(s))
460 {
461 buf_printf(err, _("%s: too few arguments"), buf->data);
462 goto out;
463 }
465 struct HookList *hl = mutt_hash_find(IdxFmtHooks, buf_string(name));
466
467 if (*s->dptr == '!')
468 {
469 s->dptr++;
470 SKIPWS(s->dptr);
471 pat_not = true;
472 }
474
475 if (!MoreArgs(s))
476 {
477 buf_printf(err, _("%s: too few arguments"), buf->data);
478 goto out;
479 }
480 parse_extract_token(fmtstring, s, TOKEN_NO_FLAGS);
481
482 exp = expando_parse(buf_string(fmtstring), IndexFormatDef, err);
483 if (!exp)
484 goto out;
485
486 if (MoreArgs(s))
487 {
488 buf_printf(err, _("%s: too many arguments"), buf->data);
489 goto out;
490 }
491
492 const char *const c_default_hook = cs_subset_string(NeoMutt->sub, "default_hook");
493 if (c_default_hook)
494 mutt_check_simple(pattern, c_default_hook);
495
496 /* check to make sure that a matching hook doesn't already exist */
497 struct Hook *hook = NULL;
498 if (hl)
499 {
500 TAILQ_FOREACH(hook, hl, entries)
501 {
502 if ((hook->regex.pat_not == pat_not) &&
504 {
505 expando_free(&hook->expando);
506 hook->expando = exp;
507 exp = NULL;
508 rc = MUTT_CMD_SUCCESS;
509 goto out;
510 }
511 }
512 }
513
514 /* MUTT_PC_PATTERN_DYNAMIC sets so that date ranges are regenerated during
515 * matching. This of course is slower, but index-format-hook is commonly
516 * used for date ranges, and they need to be evaluated relative to "now", not
517 * the hook compilation time. */
518 struct MailboxView *mv_cur = get_current_mailbox_view();
519 struct Menu *menu = get_current_menu();
520 struct PatternList *pat = mutt_pattern_comp(mv_cur, menu, buf_string(pattern),
522 err);
523 if (!pat)
524 goto out;
525
526 hook = hook_new();
527 hook->type = MUTT_IDXFMTHOOK;
528 hook->command = NULL;
530 hook->pattern = pat;
531 hook->regex.pattern = buf_strdup(pattern);
532 hook->regex.regex = NULL;
533 hook->regex.pat_not = pat_not;
534 hook->expando = exp;
535 exp = NULL;
536
537 if (!hl)
538 {
539 hl = MUTT_MEM_CALLOC(1, struct HookList);
540 TAILQ_INIT(hl);
542 }
543
544 TAILQ_INSERT_TAIL(hl, hook, entries);
545 rc = MUTT_CMD_SUCCESS;
546
547out:
548 buf_pool_release(&name);
549 buf_pool_release(&pattern);
550 buf_pool_release(&fmtstring);
551 expando_free(&exp);
552
553 return rc;
554}
555
562static HookFlags mutt_get_hook_type(const char *name)
563{
564 const struct Command **cp = NULL;
566 {
567 const struct Command *cmd = *cp;
568
569 if (((cmd->parse == mutt_parse_hook) || (cmd->parse == mutt_parse_idxfmt_hook)) &&
571 {
572 return cmd->data;
573 }
574 }
575 return MUTT_HOOK_NO_FLAGS;
576}
577
581static enum CommandResult mutt_parse_unhook(struct Buffer *buf, struct Buffer *s,
582 intptr_t data, struct Buffer *err)
583{
584 while (MoreArgs(s))
585 {
587 if (mutt_str_equal("*", buf->data))
588 {
590 {
591 buf_addstr(err, _("unhook: Can't do unhook * from within a hook"));
592 return MUTT_CMD_WARNING;
593 }
597 }
598 else
599 {
600 HookFlags type = mutt_get_hook_type(buf->data);
601
602 if (type == MUTT_HOOK_NO_FLAGS)
603 {
604 buf_printf(err, _("unhook: unknown hook type: %s"), buf->data);
605 return MUTT_CMD_ERROR;
606 }
607 if (type & (MUTT_CHARSET_HOOK | MUTT_ICONV_HOOK))
608 {
610 return MUTT_CMD_SUCCESS;
611 }
612 if (CurrentHookType == type)
613 {
614 buf_printf(err, _("unhook: Can't delete a %s from within a %s"),
615 buf->data, buf->data);
616 return MUTT_CMD_WARNING;
617 }
618 if (type == MUTT_IDXFMTHOOK)
620 else
621 mutt_delete_hooks(type);
622 }
623 }
624 return MUTT_CMD_SUCCESS;
625}
626
632void mutt_folder_hook(const char *path, const char *desc)
633{
634 if (!path && !desc)
635 return;
636
637 struct Hook *hook = NULL;
638 struct Buffer *err = buf_pool_get();
639
641
642 TAILQ_FOREACH(hook, &Hooks, entries)
643 {
644 if (!hook->command)
645 continue;
646
647 if (!(hook->type & MUTT_FOLDER_HOOK))
648 continue;
649
650 const char *match = NULL;
651 if (mutt_regex_match(&hook->regex, path))
652 match = path;
653 else if (mutt_regex_match(&hook->regex, desc))
654 match = desc;
655
656 if (match)
657 {
658 mutt_debug(LL_DEBUG1, "folder-hook '%s' matches '%s'\n", hook->regex.pattern, match);
659 mutt_debug(LL_DEBUG5, " %s\n", hook->command);
660 if (parse_rc_line_cwd(hook->command, hook->source_file, err) == MUTT_CMD_ERROR)
661 {
662 mutt_error("%s", buf_string(err));
663 break;
664 }
665 }
666 }
667 buf_pool_release(&err);
668
670}
671
680char *mutt_find_hook(HookFlags type, const char *pat)
681{
682 struct Hook *tmp = NULL;
683
684 TAILQ_FOREACH(tmp, &Hooks, entries)
685 {
686 if (tmp->type & type)
687 {
688 if (mutt_regex_match(&tmp->regex, pat))
689 return tmp->command;
690 }
691 }
692 return NULL;
693}
694
701void mutt_message_hook(struct Mailbox *m, struct Email *e, HookFlags type)
702{
703 struct Hook *hook = NULL;
704 struct PatternCache cache = { 0 };
705 struct Buffer *err = buf_pool_get();
706
707 CurrentHookType = type;
708
709 TAILQ_FOREACH(hook, &Hooks, entries)
710 {
711 if (!hook->command)
712 continue;
713
714 if (hook->type & type)
715 {
716 if ((mutt_pattern_exec(SLIST_FIRST(hook->pattern), 0, m, e, &cache) > 0) ^
717 hook->regex.pat_not)
718 {
719 if (parse_rc_line_cwd(hook->command, hook->source_file, err) == MUTT_CMD_ERROR)
720 {
721 mutt_error("%s", buf_string(err));
723 buf_pool_release(&err);
724
725 return;
726 }
727 /* Executing arbitrary commands could affect the pattern results,
728 * so the cache has to be wiped */
729 memset(&cache, 0, sizeof(cache));
730 }
731 }
732 }
733 buf_pool_release(&err);
734
736}
737
747static int addr_hook(struct Buffer *path, HookFlags type, struct Mailbox *m, struct Email *e)
748{
749 struct Hook *hook = NULL;
750 struct PatternCache cache = { 0 };
751
752 /* determine if a matching hook exists */
753 TAILQ_FOREACH(hook, &Hooks, entries)
754 {
755 if (!hook->command)
756 continue;
757
758 if (hook->type & type)
759 {
760 if ((mutt_pattern_exec(SLIST_FIRST(hook->pattern), 0, m, e, &cache) > 0) ^
761 hook->regex.pat_not)
762 {
763 buf_alloc(path, PATH_MAX);
764 mutt_make_string(path, -1, hook->expando, m, -1, e, MUTT_FORMAT_PLAIN, NULL);
765 buf_fix_dptr(path);
766 return 0;
767 }
768 }
769 }
770
771 return -1;
772}
773
779void mutt_default_save(struct Buffer *path, struct Email *e)
780{
781 struct Mailbox *m_cur = get_current_mailbox();
782 if (addr_hook(path, MUTT_SAVE_HOOK, m_cur, e) == 0)
783 return;
784
785 struct Envelope *env = e->env;
786 const struct Address *from = TAILQ_FIRST(&env->from);
787 const struct Address *reply_to = TAILQ_FIRST(&env->reply_to);
788 const struct Address *to = TAILQ_FIRST(&env->to);
789 const struct Address *cc = TAILQ_FIRST(&env->cc);
790 const struct Address *addr = NULL;
791 bool from_me = mutt_addr_is_user(from);
792
793 if (!from_me && reply_to && reply_to->mailbox)
794 addr = reply_to;
795 else if (!from_me && from && from->mailbox)
796 addr = from;
797 else if (to && to->mailbox)
798 addr = to;
799 else if (cc && cc->mailbox)
800 addr = cc;
801 else
802 addr = NULL;
803 if (addr)
804 {
805 struct Buffer *tmp = buf_pool_get();
806 mutt_safe_path(tmp, addr);
807 buf_add_printf(path, "=%s", buf_string(tmp));
808 buf_pool_release(&tmp);
809 }
810}
811
817void mutt_select_fcc(struct Buffer *path, struct Email *e)
818{
819 buf_alloc(path, PATH_MAX);
820
821 if (addr_hook(path, MUTT_FCC_HOOK, NULL, e) != 0)
822 {
823 const struct Address *to = TAILQ_FIRST(&e->env->to);
824 const struct Address *cc = TAILQ_FIRST(&e->env->cc);
825 const struct Address *bcc = TAILQ_FIRST(&e->env->bcc);
826 const bool c_save_name = cs_subset_bool(NeoMutt->sub, "save_name");
827 const bool c_force_name = cs_subset_bool(NeoMutt->sub, "force_name");
828 const char *const c_record = cs_subset_string(NeoMutt->sub, "record");
829 if ((c_save_name || c_force_name) && (to || cc || bcc))
830 {
831 const struct Address *addr = to ? to : (cc ? cc : bcc);
832 struct Buffer *buf = buf_pool_get();
833 mutt_safe_path(buf, addr);
834 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
835 buf_concat_path(path, NONULL(c_folder), buf_string(buf));
836 buf_pool_release(&buf);
837 if (!c_force_name && (mx_access(buf_string(path), W_OK) != 0))
838 buf_strcpy(path, c_record);
839 }
840 else
841 {
842 buf_strcpy(path, c_record);
843 }
844 }
845 else
846 {
847 buf_fix_dptr(path);
848 }
849
850 buf_pretty_mailbox(path);
851}
852
859static void list_hook(struct ListHead *matches, const char *match, HookFlags type)
860{
861 struct Hook *tmp = NULL;
862
863 TAILQ_FOREACH(tmp, &Hooks, entries)
864 {
865 if ((tmp->type & type) && mutt_regex_match(&tmp->regex, match))
866 {
868 }
869 }
870}
871
879void mutt_crypt_hook(struct ListHead *list, struct Address *addr)
880{
882}
883
888void mutt_account_hook(const char *url)
889{
890 /* parsing commands with URLs in an account hook can cause a recursive
891 * call. We just skip processing if this occurs. Typically such commands
892 * belong in a folder-hook -- perhaps we should warn the user. */
893 static bool inhook = false;
894 if (inhook)
895 return;
896
897 struct Hook *hook = NULL;
898 struct Buffer *err = buf_pool_get();
899
900 TAILQ_FOREACH(hook, &Hooks, entries)
901 {
902 if (!(hook->command && (hook->type & MUTT_ACCOUNT_HOOK)))
903 continue;
904
905 if (mutt_regex_match(&hook->regex, url))
906 {
907 inhook = true;
908 mutt_debug(LL_DEBUG1, "account-hook '%s' matches '%s'\n", hook->regex.pattern, url);
909 mutt_debug(LL_DEBUG5, " %s\n", hook->command);
910
911 if (parse_rc_line_cwd(hook->command, hook->source_file, err) == MUTT_CMD_ERROR)
912 {
913 mutt_error("%s", buf_string(err));
914 buf_pool_release(&err);
915
916 inhook = false;
917 goto done;
918 }
919
920 inhook = false;
921 }
922 }
923done:
924 buf_pool_release(&err);
925}
926
934{
935 struct Hook *hook = NULL;
936 struct Buffer *err = buf_pool_get();
937
938 TAILQ_FOREACH(hook, &Hooks, entries)
939 {
940 if (!(hook->command && (hook->type & MUTT_TIMEOUT_HOOK)))
941 continue;
942
943 if (parse_rc_line_cwd(hook->command, hook->source_file, err) == MUTT_CMD_ERROR)
944 {
945 mutt_error("%s", buf_string(err));
946 buf_reset(err);
947
948 /* The hooks should be independent of each other, so even though this on
949 * failed, we'll carry on with the others. */
950 }
951 }
952 buf_pool_release(&err);
953
954 /* Delete temporary attachment files */
956}
957
966{
967 struct Hook *hook = NULL;
968 struct Buffer *err = buf_pool_get();
969
970 TAILQ_FOREACH(hook, &Hooks, entries)
971 {
972 if (!(hook->command && (hook->type & type)))
973 continue;
974
975 if (parse_rc_line_cwd(hook->command, hook->source_file, err) == MUTT_CMD_ERROR)
976 {
977 mutt_error("%s", buf_string(err));
978 buf_reset(err);
979 }
980 }
981 buf_pool_release(&err);
982}
983
992const struct Expando *mutt_idxfmt_hook(const char *name, struct Mailbox *m, struct Email *e)
993{
994 if (!IdxFmtHooks)
995 return NULL;
996
997 struct HookList *hl = mutt_hash_find(IdxFmtHooks, name);
998 if (!hl)
999 return NULL;
1000
1002
1003 struct PatternCache cache = { 0 };
1004 const struct Expando *exp = NULL;
1005 struct Hook *hook = NULL;
1006
1007 TAILQ_FOREACH(hook, hl, entries)
1008 {
1009 struct Pattern *pat = SLIST_FIRST(hook->pattern);
1010 if ((mutt_pattern_exec(pat, 0, m, e, &cache) > 0) ^ hook->regex.pat_not)
1011 {
1012 exp = hook->expando;
1013 break;
1014 }
1015 }
1016
1018
1019 return exp;
1020}
1021
1025static const struct Command HookCommands[] = {
1026 // clang-format off
1027 { "account-hook", mutt_parse_hook, MUTT_ACCOUNT_HOOK },
1029 { "crypt-hook", mutt_parse_hook, MUTT_CRYPT_HOOK },
1030 { "fcc-hook", mutt_parse_hook, MUTT_FCC_HOOK },
1031 { "fcc-save-hook", mutt_parse_hook, MUTT_FCC_HOOK | MUTT_SAVE_HOOK },
1032 { "folder-hook", mutt_parse_hook, MUTT_FOLDER_HOOK },
1034 { "index-format-hook", mutt_parse_idxfmt_hook, MUTT_IDXFMTHOOK },
1035 { "mbox-hook", mutt_parse_hook, MUTT_MBOX_HOOK },
1036 { "message-hook", mutt_parse_hook, MUTT_MESSAGE_HOOK },
1037 { "pgp-hook", mutt_parse_hook, MUTT_CRYPT_HOOK },
1038 { "reply-hook", mutt_parse_hook, MUTT_REPLY_HOOK },
1039 { "save-hook", mutt_parse_hook, MUTT_SAVE_HOOK },
1040 { "send-hook", mutt_parse_hook, MUTT_SEND_HOOK },
1041 { "send2-hook", mutt_parse_hook, MUTT_SEND2_HOOK },
1042 { "shutdown-hook", mutt_parse_hook, MUTT_SHUTDOWN_HOOK | MUTT_GLOBAL_HOOK },
1043 { "startup-hook", mutt_parse_hook, MUTT_STARTUP_HOOK | MUTT_GLOBAL_HOOK },
1044 { "timeout-hook", mutt_parse_hook, MUTT_TIMEOUT_HOOK | MUTT_GLOBAL_HOOK },
1045 { "unhook", mutt_parse_unhook, 0 },
1046 { NULL, NULL, 0 },
1047 // clang-format on
1048};
1049
1053void hooks_init(void)
1054{
1056}
Email Address Handling.
Email Aliases.
bool mutt_addr_is_user(const struct Address *addr)
Does the address belong to the user.
Definition: alias.c:596
#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
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
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:178
char * mutt_get_sourced_cwd(void)
Get the current file path that is being parsed.
Definition: commands.c:198
Functions to parse commands in a config file.
struct PatternList * mutt_pattern_comp(struct MailboxView *mv, struct Menu *menu, const char *s, PatternCompFlags flags, struct Buffer *err)
Create a Pattern.
Definition: compile.c:906
void mutt_pattern_free(struct PatternList **pat)
Free a Pattern.
Definition: compile.c:777
int mutt_comp_valid_command(const char *cmd)
Is this command string allowed?
Definition: compress.c:381
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:803
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 *tok, TokenFlags flags)
Extract one token from a string.
Definition: extract.c:49
#define TOKEN_SPACE
Don't treat whitespace as a term.
Definition: extract.h:49
#define MoreArgs(buf)
Definition: extract.h:32
#define TOKEN_NO_FLAGS
No flags are set.
Definition: extract.h:46
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.
static enum CommandResult mutt_parse_unhook(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'unhook' command - Implements Command::parse() -.
Definition: hook.c:581
static enum CommandResult mutt_parse_idxfmt_hook(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'index-format-hook' command - Implements Command::parse() -.
Definition: hook.c:442
enum CommandResult mutt_parse_hook(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'hook' family of commands - Implements Command::parse() -.
Definition: hook.c:166
enum CommandResult mutt_parse_charset_iconv_hook(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse 'charset-hook' and 'iconv-hook' commands - Implements Command::parse() -.
Definition: hook.c:123
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:416
#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:779
void mutt_timeout_hook(void)
Execute any timeout hooks.
Definition: hook.c:933
static struct HashTable * IdxFmtHooks
All Index Format hooks.
Definition: hook.c:82
char * mutt_find_hook(HookFlags type, const char *pat)
Find a matching hook.
Definition: hook.c:680
void mutt_startup_shutdown_hook(HookFlags type)
Execute any startup/shutdown hooks.
Definition: hook.c:965
void mutt_delete_hooks(HookFlags type)
Delete matching hooks.
Definition: hook.c:398
static void list_hook(struct ListHead *matches, const char *match, HookFlags type)
Find hook strings matching.
Definition: hook.c:859
void mutt_account_hook(const char *url)
Perform an account hook.
Definition: hook.c:888
static void delete_idxfmt_hooks(void)
Delete all the index-format-hooks.
Definition: hook.c:434
static void hook_free(struct Hook **ptr)
Free a Hook.
Definition: hook.c:91
void mutt_folder_hook(const char *path, const char *desc)
Perform a folder hook.
Definition: hook.c:632
void mutt_select_fcc(struct Buffer *path, struct Email *e)
Select the FCC path for an email.
Definition: hook.c:817
const struct Expando * mutt_idxfmt_hook(const char *name, struct Mailbox *m, struct Email *e)
Get index-format-hook format string.
Definition: hook.c:992
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:747
const struct ExpandoDefinition IndexFormatDef[]
Expando definitions.
Definition: mutt_config.c:312
void hooks_init(void)
Setup feature commands.
Definition: hook.c:1053
static struct HookList Hooks
All simple hooks, e.g. MUTT_FOLDER_HOOK.
Definition: hook.c:79
static HookFlags mutt_get_hook_type(const char *name)
Find a hook by name.
Definition: hook.c:562
static HookFlags CurrentHookType
The type of the hook currently being executed, e.g. MUTT_SAVE_HOOK.
Definition: hook.c:85
static const struct Command HookCommands[]
Hook Commands.
Definition: hook.c:1025
void mutt_crypt_hook(struct ListHead *list, struct Address *addr)
Find crypto hooks for an Address.
Definition: hook.c:879
static struct Hook * hook_new(void)
Create a Hook.
Definition: hook.c:115
void mutt_message_hook(struct Mailbox *m, struct Email *e, HookFlags type)
Perform a message hook.
Definition: hook.c:701
Parse and execute user-defined hooks.
#define MUTT_OPEN_HOOK
open-hook: to read a compressed mailbox
Definition: hook.h:49
#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 mutt_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_CLOSE_HOOK
close-hook: write to a compressed mailbox
Definition: hook.h:51
#define MUTT_ACCOUNT_HOOK
account-hook: when changing between accounts
Definition: hook.h:46
#define MUTT_APPEND_HOOK
append-hook: append to a compressed mailbox
Definition: hook.h:50
#define MUTT_GLOBAL_HOOK
Hooks which don't take a regex.
Definition: hook.h:56
#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 Menu * get_current_menu(void)
Get the current Menu.
Definition: index.c:737
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:55
#define MUTT_MEM_CALLOC(n, type)
Definition: memory.h:40
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 _(a)
Definition: message.h:28
bool mutt_regex_match(const struct Regex *regex, const char *str)
Shorthand to mutt_regex_capture()
Definition: regex.c:614
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:673
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:254
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:661
#define PATH_MAX
Definition: mutt.h:42
void mutt_temp_attachments_cleanup(void)
Delete all temporary attachments.
Definition: mutt_attach.c:1305
void buf_expand_path_regex(struct Buffer *buf, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:122
void buf_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:519
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:315
void mutt_safe_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:682
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.
API for encryption/signing of emails.
#define WithCrypto
Definition: lib.h:122
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:112
#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:37
#define SKIPWS(ch)
Definition: string2.h:45
An email address.
Definition: address.h:36
struct Buffer * mailbox
Mailbox and host address.
Definition: address.h:38
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)(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Definition: command.h:64
intptr_t data
Data or flags to pass to the command.
Definition: command.h:66
const char * name
Name of the command.
Definition: command.h:51
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:44
Parsed Expando trees.
Definition: expando.h:41
A Hash Table.
Definition: hash.h:99
A list of user hooks.
Definition: hook.c:67
HookFlags type
Hook type.
Definition: hook.c:68
struct PatternList * pattern
Used for fcc,save,send-hook.
Definition: hook.c:72
struct Regex regex
Regular expression.
Definition: hook.c:69
char * command
Filename, command or pattern to execute.
Definition: hook.c:70
struct Expando * expando
Used for format hooks.
Definition: hook.c:73
char * source_file
Used for relative-directory source.
Definition: hook.c:71
View of a Mailbox.
Definition: mview.h:40
A mailbox.
Definition: mailbox.h:79
Definition: lib.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