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