]> git.llucax.com Git - software/mutt-debian.git/blob - hook.c
sidebar-{dotted,sorted}: documented the options that those two patches are introducin...
[software/mutt-debian.git] / hook.c
1 /* 
2  * Copyright (C) 1996-2002,2004,2007 Michael R. Elkins <me@mutt.org>, and others
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 #if HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include "mutt.h"
24 #include "mailbox.h"
25 #include "mutt_crypt.h"
26
27 #include <limits.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <unistd.h>
32
33 typedef struct hook
34 {
35   int type;             /* hook type */
36   REGEXP rx;            /* regular expression */
37   char *command;        /* filename, command or pattern to execute */
38   pattern_t *pattern;   /* used for fcc,save,send-hook */
39   struct hook *next;
40 } HOOK;
41
42 static HOOK *Hooks = NULL;
43
44 static int current_hook_type = 0;
45
46 int mutt_parse_hook (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
47 {
48   HOOK *ptr;
49   BUFFER command, pattern;
50   int rc, not = 0;
51   regex_t *rx = NULL;
52   pattern_t *pat = NULL;
53   char path[_POSIX_PATH_MAX];
54
55   memset (&pattern, 0, sizeof (pattern));
56   memset (&command, 0, sizeof (command));
57
58   if (*s->dptr == '!')
59   {
60     s->dptr++;
61     SKIPWS (s->dptr);
62     not = 1;
63   }
64
65   mutt_extract_token (&pattern, s, 0);
66
67   if (!MoreArgs (s))
68   {
69     strfcpy (err->data, _("too few arguments"), err->dsize);
70     goto error;
71   }
72
73   mutt_extract_token (&command, s, (data & (M_FOLDERHOOK | M_SENDHOOK | M_SEND2HOOK | M_ACCOUNTHOOK | M_REPLYHOOK)) ?  M_TOKEN_SPACE : 0);
74
75   if (!command.data)
76   {
77     strfcpy (err->data, _("too few arguments"), err->dsize);
78     goto error;
79   }
80
81   if (MoreArgs (s))
82   {
83     strfcpy (err->data, _("too many arguments"), err->dsize);
84     goto error;
85   }
86
87   if (data & (M_FOLDERHOOK | M_MBOXHOOK))
88   {
89     strfcpy (path, pattern.data, sizeof (path));
90     _mutt_expand_path (path, sizeof (path), 1);
91     FREE (&pattern.data);
92     memset (&pattern, 0, sizeof (pattern));
93     pattern.data = safe_strdup (path);
94   }
95   else if (DefaultHook && !(data & (M_CHARSETHOOK | M_ICONVHOOK | M_ACCOUNTHOOK))
96            && (!WithCrypto || !(data & M_CRYPTHOOK))
97       )
98   {
99     char tmp[HUGE_STRING];
100
101     /* At this stage remain only message-hooks, reply-hooks, send-hooks,
102      * send2-hooks, save-hooks, and fcc-hooks: All those allowing full
103      * patterns. If given a simple regexp, we expand $default_hook.
104      */
105     strfcpy (tmp, pattern.data, sizeof (tmp));
106     mutt_check_simple (tmp, sizeof (tmp), DefaultHook);
107     FREE (&pattern.data);
108     memset (&pattern, 0, sizeof (pattern));
109     pattern.data = safe_strdup (tmp);
110   }
111
112   if (data & (M_MBOXHOOK | M_SAVEHOOK | M_FCCHOOK))
113   {
114     strfcpy (path, command.data, sizeof (path));
115     mutt_expand_path (path, sizeof (path));
116     FREE (&command.data);
117     memset (&command, 0, sizeof (command));
118     command.data = safe_strdup (path);
119   }
120
121   /* check to make sure that a matching hook doesn't already exist */
122   for (ptr = Hooks; ptr; ptr = ptr->next)
123   {
124     if (ptr->type == data &&
125         ptr->rx.not == not &&
126         !mutt_strcmp (pattern.data, ptr->rx.pattern))
127     {
128       if (data & (M_FOLDERHOOK | M_SENDHOOK | M_SEND2HOOK | M_MESSAGEHOOK | M_ACCOUNTHOOK | M_REPLYHOOK))
129       {
130         /* these hooks allow multiple commands with the same
131          * pattern, so if we've already seen this pattern/command pair, just
132          * ignore it instead of creating a duplicate */
133         if (!mutt_strcmp (ptr->command, command.data))
134         {
135           FREE (&command.data);
136           FREE (&pattern.data);
137           return 0;
138         }
139       }
140       else
141       {
142         /* other hooks only allow one command per pattern, so update the
143          * entry with the new command.  this currently does not change the
144          * order of execution of the hooks, which i think is desirable since
145          * a common action to perform is to change the default (.) entry
146          * based upon some other information. */
147         FREE (&ptr->command);
148         ptr->command = command.data;
149         FREE (&pattern.data);
150         return 0;
151       }
152     }
153     if (!ptr->next)
154       break;
155   }
156
157   if (data & (M_SENDHOOK | M_SEND2HOOK | M_SAVEHOOK | M_FCCHOOK | M_MESSAGEHOOK | M_REPLYHOOK))
158   {
159     if ((pat = mutt_pattern_comp (pattern.data,
160            (data & (M_SENDHOOK | M_SEND2HOOK | M_FCCHOOK)) ? 0 : M_FULL_MSG,
161                                   err)) == NULL)
162       goto error;
163   }
164   else
165   {
166     /* Hooks not allowing full patterns: Check syntax of regexp */
167     rx = safe_malloc (sizeof (regex_t));
168 #ifdef M_CRYPTHOOK
169     if ((rc = REGCOMP (rx, NONULL(pattern.data), ((data & (M_CRYPTHOOK|M_CHARSETHOOK|M_ICONVHOOK)) ? REG_ICASE : 0))) != 0)
170 #else
171     if ((rc = REGCOMP (rx, NONULL(pattern.data), (data & (M_CHARSETHOOK|M_ICONVHOOK)) ? REG_ICASE : 0)) != 0)
172 #endif /* M_CRYPTHOOK */
173     {
174       regerror (rc, rx, err->data, err->dsize);
175       FREE (&rx);
176       goto error;
177     }
178   }
179
180   if (ptr)
181   {
182     ptr->next = safe_calloc (1, sizeof (HOOK));
183     ptr = ptr->next;
184   }
185   else
186     Hooks = ptr = safe_calloc (1, sizeof (HOOK));
187   ptr->type = data;
188   ptr->command = command.data;
189   ptr->pattern = pat;
190   ptr->rx.pattern = pattern.data;
191   ptr->rx.rx = rx;
192   ptr->rx.not = not;
193   return 0;
194
195 error:
196   FREE (&pattern.data);
197   FREE (&command.data);
198   return (-1);
199 }
200
201 static void delete_hook (HOOK *h)
202 {
203   FREE (&h->command);
204   FREE (&h->rx.pattern);
205   if (h->rx.rx)
206   {
207     regfree (h->rx.rx);
208   }
209   mutt_pattern_free (&h->pattern);
210   FREE (&h);
211 }
212
213 /* Deletes all hooks of type ``type'', or all defined hooks if ``type'' is 0 */
214 static void delete_hooks (int type)
215 {
216   HOOK *h;
217   HOOK *prev;
218
219   while (h = Hooks, h && (type == 0 || type == h->type))
220   {
221     Hooks = h->next;
222     delete_hook (h);
223   }
224
225   prev = h; /* Unused assignment to avoid compiler warnings */
226
227   while (h)
228   {
229     if (type == h->type)
230     {
231       prev->next = h->next;
232       delete_hook (h);
233     }
234     else
235       prev = h;
236     h = prev->next;
237   }
238 }
239
240 int mutt_parse_unhook (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
241 {
242   while (MoreArgs (s))
243   {
244     mutt_extract_token (buf, s, 0);
245     if (mutt_strcmp ("*", buf->data) == 0)
246     {
247       if (current_hook_type)
248       {
249         snprintf (err->data, err->dsize,
250                   _("unhook: Can't do unhook * from within a hook."));
251         return -1;
252       }
253       delete_hooks (0);
254     }
255     else
256     {
257       int type = mutt_get_hook_type (buf->data);
258
259       if (!type)
260       {
261         snprintf (err->data, err->dsize,
262                  _("unhook: unknown hook type: %s"), buf->data);
263         return (-1);
264       }
265       if (current_hook_type == type)
266       {
267         snprintf (err->data, err->dsize,
268                   _("unhook: Can't delete a %s from within a %s."),
269                   buf->data, buf->data);
270         return -1;
271       }
272       delete_hooks (type);
273     }
274   }
275   return 0;
276 }
277
278 void mutt_folder_hook (char *path)
279 {
280   HOOK *tmp = Hooks;
281   BUFFER err, token;
282   char buf[STRING];
283
284   current_hook_type = M_FOLDERHOOK;
285   
286   err.data = buf;
287   err.dsize = sizeof (buf);
288   memset (&token, 0, sizeof (token));
289   for (; tmp; tmp = tmp->next)
290   {
291     if(!tmp->command)
292       continue;
293
294     if (tmp->type & M_FOLDERHOOK)
295     {
296       if ((regexec (tmp->rx.rx, path, 0, NULL, 0) == 0) ^ tmp->rx.not)
297       {
298         if (mutt_parse_rc_line (tmp->command, &token, &err) == -1)
299         {
300           mutt_error ("%s", err.data);
301           FREE (&token.data);
302           mutt_sleep (1);       /* pause a moment to let the user see the error */
303           current_hook_type = 0;
304           return;
305         }
306       }
307     }
308   }
309   FREE (&token.data);
310   
311   current_hook_type = 0;
312 }
313
314 char *mutt_find_hook (int type, const char *pat)
315 {
316   HOOK *tmp = Hooks;
317
318   for (; tmp; tmp = tmp->next)
319     if (tmp->type & type)
320     {
321       if (regexec (tmp->rx.rx, pat, 0, NULL, 0) == 0)
322         return (tmp->command);
323     }
324   return (NULL);
325 }
326
327 void mutt_message_hook (CONTEXT *ctx, HEADER *hdr, int type)
328 {
329   BUFFER err, token;
330   HOOK *hook;
331   char buf[STRING];
332
333   current_hook_type = type;
334   
335   err.data = buf;
336   err.dsize = sizeof (buf);
337   memset (&token, 0, sizeof (token));
338   for (hook = Hooks; hook; hook = hook->next)
339   {
340     if(!hook->command)
341       continue;
342
343     if (hook->type & type)
344       if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr) > 0) ^ hook->rx.not)
345         if (mutt_parse_rc_line (hook->command, &token, &err) != 0)
346         {
347           FREE (&token.data);
348           mutt_error ("%s", err.data);
349           mutt_sleep (1);
350           current_hook_type = 0;
351           return;
352         }
353   }
354   FREE (&token.data);
355   current_hook_type = 0;
356 }
357
358 static int
359 mutt_addr_hook (char *path, size_t pathlen, int type, CONTEXT *ctx, HEADER *hdr)
360 {
361   HOOK *hook;
362
363   /* determine if a matching hook exists */
364   for (hook = Hooks; hook; hook = hook->next)
365   {
366     if(!hook->command)
367       continue;
368
369     if (hook->type & type)
370       if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr) > 0) ^ hook->rx.not)
371       {
372         mutt_make_string (path, pathlen, hook->command, ctx, hdr);
373         return 0;
374       }
375   }
376
377   return -1;
378 }
379
380 void mutt_default_save (char *path, size_t pathlen, HEADER *hdr)
381 {
382   *path = 0;
383   if (mutt_addr_hook (path, pathlen, M_SAVEHOOK, Context, hdr) != 0)
384   {
385     char tmp[_POSIX_PATH_MAX];
386     ADDRESS *adr;
387     ENVELOPE *env = hdr->env;
388     int fromMe = mutt_addr_is_user (env->from);
389
390     if (!fromMe && env->reply_to && env->reply_to->mailbox)
391       adr = env->reply_to;
392     else if (!fromMe && env->from && env->from->mailbox)
393       adr = env->from;
394     else if (env->to && env->to->mailbox)
395       adr = env->to;
396     else if (env->cc && env->cc->mailbox)
397       adr = env->cc;
398     else
399       adr = NULL;
400     if (adr)
401     {
402       mutt_safe_path (tmp, sizeof (tmp), adr);
403       snprintf (path, pathlen, "=%s", tmp);
404     }
405   }
406 }
407
408 void mutt_select_fcc (char *path, size_t pathlen, HEADER *hdr)
409 {
410   ADDRESS *adr;
411   char buf[_POSIX_PATH_MAX];
412   ENVELOPE *env = hdr->env;
413
414   if (mutt_addr_hook (path, pathlen, M_FCCHOOK, NULL, hdr) != 0)
415   {
416     if ((option (OPTSAVENAME) || option (OPTFORCENAME)) &&
417         (env->to || env->cc || env->bcc))
418     {
419       adr = env->to ? env->to : (env->cc ? env->cc : env->bcc);
420       mutt_safe_path (buf, sizeof (buf), adr);
421       mutt_concat_path (path, NONULL(Maildir), buf, pathlen);
422       if (!option (OPTFORCENAME) && mx_access (path, W_OK) != 0)
423         strfcpy (path, NONULL (Outbox), pathlen);
424     }
425     else
426       strfcpy (path, NONULL (Outbox), pathlen);
427   }
428   mutt_pretty_mailbox (path, pathlen);
429 }
430
431 static char *_mutt_string_hook (const char *match, int hook)
432 {
433   HOOK *tmp = Hooks;
434
435   for (; tmp; tmp = tmp->next)
436   {
437     if ((tmp->type & hook) && ((match &&
438          regexec (tmp->rx.rx, match, 0, NULL, 0) == 0) ^ tmp->rx.not))
439       return (tmp->command);
440   }
441   return (NULL);
442 }
443
444 char *mutt_charset_hook (const char *chs)
445 {
446   return _mutt_string_hook (chs, M_CHARSETHOOK);
447 }
448
449 char *mutt_iconv_hook (const char *chs)
450 {
451   return _mutt_string_hook (chs, M_ICONVHOOK);
452 }
453
454 char *mutt_crypt_hook (ADDRESS *adr)
455 {
456   return _mutt_string_hook (adr->mailbox, M_CRYPTHOOK);
457 }
458
459 #ifdef USE_SOCKET
460 void mutt_account_hook (const char* url)
461 {
462   /* parsing commands with URLs in an account hook can cause a recursive
463    * call. We just skip processing if this occurs. Typically such commands
464    * belong in a folder-hook -- perhaps we should warn the user. */
465   static int inhook = 0;
466
467   HOOK* hook;
468   BUFFER token;
469   BUFFER err;
470   char buf[STRING];
471
472   if (inhook)
473     return;
474
475   err.data = buf;
476   err.dsize = sizeof (buf);
477   memset (&token, 0, sizeof (token));
478
479   for (hook = Hooks; hook; hook = hook->next)
480   {
481     if (! (hook->command && (hook->type & M_ACCOUNTHOOK)))
482       continue;
483
484     if ((regexec (hook->rx.rx, url, 0, NULL, 0) == 0) ^ hook->rx.not)
485     {
486       inhook = 1;
487
488       if (mutt_parse_rc_line (hook->command, &token, &err) == -1)
489       {
490         FREE (&token.data);
491         mutt_error ("%s", err.data);
492         mutt_sleep (1);
493
494         inhook = 0;
495         return;
496       }
497
498       inhook = 0;
499     }
500   }
501
502   FREE (&token.data);
503 }
504 #endif