NeoMutt  2025-12-11-911-gd8d604
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
exec.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 "attach/lib.h"
49#include "commands/lib.h"
50#include "expando/lib.h"
51#include "index/lib.h"
52#include "parse/lib.h"
53#include "pattern/lib.h"
54#include "hook.h"
55#include "module_data.h"
56#include "muttlib.h"
57#include "mx.h"
58
64void exec_folder_hook(const char *path, const char *desc)
65{
66 if (!path && !desc)
67 return;
68
70 struct Hook *hook = NULL;
71 struct ParseContext *pc = parse_context_new();
72 struct ParseError *pe = parse_error_new();
73
75
76 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
77 {
78 if (!hook->command)
79 continue;
80
81 if (!(hook->id == CMD_FOLDER_HOOK))
82 continue;
83
84 const char *match = NULL;
85 if (mutt_regex_match(&hook->regex, path))
86 match = path;
87 else if (mutt_regex_match(&hook->regex, desc))
88 match = desc;
89
90 if (match)
91 {
92 mutt_debug(LL_DEBUG1, "folder-hook '%s' matches '%s'\n", hook->regex.pattern, match);
93 mutt_debug(LL_DEBUG5, " %s\n", hook->command);
94 if (parse_rc_line_cwd(hook->command, hook->source_file, pc, pe) == MUTT_CMD_ERROR)
95 {
96 mutt_error("%s", buf_string(pe->message));
97 break;
98 }
99 }
100 }
101
102 mod_data->current_hook_id = CMD_NONE;
104 parse_error_free(&pe);
105}
106
115char *mutt_find_hook(enum CommandId id, const char *pat)
116{
118 struct Hook *hook = NULL;
119
120 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
121 {
122 if (hook->id == id)
123 {
124 if (mutt_regex_match(&hook->regex, pat))
125 return hook->command;
126 }
127 }
128 return NULL;
129}
130
137void exec_message_hook(struct Mailbox *m, struct Email *e, enum CommandId id)
138{
140 struct Hook *hook = NULL;
141 struct PatternCache cache = { 0 };
142 struct ParseContext *pc = parse_context_new();
143 struct ParseError *pe = parse_error_new();
144
145 mod_data->current_hook_id = id;
146
147 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
148 {
149 if (!hook->command)
150 continue;
151
152 if (hook->id == id)
153 {
154 if ((mutt_pattern_exec(SLIST_FIRST(hook->pattern), 0, m, e, &cache) > 0) ^
155 hook->regex.pat_not)
156 {
157 if (parse_rc_line_cwd(hook->command, hook->source_file, pc, pe) == MUTT_CMD_ERROR)
158 {
159 mutt_error("%s", buf_string(pe->message));
160 goto done;
161 }
162 /* Executing arbitrary commands could affect the pattern results,
163 * so the cache has to be wiped */
164 memset(&cache, 0, sizeof(cache));
165 }
166 }
167 }
168
169done:
170 mod_data->current_hook_id = CMD_NONE;
172 parse_error_free(&pe);
173}
174
184static int addr_hook(struct Buffer *path, enum CommandId id, struct Mailbox *m,
185 struct Email *e)
186{
188 struct Hook *hook = NULL;
189 struct PatternCache cache = { 0 };
190
191 /* determine if a matching hook exists */
192 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
193 {
194 if (!hook->command)
195 continue;
196
197 if (hook->id == id)
198 {
199 if ((mutt_pattern_exec(SLIST_FIRST(hook->pattern), 0, m, e, &cache) > 0) ^
200 hook->regex.pat_not)
201 {
202 buf_alloc(path, PATH_MAX);
203 mutt_make_string(path, -1, hook->expando, m, -1, e, MUTT_FORMAT_PLAIN, NULL);
204 buf_fix_dptr(path);
205 return 0;
206 }
207 }
208 }
209
210 return -1;
211}
212
218void mutt_default_save(struct Buffer *path, struct Email *e)
219{
220 struct Mailbox *m_cur = get_current_mailbox();
221 if (addr_hook(path, CMD_SAVE_HOOK, m_cur, e) == 0)
222 return;
223
224 struct Envelope *env = e->env;
225 const struct Address *from = TAILQ_FIRST(&env->from);
226 const struct Address *reply_to = TAILQ_FIRST(&env->reply_to);
227 const struct Address *to = TAILQ_FIRST(&env->to);
228 const struct Address *cc = TAILQ_FIRST(&env->cc);
229 const struct Address *addr = NULL;
230 bool from_me = mutt_addr_is_user(from);
231
232 if (!from_me && reply_to && reply_to->mailbox)
233 addr = reply_to;
234 else if (!from_me && from && from->mailbox)
235 addr = from;
236 else if (to && to->mailbox)
237 addr = to;
238 else if (cc && cc->mailbox)
239 addr = cc;
240 else
241 addr = NULL;
242 if (addr)
243 {
244 struct Buffer *tmp = buf_pool_get();
245 generate_save_path(tmp, addr);
246 buf_add_printf(path, "=%s", buf_string(tmp));
247 buf_pool_release(&tmp);
248 }
249}
250
256void mutt_select_fcc(struct Buffer *path, struct Email *e)
257{
258 buf_alloc(path, PATH_MAX);
259
260 if (addr_hook(path, CMD_FCC_HOOK, NULL, e) != 0)
261 {
262 const struct Address *to = TAILQ_FIRST(&e->env->to);
263 const struct Address *cc = TAILQ_FIRST(&e->env->cc);
264 const struct Address *bcc = TAILQ_FIRST(&e->env->bcc);
265 const bool c_save_name = cs_subset_bool(NeoMutt->sub, "save_name");
266 const bool c_force_name = cs_subset_bool(NeoMutt->sub, "force_name");
267 const char *const c_record = cs_subset_string(NeoMutt->sub, "record");
268 if ((c_save_name || c_force_name) && (to || cc || bcc))
269 {
270 const struct Address *addr = to ? to : (cc ? cc : bcc);
271 struct Buffer *buf = buf_pool_get();
272 generate_save_path(buf, addr);
273 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
274 buf_concat_path(path, NONULL(c_folder), buf_string(buf));
275 buf_pool_release(&buf);
276 if (!c_force_name && (mx_access(buf_string(path), W_OK) != 0))
277 buf_strcpy(path, c_record);
278 }
279 else
280 {
281 buf_strcpy(path, c_record);
282 }
283 }
284 else
285 {
286 buf_fix_dptr(path);
287 }
288
289 pretty_mailbox(path);
290}
291
298static void list_hook(struct ListHead *matches, const char *match, enum CommandId id)
299{
301 struct Hook *tmp = NULL;
302
303 TAILQ_FOREACH(tmp, &mod_data->hooks, entries)
304 {
305 if ((tmp->id == id) && mutt_regex_match(&tmp->regex, match))
306 {
308 }
309 }
310}
311
319void mutt_crypt_hook(struct ListHead *list, struct Address *addr)
320{
322}
323
328void exec_account_hook(const char *url)
329{
330 /* parsing commands with URLs in an account hook can cause a recursive
331 * call. We just skip processing if this occurs. Typically such commands
332 * belong in a folder-hook -- perhaps we should warn the user. */
333 static bool inhook = false;
334 if (inhook)
335 return;
336
338 struct Hook *hook = NULL;
339 struct ParseContext *pc = parse_context_new();
340 struct ParseError *pe = parse_error_new();
341
343
344 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
345 {
346 if (!(hook->command && (hook->id == CMD_ACCOUNT_HOOK)))
347 continue;
348
349 if (mutt_regex_match(&hook->regex, url))
350 {
351 inhook = true;
352 mutt_debug(LL_DEBUG1, "account-hook '%s' matches '%s'\n", hook->regex.pattern, url);
353 mutt_debug(LL_DEBUG5, " %s\n", hook->command);
354
355 if (parse_rc_line_cwd(hook->command, hook->source_file, pc, pe) == MUTT_CMD_ERROR)
356 {
357 mutt_error("%s", buf_string(pe->message));
358 inhook = false;
359 goto done;
360 }
361
362 inhook = false;
363 }
364 }
365
366done:
367 mod_data->current_hook_id = CMD_NONE;
369 parse_error_free(&pe);
370}
371
379{
381 struct Hook *hook = NULL;
382 struct ParseContext *pc = parse_context_new();
383 struct ParseError *pe = parse_error_new();
384
386
387 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
388 {
389 if (!(hook->command && (hook->id == CMD_TIMEOUT_HOOK)))
390 continue;
391
392 if (parse_rc_line_cwd(hook->command, hook->source_file, pc, pe) == MUTT_CMD_ERROR)
393 {
394 mutt_error("%s", buf_string(pe->message));
396
397 /* The hooks should be independent of each other, so even though this one
398 * failed, we'll carry on with the others. */
399 }
400 }
401
402 /* Delete temporary attachment files */
405
406 mod_data->current_hook_id = CMD_NONE;
408 parse_error_free(&pe);
409}
410
419{
421 struct Hook *hook = NULL;
422 struct ParseContext *pc = parse_context_new();
423 struct ParseError *pe = parse_error_new();
424
425 mod_data->current_hook_id = id;
426
427 TAILQ_FOREACH(hook, &mod_data->hooks, entries)
428 {
429 if (!(hook->command && (hook->id == id)))
430 continue;
431
432 if (parse_rc_line_cwd(hook->command, hook->source_file, pc, pe) == MUTT_CMD_ERROR)
433 {
434 mutt_error("%s", buf_string(pe->message));
436 }
437 }
438
439 mod_data->current_hook_id = CMD_NONE;
441 parse_error_free(&pe);
442}
443
452const struct Expando *mutt_idxfmt_hook(const char *name, struct Mailbox *m, struct Email *e)
453{
455 if (!mod_data->idx_fmt_hooks)
456 return NULL;
457
458 struct HookList *hl = mutt_hash_find(mod_data->idx_fmt_hooks, name);
459 if (!hl)
460 return NULL;
461
462 struct PatternCache cache = { 0 };
463 const struct Expando *exp = NULL;
464 struct Hook *hook = NULL;
465
466 TAILQ_FOREACH(hook, hl, entries)
467 {
468 struct Pattern *pat = SLIST_FIRST(hook->pattern);
469 if ((mutt_pattern_exec(pat, 0, m, e, &cache) > 0) ^ hook->regex.pat_not)
470 {
471 exp = hook->expando;
472 break;
473 }
474 }
475
476 return exp;
477}
Email Address Handling.
Email Aliases.
bool mutt_addr_is_user(const struct Address *addr)
Does the address belong to the user.
Definition alias.c:600
GUI display the mailboxes in a side panel.
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:204
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition buffer.c:182
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
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
CommandId
ID of Command.
Definition command.h:61
@ CMD_FCC_HOOK
:fcc-hook
Definition command.h:78
@ CMD_NONE
No Command.
Definition command.h:62
@ CMD_SAVE_HOOK
:save-hook
Definition command.h:107
@ CMD_TIMEOUT_HOOK
:timeout-hook
Definition command.h:124
@ CMD_ACCOUNT_HOOK
:account-hook
Definition command.h:63
@ CMD_CRYPT_HOOK
:crypt-hook
Definition command.h:75
@ CMD_FOLDER_HOOK
:folder-hook
Definition command.h:81
@ MUTT_CMD_ERROR
Error: Can't help the user.
Definition command.h:38
NeoMutt Commands.
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.
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:824
Structs that make up an email.
Parse Expando string.
#define mutt_error(...)
Definition logging2.h:94
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
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:364
User-defined Hooks.
void mutt_default_save(struct Buffer *path, struct Email *e)
Find the default save path for an email.
Definition exec.c:218
void exec_startup_shutdown_hook(enum CommandId id)
Execute any startup/shutdown hooks.
Definition exec.c:418
char * mutt_find_hook(enum CommandId id, const char *pat)
Find a matching hook.
Definition exec.c:115
void mutt_select_fcc(struct Buffer *path, struct Email *e)
Select the FCC path for an email.
Definition exec.c:256
void exec_folder_hook(const char *path, const char *desc)
Perform a folder hook.
Definition exec.c:64
const struct Expando * mutt_idxfmt_hook(const char *name, struct Mailbox *m, struct Email *e)
Get index-format-hook format string.
Definition exec.c:452
static int addr_hook(struct Buffer *path, enum CommandId id, struct Mailbox *m, struct Email *e)
Perform an address hook (get a path)
Definition exec.c:184
static void list_hook(struct ListHead *matches, const char *match, enum CommandId id)
Find hook strings matching.
Definition exec.c:298
void exec_timeout_hook(void)
Execute any timeout hooks.
Definition exec.c:378
void exec_message_hook(struct Mailbox *m, struct Email *e, enum CommandId id)
Perform a message hook.
Definition exec.c:137
void mutt_crypt_hook(struct ListHead *list, struct Address *addr)
Find crypto hooks for an Address.
Definition exec.c:319
void exec_account_hook(const char *url)
Perform an account hook.
Definition exec.c:328
Hooks private Module data.
GUI manage the main index (list of emails)
struct Mailbox * get_current_mailbox(void)
Get the current Mailbox.
Definition index.c:726
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:49
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
@ MODULE_ID_ATTACH
ModuleAttach, Attachments
Definition module_api.h:49
@ MODULE_ID_HOOKS
ModuleHooks, Hook Commands
Definition module_api.h:70
Convenience wrapper for the library headers.
bool mutt_regex_match(const struct Regex *regex, const char *str)
Shorthand to mutt_regex_capture()
Definition regex.c:614
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
#define PATH_MAX
Definition mutt.h:49
void mutt_temp_attachments_cleanup(struct ListHead *list)
Delete all temporary attachments.
void generate_save_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition muttlib.c:608
void pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition muttlib.c:428
Some miscellaneous functions.
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition mx.c:167
API for mailboxes.
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:663
Text parsing functions.
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:1151
Match patterns to emails.
void parse_context_free(struct ParseContext **pptr)
Free a ParseContext.
Definition pcontext.c:48
struct ParseContext * parse_context_new(void)
Create a new ParseContext.
Definition pcontext.c:37
void parse_error_reset(struct ParseError *pe)
Clear the contents of a ParseError.
Definition perror.c:66
void parse_error_free(struct ParseError **pptr)
Free a ParseError.
Definition perror.c:50
struct ParseError * parse_error_new(void)
Create a new ParseError.
Definition perror.c:37
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
#define TAILQ_FIRST(head)
Definition queue.h:780
#define SLIST_FIRST(head)
Definition queue.h:227
@ MUTT_FORMAT_PLAIN
Do not prepend DISP_TO, DISP_CC ...
Definition render.h:43
enum CommandResult parse_rc_line_cwd(const char *line, char *cwd, struct ParseContext *pc, struct ParseError *pe)
Parse and run a muttrc line in a relative directory.
Definition source.c:287
#define NONULL(x)
Definition string2.h:44
An email address.
Definition address.h:35
struct Buffer * mailbox
Mailbox and host address.
Definition address.h:37
Attach private Module data.
Definition module_data.h:32
struct ListHead temp_attachments
List of temporary files for displaying attachments.
Definition module_data.h:43
String manipulation buffer.
Definition buffer.h:36
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
Parsed Expando trees.
Definition expando.h:41
A list of user hooks.
Definition hook.h:33
struct PatternList * pattern
Used for fcc,save,send-hook.
Definition hook.h:38
struct Regex regex
Regular expression.
Definition hook.h:35
char * command
Filename, command or pattern to execute.
Definition hook.h:36
struct Expando * expando
Used for format hooks.
Definition hook.h:39
enum CommandId id
Hook CommandId, e.g. CMD_FOLDER_HOOK.
Definition hook.h:34
char * source_file
Used for relative-directory source.
Definition hook.h:37
Hooks private Module data.
Definition module_data.h:33
struct HashTable * idx_fmt_hooks
All Index Format hooks.
Definition module_data.h:36
struct HookList hooks
All simple hooks, e.g. CMD_FOLDER_HOOK.
Definition module_data.h:35
int current_hook_id
The ID of the Hook currently being executed.
Definition module_data.h:37
A mailbox.
Definition mailbox.h:81
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
Context for config parsing (history/backtrace)
Definition pcontext.h:34
Detailed error information from config parsing.
Definition perror.h:34
struct Buffer * message
Error message.
Definition perror.h:35
Cache commonly-used patterns.
Definition lib.h:130
A simple (non-regex) pattern.
Definition lib.h:84
char * pattern
printable version
Definition regex3.h:86
bool pat_not
do not match
Definition regex3.h:88