Andreas Krennmair:
[apps/madmutt.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., 59 Temple Place - Suite 330, Boston, MA  02111, 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], 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 (buf, adr->mailbox, sizeof (buf));
235     if ((pc = strchr (buf, '@')))
236       *pc = 0;
237   }
238   else
239     buf[0] = '\0';
240
241   /* Don't suggest a bad alias name in the event of a strange local part. */
242   mutt_check_alias_name (buf, 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))
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 = safe_fopen (buf, "a")))
335   {
336     if (mutt_check_alias_name (new->name, NULL))
337       mutt_quote_filename (buf, sizeof (buf), new->name);
338     else
339       strfcpy (buf, new->name, sizeof (buf));
340     fprintf (rc, "alias %s ", buf);
341     buf[0] = 0;
342     rfc822_write_address (buf, sizeof (buf), new->addr, 0);
343     write_safe_address (rc, buf);
344     fputc ('\n', rc);
345     fclose (rc);
346     mutt_message _("Alias added.");
347   }
348   else
349     mutt_perror (buf);
350 }
351
352 /* 
353  * Sanity-check an alias name:  Only characters which are non-special to both
354  * the RFC 822 and the mutt configuration parser are permitted.
355  */
356
357 static int check_alias_name_char (char c)
358 {
359   return (c == '-' || c == '_' || c == '+' || c == '=' || c == '.' ||
360           isalnum ((unsigned char) c));
361 }
362
363 int mutt_check_alias_name (const char *s, char *d)
364 {
365   int rv = 0;
366   for (; *s; s++) 
367   {
368     if (!check_alias_name_char (*s))
369     {
370       if (!d)
371         return -1;
372       else
373       {
374         *d++ = '_';
375         rv = -1;
376       }
377     }
378     else if (d)
379       *d++ = *s;
380   }
381   if (d)
382     *d++ = *s;
383   return rv;
384 }
385
386 /*
387  * This routine looks to see if the user has an alias defined for the given
388  * address.
389  */
390 ADDRESS *alias_reverse_lookup (ADDRESS *a)
391 {
392   ALIAS *t = Aliases;
393   ADDRESS *ap;
394
395   if (!a || !a->mailbox)
396     return NULL;
397
398   for (; t; t = t->next)
399   {
400     /* cycle through all addresses if this is a group alias */
401     for (ap = t->addr; ap; ap = ap->next)
402     {
403       if (!ap->group && ap->mailbox &&
404           ascii_strcasecmp (ap->mailbox, a->mailbox) == 0)
405         return ap;
406     }
407   }
408   return 0;
409 }
410
411 /* alias_complete() -- alias completion routine
412  *
413  * given a partial alias, this routine attempts to fill in the alias
414  * from the alias list as much as possible. if given empty search string
415  * or found nothing, present all aliases
416  */
417 int mutt_alias_complete (char *s, size_t buflen)
418 {
419   ALIAS *a = Aliases;
420   ALIAS *a_list = NULL, *a_cur = NULL;
421   char bestname[HUGE_STRING];
422   int i;
423
424 #define min(a,b)        ((a<b)?a:b)
425
426   if (s[0] != 0) /* avoid empty string as strstr argument */
427   {
428     memset (bestname, 0, sizeof (bestname));
429
430     while (a)
431     {
432       if (a->name && strstr (a->name, s) == a->name)
433       {
434         if (!bestname[0]) /* init */
435           strfcpy (bestname, a->name,
436                    min (mutt_strlen (a->name) + 1, sizeof (bestname)));
437         else
438         {
439           for (i = 0 ; a->name[i] && a->name[i] == bestname[i] ; i++)
440             ;
441           bestname[i] = 0;
442         }
443       }
444       a = a->next;
445     }
446
447     if (bestname[0] != 0)
448     {
449       if (mutt_strcmp (bestname, s) != 0)
450       {
451         /* we are adding something to the completion */
452         strfcpy (s, bestname, mutt_strlen (bestname) + 1);
453         return 1;
454       }
455
456       /* build alias list and show it */
457
458       a = Aliases;
459       while (a)
460       {
461         if (a->name && (strstr (a->name, s) == a->name))
462         {
463           if (!a_list)  /* init */
464             a_cur = a_list = (ALIAS *) safe_malloc (sizeof (ALIAS));
465           else
466           {
467             a_cur->next = (ALIAS *) safe_malloc (sizeof (ALIAS));
468             a_cur = a_cur->next;
469           }
470           memcpy (a_cur, a, sizeof (ALIAS));
471           a_cur->next = NULL;
472         }
473         a = a->next;
474       }
475     }
476   }
477
478   bestname[0] = 0;
479   mutt_alias_menu (bestname, sizeof(bestname), a_list ? a_list : Aliases);
480   if (bestname[0] != 0)
481     strfcpy (s, bestname, buflen);
482
483   /* free the alias list */
484   while (a_list)
485   {
486     a_cur = a_list;
487     a_list = a_list->next;
488     FREE (&a_cur);
489   }
490
491   /* remove any aliases marked for deletion */
492   a_list = NULL;
493   for (a_cur = Aliases; a_cur;)
494   {
495     if (a_cur->del)
496     {
497       if (a_list)
498         a_list->next = a_cur->next;
499       else
500         Aliases = a_cur->next;
501       
502       a_cur->next = NULL;
503       mutt_free_alias (&a_cur);
504       
505       if (a_list)
506         a_cur = a_list;
507       else
508         a_cur = Aliases;
509     }
510     else
511     {
512       a_list = a_cur;
513       a_cur  = a_cur->next;
514     }
515   }
516   
517   return 0;
518 }
519
520 static int string_is_address(const char *str, const char *u, const char *d)
521 {
522   char buf[LONG_STRING];
523   
524   snprintf(buf, sizeof(buf), "%s@%s", NONULL(u), NONULL(d));
525   if (ascii_strcasecmp(str, buf) == 0)
526     return 1;
527   
528   return 0;
529 }
530
531 /* returns TRUE if the given address belongs to the user. */
532 int mutt_addr_is_user (ADDRESS *addr)
533 {
534   /* NULL address is assumed to be the user. */
535   if (!addr)
536   {
537     dprint (5, (debugfile, "mail_addr_is_user: yes, NULL address\n"));
538     return 1;
539   }
540   if (!addr->mailbox)
541   {
542     dprint (5, (debugfile, "mail_addr_is_user: no, no mailbox\n"));
543     return 0;
544   }
545
546   if (ascii_strcasecmp (addr->mailbox, Username) == 0)
547   {
548     dprint (5, (debugfile, "mail_addr_is_user: yes, %s = %s\n", addr->mailbox, Username));
549     return 1;
550   }
551   if (string_is_address(addr->mailbox, Username, Hostname))
552   {
553     dprint (5, (debugfile, "mail_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, Hostname));
554     return 1;
555   }
556   if (string_is_address(addr->mailbox, Username, mutt_fqdn(0)))
557   {
558     dprint (5, (debugfile, "mail_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, mutt_fqdn (0)));
559     return 1;
560   }
561   if (string_is_address(addr->mailbox, Username, mutt_fqdn(1)))
562   {
563     dprint (5, (debugfile, "mail_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, mutt_fqdn (1)));
564     return 1;
565   }
566
567   if (From && !ascii_strcasecmp (From->mailbox, addr->mailbox))
568   {
569     dprint (5, (debugfile, "mail_addr_is_user: yes, %s = %s\n", addr->mailbox, From->mailbox));
570     return 1;
571   }
572
573   if (mutt_match_rx_list (addr->mailbox, Alternates))
574   {
575     dprint (5, (debugfile, "mail_addr_is_user: yes, %s matched by alternates.\n", addr->mailbox));
576     if (mutt_match_rx_list (addr->mailbox, UnAlternates))
577       dprint (5, (debugfile, "mail_addr_is_user: but, %s matched by unalternates.\n", addr->mailbox));
578     else
579       return 1;
580   }
581   
582   dprint (5, (debugfile, "mail_addr_is_user: no, all failed.\n"));
583   return 0;
584 }