NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
hook.c
Go to the documentation of this file.
1
31#include "config.h"
32#include <limits.h>
33#include <stdbool.h>
34#include <stdio.h>
35#include <string.h>
36#include <unistd.h>
37#include "mutt/lib.h"
38#include "address/lib.h"
39#include "config/lib.h"
40#include "email/lib.h"
41#include "core/lib.h"
42#include "alias/lib.h"
43#include "hook.h"
44#include "attach/lib.h"
45#include "index/lib.h"
46#include "ncrypt/lib.h"
47#include "parse/lib.h"
48#include "pattern/lib.h"
49#include "commands.h"
50#include "format_flags.h"
51#include "globals.h" // IWYU pragma: keep
52#include "hdrline.h"
53#include "muttlib.h"
54#include "mx.h"
55#ifdef USE_COMP_MBOX
56#include "compmbox/lib.h"
57#endif
58
62struct Hook
63{
65 struct Regex regex;
66 char *command;
68 struct PatternList *pattern;
69 TAILQ_ENTRY(Hook) entries;
70};
71TAILQ_HEAD(HookList, Hook);
72
74static struct HookList Hooks = TAILQ_HEAD_INITIALIZER(Hooks);
75
77static struct HashTable *IdxFmtHooks = NULL;
78
81
86 intptr_t data, struct Buffer *err)
87{
88 struct Buffer *alias = buf_pool_get();
89 struct Buffer *charset = buf_pool_get();
90
91 int rc = MUTT_CMD_ERROR;
92
93 if (parse_extract_token(alias, s, TOKEN_NO_FLAGS) < 0)
94 goto done;
95 if (parse_extract_token(charset, s, TOKEN_NO_FLAGS) < 0)
96 goto done;
97
99
100 if (buf_is_empty(alias) || buf_is_empty(charset))
101 {
102 buf_printf(err, _("%s: too few arguments"), buf->data);
103 rc = MUTT_CMD_WARNING;
104 }
105 else if (MoreArgs(s))
106 {
107 buf_printf(err, _("%s: too many arguments"), buf->data);
108 buf_reset(s); // clean up buffer to avoid a mess with further rcfile processing
109 rc = MUTT_CMD_WARNING;
110 }
111 else if (mutt_ch_lookup_add(type, buf_string(alias), buf_string(charset), err))
112 {
113 rc = MUTT_CMD_SUCCESS;
114 }
115
116done:
117 buf_pool_release(&alias);
118 buf_pool_release(&charset);
119
120 return rc;
121}
122
128enum CommandResult mutt_parse_hook(struct Buffer *buf, struct Buffer *s,
129 intptr_t data, struct Buffer *err)
130{
131 struct Hook *hook = NULL;
132 int rc = MUTT_CMD_ERROR;
133 bool pat_not = false;
134 bool use_regex = true;
135 regex_t *rx = NULL;
136 struct PatternList *pat = NULL;
137 const bool folder_or_mbox = (data & (MUTT_FOLDER_HOOK | MUTT_MBOX_HOOK));
138
139 struct Buffer *cmd = buf_pool_get();
140 struct Buffer *pattern = buf_pool_get();
141
142 if (~data & MUTT_GLOBAL_HOOK) /* NOT a global hook */
143 {
144 if (*s->dptr == '!')
145 {
146 s->dptr++;
147 SKIPWS(s->dptr);
148 pat_not = true;
149 }
150
152 if (folder_or_mbox && mutt_str_equal(buf_string(pattern), "-noregex"))
153 {
154 use_regex = false;
155 if (!MoreArgs(s))
156 {
157 buf_printf(err, _("%s: too few arguments"), buf->data);
158 rc = MUTT_CMD_WARNING;
159 goto cleanup;
160 }
162 }
163
164 if (!MoreArgs(s))
165 {
166 buf_printf(err, _("%s: too few arguments"), buf->data);
167 rc = MUTT_CMD_WARNING;
168 goto cleanup;
169 }
170 }
171
172 parse_extract_token(cmd, s,
177
178 if (buf_is_empty(cmd))
179 {
180 buf_printf(err, _("%s: too few arguments"), buf->data);
181 rc = MUTT_CMD_WARNING;
182 goto cleanup;
183 }
184
185 if (MoreArgs(s))
186 {
187 buf_printf(err, _("%s: too many arguments"), buf->data);
188 rc = MUTT_CMD_WARNING;
189 goto cleanup;
190 }
191
192 const char *const c_default_hook = cs_subset_string(NeoMutt->sub, "default_hook");
193 if (folder_or_mbox)
194 {
195 /* Accidentally using the ^ mailbox shortcut in the .neomuttrc is a
196 * common mistake */
197 if ((pattern->data[0] == '^') && !CurrentFolder)
198 {
199 buf_strcpy(err, _("current mailbox shortcut '^' is unset"));
200 goto cleanup;
201 }
202
203 struct Buffer *tmp = buf_pool_get();
204 buf_copy(tmp, pattern);
205 buf_expand_path_regex(tmp, use_regex);
206
207 /* Check for other mailbox shortcuts that expand to the empty string.
208 * This is likely a mistake too */
209 if (buf_is_empty(tmp) && !buf_is_empty(pattern))
210 {
211 buf_strcpy(err, _("mailbox shortcut expanded to empty regex"));
212 buf_pool_release(&tmp);
213 goto cleanup;
214 }
215
216 if (use_regex)
217 {
218 buf_copy(pattern, tmp);
219 }
220 else
221 {
223 }
224 buf_pool_release(&tmp);
225 }
226#ifdef USE_COMP_MBOX
228 {
229 if (mutt_comp_valid_command(buf_string(cmd)) == 0)
230 {
231 buf_strcpy(err, _("badly formatted command string"));
232 goto cleanup;
233 }
234 }
235#endif
236 else if (c_default_hook && (~data & MUTT_GLOBAL_HOOK) &&
238 {
239 /* At this stage only these hooks remain:
240 * fcc-, fcc-save-, index-format-, message-, reply-, save-, send- and send2-hook
241 * If given a plain string, or regex, we expand it using $default_hook. */
242 mutt_check_simple(pattern, c_default_hook);
243 }
244
246 {
247 buf_expand_path(cmd);
248 }
249
250 /* check to make sure that a matching hook doesn't already exist */
251 TAILQ_FOREACH(hook, &Hooks, entries)
252 {
254 {
255 /* Ignore duplicate global hooks */
256 if (mutt_str_equal(hook->command, buf_string(cmd)))
257 {
258 rc = MUTT_CMD_SUCCESS;
259 goto cleanup;
260 }
261 }
262 else if ((hook->type == data) && (hook->regex.pat_not == pat_not) &&
263 mutt_str_equal(buf_string(pattern), hook->regex.pattern))
264 {
268 {
269 /* these hooks allow multiple commands with the same
270 * pattern, so if we've already seen this pattern/command pair, just
271 * ignore it instead of creating a duplicate */
272 if (mutt_str_equal(hook->command, buf_string(cmd)))
273 {
274 rc = MUTT_CMD_SUCCESS;
275 goto cleanup;
276 }
277 }
278 else
279 {
280 /* other hooks only allow one command per pattern, so update the
281 * entry with the new command. this currently does not change the
282 * order of execution of the hooks, which i think is desirable since
283 * a common action to perform is to change the default (.) entry
284 * based upon some other information. */
285 FREE(&hook->command);
286 hook->command = buf_strdup(cmd);
288 rc = MUTT_CMD_SUCCESS;
289 goto cleanup;
290 }
291 }
292 }
293
296 {
297 PatternCompFlags comp_flags;
298
299 if (data & (MUTT_SEND2_HOOK))
300 comp_flags = MUTT_PC_SEND_MODE_SEARCH;
301 else if (data & (MUTT_SEND_HOOK | MUTT_FCC_HOOK))
302 comp_flags = MUTT_PC_NO_FLAGS;
303 else
304 comp_flags = MUTT_PC_FULL_MSG;
305
306 struct MailboxView *mv_cur = get_current_mailbox_view();
307 struct Menu *menu = get_current_menu();
308 pat = mutt_pattern_comp(mv_cur, menu, buf_string(pattern), comp_flags, err);
309 if (!pat)
310 goto cleanup;
311 }
312 else if (~data & MUTT_GLOBAL_HOOK) /* NOT a global hook */
313 {
314 /* Hooks not allowing full patterns: Check syntax of regex */
315 rx = mutt_mem_calloc(1, sizeof(regex_t));
316 int rc2 = REG_COMP(rx, buf_string(pattern), ((data & MUTT_CRYPT_HOOK) ? REG_ICASE : 0));
317 if (rc2 != 0)
318 {
319 regerror(rc2, rx, err->data, err->dsize);
320 FREE(&rx);
321 goto cleanup;
322 }
323 }
324
325 hook = mutt_mem_calloc(1, sizeof(struct Hook));
326 hook->type = data;
327 hook->command = buf_strdup(cmd);
329 hook->pattern = pat;
330 hook->regex.pattern = buf_strdup(pattern);
331 hook->regex.regex = rx;
332 hook->regex.pat_not = pat_not;
333 TAILQ_INSERT_TAIL(&Hooks, hook, entries);
334 rc = MUTT_CMD_SUCCESS;
335
336cleanup:
337 buf_pool_release(&cmd);
338 buf_pool_release(&pattern);
339 return rc;
340}
341
346static void delete_hook(struct Hook *h)
347{
348 FREE(&h->command);
349 FREE(&h->source_file);
350 FREE(&h->regex.pattern);
351 if (h->regex.regex)
352 {
353 regfree(h->regex.regex);
354 FREE(&h->regex.regex);
355 }
357 FREE(&h);
358}
359
367{
368 struct Hook *h = NULL;
369 struct Hook *tmp = NULL;
370
371 TAILQ_FOREACH_SAFE(h, &Hooks, entries, tmp)
372 {
373 if ((type == MUTT_HOOK_NO_FLAGS) || (type == h->type))
374 {
375 TAILQ_REMOVE(&Hooks, h, entries);
376 delete_hook(h);
377 }
378 }
379}
380
384static void idxfmt_hashelem_free(int type, void *obj, intptr_t data)
385{
386 struct HookList *hl = obj;
387 struct Hook *h = NULL;
388 struct Hook *tmp = NULL;
389
390 TAILQ_FOREACH_SAFE(h, hl, entries, tmp)
391 {
392 TAILQ_REMOVE(hl, h, entries);
393 delete_hook(h);
394 }
395
396 FREE(&hl);
397}
398
402static void delete_idxfmt_hooks(void)
403{
405}
406
410static enum CommandResult mutt_parse_idxfmt_hook(struct Buffer *buf, struct Buffer *s,
411 intptr_t data, struct Buffer *err)
412{
414 bool pat_not = false;
415
416 struct Buffer *name = buf_pool_get();
417 struct Buffer *pattern = buf_pool_get();
418 struct Buffer *fmtstring = buf_pool_get();
419
420 if (!IdxFmtHooks)
421 {
424 }
425
426 if (!MoreArgs(s))
427 {
428 buf_printf(err, _("%s: too few arguments"), buf->data);
429 goto out;
430 }
432 struct HookList *hl = mutt_hash_find(IdxFmtHooks, buf_string(name));
433
434 if (*s->dptr == '!')
435 {
436 s->dptr++;
437 SKIPWS(s->dptr);
438 pat_not = true;
439 }
441
442 if (!MoreArgs(s))
443 {
444 buf_printf(err, _("%s: too few arguments"), buf->data);
445 goto out;
446 }
447 parse_extract_token(fmtstring, s, TOKEN_NO_FLAGS);
448
449 if (MoreArgs(s))
450 {
451 buf_printf(err, _("%s: too many arguments"), buf->data);
452 goto out;
453 }
454
455 const char *const c_default_hook = cs_subset_string(NeoMutt->sub, "default_hook");
456 if (c_default_hook)
457 mutt_check_simple(pattern, c_default_hook);
458
459 /* check to make sure that a matching hook doesn't already exist */
460 struct Hook *hook = NULL;
461 if (hl)
462 {
463 TAILQ_FOREACH(hook, hl, entries)
464 {
465 if ((hook->regex.pat_not == pat_not) &&
467 {
468 mutt_str_replace(&hook->command, buf_string(fmtstring));
469 rc = MUTT_CMD_SUCCESS;
470 goto out;
471 }
472 }
473 }
474
475 /* MUTT_PC_PATTERN_DYNAMIC sets so that date ranges are regenerated during
476 * matching. This of course is slower, but index-format-hook is commonly
477 * used for date ranges, and they need to be evaluated relative to "now", not
478 * the hook compilation time. */
479 struct MailboxView *mv_cur = get_current_mailbox_view();
480 struct Menu *menu = get_current_menu();
481 struct PatternList *pat = mutt_pattern_comp(mv_cur, menu, buf_string(pattern),
483 err);
484 if (!pat)
485 goto out;
486
487 hook = mutt_mem_calloc(1, sizeof(struct Hook));
488 hook->type = MUTT_IDXFMTHOOK;
489 hook->command = buf_strdup(fmtstring);
491 hook->pattern = pat;
492 hook->regex.pattern = buf_strdup(pattern);
493 hook->regex.regex = NULL;
494 hook->regex.pat_not = pat_not;
495
496 if (!hl)
497 {
498 hl = mutt_mem_calloc(1, sizeof(*hl));
499 TAILQ_INIT(hl);
501 }
502
503 TAILQ_INSERT_TAIL(hl, hook, entries);
504 rc = MUTT_CMD_SUCCESS;
505
506out:
507 buf_pool_release(&name);
508 buf_pool_release(&pattern);
509 buf_pool_release(&fmtstring);
510
511 return rc;
512}
513
520static HookFlags mutt_get_hook_type(const char *name)
521{
522 struct Command *c = NULL;
523 for (size_t i = 0, size = commands_array(&c); i < size; i++)
524 {
525 if (((c[i].parse == mutt_parse_hook) || (c[i].parse == mutt_parse_idxfmt_hook)) &&
527 {
528 return c[i].data;
529 }
530 }
531 return MUTT_HOOK_NO_FLAGS;
532}
533
537static enum CommandResult mutt_parse_unhook(struct Buffer *buf, struct Buffer *s,
538 intptr_t data, struct Buffer *err)
539{
540 while (MoreArgs(s))
541 {
543 if (mutt_str_equal("*", buf->data))
544 {
546 {
547 buf_addstr(err, _("unhook: Can't do unhook * from within a hook"));
548 return MUTT_CMD_WARNING;
549 }
553 }
554 else
555 {
556 HookFlags type = mutt_get_hook_type(buf->data);
557
558 if (type == MUTT_HOOK_NO_FLAGS)
559 {
560 buf_printf(err, _("unhook: unknown hook type: %s"), buf->data);
561 return MUTT_CMD_ERROR;
562 }
563 if (type & (MUTT_CHARSET_HOOK | MUTT_ICONV_HOOK))
564 {
566 return MUTT_CMD_SUCCESS;
567 }
568 if (CurrentHookType == type)
569 {
570 buf_printf(err, _("unhook: Can't delete a %s from within a %s"),
571 buf->data, buf->data);
572 return MUTT_CMD_WARNING;
573 }
574 if (type == MUTT_IDXFMTHOOK)
576 else
577 mutt_delete_hooks(type);
578 }
579 }
580 return MUTT_CMD_SUCCESS;
581}
582
588void mutt_folder_hook(const char *path, const char *desc)
589{
590 if (!path && !desc)
591 return;
592
593 struct Hook *hook = NULL;
594 struct Buffer *err = buf_pool_get();
595
597
598 TAILQ_FOREACH(hook, &Hooks, entries)
599 {
600 if (!hook->command)
601 continue;
602
603 if (!(hook->type & MUTT_FOLDER_HOOK))
604 continue;
605
606 const char *match = NULL;
607 if (mutt_regex_match(&hook->regex, path))
608 match = path;
609 else if (mutt_regex_match(&hook->regex, desc))
610 match = desc;
611
612 if (match)
613 {
614 mutt_debug(LL_DEBUG1, "folder-hook '%s' matches '%s'\n", hook->regex.pattern, match);
615 mutt_debug(LL_DEBUG5, " %s\n", hook->command);
616 if (parse_rc_line_cwd(hook->command, hook->source_file, err) == MUTT_CMD_ERROR)
617 {
618 mutt_error("%s", buf_string(err));
619 break;
620 }
621 }
622 }
623 buf_pool_release(&err);
624
626}
627
636char *mutt_find_hook(HookFlags type, const char *pat)
637{
638 struct Hook *tmp = NULL;
639
640 TAILQ_FOREACH(tmp, &Hooks, entries)
641 {
642 if (tmp->type & type)
643 {
644 if (mutt_regex_match(&tmp->regex, pat))
645 return tmp->command;
646 }
647 }
648 return NULL;
649}
650
657void mutt_message_hook(struct Mailbox *m, struct Email *e, HookFlags type)
658{
659 struct Hook *hook = NULL;
660 struct PatternCache cache = { 0 };
661 struct Buffer *err = buf_pool_get();
662
663 CurrentHookType = type;
664
665 TAILQ_FOREACH(hook, &Hooks, entries)
666 {
667 if (!hook->command)
668 continue;
669
670 if (hook->type & type)
671 {
672 if ((mutt_pattern_exec(SLIST_FIRST(hook->pattern), 0, m, e, &cache) > 0) ^
673 hook->regex.pat_not)
674 {
675 if (parse_rc_line_cwd(hook->command, hook->source_file, err) == MUTT_CMD_ERROR)
676 {
677 mutt_error("%s", buf_string(err));
679 buf_pool_release(&err);
680
681 return;
682 }
683 /* Executing arbitrary commands could affect the pattern results,
684 * so the cache has to be wiped */
685 memset(&cache, 0, sizeof(cache));
686 }
687 }
688 }
689 buf_pool_release(&err);
690
692}
693
704static int addr_hook(char *path, size_t pathlen, HookFlags type,
705 struct Mailbox *m, struct Email *e)
706{
707 struct Hook *hook = NULL;
708 struct PatternCache cache = { 0 };
709
710 /* determine if a matching hook exists */
711 TAILQ_FOREACH(hook, &Hooks, entries)
712 {
713 if (!hook->command)
714 continue;
715
716 if (hook->type & type)
717 {
718 if ((mutt_pattern_exec(SLIST_FIRST(hook->pattern), 0, m, e, &cache) > 0) ^
719 hook->regex.pat_not)
720 {
721 mutt_make_string(path, pathlen, 0, hook->command, m, -1, e, MUTT_FORMAT_PLAIN, NULL);
722 return 0;
723 }
724 }
725 }
726
727 return -1;
728}
729
736void mutt_default_save(char *path, size_t pathlen, struct Email *e)
737{
738 *path = '\0';
739 struct Mailbox *m_cur = get_current_mailbox();
740 if (addr_hook(path, pathlen, MUTT_SAVE_HOOK, m_cur, e) == 0)
741 return;
742
743 struct Envelope *env = e->env;
744 const struct Address *from = TAILQ_FIRST(&env->from);
745 const struct Address *reply_to = TAILQ_FIRST(&env->reply_to);
746 const struct Address *to = TAILQ_FIRST(&env->to);
747 const struct Address *cc = TAILQ_FIRST(&env->cc);
748 const struct Address *addr = NULL;
749 bool from_me = mutt_addr_is_user(from);
750
751 if (!from_me && reply_to && reply_to->mailbox)
752 addr = reply_to;
753 else if (!from_me && from && from->mailbox)
754 addr = from;
755 else if (to && to->mailbox)
756 addr = to;
757 else if (cc && cc->mailbox)
758 addr = cc;
759 else
760 addr = NULL;
761 if (addr)
762 {
763 struct Buffer *tmp = buf_pool_get();
764 mutt_safe_path(tmp, addr);
765 snprintf(path, pathlen, "=%s", buf_string(tmp));
766 buf_pool_release(&tmp);
767 }
768}
769
775void mutt_select_fcc(struct Buffer *path, struct Email *e)
776{
777 buf_alloc(path, PATH_MAX);
778
779 if (addr_hook(path->data, path->dsize, MUTT_FCC_HOOK, NULL, e) != 0)
780 {
781 const struct Address *to = TAILQ_FIRST(&e->env->to);
782 const struct Address *cc = TAILQ_FIRST(&e->env->cc);
783 const struct Address *bcc = TAILQ_FIRST(&e->env->bcc);
784 const bool c_save_name = cs_subset_bool(NeoMutt->sub, "save_name");
785 const bool c_force_name = cs_subset_bool(NeoMutt->sub, "force_name");
786 const char *const c_record = cs_subset_string(NeoMutt->sub, "record");
787 if ((c_save_name || c_force_name) && (to || cc || bcc))
788 {
789 const struct Address *addr = to ? to : (cc ? cc : bcc);
790 struct Buffer *buf = buf_pool_get();
791 mutt_safe_path(buf, addr);
792 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
793 buf_concat_path(path, NONULL(c_folder), buf_string(buf));
794 buf_pool_release(&buf);
795 if (!c_force_name && (mx_access(buf_string(path), W_OK) != 0))
796 buf_strcpy(path, c_record);
797 }
798 else
799 {
800 buf_strcpy(path, c_record);
801 }
802 }
803 else
804 {
805 buf_fix_dptr(path);
806 }
807
808 buf_pretty_mailbox(path);
809}
810
817static void list_hook(struct ListHead *matches, const char *match, HookFlags type)
818{
819 struct Hook *tmp = NULL;
820
821 TAILQ_FOREACH(tmp, &Hooks, entries)
822 {
823 if ((tmp->type & type) && mutt_regex_match(&tmp->regex, match))
824 {
826 }
827 }
828}
829
837void mutt_crypt_hook(struct ListHead *list, struct Address *addr)
838{
840}
841
846void mutt_account_hook(const char *url)
847{
848 /* parsing commands with URLs in an account hook can cause a recursive
849 * call. We just skip processing if this occurs. Typically such commands
850 * belong in a folder-hook -- perhaps we should warn the user. */
851 static bool inhook = false;
852 if (inhook)
853 return;
854
855 struct Hook *hook = NULL;
856 struct Buffer *err = buf_pool_get();
857
858 TAILQ_FOREACH(hook, &Hooks, entries)
859 {
860 if (!(hook->command && (hook->type & MUTT_ACCOUNT_HOOK)))
861 continue;
862
863 if (mutt_regex_match(&hook->regex, url))
864 {
865 inhook = true;
866 mutt_debug(LL_DEBUG1, "account-hook '%s' matches '%s'\n", hook->regex.pattern, url);
867 mutt_debug(LL_DEBUG5, " %s\n", hook->command);
868
869 if (parse_rc_line_cwd(hook->command, hook->source_file, err) == MUTT_CMD_ERROR)
870 {
871 mutt_error("%s", buf_string(err));
872 buf_pool_release(&err);
873
874 inhook = false;
875 goto done;
876 }
877
878 inhook = false;
879 }
880 }
881done:
882 buf_pool_release(&err);
883}
884
892{
893 struct Hook *hook = NULL;
894 struct Buffer err;
895 char buf[256] = { 0 };
896
897 buf_init(&err);
898 err.data = buf;
899 err.dsize = sizeof(buf);
900
901 TAILQ_FOREACH(hook, &Hooks, entries)
902 {
903 if (!(hook->command && (hook->type & MUTT_TIMEOUT_HOOK)))
904 continue;
905
906 if (parse_rc_line_cwd(hook->command, hook->source_file, &err) == MUTT_CMD_ERROR)
907 {
908 mutt_error("%s", err.data);
909 buf_reset(&err);
910
911 /* The hooks should be independent of each other, so even though this on
912 * failed, we'll carry on with the others. */
913 }
914 }
915
916 /* Delete temporary attachment files */
918}
919
928{
929 struct Hook *hook = NULL;
930 struct Buffer err = buf_make(0);
931 char buf[256] = { 0 };
932
933 err.data = buf;
934 err.dsize = sizeof(buf);
935
936 TAILQ_FOREACH(hook, &Hooks, entries)
937 {
938 if (!(hook->command && (hook->type & type)))
939 continue;
940
941 if (parse_rc_line_cwd(hook->command, hook->source_file, &err) == MUTT_CMD_ERROR)
942 {
943 mutt_error("%s", err.data);
944 buf_reset(&err);
945 }
946 }
947}
948
957const char *mutt_idxfmt_hook(const char *name, struct Mailbox *m, struct Email *e)
958{
959 if (!IdxFmtHooks)
960 return NULL;
961
962 struct HookList *hl = mutt_hash_find(IdxFmtHooks, name);
963 if (!hl)
964 return NULL;
965
967
968 struct PatternCache cache = { 0 };
969 const char *fmtstring = NULL;
970 struct Hook *hook = NULL;
971
972 TAILQ_FOREACH(hook, hl, entries)
973 {
974 struct Pattern *pat = SLIST_FIRST(hook->pattern);
975 if ((mutt_pattern_exec(pat, 0, m, e, &cache) > 0) ^ hook->regex.pat_not)
976 {
977 fmtstring = hook->command;
978 break;
979 }
980 }
981
983
984 return fmtstring;
985}
986
990static const struct Command HookCommands[] = {
991 // clang-format off
992 { "account-hook", mutt_parse_hook, MUTT_ACCOUNT_HOOK },
994 { "crypt-hook", mutt_parse_hook, MUTT_CRYPT_HOOK },
995 { "fcc-hook", mutt_parse_hook, MUTT_FCC_HOOK },
996 { "fcc-save-hook", mutt_parse_hook, MUTT_FCC_HOOK | MUTT_SAVE_HOOK },
997 { "folder-hook", mutt_parse_hook, MUTT_FOLDER_HOOK },
999 { "index-format-hook", mutt_parse_idxfmt_hook, MUTT_IDXFMTHOOK },
1000 { "mbox-hook", mutt_parse_hook, MUTT_MBOX_HOOK },
1001 { "message-hook", mutt_parse_hook, MUTT_MESSAGE_HOOK },
1002 { "pgp-hook", mutt_parse_hook, MUTT_CRYPT_HOOK },
1003 { "reply-hook", mutt_parse_hook, MUTT_REPLY_HOOK },
1004 { "save-hook", mutt_parse_hook, MUTT_SAVE_HOOK },
1005 { "send-hook", mutt_parse_hook, MUTT_SEND_HOOK },
1006 { "send2-hook", mutt_parse_hook, MUTT_SEND2_HOOK },
1007 { "shutdown-hook", mutt_parse_hook, MUTT_SHUTDOWN_HOOK | MUTT_GLOBAL_HOOK },
1008 { "startup-hook", mutt_parse_hook, MUTT_STARTUP_HOOK | MUTT_GLOBAL_HOOK },
1009 { "timeout-hook", mutt_parse_hook, MUTT_TIMEOUT_HOOK | MUTT_GLOBAL_HOOK },
1010 { "unhook", mutt_parse_unhook, 0 },
1011 // clang-format on
1012};
1013
1017void hooks_init(void)
1018{
1020}
Email Address Handling.
Email Aliases.
bool mutt_addr_is_user(const struct Address *addr)
Does the address belong to the user.
Definition: alias.c:570
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:173
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:303
struct Buffer buf_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:70
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:194
struct Buffer * buf_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:55
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:238
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition: buffer.c:572
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:542
size_t buf_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition: buffer.c:484
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:349
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
CommandResult
Error codes for command_t parse functions.
Definition: command.h:36
@ MUTT_CMD_SUCCESS
Success: Command worked.
Definition: command.h:39
@ MUTT_CMD_ERROR
Error: Can't help the user.
Definition: command.h:37
@ MUTT_CMD_WARNING
Warning: Help given to the user.
Definition: command.h:38
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:159
char * mutt_get_sourced_cwd(void)
Get the current file path that is being parsed.
Definition: commands.c:179
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:778
int mutt_comp_valid_command(const char *cmd)
Is this command string allowed?
Definition: compress.c:416
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:292
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
Convenience wrapper for the config headers.
void commands_register(const struct Command *cmds, const size_t num_cmds)
Add commands to Commands array.
Definition: command.c:53
size_t commands_array(struct Command **first)
Get Commands array.
Definition: command.c:75
Convenience wrapper for the core headers.
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:1137
int parse_extract_token(struct Buffer *dest, struct Buffer *tok, 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:48
#define MoreArgs(buf)
Definition: extract.h:31
#define TOKEN_NO_FLAGS
No flags are set.
Definition: extract.h:45
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition: file.c:709
Flags to control mutt_expando_format()
#define MUTT_FORMAT_PLAIN
Do not prepend DISP_TO, DISP_CC ...
Definition: format_flags.h:38
char * CurrentFolder
Currently selected mailbox.
Definition: globals.c:44
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:537
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:410
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:128
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:85
static void idxfmt_hashelem_free(int type, void *obj, intptr_t data)
Delete an index-format-hook from the Hash Table - Implements hash_hdata_free_t -.
Definition: hook.c:384
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
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:112
void mutt_make_string(char *buf, size_t buflen, int cols, const char *s, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition: hdrline.c:1426
String processing routines to generate the mail index.
static int addr_hook(char *path, size_t pathlen, HookFlags type, struct Mailbox *m, struct Email *e)
Perform an address hook (get a path)
Definition: hook.c:704
void mutt_timeout_hook(void)
Execute any timeout hooks.
Definition: hook.c:891
static struct HashTable * IdxFmtHooks
All Index Format hooks.
Definition: hook.c:77
char * mutt_find_hook(HookFlags type, const char *pat)
Find a matching hook.
Definition: hook.c:636
void mutt_startup_shutdown_hook(HookFlags type)
Execute any startup/shutdown hooks.
Definition: hook.c:927
void mutt_delete_hooks(HookFlags type)
Delete matching hooks.
Definition: hook.c:366
static void list_hook(struct ListHead *matches, const char *match, HookFlags type)
Find hook strings matching.
Definition: hook.c:817
void mutt_account_hook(const char *url)
Perform an account hook.
Definition: hook.c:846
static void delete_idxfmt_hooks(void)
Delete all the index-format-hooks.
Definition: hook.c:402
const char * mutt_idxfmt_hook(const char *name, struct Mailbox *m, struct Email *e)
Get index-format-hook format string.
Definition: hook.c:957
void mutt_default_save(char *path, size_t pathlen, struct Email *e)
Find the default save path for an email.
Definition: hook.c:736
void mutt_folder_hook(const char *path, const char *desc)
Perform a folder hook.
Definition: hook.c:588
void mutt_select_fcc(struct Buffer *path, struct Email *e)
Select the FCC path for an email.
Definition: hook.c:775
void hooks_init(void)
Setup feature commands.
Definition: hook.c:1017
static struct HookList Hooks
All simple hooks, e.g. MUTT_FOLDER_HOOK.
Definition: hook.c:74
static HookFlags mutt_get_hook_type(const char *name)
Find a hook by name.
Definition: hook.c:520
static HookFlags CurrentHookType
The type of the hook currently being executed, e.g. MUTT_SAVE_HOOK.
Definition: hook.c:80
static const struct Command HookCommands[]
Hook Commands.
Definition: hook.c:990
void mutt_crypt_hook(struct ListHead *list, struct Address *addr)
Find crypto hooks for an Address.
Definition: hook.c:837
static void delete_hook(struct Hook *h)
Delete a Hook.
Definition: hook.c:346
void mutt_message_hook(struct Mailbox *m, struct Email *e, HookFlags type)
Perform a message hook.
Definition: hook.c:657
Parse and execute user-defined hooks.
#define MUTT_OPEN_HOOK
open-hook: to read a compressed mailbox
Definition: hook.h:51
#define MUTT_ICONV_HOOK
iconv-hook: create a system charset alias
Definition: hook.h:44
#define MUTT_FOLDER_HOOK
folder-hook: when entering a mailbox
Definition: hook.h:38
#define MUTT_SAVE_HOOK
save-hook: set a default folder when saving an email
Definition: hook.h:42
#define MUTT_SEND_HOOK
send-hook: when composing a new email
Definition: hook.h:40
uint32_t HookFlags
Flags for mutt_parse_hook(), e.g. MUTT_FOLDER_HOOK.
Definition: hook.h:36
#define MUTT_FCC_HOOK
fcc-hook: to save outgoing email
Definition: hook.h:41
#define MUTT_CLOSE_HOOK
close-hook: write to a compressed mailbox
Definition: hook.h:53
#define MUTT_ACCOUNT_HOOK
account-hook: when changing between accounts
Definition: hook.h:47
#define MUTT_APPEND_HOOK
append-hook: append to a compressed mailbox
Definition: hook.h:52
#define MUTT_GLOBAL_HOOK
Hooks which don't take a regex.
Definition: hook.h:59
#define MUTT_STARTUP_HOOK
startup-hook: run when starting NeoMutt
Definition: hook.h:57
#define MUTT_SEND2_HOOK
send2-hook: when changing fields in the compose menu
Definition: hook.h:49
#define MUTT_CRYPT_HOOK
crypt-hook: automatically select a PGP/SMIME key
Definition: hook.h:46
#define MUTT_MBOX_HOOK
mbox-hook: move messages after reading them
Definition: hook.h:39
#define MUTT_REPLY_HOOK
reply-hook: when replying to an email
Definition: hook.h:48
#define MUTT_TIMEOUT_HOOK
timeout-hook: run a command periodically
Definition: hook.h:56
#define MUTT_MESSAGE_HOOK
message-hook: run before displaying a message
Definition: hook.h:45
#define MUTT_SHUTDOWN_HOOK
shutdown-hook: run when leaving NeoMutt
Definition: hook.h:58
#define MUTT_IDXFMTHOOK
index-format-hook: customise the format of the index
Definition: hook.h:55
#define MUTT_CHARSET_HOOK
charset-hook: create a charset alias for malformed emails
Definition: hook.h:43
#define MUTT_HOOK_NO_FLAGS
No flags are set.
Definition: hook.h:37
GUI manage the main index (list of emails)
struct MailboxView * get_current_mailbox_view(void)
Get the current Mailbox view.
Definition: index.c:630
struct Mailbox * get_current_mailbox(void)
Get the current Mailbox.
Definition: index.c:662
struct Menu * get_current_menu(void)
Get the current Menu.
Definition: index.c:678
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
@ LL_DEBUG5
Log at debug level 5.
Definition: logging2.h:47
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
#define FREE(x)
Definition: memory.h:45
#define mutt_array_size(x)
Definition: memory.h:38
void mutt_ch_lookup_remove(void)
Remove all the character set lookups.
Definition: charset.c:536
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:504
LookupType
Types of character set lookups.
Definition: charset.h:66
@ MUTT_LOOKUP_ICONV
Character set conversion.
Definition: charset.h:68
@ MUTT_LOOKUP_CHARSET
Alias for another character set.
Definition: charset.h:67
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:636
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:810
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:327
#define PATH_MAX
Definition: mutt.h:41
void mutt_temp_attachments_cleanup(void)
Delete all temporary attachments.
Definition: mutt_attach.c:1316
void buf_expand_path_regex(struct Buffer *buf, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:138
void buf_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:562
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:335
void mutt_safe_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:726
Some miscellaneous functions.
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition: mx.c:182
API for mailboxes.
API for encryption/signing of emails.
#define WithCrypto
Definition: lib.h:117
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:113
#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:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
#define TAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:735
#define TAILQ_INIT(head)
Definition: queue.h:765
#define TAILQ_HEAD(name, type)
Definition: queue.h:623
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:809
#define TAILQ_FIRST(head)
Definition: queue.h:723
#define SLIST_FIRST(head)
Definition: queue.h:229
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:841
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:637
#define TAILQ_ENTRY(type)
Definition: queue.h:640
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:53
#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:34
char * dptr
Current read/write position.
Definition: buffer.h:36
size_t dsize
Length of data.
Definition: buffer.h:37
char * data
Pointer to data.
Definition: buffer.h:35
enum CommandResult(* parse)(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Definition: command.h:65
intptr_t data
Data or flags to pass to the command.
Definition: command.h:67
const char * name
Name of the command.
Definition: command.h:52
The envelope/body of an email.
Definition: email.h:37
struct Envelope * env
Envelope information.
Definition: email.h:66
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
A Hash Table.
Definition: hash.h:98
A list of user hooks.
Definition: hook.c:63
HookFlags type
Hook type.
Definition: hook.c:64
struct PatternList * pattern
Used for fcc,save,send-hook.
Definition: hook.c:68
struct Regex regex
Regular expression.
Definition: hook.c:65
char * command
Filename, command or pattern to execute.
Definition: hook.c:66
char * source_file
Used for relative-directory source.
Definition: hook.c:67
View of a Mailbox.
Definition: mview.h:39
A mailbox.
Definition: mailbox.h:79
Definition: lib.h:70
enum MenuType type
Menu definition for keymap entries.
Definition: lib.h:74
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
Cache commonly-used patterns.
Definition: lib.h:117
A simple (non-regex) pattern.
Definition: lib.h:77
Cached regular expression.
Definition: regex3.h:89
char * pattern
printable version
Definition: regex3.h:90
bool pat_not
do not match
Definition: regex3.h:92
regex_t * regex
compiled expression
Definition: regex3.h:91