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