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