]> git.llucax.com Git - software/mutt-debian.git/blob - hook.c
MH dirs are now correctly parsed (Closes: 538128)
[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       regfree (rx);
176       FREE (&rx);
177       goto error;
178     }
179   }
180
181   if (ptr)
182   {
183     ptr->next = safe_calloc (1, sizeof (HOOK));
184     ptr = ptr->next;
185   }
186   else
187     Hooks = ptr = safe_calloc (1, sizeof (HOOK));
188   ptr->type = data;
189   ptr->command = command.data;
190   ptr->pattern = pat;
191   ptr->rx.pattern = pattern.data;
192   ptr->rx.rx = rx;
193   ptr->rx.not = not;
194   return 0;
195
196 error:
197   FREE (&pattern.data);
198   FREE (&command.data);
199   return (-1);
200 }
201
202 static void delete_hook (HOOK *h)
203 {
204   FREE (&h->command);
205   FREE (&h->rx.pattern);
206   if (h->rx.rx)
207   {
208     regfree (h->rx.rx);
209   }
210   mutt_pattern_free (&h->pattern);
211   FREE (&h);
212 }
213
214 /* Deletes all hooks of type ``type'', or all defined hooks if ``type'' is 0 */
215 static void delete_hooks (int type)
216 {
217   HOOK *h;
218   HOOK *prev;
219
220   while (h = Hooks, h && (type == 0 || type == h->type))
221   {
222     Hooks = h->next;
223     delete_hook (h);
224   }
225
226   prev = h; /* Unused assignment to avoid compiler warnings */
227
228   while (h)
229   {
230     if (type == h->type)
231     {
232       prev->next = h->next;
233       delete_hook (h);
234     }
235     else
236       prev = h;
237     h = prev->next;
238   }
239 }
240
241 int mutt_parse_unhook (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
242 {
243   while (MoreArgs (s))
244   {
245     mutt_extract_token (buf, s, 0);
246     if (mutt_strcmp ("*", buf->data) == 0)
247     {
248       if (current_hook_type)
249       {
250         snprintf (err->data, err->dsize,
251                   _("unhook: Can't do unhook * from within a hook."));
252         return -1;
253       }
254       delete_hooks (0);
255     }
256     else
257     {
258       int type = mutt_get_hook_type (buf->data);
259
260       if (!type)
261       {
262         snprintf (err->data, err->dsize,
263                  _("unhook: unknown hook type: %s"), buf->data);
264         return (-1);
265       }
266       if (current_hook_type == type)
267       {
268         snprintf (err->data, err->dsize,
269                   _("unhook: Can't delete a %s from within a %s."),
270                   buf->data, buf->data);
271         return -1;
272       }
273       delete_hooks (type);
274     }
275   }
276   return 0;
277 }
278
279 void mutt_folder_hook (char *path)
280 {
281   HOOK *tmp = Hooks;
282   BUFFER err, token;
283   char buf[STRING];
284
285   current_hook_type = M_FOLDERHOOK;
286   
287   err.data = buf;
288   err.dsize = sizeof (buf);
289   memset (&token, 0, sizeof (token));
290   for (; tmp; tmp = tmp->next)
291   {
292     if(!tmp->command)
293       continue;
294
295     if (tmp->type & M_FOLDERHOOK)
296     {
297       if ((regexec (tmp->rx.rx, path, 0, NULL, 0) == 0) ^ tmp->rx.not)
298       {
299         if (mutt_parse_rc_line (tmp->command, &token, &err) == -1)
300         {
301           mutt_error ("%s", err.data);
302           FREE (&token.data);
303           mutt_sleep (1);       /* pause a moment to let the user see the error */
304           current_hook_type = 0;
305           return;
306         }
307       }
308     }
309   }
310   FREE (&token.data);
311   
312   current_hook_type = 0;
313 }
314
315 char *mutt_find_hook (int type, const char *pat)
316 {
317   HOOK *tmp = Hooks;
318
319   for (; tmp; tmp = tmp->next)
320     if (tmp->type & type)
321     {
322       if (regexec (tmp->rx.rx, pat, 0, NULL, 0) == 0)
323         return (tmp->command);
324     }
325   return (NULL);
326 }
327
328 void mutt_message_hook (CONTEXT *ctx, HEADER *hdr, int type)
329 {
330   BUFFER err, token;
331   HOOK *hook;
332   char buf[STRING];
333
334   current_hook_type = type;
335   
336   err.data = buf;
337   err.dsize = sizeof (buf);
338   memset (&token, 0, sizeof (token));
339   for (hook = Hooks; hook; hook = hook->next)
340   {
341     if(!hook->command)
342       continue;
343
344     if (hook->type & type)
345       if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr) > 0) ^ hook->rx.not)
346         if (mutt_parse_rc_line (hook->command, &token, &err) != 0)
347         {
348           FREE (&token.data);
349           mutt_error ("%s", err.data);
350           mutt_sleep (1);
351           current_hook_type = 0;
352           return;
353         }
354   }
355   FREE (&token.data);
356   current_hook_type = 0;
357 }
358
359 static int
360 mutt_addr_hook (char *path, size_t pathlen, int type, CONTEXT *ctx, HEADER *hdr)
361 {
362   HOOK *hook;
363
364   /* determine if a matching hook exists */
365   for (hook = Hooks; hook; hook = hook->next)
366   {
367     if(!hook->command)
368       continue;
369
370     if (hook->type & type)
371       if ((mutt_pattern_exec (hook->pattern, 0, ctx, hdr) > 0) ^ hook->rx.not)
372       {
373         mutt_make_string (path, pathlen, hook->command, ctx, hdr);
374         return 0;
375       }
376   }
377
378   return -1;
379 }
380
381 void mutt_default_save (char *path, size_t pathlen, HEADER *hdr)
382 {
383   *path = 0;
384   if (mutt_addr_hook (path, pathlen, M_SAVEHOOK, Context, hdr) != 0)
385   {
386     char tmp[_POSIX_PATH_MAX];
387     ADDRESS *adr;
388     ENVELOPE *env = hdr->env;
389     int fromMe = mutt_addr_is_user (env->from);
390
391     if (!fromMe && env->reply_to && env->reply_to->mailbox)
392       adr = env->reply_to;
393     else if (!fromMe && env->from && env->from->mailbox)
394       adr = env->from;
395     else if (env->to && env->to->mailbox)
396       adr = env->to;
397     else if (env->cc && env->cc->mailbox)
398       adr = env->cc;
399     else
400       adr = NULL;
401     if (adr)
402     {
403       mutt_safe_path (tmp, sizeof (tmp), adr);
404       snprintf (path, pathlen, "=%s", tmp);
405     }
406   }
407 }
408
409 void mutt_select_fcc (char *path, size_t pathlen, HEADER *hdr)
410 {
411   ADDRESS *adr;
412   char buf[_POSIX_PATH_MAX];
413   ENVELOPE *env = hdr->env;
414
415   if (mutt_addr_hook (path, pathlen, M_FCCHOOK, NULL, hdr) != 0)
416   {
417     if ((option (OPTSAVENAME) || option (OPTFORCENAME)) &&
418         (env->to || env->cc || env->bcc))
419     {
420       adr = env->to ? env->to : (env->cc ? env->cc : env->bcc);
421       mutt_safe_path (buf, sizeof (buf), adr);
422       mutt_concat_path (path, NONULL(Maildir), buf, pathlen);
423       if (!option (OPTFORCENAME) && mx_access (path, W_OK) != 0)
424         strfcpy (path, NONULL (Outbox), pathlen);
425     }
426     else
427       strfcpy (path, NONULL (Outbox), pathlen);
428   }
429   mutt_pretty_mailbox (path, pathlen);
430 }
431
432 static char *_mutt_string_hook (const char *match, int hook)
433 {
434   HOOK *tmp = Hooks;
435
436   for (; tmp; tmp = tmp->next)
437   {
438     if ((tmp->type & hook) && ((match &&
439          regexec (tmp->rx.rx, match, 0, NULL, 0) == 0) ^ tmp->rx.not))
440       return (tmp->command);
441   }
442   return (NULL);
443 }
444
445 char *mutt_charset_hook (const char *chs)
446 {
447   return _mutt_string_hook (chs, M_CHARSETHOOK);
448 }
449
450 char *mutt_iconv_hook (const char *chs)
451 {
452   return _mutt_string_hook (chs, M_ICONVHOOK);
453 }
454
455 char *mutt_crypt_hook (ADDRESS *adr)
456 {
457   return _mutt_string_hook (adr->mailbox, M_CRYPTHOOK);
458 }
459
460 #ifdef USE_SOCKET
461 void mutt_account_hook (const char* url)
462 {
463   /* parsing commands with URLs in an account hook can cause a recursive
464    * call. We just skip processing if this occurs. Typically such commands
465    * belong in a folder-hook -- perhaps we should warn the user. */
466   static int inhook = 0;
467
468   HOOK* hook;
469   BUFFER token;
470   BUFFER err;
471   char buf[STRING];
472
473   if (inhook)
474     return;
475
476   err.data = buf;
477   err.dsize = sizeof (buf);
478   memset (&token, 0, sizeof (token));
479
480   for (hook = Hooks; hook; hook = hook->next)
481   {
482     if (! (hook->command && (hook->type & M_ACCOUNTHOOK)))
483       continue;
484
485     if ((regexec (hook->rx.rx, url, 0, NULL, 0) == 0) ^ hook->rx.not)
486     {
487       inhook = 1;
488
489       if (mutt_parse_rc_line (hook->command, &token, &err) == -1)
490       {
491         FREE (&token.data);
492         mutt_error ("%s", err.data);
493         mutt_sleep (1);
494
495         inhook = 0;
496         return;
497       }
498
499       inhook = 0;
500     }
501   }
502
503   FREE (&token.data);
504 }
505 #endif