]> git.llucax.com Git - software/mutt-debian.git/blob - alias.c
Move Mutt with NNTP support to mutt-nntp package
[software/mutt-debian.git] / alias.c
1 /*
2  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
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 "mutt_regex.h"
25 #include "mutt_curses.h"
26 #include "mutt_idna.h"
27
28 #include <string.h>
29 #include <ctype.h>
30
31 ADDRESS *mutt_lookup_alias (const char *s)
32 {
33   ALIAS *t = Aliases;
34
35   for (; t; t = t->next)
36     if (!mutt_strcasecmp (s, t->name))
37       return (t->addr);
38   return (NULL);   /* no such alias */
39 }
40
41 static ADDRESS *mutt_expand_aliases_r (ADDRESS *a, LIST **expn)
42 {
43   ADDRESS *head = NULL, *last = NULL, *t, *w;
44   LIST *u;
45   char i;
46   const char *fqdn;
47   
48   while (a)
49   {
50     if (!a->group && !a->personal && a->mailbox && strchr (a->mailbox, '@') == NULL)
51     {
52       t = mutt_lookup_alias (a->mailbox);
53
54       if (t)
55       { 
56         i = 0;
57         for (u = *expn; u; u = u->next)
58         {
59           if (mutt_strcmp (a->mailbox, u->data) == 0) /* alias already found */
60           {
61             dprint (1, (debugfile, "mutt_expand_aliases_r(): loop in alias found for '%s'\n", a->mailbox));
62             i = 1;
63             break;
64           }
65         }
66
67         if (!i)
68         {
69           u = safe_malloc (sizeof (LIST));
70           u->data = safe_strdup (a->mailbox);
71           u->next = *expn;
72           *expn = u;
73           w = rfc822_cpy_adr (t, 0);
74           w = mutt_expand_aliases_r (w, expn);
75           if (head)
76             last->next = w;
77           else
78             head = last = w;
79           while (last && last->next)
80             last = last->next;
81         }
82         t = a;
83         a = a->next;
84         t->next = NULL;
85         rfc822_free_address (&t);
86         continue;
87       }
88       else
89       {
90         struct passwd *pw = getpwnam (a->mailbox);
91
92         if (pw)
93         {
94           char namebuf[STRING];
95           
96           mutt_gecos_name (namebuf, sizeof (namebuf), pw);
97           mutt_str_replace (&a->personal, namebuf);
98           
99 #ifdef EXACT_ADDRESS
100           FREE (&a->val);
101 #endif
102         }
103       }
104     }
105
106     if (head)
107     {
108       last->next = a;
109       last = last->next;
110     }
111     else
112       head = last = a;
113     a = a->next;
114     last->next = NULL;
115   }
116
117   if (option (OPTUSEDOMAIN) && (fqdn = mutt_fqdn(1)))
118   {
119     /* now qualify all local addresses */
120     rfc822_qualify (head, fqdn);
121   }
122
123   return (head);
124 }
125
126 ADDRESS *mutt_expand_aliases (ADDRESS *a)
127 {
128   ADDRESS *t;
129   LIST *expn = NULL; /* previously expanded aliases to avoid loops */
130
131   t = mutt_expand_aliases_r (a, &expn);
132   mutt_free_list (&expn);
133   return (mutt_remove_duplicates (t));
134 }
135
136 void mutt_expand_aliases_env (ENVELOPE *env)
137 {
138   env->from = mutt_expand_aliases (env->from);
139   env->to = mutt_expand_aliases (env->to);
140   env->cc = mutt_expand_aliases (env->cc);
141   env->bcc = mutt_expand_aliases (env->bcc);
142   env->reply_to = mutt_expand_aliases (env->reply_to);
143   env->mail_followup_to = mutt_expand_aliases (env->mail_followup_to);
144 }
145
146
147 /* 
148  * if someone has an address like
149  *      From: Michael `/bin/rm -f ~` Elkins <me@mutt.org>
150  * and the user creates an alias for this, Mutt could wind up executing
151  * the backticks because it writes aliases like
152  *      alias me Michael `/bin/rm -f ~` Elkins <me@mutt.org>
153  * To avoid this problem, use a backslash (\) to quote any backticks.  We also
154  * need to quote backslashes as well, since you could defeat the above by
155  * doing
156  *      From: Michael \`/bin/rm -f ~\` Elkins <me@mutt.org>
157  * since that would get aliased as
158  *      alias me Michael \\`/bin/rm -f ~\\` Elkins <me@mutt.org>
159  * which still gets evaluated because the double backslash is not a quote.
160  * 
161  * Additionally, we need to quote ' and " characters - otherwise, mutt will
162  * interpret them on the wrong parsing step.
163  * 
164  * $ wants to be quoted since it may indicate the start of an environment
165  * variable.
166  */
167
168 static void write_safe_address (FILE *fp, char *s)
169 {
170   while (*s)
171   {
172     if (*s == '\\' || *s == '`' || *s == '\'' || *s == '"'
173         || *s == '$')
174       fputc ('\\', fp);
175     fputc (*s, fp);
176     s++;
177   }
178 }
179
180 ADDRESS *mutt_get_address (ENVELOPE *env, char **pfxp)
181 {
182   ADDRESS *adr;
183   char *pfx = NULL;
184
185   if (mutt_addr_is_user (env->from))
186   {
187     if (env->to && !mutt_is_mail_list (env->to))
188     {
189       pfx = "To";
190       adr = env->to;
191     }
192     else
193     {
194       pfx = "Cc";
195       adr = env->cc;
196     }
197   }
198   else if (env->reply_to && !mutt_is_mail_list (env->reply_to))
199   {
200     pfx = "Reply-To";
201     adr = env->reply_to;
202   }
203   else
204   {
205     adr = env->from;
206     pfx = "From";
207   }
208
209   if (pfxp) *pfxp = pfx;
210
211   return adr;
212 }
213
214 static void recode_buf (char *buf, size_t buflen)
215 {
216   char *s;
217
218   if (!ConfigCharset || !*ConfigCharset || !Charset)
219     return;
220   s = safe_strdup (buf);
221   if (!s)
222     return;
223   if (mutt_convert_string (&s, Charset, ConfigCharset, 0) == 0)
224     strfcpy (buf, s, buflen);
225   FREE(&s);
226 }
227
228 void mutt_create_alias (ENVELOPE *cur, ADDRESS *iadr)
229 {
230   ALIAS *new, *t;
231   char buf[LONG_STRING], tmp[LONG_STRING], prompt[SHORT_STRING], *pc;
232   char *err = NULL;
233   char fixed[LONG_STRING];
234   FILE *rc;
235   ADDRESS *adr = NULL;
236
237   if (cur)
238   {
239     adr = mutt_get_address (cur, NULL);
240   }
241   else if (iadr)
242   {
243     adr = iadr;
244   }
245
246   if (adr && adr->mailbox)
247   {
248     strfcpy (tmp, adr->mailbox, sizeof (tmp));
249     if ((pc = strchr (tmp, '@')))
250       *pc = 0;
251   }
252   else
253     tmp[0] = '\0';
254
255   /* Don't suggest a bad alias name in the event of a strange local part. */
256   mutt_check_alias_name (tmp, buf, sizeof (buf));
257   
258 retry_name:
259   /* add a new alias */
260   if (mutt_get_field (_("Alias as: "), buf, sizeof (buf), 0) != 0 || !buf[0])
261     return;
262
263   /* check to see if the user already has an alias defined */
264   if (mutt_lookup_alias (buf))
265   {
266     mutt_error _("You already have an alias defined with that name!");
267     return;
268   }
269   
270   if (mutt_check_alias_name (buf, fixed, sizeof (fixed)))
271   {
272     switch (mutt_yesorno (_("Warning: This alias name may not work.  Fix it?"), M_YES))
273     {
274       case M_YES:
275         strfcpy (buf, fixed, sizeof (buf));
276         goto retry_name;
277       case -1: 
278         return;
279     }
280   }
281   
282   new       = safe_calloc (1, sizeof (ALIAS));
283   new->self = new;
284   new->name = safe_strdup (buf);
285
286   mutt_addrlist_to_local (adr);
287   
288   if (adr)
289     strfcpy (buf, adr->mailbox, sizeof (buf));
290   else
291     buf[0] = 0;
292
293   mutt_addrlist_to_idna (adr, NULL);
294   
295   do
296   {
297     if (mutt_get_field (_("Address: "), buf, sizeof (buf), 0) != 0 || !buf[0])
298     {
299       mutt_free_alias (&new);
300       return;
301     }
302     
303     if((new->addr = rfc822_parse_adrlist (new->addr, buf)) == NULL)
304       BEEP ();
305     if (mutt_addrlist_to_idna (new->addr, &err))
306     {
307       mutt_error (_("Error: '%s' is a bad IDN."), err);
308       mutt_sleep (2);
309       continue;
310     }
311   }
312   while(new->addr == NULL);
313   
314   if (adr && adr->personal && !mutt_is_mail_list (adr))
315     strfcpy (buf, adr->personal, sizeof (buf));
316   else
317     buf[0] = 0;
318
319   if (mutt_get_field (_("Personal name: "), buf, sizeof (buf), 0) != 0)
320   {
321     mutt_free_alias (&new);
322     return;
323   }
324   new->addr->personal = safe_strdup (buf);
325
326   buf[0] = 0;
327   rfc822_write_address (buf, sizeof (buf), new->addr, 1);
328   snprintf (prompt, sizeof (prompt), _("[%s = %s] Accept?"), new->name, buf);
329   if (mutt_yesorno (prompt, M_YES) != M_YES)
330   {
331     mutt_free_alias (&new);
332     return;
333   }
334
335   mutt_alias_add_reverse (new);
336   
337   if ((t = Aliases))
338   {
339     while (t->next)
340       t = t->next;
341     t->next = new;
342   }
343   else
344     Aliases = new;
345
346   strfcpy (buf, NONULL (AliasFile), sizeof (buf));
347   if (mutt_get_field (_("Save to file: "), buf, sizeof (buf), M_FILE) != 0)
348     return;
349   mutt_expand_path (buf, sizeof (buf));
350   if ((rc = fopen (buf, "a+")))
351   {
352     /* terminate existing file with \n if necessary */
353     if (fseek (rc, 0, SEEK_END))
354       goto fseek_err;
355     if (ftell(rc) > 0)
356     {
357       if (fseek (rc, -1, SEEK_CUR) < 0)
358         goto fseek_err;
359       if (fread(buf, 1, 1, rc) != 1)
360       {
361         mutt_perror (_("Error reading alias file"));
362         safe_fclose (&rc);
363         return;
364       }
365       if (fseek (rc, 0, SEEK_END) < 0)
366         goto fseek_err;
367       if (buf[0] != '\n')
368         fputc ('\n', rc);
369     }
370
371     if (mutt_check_alias_name (new->name, NULL, 0))
372       mutt_quote_filename (buf, sizeof (buf), new->name);
373     else
374       strfcpy (buf, new->name, sizeof (buf));
375     recode_buf (buf, sizeof (buf));
376     fprintf (rc, "alias %s ", buf);
377     buf[0] = 0;
378     rfc822_write_address (buf, sizeof (buf), new->addr, 0);
379     recode_buf (buf, sizeof (buf));
380     write_safe_address (rc, buf);
381     fputc ('\n', rc);
382     safe_fclose (&rc);
383     mutt_message _("Alias added.");
384   }
385   else
386     mutt_perror (buf);
387
388   return;
389   
390   fseek_err:
391   mutt_perror (_("Error seeking in alias file"));
392   safe_fclose (&rc);
393   return;
394 }
395
396 /* 
397  * Sanity-check an alias name:  Only characters which are non-special to both
398  * the RFC 822 and the mutt configuration parser are permitted.
399  */
400
401 int mutt_check_alias_name (const char *s, char *dest, size_t destlen)
402 {
403   wchar_t wc;
404   mbstate_t mb;
405   size_t l;
406   int rv = 0, bad = 0, dry = !dest || !destlen;
407
408   memset (&mb, 0, sizeof (mbstate_t));
409
410   if (!dry)
411     destlen--;
412   for (; s && *s && (dry || destlen) &&
413        (l = mbrtowc (&wc, s, MB_CUR_MAX, &mb)) != 0;
414        s += l, destlen -= l)
415   {
416     bad = l == (size_t)(-1) || l == (size_t)(-2); /* conversion error */
417     bad = bad || (!dry && l > destlen);         /* too few room for mb char */
418     if (l == 1)
419       bad = bad || (strchr ("-_+=.", *s) == NULL && !iswalnum (wc));
420     else
421       bad = bad || !iswalnum (wc);
422     if (bad)
423     {
424       if (dry)
425         return -1;
426       *dest++ = '_';
427       rv = -1;
428     }
429     else if (!dry)
430     {
431       memcpy (dest, s, l);
432       dest += l;
433     }
434   }
435   if (!dry)
436     *dest = 0;
437   return rv;
438 }
439
440 /*
441  * This routine looks to see if the user has an alias defined for the given
442  * address.
443  */
444 ADDRESS *alias_reverse_lookup (ADDRESS *a)
445 {
446   if (!a || !a->mailbox)
447       return NULL;
448   
449   return hash_find (ReverseAlias, a->mailbox);
450 }
451
452 void mutt_alias_add_reverse (ALIAS *t)
453 {
454   ADDRESS *ap;
455   if (!t)
456     return;
457   
458   for (ap = t->addr; ap; ap = ap->next)
459   {
460     if (!ap->group && ap->mailbox)
461       hash_insert (ReverseAlias, ap->mailbox, ap, 1);
462   }
463 }
464
465 void mutt_alias_delete_reverse (ALIAS *t)
466 {
467   ADDRESS *ap;
468   if (!t)
469     return;
470   
471   for (ap = t->addr; ap; ap = ap->next)
472   {
473     if (!ap->group && ap->mailbox)
474       hash_delete (ReverseAlias, ap->mailbox, ap, NULL);
475   }
476 }
477
478 /* alias_complete() -- alias completion routine
479  *
480  * given a partial alias, this routine attempts to fill in the alias
481  * from the alias list as much as possible. if given empty search string
482  * or found nothing, present all aliases
483  */
484 int mutt_alias_complete (char *s, size_t buflen)
485 {
486   ALIAS *a = Aliases;
487   ALIAS *a_list = NULL, *a_cur = NULL;
488   char bestname[HUGE_STRING];
489   int i;
490
491 #ifndef min
492 #define min(a,b)        ((a<b)?a:b)
493 #endif
494
495   if (s[0] != 0) /* avoid empty string as strstr argument */
496   {
497     memset (bestname, 0, sizeof (bestname));
498
499     while (a)
500     {
501       if (a->name && strstr (a->name, s) == a->name)
502       {
503         if (!bestname[0]) /* init */
504           strfcpy (bestname, a->name,
505                    min (mutt_strlen (a->name) + 1, sizeof (bestname)));
506         else
507         {
508           for (i = 0 ; a->name[i] && a->name[i] == bestname[i] ; i++)
509             ;
510           bestname[i] = 0;
511         }
512       }
513       a = a->next;
514     }
515
516     if (bestname[0] != 0)
517     {
518       if (mutt_strcmp (bestname, s) != 0)
519       {
520         /* we are adding something to the completion */
521         strfcpy (s, bestname, mutt_strlen (bestname) + 1);
522         return 1;
523       }
524
525       /* build alias list and show it */
526
527       a = Aliases;
528       while (a)
529       {
530         if (a->name && (strstr (a->name, s) == a->name))
531         {
532           if (!a_list)  /* init */
533             a_cur = a_list = (ALIAS *) safe_malloc (sizeof (ALIAS));
534           else
535           {
536             a_cur->next = (ALIAS *) safe_malloc (sizeof (ALIAS));
537             a_cur = a_cur->next;
538           }
539           memcpy (a_cur, a, sizeof (ALIAS));
540           a_cur->next = NULL;
541         }
542         a = a->next;
543       }
544     }
545   }
546
547   bestname[0] = 0;
548   mutt_alias_menu (bestname, sizeof(bestname), a_list ? a_list : Aliases);
549   if (bestname[0] != 0)
550     strfcpy (s, bestname, buflen);
551
552   /* free the alias list */
553   while (a_list)
554   {
555     a_cur = a_list;
556     a_list = a_list->next;
557     FREE (&a_cur);
558   }
559
560   /* remove any aliases marked for deletion */
561   a_list = NULL;
562   for (a_cur = Aliases; a_cur;)
563   {
564     if (a_cur->del)
565     {
566       if (a_list)
567         a_list->next = a_cur->next;
568       else
569         Aliases = a_cur->next;
570       
571       a_cur->next = NULL;
572       mutt_free_alias (&a_cur);
573       
574       if (a_list)
575         a_cur = a_list;
576       else
577         a_cur = Aliases;
578     }
579     else
580     {
581       a_list = a_cur;
582       a_cur  = a_cur->next;
583     }
584   }
585   
586   return 0;
587 }
588
589 static int string_is_address(const char *str, const char *u, const char *d)
590 {
591   char buf[LONG_STRING];
592   
593   snprintf(buf, sizeof(buf), "%s@%s", NONULL(u), NONULL(d));
594   if (ascii_strcasecmp(str, buf) == 0)
595     return 1;
596   
597   return 0;
598 }
599
600 /* returns TRUE if the given address belongs to the user. */
601 int mutt_addr_is_user (ADDRESS *addr)
602 {
603   const char *fqdn;
604
605   /* NULL address is assumed to be the user. */
606   if (!addr)
607   {
608     dprint (5, (debugfile, "mutt_addr_is_user: yes, NULL address\n"));
609     return 1;
610   }
611   if (!addr->mailbox)
612   {
613     dprint (5, (debugfile, "mutt_addr_is_user: no, no mailbox\n"));
614     return 0;
615   }
616
617   if (ascii_strcasecmp (addr->mailbox, Username) == 0)
618   {
619     dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s\n", addr->mailbox, Username));
620     return 1;
621   }
622   if (string_is_address(addr->mailbox, Username, Hostname))
623   {
624     dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, Hostname));
625     return 1;
626   }
627   fqdn = mutt_fqdn (0);
628   if (string_is_address(addr->mailbox, Username, fqdn))
629   {
630     dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, NONULL(fqdn)));
631     return 1;
632   }
633   fqdn = mutt_fqdn (1);
634   if (string_is_address(addr->mailbox, Username, fqdn))
635   {
636     dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, NONULL(fqdn)));
637     return 1;
638   }
639
640   if (From && !ascii_strcasecmp (From->mailbox, addr->mailbox))
641   {
642     dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s\n", addr->mailbox, From->mailbox));
643     return 1;
644   }
645
646   if (mutt_match_rx_list (addr->mailbox, Alternates))
647   {
648     dprint (5, (debugfile, "mutt_addr_is_user: yes, %s matched by alternates.\n", addr->mailbox));
649     if (mutt_match_rx_list (addr->mailbox, UnAlternates))
650       dprint (5, (debugfile, "mutt_addr_is_user: but, %s matched by unalternates.\n", addr->mailbox));
651     else
652       return 1;
653   }
654   
655   dprint (5, (debugfile, "mutt_addr_is_user: no, all failed.\n"));
656   return 0;
657 }