2 * Copyright (C) 1996-2002,2004,2007 Michael R. Elkins <me@mutt.org>, and others
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include "mutt_crypt.h"
39 int type; /* hook type */
40 REGEXP rx; /* regular expression */
41 char *command; /* filename, command or pattern to execute */
42 pattern_t *pattern; /* used for fcc,save,send-hook */
46 static HOOK *Hooks = NULL;
47 static HASH *IdxFmtHooks = NULL;
49 static int current_hook_type = 0;
51 int mutt_parse_hook (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err)
54 BUFFER *command, *pattern;
57 pattern_t *pat = NULL;
60 command = mutt_buffer_pool_get ();
61 pattern = mutt_buffer_pool_get ();
70 mutt_extract_token (pattern, s, 0);
74 strfcpy (err->data, _("too few arguments"), err->dsize);
78 mutt_extract_token (command, s, (data & (MUTT_FOLDERHOOK | MUTT_SENDHOOK | MUTT_SEND2HOOK | MUTT_ACCOUNTHOOK | MUTT_REPLYHOOK)) ? MUTT_TOKEN_SPACE : 0);
80 if (!mutt_buffer_len (command))
82 strfcpy (err->data, _("too few arguments"), err->dsize);
88 strfcpy (err->data, _("too many arguments"), err->dsize);
92 if (data & (MUTT_FOLDERHOOK | MUTT_MBOXHOOK))
96 /* Accidentally using the ^ mailbox shortcut in the .muttrc is a
98 if ((*(pattern->data) == '^') && (!CurrentFolder))
100 strfcpy (err->data, _("current mailbox shortcut '^' is unset"), err->dsize);
104 tmp = mutt_buffer_pool_get ();
105 mutt_buffer_strcpy (tmp, mutt_b2s (pattern));
106 _mutt_buffer_expand_path (tmp, 1);
108 /* Check for other mailbox shortcuts that expand to the empty string.
109 * This is likely a mistake too */
110 if (!mutt_buffer_len (tmp) && mutt_buffer_len (pattern))
112 strfcpy (err->data, _("mailbox shortcut expanded to empty regexp"), err->dsize);
113 mutt_buffer_pool_release (&tmp);
117 mutt_buffer_strcpy (pattern, mutt_b2s (tmp));
118 mutt_buffer_pool_release (&tmp);
120 #ifdef USE_COMPRESSED
121 else if (data & (MUTT_APPENDHOOK | MUTT_OPENHOOK | MUTT_CLOSEHOOK))
123 if (mutt_comp_valid_command (mutt_b2s (command)) == 0)
125 strfcpy (err->data, _("badly formatted command string"), err->dsize);
130 else if (DefaultHook && !(data & (MUTT_CHARSETHOOK | MUTT_ICONVHOOK | MUTT_ACCOUNTHOOK))
131 && (!WithCrypto || !(data & MUTT_CRYPTHOOK))
134 /* At this stage remain only message-hooks, reply-hooks, send-hooks,
135 * send2-hooks, save-hooks, and fcc-hooks: All those allowing full
136 * patterns. If given a simple regexp, we expand $default_hook.
138 mutt_check_simple (pattern, DefaultHook);
141 if (data & (MUTT_MBOXHOOK | MUTT_SAVEHOOK | MUTT_FCCHOOK))
143 mutt_buffer_expand_path (command);
146 /* check to make sure that a matching hook doesn't already exist */
147 for (ptr = Hooks; ptr; ptr = ptr->next)
149 if (ptr->type == data &&
150 ptr->rx.not == not &&
151 !mutt_strcmp (mutt_b2s (pattern), ptr->rx.pattern))
153 if (data & (MUTT_FOLDERHOOK | MUTT_SENDHOOK | MUTT_SEND2HOOK | MUTT_MESSAGEHOOK | MUTT_ACCOUNTHOOK | MUTT_REPLYHOOK | MUTT_CRYPTHOOK))
155 /* these hooks allow multiple commands with the same
156 * pattern, so if we've already seen this pattern/command pair, just
157 * ignore it instead of creating a duplicate */
158 if (!mutt_strcmp (ptr->command, mutt_b2s (command)))
166 /* other hooks only allow one command per pattern, so update the
167 * entry with the new command. this currently does not change the
168 * order of execution of the hooks, which i think is desirable since
169 * a common action to perform is to change the default (.) entry
170 * based upon some other information. */
171 FREE (&ptr->command);
172 ptr->command = safe_strdup (mutt_b2s (command));
181 if (data & (MUTT_SENDHOOK | MUTT_SEND2HOOK | MUTT_SAVEHOOK | MUTT_FCCHOOK | MUTT_MESSAGEHOOK | MUTT_REPLYHOOK))
183 if ((pat = mutt_pattern_comp (pattern->data,
184 (data & (MUTT_SENDHOOK | MUTT_SEND2HOOK | MUTT_FCCHOOK)) ? 0 : MUTT_FULL_MSG,
191 /* Hooks not allowing full patterns: Check syntax of regexp */
192 rx = safe_malloc (sizeof (regex_t));
193 #ifdef MUTT_CRYPTHOOK
194 if ((rv = REGCOMP (rx, mutt_b2s (pattern), ((data & (MUTT_CRYPTHOOK|MUTT_CHARSETHOOK|MUTT_ICONVHOOK)) ? REG_ICASE : 0))) != 0)
196 if ((rv = REGCOMP (rx, mutt_b2s (pattern), (data & (MUTT_CHARSETHOOK|MUTT_ICONVHOOK)) ? REG_ICASE : 0)) != 0)
197 #endif /* MUTT_CRYPTHOOK */
199 regerror (rv, rx, err->data, err->dsize);
207 ptr->next = safe_calloc (1, sizeof (HOOK));
211 Hooks = ptr = safe_calloc (1, sizeof (HOOK));
213 ptr->command = safe_strdup (mutt_b2s (command));
215 ptr->rx.pattern = safe_strdup (mutt_b2s (pattern));
222 mutt_buffer_pool_release (&command);
223 mutt_buffer_pool_release (&pattern);
227 static void delete_hook (HOOK *h)
230 FREE (&h->rx.pattern);
235 mutt_pattern_free (&h->pattern);
239 /* Deletes all hooks of type ``type'', or all defined hooks if ``type'' is 0 */
240 static void delete_hooks (int type)
245 while (h = Hooks, h && (type == 0 || type == h->type))
251 prev = h; /* Unused assignment to avoid compiler warnings */
257 prev->next = h->next;
266 static void delete_idxfmt_hooklist (void *list)
279 static void delete_idxfmt_hooks (void)
281 hash_destroy (&IdxFmtHooks, delete_idxfmt_hooklist);
284 int mutt_parse_idxfmt_hook (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err)
287 BUFFER *name, *pattern, *fmtstring;
288 int rc = -1, not = 0;
289 pattern_t *pat = NULL;
292 name = mutt_buffer_pool_get ();
293 pattern = mutt_buffer_pool_get ();
294 fmtstring = mutt_buffer_pool_get ();
297 IdxFmtHooks = hash_create (30, MUTT_HASH_STRDUP_KEYS);
301 strfcpy (err->data, _("not enough arguments"), err->dsize);
304 mutt_extract_token (name, s, 0);
305 hooks = hash_find (IdxFmtHooks, mutt_b2s (name));
313 mutt_extract_token (pattern, s, 0);
317 strfcpy (err->data, _("too few arguments"), err->dsize);
320 mutt_extract_token (fmtstring, s, 0);
324 strfcpy (err->data, _("too many arguments"), err->dsize);
329 mutt_check_simple (pattern, DefaultHook);
331 /* check to make sure that a matching hook doesn't already exist */
332 for (ptr = hooks; ptr; ptr = ptr->next)
334 if ((ptr->rx.not == not) &&
335 !mutt_strcmp (mutt_b2s (pattern), ptr->rx.pattern))
337 FREE (&ptr->command);
338 ptr->command = safe_strdup (mutt_b2s (fmtstring));
346 /* MUTT_PATTERN_DYNAMIC sets so that date ranges are regenerated during
347 * matching. This of course is slower, but index-format-hook is commonly
348 * used for date ranges, and they need to be evaluated relative to "now", not
349 * the hook compilation time.
351 if ((pat = mutt_pattern_comp (pattern->data,
352 MUTT_FULL_MSG | MUTT_PATTERN_DYNAMIC,
358 ptr->next = safe_calloc (1, sizeof (HOOK));
362 ptr = safe_calloc (1, sizeof (HOOK));
364 ptr->command = safe_strdup (mutt_b2s (fmtstring));
366 ptr->rx.pattern = safe_strdup (mutt_b2s (pattern));
371 hash_insert (IdxFmtHooks, mutt_b2s (name), ptr);
376 mutt_buffer_pool_release (&name);
377 mutt_buffer_pool_release (&pattern);
378 mutt_buffer_pool_release (&fmtstring);
383 int mutt_parse_unhook (BUFFER *buf, BUFFER *s, union pointer_long_t udata, BUFFER *err)
387 mutt_extract_token (buf, s, 0);
388 if (mutt_strcmp ("*", buf->data) == 0)
390 if (current_hook_type)
392 snprintf (err->data, err->dsize, "%s",
393 _("unhook: Can't do unhook * from within a hook."));
397 delete_idxfmt_hooks ();
401 int type = mutt_get_hook_type (buf->data);
405 snprintf (err->data, err->dsize,
406 _("unhook: unknown hook type: %s"), buf->data);
409 if (current_hook_type == type)
411 snprintf (err->data, err->dsize,
412 _("unhook: Can't delete a %s from within a %s."),
413 buf->data, buf->data);
416 if (type == MUTT_IDXFMTHOOK)
417 delete_idxfmt_hooks ();
425 void mutt_folder_hook (const char *path)
430 current_hook_type = MUTT_FOLDERHOOK;
432 mutt_buffer_init (&err);
434 err.data = safe_malloc (err.dsize);
435 mutt_buffer_init (&token);
436 for (; tmp; tmp = tmp->next)
441 if (tmp->type & MUTT_FOLDERHOOK)
443 if ((regexec (tmp->rx.rx, path, 0, NULL, 0) == 0) ^ tmp->rx.not)
445 if (mutt_parse_rc_line (tmp->command, &token, &err) == -1)
447 mutt_error ("%s", err.data);
449 mutt_sleep (1); /* pause a moment to let the user see the error */
450 current_hook_type = 0;
461 current_hook_type = 0;
464 char *mutt_find_hook (int type, const char *pat)
468 for (; tmp; tmp = tmp->next)
469 if (tmp->type & type)
471 if (regexec (tmp->rx.rx, pat, 0, NULL, 0) == 0)
472 return (tmp->command);
477 void mutt_message_hook (CONTEXT *ctx, HEADER *hdr, int type)
481 pattern_cache_t cache;
483 current_hook_type = type;
485 mutt_buffer_init (&err);
487 err.data = safe_malloc (err.dsize);
488 mutt_buffer_init (&token);
489 memset (&cache, 0, sizeof (cache));
490 for (hook = Hooks; hook; hook = hook->next)
495 if (hook->type & type)
496 if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr, &cache) > 0) ^ hook->rx.not)
498 if (mutt_parse_rc_line (hook->command, &token, &err) != 0)
501 mutt_error ("%s", err.data);
503 current_hook_type = 0;
508 /* Executing arbitrary commands could affect the pattern results,
509 * so the cache has to be wiped */
510 memset (&cache, 0, sizeof (cache));
516 current_hook_type = 0;
520 mutt_addr_hook (char *path, size_t pathlen, int type, CONTEXT *ctx, HEADER *hdr)
523 pattern_cache_t cache;
525 memset (&cache, 0, sizeof (cache));
526 /* determine if a matching hook exists */
527 for (hook = Hooks; hook; hook = hook->next)
532 if (hook->type & type)
533 if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr, &cache) > 0) ^ hook->rx.not)
535 mutt_make_string (path, pathlen, hook->command, ctx, hdr);
543 void mutt_default_save (char *path, size_t pathlen, HEADER *hdr)
546 if (mutt_addr_hook (path, pathlen, MUTT_SAVEHOOK, Context, hdr) != 0)
550 ENVELOPE *env = hdr->env;
551 int fromMe = mutt_addr_is_user (env->from);
553 if (!fromMe && env->reply_to && env->reply_to->mailbox)
555 else if (!fromMe && env->from && env->from->mailbox)
557 else if (env->to && env->to->mailbox)
559 else if (env->cc && env->cc->mailbox)
565 tmp = mutt_buffer_pool_get ();
566 mutt_safe_path (tmp, adr);
567 snprintf (path, pathlen, "=%s", mutt_b2s (tmp));
568 mutt_buffer_pool_release (&tmp);
573 void mutt_select_fcc (BUFFER *path, HEADER *hdr)
577 ENVELOPE *env = hdr->env;
579 mutt_buffer_increase_size (path, _POSIX_PATH_MAX);
581 if (mutt_addr_hook (path->data, path->dsize, MUTT_FCCHOOK, NULL, hdr) != 0)
583 if ((option (OPTSAVENAME) || option (OPTFORCENAME)) &&
584 (env->to || env->cc || env->bcc))
586 adr = env->to ? env->to : (env->cc ? env->cc : env->bcc);
587 buf = mutt_buffer_pool_get ();
588 mutt_safe_path (buf, adr);
589 mutt_buffer_concat_path (path, NONULL(Maildir), mutt_b2s (buf));
590 mutt_buffer_pool_release (&buf);
591 if (!option (OPTFORCENAME) && mx_access (mutt_b2s (path), W_OK) != 0)
592 mutt_buffer_strcpy (path, NONULL (Outbox));
595 mutt_buffer_strcpy (path, NONULL (Outbox));
598 mutt_buffer_fix_dptr (path);
600 mutt_buffer_pretty_mailbox (path);
603 static char *_mutt_string_hook (const char *match, int hook)
607 for (; tmp; tmp = tmp->next)
609 if ((tmp->type & hook) &&
610 ((match && regexec (tmp->rx.rx, match, 0, NULL, 0) == 0) ^ tmp->rx.not))
611 return (tmp->command);
616 static LIST *_mutt_list_hook (const char *match, int hook)
619 LIST *matches = NULL;
621 for (; tmp; tmp = tmp->next)
623 if ((tmp->type & hook) &&
624 ((match && regexec (tmp->rx.rx, match, 0, NULL, 0) == 0) ^ tmp->rx.not))
625 matches = mutt_add_list (matches, tmp->command);
630 char *mutt_charset_hook (const char *chs)
632 return _mutt_string_hook (chs, MUTT_CHARSETHOOK);
635 char *mutt_iconv_hook (const char *chs)
637 return _mutt_string_hook (chs, MUTT_ICONVHOOK);
640 LIST *mutt_crypt_hook (ADDRESS *adr)
642 return _mutt_list_hook (adr->mailbox, MUTT_CRYPTHOOK);
646 void mutt_account_hook (const char* url)
648 /* parsing commands with URLs in an account hook can cause a recursive
649 * call. We just skip processing if this occurs. Typically such commands
650 * belong in a folder-hook -- perhaps we should warn the user. */
651 static int inhook = 0;
660 mutt_buffer_init (&err);
662 err.data = safe_malloc (err.dsize);
663 mutt_buffer_init (&token);
665 for (hook = Hooks; hook; hook = hook->next)
667 if (! (hook->command && (hook->type & MUTT_ACCOUNTHOOK)))
670 if ((regexec (hook->rx.rx, url, 0, NULL, 0) == 0) ^ hook->rx.not)
674 if (mutt_parse_rc_line (hook->command, &token, &err) == -1)
677 mutt_error ("%s", err.data);
694 const char *mutt_idxfmt_hook (const char *name, CONTEXT *ctx, HEADER *hdr)
696 HOOK *hooklist, *hook;
697 pattern_cache_t cache;
698 const char *fmtstring = NULL;
703 current_hook_type = MUTT_IDXFMTHOOK;
704 hooklist = hash_find (IdxFmtHooks, name);
705 memset (&cache, 0, sizeof (cache));
707 for (hook = hooklist; hook; hook = hook->next)
709 if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr, &cache) > 0) ^ hook->rx.not)
711 fmtstring = hook->command;
716 current_hook_type = 0;