]> git.llucax.com Git - software/mutt-debian.git/blob - alias.c
Imported Upstream version 1.5.18
[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);
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 backtics 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 backtics.  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 void mutt_create_alias (ENVELOPE *cur, ADDRESS *iadr)
215 {
216   ALIAS *new, *t;
217   char buf[LONG_STRING], tmp[LONG_STRING], prompt[SHORT_STRING], *pc;
218   char *err = NULL;
219   char fixed[LONG_STRING];
220   FILE *rc;
221   ADDRESS *adr = NULL;
222
223   if (cur)
224   {
225     adr = mutt_get_address (cur, NULL);
226   }
227   else if (iadr)
228   {
229     adr = iadr;
230   }
231
232   if (adr && adr->mailbox)
233   {
234     strfcpy (tmp, adr->mailbox, sizeof (tmp));
235     if ((pc = strchr (tmp, '@')))
236       *pc = 0;
237   }
238   else
239     tmp[0] = '\0';
240
241   /* Don't suggest a bad alias name in the event of a strange local part. */
242   mutt_check_alias_name (tmp, buf, sizeof (buf));
243   
244 retry_name:
245   /* add a new alias */
246   if (mutt_get_field (_("Alias as: "), buf, sizeof (buf), 0) != 0 || !buf[0])
247     return;
248
249   /* check to see if the user already has an alias defined */
250   if (mutt_lookup_alias (buf))
251   {
252     mutt_error _("You already have an alias defined with that name!");
253     return;
254   }
255   
256   if (mutt_check_alias_name (buf, fixed, sizeof (fixed)))
257   {
258     switch (mutt_yesorno (_("Warning: This alias name may not work.  Fix it?"), M_YES))
259     {
260       case M_YES:
261         strfcpy (buf, fixed, sizeof (buf));
262         goto retry_name;
263       case -1: 
264         return;
265     }
266   }
267   
268   new       = safe_calloc (1, sizeof (ALIAS));
269   new->self = new;
270   new->name = safe_strdup (buf);
271
272   mutt_addrlist_to_local (adr);
273   
274   if (adr)
275     strfcpy (buf, adr->mailbox, sizeof (buf));
276   else
277     buf[0] = 0;
278
279   mutt_addrlist_to_idna (adr, NULL);
280   
281   do
282   {
283     if (mutt_get_field (_("Address: "), buf, sizeof (buf), 0) != 0 || !buf[0])
284     {
285       mutt_free_alias (&new);
286       return;
287     }
288     
289     if((new->addr = rfc822_parse_adrlist (new->addr, buf)) == NULL)
290       BEEP ();
291     if (mutt_addrlist_to_idna (new->addr, &err))
292     {
293       mutt_error (_("Error: '%s' is a bad IDN."), err);
294       mutt_sleep (2);
295       continue;
296     }
297   }
298   while(new->addr == NULL);
299   
300   if (adr && adr->personal && !mutt_is_mail_list (adr))
301     strfcpy (buf, adr->personal, sizeof (buf));
302   else
303     buf[0] = 0;
304
305   if (mutt_get_field (_("Personal name: "), buf, sizeof (buf), 0) != 0)
306   {
307     mutt_free_alias (&new);
308     return;
309   }
310   new->addr->personal = safe_strdup (buf);
311
312   buf[0] = 0;
313   rfc822_write_address (buf, sizeof (buf), new->addr, 1);
314   snprintf (prompt, sizeof (prompt), _("[%s = %s] Accept?"), new->name, buf);
315   if (mutt_yesorno (prompt, M_YES) != M_YES)
316   {
317     mutt_free_alias (&new);
318     return;
319   }
320
321   if ((t = Aliases))
322   {
323     while (t->next)
324       t = t->next;
325     t->next = new;
326   }
327   else
328     Aliases = new;
329
330   strfcpy (buf, NONULL (AliasFile), sizeof (buf));
331   if (mutt_get_field (_("Save to file: "), buf, sizeof (buf), M_FILE) != 0)
332     return;
333   mutt_expand_path (buf, sizeof (buf));
334   if ((rc = fopen (buf, "a+")))
335   {
336     /* terminate existing file with \n if necessary */
337     if (fseek (rc, 0, SEEK_END))
338       goto fseek_err;
339     if (ftell(rc) > 0)
340     {
341       if (fseek (rc, -1, SEEK_CUR) < 0)
342         goto fseek_err;
343       if (fread(buf, 1, 1, rc) < 0)
344       {
345         mutt_perror (_("Error reading alias file"));
346         return;
347       }
348       if (fseek (rc, 0, SEEK_END) < 0)
349         goto fseek_err;
350       if (buf[0] != '\n')
351         fputc ('\n', rc);
352     }
353
354     if (mutt_check_alias_name (new->name, NULL, 0))
355       mutt_quote_filename (buf, sizeof (buf), new->name);
356     else
357       strfcpy (buf, new->name, sizeof (buf));
358     fprintf (rc, "alias %s ", buf);
359     buf[0] = 0;
360     rfc822_write_address (buf, sizeof (buf), new->addr, 0);
361     write_safe_address (rc, buf);
362     fputc ('\n', rc);
363     fclose (rc);
364     mutt_message _("Alias added.");
365   }
366   else
367     mutt_perror (buf);
368
369   return;
370   
371   fseek_err:
372   mutt_perror (_("Error seeking in alias file"));
373   fclose(rc);
374   return;
375 }
376
377 /* 
378  * Sanity-check an alias name:  Only characters which are non-special to both
379  * the RFC 822 and the mutt configuration parser are permitted.
380  */
381
382 int mutt_check_alias_name (const char *s, char *dest, size_t destlen)
383 {
384   wchar_t wc;
385   mbstate_t mb;
386   size_t l;
387   int rv = 0, bad = 0, dry = !dest || !destlen;
388
389   memset (&mb, 0, sizeof (mbstate_t));
390
391   if (!dry)
392     destlen--;
393   for (; s && *s && (dry || destlen) &&
394        (l = mbrtowc (&wc, s, MB_CUR_MAX, &mb)) != 0;
395        s += l, destlen -= l)
396   {
397     bad = l == (size_t)(-1) || l == (size_t)(-2); /* conversion error */
398     bad = bad || (!dry && l > destlen);         /* too few room for mb char */
399     if (l == 1)
400       bad = bad || (strchr ("-_+=.", *s) == NULL && !iswalnum (wc));
401     else
402       bad = bad || !iswalnum (wc);
403     if (bad)
404     {
405       if (dry)
406         return -1;
407       *dest++ = '_';
408       rv = -1;
409     }
410     else if (!dry)
411     {
412       memcpy (dest, s, l);
413       dest += l;
414     }
415   }
416   if (!dry)
417     *dest = 0;
418   return rv;
419 }
420
421 /*
422  * This routine looks to see if the user has an alias defined for the given
423  * address.
424  */
425 ADDRESS *alias_reverse_lookup (ADDRESS *a)
426 {
427   ALIAS *t = Aliases;
428   ADDRESS *ap;
429
430   if (!a || !a->mailbox)
431     return NULL;
432
433   for (; t; t = t->next)
434   {
435     /* cycle through all addresses if this is a group alias */
436     for (ap = t->addr; ap; ap = ap->next)
437     {
438       if (!ap->group && ap->mailbox &&
439           ascii_strcasecmp (ap->mailbox, a->mailbox) == 0)
440         return ap;
441     }
442   }
443   return 0;
444 }
445
446 /* alias_complete() -- alias completion routine
447  *
448  * given a partial alias, this routine attempts to fill in the alias
449  * from the alias list as much as possible. if given empty search string
450  * or found nothing, present all aliases
451  */
452 int mutt_alias_complete (char *s, size_t buflen)
453 {
454   ALIAS *a = Aliases;
455   ALIAS *a_list = NULL, *a_cur = NULL;
456   char bestname[HUGE_STRING];
457   int i;
458
459 #define min(a,b)        ((a<b)?a:b)
460
461   if (s[0] != 0) /* avoid empty string as strstr argument */
462   {
463     memset (bestname, 0, sizeof (bestname));
464
465     while (a)
466     {
467       if (a->name && strstr (a->name, s) == a->name)
468       {
469         if (!bestname[0]) /* init */
470           strfcpy (bestname, a->name,
471                    min (mutt_strlen (a->name) + 1, sizeof (bestname)));
472         else
473         {
474           for (i = 0 ; a->name[i] && a->name[i] == bestname[i] ; i++)
475             ;
476           bestname[i] = 0;
477         }
478       }
479       a = a->next;
480     }
481
482     if (bestname[0] != 0)
483     {
484       if (mutt_strcmp (bestname, s) != 0)
485       {
486         /* we are adding something to the completion */
487         strfcpy (s, bestname, mutt_strlen (bestname) + 1);
488         return 1;
489       }
490
491       /* build alias list and show it */
492
493       a = Aliases;
494       while (a)
495       {
496         if (a->name && (strstr (a->name, s) == a->name))
497         {
498           if (!a_list)  /* init */
499             a_cur = a_list = (ALIAS *) safe_malloc (sizeof (ALIAS));
500           else
501           {
502             a_cur->next = (ALIAS *) safe_malloc (sizeof (ALIAS));
503             a_cur = a_cur->next;
504           }
505           memcpy (a_cur, a, sizeof (ALIAS));
506           a_cur->next = NULL;
507         }
508         a = a->next;
509       }
510     }
511   }
512
513   bestname[0] = 0;
514   mutt_alias_menu (bestname, sizeof(bestname), a_list ? a_list : Aliases);
515   if (bestname[0] != 0)
516     strfcpy (s, bestname, buflen);
517
518   /* free the alias list */
519   while (a_list)
520   {
521     a_cur = a_list;
522     a_list = a_list->next;
523     FREE (&a_cur);
524   }
525
526   /* remove any aliases marked for deletion */
527   a_list = NULL;
528   for (a_cur = Aliases; a_cur;)
529   {
530     if (a_cur->del)
531     {
532       if (a_list)
533         a_list->next = a_cur->next;
534       else
535         Aliases = a_cur->next;
536       
537       a_cur->next = NULL;
538       mutt_free_alias (&a_cur);
539       
540       if (a_list)
541         a_cur = a_list;
542       else
543         a_cur = Aliases;
544     }
545     else
546     {
547       a_list = a_cur;
548       a_cur  = a_cur->next;
549     }
550   }
551   
552   return 0;
553 }
554
555 static int string_is_address(const char *str, const char *u, const char *d)
556 {
557   char buf[LONG_STRING];
558   
559   snprintf(buf, sizeof(buf), "%s@%s", NONULL(u), NONULL(d));
560   if (ascii_strcasecmp(str, buf) == 0)
561     return 1;
562   
563   return 0;
564 }
565
566 /* returns TRUE if the given address belongs to the user. */
567 int mutt_addr_is_user (ADDRESS *addr)
568 {
569   /* NULL address is assumed to be the user. */
570   if (!addr)
571   {
572     dprint (5, (debugfile, "mutt_addr_is_user: yes, NULL address\n"));
573     return 1;
574   }
575   if (!addr->mailbox)
576   {
577     dprint (5, (debugfile, "mutt_addr_is_user: no, no mailbox\n"));
578     return 0;
579   }
580
581   if (ascii_strcasecmp (addr->mailbox, Username) == 0)
582   {
583     dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s\n", addr->mailbox, Username));
584     return 1;
585   }
586   if (string_is_address(addr->mailbox, Username, Hostname))
587   {
588     dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, Hostname));
589     return 1;
590   }
591   if (string_is_address(addr->mailbox, Username, mutt_fqdn(0)))
592   {
593     dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, mutt_fqdn (0)));
594     return 1;
595   }
596   if (string_is_address(addr->mailbox, Username, mutt_fqdn(1)))
597   {
598     dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, mutt_fqdn (1)));
599     return 1;
600   }
601
602   if (From && !ascii_strcasecmp (From->mailbox, addr->mailbox))
603   {
604     dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s\n", addr->mailbox, From->mailbox));
605     return 1;
606   }
607
608   if (mutt_match_rx_list (addr->mailbox, Alternates))
609   {
610     dprint (5, (debugfile, "mutt_addr_is_user: yes, %s matched by alternates.\n", addr->mailbox));
611     if (mutt_match_rx_list (addr->mailbox, UnAlternates))
612       dprint (5, (debugfile, "mutt_addr_is_user: but, %s matched by unalternates.\n", addr->mailbox));
613     else
614       return 1;
615   }
616   
617   dprint (5, (debugfile, "mutt_addr_is_user: no, all failed.\n"));
618   return 0;
619 }