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