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