NeoMutt  2025-12-11-435-g4ac674
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 "muttlib.h"
56#include "mx.h"
57#include "parse.h"
58
64void exec_folder_hook(const char *path, const char *desc)
65{
66 if (!path && !desc)
67 return;
68
69 struct Hook *hook = NULL;
70 struct ParseContext *pc = parse_context_new();
71 struct ParseError *pe = parse_error_new();
72
74
75 TAILQ_FOREACH(hook, &Hooks, entries)
76 {
77 if (!hook->command)
78 continue;
79
80 if (!(hook->id == CMD_FOLDER_HOOK))
81 continue;
82
83 const char *match = NULL;
84 if (mutt_regex_match(&hook->regex, path))
85 match = path;
86 else if (mutt_regex_match(&hook->regex, desc))
87 match = desc;
88
89 if (match)
90 {
91 mutt_debug(LL_DEBUG1, "folder-hook '%s' matches '%s'\n", hook->regex.pattern, match);
92 mutt_debug(LL_DEBUG5, " %s\n", hook->command);
93 if (parse_rc_line_cwd(hook->command, hook->source_file, pc, pe) == MUTT_CMD_ERROR)
94 {
95 mutt_error("%s", buf_string(pe->message));
96 break;
97 }
98 }
99 }
100
103 parse_error_free(&pe);
104}
105
114char *mutt_find_hook(enum CommandId id, const char *pat)
115{
116 struct Hook *hook = NULL;
117
118 TAILQ_FOREACH(hook, &Hooks, entries)
119 {
120 if (hook->id == id)
121 {
122 if (mutt_regex_match(&hook->regex, pat))
123 return hook->command;
124 }
125 }
126 return NULL;
127}
128
135void exec_message_hook(struct Mailbox *m, struct Email *e, enum CommandId id)
136{
137 struct Hook *hook = NULL;
138 struct PatternCache cache = { 0 };
139 struct ParseContext *pc = parse_context_new();
140 struct ParseError *pe = parse_error_new();
141
142 CurrentHookId = id;
143
144 TAILQ_FOREACH(hook, &Hooks, entries)
145 {
146 if (!hook->command)
147 continue;
148
149 if (hook->id == id)
150 {
151 if ((mutt_pattern_exec(SLIST_FIRST(hook->pattern), 0, m, e, &cache) > 0) ^
152 hook->regex.pat_not)
153 {
154 if (parse_rc_line_cwd(hook->command, hook->source_file, pc, pe) == MUTT_CMD_ERROR)
155 {
156 mutt_error("%s", buf_string(pe->message));
157 goto done;
158 }
159 /* Executing arbitrary commands could affect the pattern results,
160 * so the cache has to be wiped */
161 memset(&cache, 0, sizeof(cache));
162 }
163 }
164 }
165
166done:
169 parse_error_free(&pe);
170}
171
181static int addr_hook(struct Buffer *path, enum CommandId id, struct Mailbox *m,
182 struct Email *e)
183{
184 struct Hook *hook = NULL;
185 struct PatternCache cache = { 0 };
186
187 /* determine if a matching hook exists */
188 TAILQ_FOREACH(hook, &Hooks, entries)
189 {
190 if (!hook->command)
191 continue;
192
193 if (hook->id == id)
194 {
195 if ((mutt_pattern_exec(SLIST_FIRST(hook->pattern), 0, m, e, &cache) > 0) ^
196 hook->regex.pat_not)
197 {
198 buf_alloc(path, PATH_MAX);
199 mutt_make_string(path, -1, hook->expando, m, -1, e, MUTT_FORMAT_PLAIN, NULL);
200 buf_fix_dptr(path);
201 return 0;
202 }
203 }
204 }
205
206 return -1;
207}
208
214void mutt_default_save(struct Buffer *path, struct Email *e)
215{
216 struct Mailbox *m_cur = get_current_mailbox();
217 if (addr_hook(path, CMD_SAVE_HOOK, m_cur, e) == 0)
218 return;
219
220 struct Envelope *env = e->env;
221 const struct Address *from = TAILQ_FIRST(&env->from);
222 const struct Address *reply_to = TAILQ_FIRST(&env->reply_to);
223 const struct Address *to = TAILQ_FIRST(&env->to);
224 const struct Address *cc = TAILQ_FIRST(&env->cc);
225 const struct Address *addr = NULL;
226 bool from_me = mutt_addr_is_user(from);
227
228 if (!from_me && reply_to && reply_to->mailbox)
229 addr = reply_to;
230 else if (!from_me && from && from->mailbox)
231 addr = from;
232 else if (to && to->mailbox)
233 addr = to;
234 else if (cc && cc->mailbox)
235 addr = cc;
236 else
237 addr = NULL;
238 if (addr)
239 {
240 struct Buffer *tmp = buf_pool_get();
241 generate_save_path(tmp, addr);
242 buf_add_printf(path, "=%s", buf_string(tmp));
243 buf_pool_release(&tmp);
244 }
245}
246
252void mutt_select_fcc(struct Buffer *path, struct Email *e)
253{
254 buf_alloc(path, PATH_MAX);
255
256 if (addr_hook(path, CMD_FCC_HOOK, NULL, e) != 0)
257 {
258 const struct Address *to = TAILQ_FIRST(&e->env->to);
259 const struct Address *cc = TAILQ_FIRST(&e->env->cc);
260 const struct Address *bcc = TAILQ_FIRST(&e->env->bcc);
261 const bool c_save_name = cs_subset_bool(NeoMutt->sub, "save_name");
262 const bool c_force_name = cs_subset_bool(NeoMutt->sub, "force_name");
263 const char *const c_record = cs_subset_string(NeoMutt->sub, "record");
264 if ((c_save_name || c_force_name) && (to || cc || bcc))
265 {
266 const struct Address *addr = to ? to : (cc ? cc : bcc);
267 struct Buffer *buf = buf_pool_get();
268 generate_save_path(buf, addr);
269 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
270 buf_concat_path(path, NONULL(c_folder), buf_string(buf));
271 buf_pool_release(&buf);
272 if (!c_force_name && (mx_access(buf_string(path), W_OK) != 0))
273 buf_strcpy(path, c_record);
274 }
275 else
276 {
277 buf_strcpy(path, c_record);
278 }
279 }
280 else
281 {
282 buf_fix_dptr(path);
283 }
284
285 pretty_mailbox(path);
286}
287
294static void list_hook(struct ListHead *matches, const char *match, enum CommandId id)
295{
296 struct Hook *tmp = NULL;
297
298 TAILQ_FOREACH(tmp, &Hooks, entries)
299 {
300 if ((tmp->id == id) && mutt_regex_match(&tmp->regex, match))
301 {
303 }
304 }
305}
306
314void mutt_crypt_hook(struct ListHead *list, struct Address *addr)
315{
317}
318
323void exec_account_hook(const char *url)
324{
325 /* parsing commands with URLs in an account hook can cause a recursive
326 * call. We just skip processing if this occurs. Typically such commands
327 * belong in a folder-hook -- perhaps we should warn the user. */
328 static bool inhook = false;
329 if (inhook)
330 return;
331
332 struct Hook *hook = NULL;
333 struct ParseContext *pc = parse_context_new();
334 struct ParseError *pe = parse_error_new();
335
337
338 TAILQ_FOREACH(hook, &Hooks, entries)
339 {
340 if (!(hook->command && (hook->id == CMD_ACCOUNT_HOOK)))
341 continue;
342
343 if (mutt_regex_match(&hook->regex, url))
344 {
345 inhook = true;
346 mutt_debug(LL_DEBUG1, "account-hook '%s' matches '%s'\n", hook->regex.pattern, url);
347 mutt_debug(LL_DEBUG5, " %s\n", hook->command);
348
349 if (parse_rc_line_cwd(hook->command, hook->source_file, pc, pe) == MUTT_CMD_ERROR)
350 {
351 mutt_error("%s", buf_string(pe->message));
352 inhook = false;
353 goto done;
354 }
355
356 inhook = false;
357 }
358 }
359
360done:
363 parse_error_free(&pe);
364}
365
373{
374 struct Hook *hook = NULL;
375 struct ParseContext *pc = parse_context_new();
376 struct ParseError *pe = parse_error_new();
377
379
380 TAILQ_FOREACH(hook, &Hooks, entries)
381 {
382 if (!(hook->command && (hook->id == CMD_TIMEOUT_HOOK)))
383 continue;
384
385 if (parse_rc_line_cwd(hook->command, hook->source_file, pc, pe) == MUTT_CMD_ERROR)
386 {
387 mutt_error("%s", buf_string(pe->message));
389
390 /* The hooks should be independent of each other, so even though this one
391 * failed, we'll carry on with the others. */
392 }
393 }
394
395 /* Delete temporary attachment files */
397
400 parse_error_free(&pe);
401}
402
411{
412 struct Hook *hook = NULL;
413 struct ParseContext *pc = parse_context_new();
414 struct ParseError *pe = parse_error_new();
415
416 CurrentHookId = id;
417
418 TAILQ_FOREACH(hook, &Hooks, entries)
419 {
420 if (!(hook->command && (hook->id == id)))
421 continue;
422
423 if (parse_rc_line_cwd(hook->command, hook->source_file, pc, pe) == MUTT_CMD_ERROR)
424 {
425 mutt_error("%s", buf_string(pe->message));
427 }
428 }
429
432 parse_error_free(&pe);
433}
434
443const struct Expando *mutt_idxfmt_hook(const char *name, struct Mailbox *m, struct Email *e)
444{
445 if (!IdxFmtHooks)
446 return NULL;
447
448 struct HookList *hl = mutt_hash_find(IdxFmtHooks, name);
449 if (!hl)
450 return NULL;
451
452 struct PatternCache cache = { 0 };
453 const struct Expando *exp = NULL;
454 struct Hook *hook = NULL;
455
456 TAILQ_FOREACH(hook, hl, entries)
457 {
458 struct Pattern *pat = SLIST_FIRST(hook->pattern);
459 if ((mutt_pattern_exec(pat, 0, m, e, &cache) > 0) ^ hook->regex.pat_not)
460 {
461 exp = hook->expando;
462 break;
463 }
464 }
465
466 return exp;
467}
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:58
@ CMD_FCC_HOOK
:fcc-hook
Definition command.h:75
@ CMD_NONE
No Command.
Definition command.h:59
@ CMD_SAVE_HOOK
:save-hook
Definition command.h:104
@ CMD_TIMEOUT_HOOK
:timeout-hook
Definition command.h:121
@ CMD_ACCOUNT_HOOK
:account-hook
Definition command.h:60
@ CMD_CRYPT_HOOK
:crypt-hook
Definition command.h:72
@ CMD_FOLDER_HOOK
:folder-hook
Definition command.h:78
@ 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:802
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:214
void exec_startup_shutdown_hook(enum CommandId id)
Execute any startup/shutdown hooks.
Definition exec.c:410
char * mutt_find_hook(enum CommandId id, const char *pat)
Find a matching hook.
Definition exec.c:114
void mutt_select_fcc(struct Buffer *path, struct Email *e)
Select the FCC path for an email.
Definition exec.c:252
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:443
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:181
static void list_hook(struct ListHead *matches, const char *match, enum CommandId id)
Find hook strings matching.
Definition exec.c:294
void exec_timeout_hook(void)
Execute any timeout hooks.
Definition exec.c:372
void exec_message_hook(struct Mailbox *m, struct Email *e, enum CommandId id)
Perform a message hook.
Definition exec.c:135
void mutt_crypt_hook(struct ListHead *list, struct Address *addr)
Find crypto hooks for an Address.
Definition exec.c:314
void exec_account_hook(const char *url)
Perform an account hook.
Definition exec.c:323
enum CommandId CurrentHookId
The ID of the Hook currently being executed, e.g. CMD_SAVE_HOOK.
Definition parse.c:55
struct HashTable * IdxFmtHooks
All Index Format hooks.
Definition parse.c:52
struct HookList Hooks
All simple hooks, e.g. CMD_FOLDER_HOOK.
Definition parse.c:49
Parse user-defined Hooks.
GUI manage the main index (list of emails)
struct Mailbox * get_current_mailbox(void)
Get the current Mailbox.
Definition index.c:721
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition list.c:65
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:49
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
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(void)
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:607
void pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition muttlib.c:427
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.
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:1146
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
#define MUTT_FORMAT_PLAIN
Do not prepend DISP_TO, DISP_CC ...
Definition render.h:39
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
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
A mailbox.
Definition mailbox.h:78
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:118
A simple (non-regex) pattern.
Definition lib.h:78
char * pattern
printable version
Definition regex3.h:86
bool pat_not
do not match
Definition regex3.h:88