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