mutt stable branch with some hacks
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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
31ADDRESS *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
41static 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 {
50 if (!a->group && !a->personal && a->mailbox && strchr (a->mailbox, '@') == NULL)
51 {
52 t = mutt_lookup_alias (a->mailbox);
53
54 if (t)
55 {
56 i = 0;
57 for (u = *expn; u; u = u->next)
58 {
59 if (mutt_strcmp (a->mailbox, u->data) == 0) /* alias already found */
60 {
61 dprint (1, (debugfile, "mutt_expand_aliases_r(): loop in alias found for '%s'\n", a->mailbox));
62 i = 1;
63 break;
64 }
65 }
66
67 if (!i)
68 {
69 u = safe_malloc (sizeof (LIST));
70 u->data = safe_strdup (a->mailbox);
71 u->next = *expn;
72 *expn = u;
73 w = rfc822_cpy_adr (t, 0);
74 w = mutt_expand_aliases_r (w, expn);
75 if (head)
76 last->next = w;
77 else
78 head = last = w;
79 while (last && last->next)
80 last = last->next;
81 }
82 t = a;
83 a = a->next;
84 t->next = NULL;
85 rfc822_free_address (&t);
86 continue;
87 }
88 else
89 {
90 struct passwd *pw = getpwnam (a->mailbox);
91
92 if (pw)
93 {
94 char namebuf[STRING];
95
96 mutt_gecos_name (namebuf, sizeof (namebuf), pw);
97 mutt_str_replace (&a->personal, namebuf);
98
99#ifdef EXACT_ADDRESS
100 FREE (&a->val);
101#endif
102 }
103 }
104 }
105
106 if (head)
107 {
108 last->next = a;
109 last = last->next;
110 }
111 else
112 head = last = a;
113 a = a->next;
114 last->next = NULL;
115 }
116
117 if (option (OPTUSEDOMAIN) && (fqdn = mutt_fqdn(1)))
118 {
119 /* now qualify all local addresses */
120 rfc822_qualify (head, fqdn);
121 }
122
123 return (head);
124}
125
126ADDRESS *mutt_expand_aliases (ADDRESS *a)
127{
128 ADDRESS *t;
129 LIST *expn = NULL; /* previously expanded aliases to avoid loops */
130
131 t = mutt_expand_aliases_r (a, &expn);
132 mutt_free_list (&expn);
133 return (mutt_remove_duplicates (t));
134}
135
136void mutt_expand_aliases_env (ENVELOPE *env)
137{
138 env->from = mutt_expand_aliases (env->from);
139 env->to = mutt_expand_aliases (env->to);
140 env->cc = mutt_expand_aliases (env->cc);
141 env->bcc = mutt_expand_aliases (env->bcc);
142 env->reply_to = mutt_expand_aliases (env->reply_to);
143 env->mail_followup_to = mutt_expand_aliases (env->mail_followup_to);
144}
145
146
147/*
148 * if someone has an address like
149 * From: Michael `/bin/rm -f ~` Elkins <me@mutt.org>
150 * and the user creates an alias for this, Mutt could wind up executing
151 * the backticks because it writes aliases like
152 * alias me Michael `/bin/rm -f ~` Elkins <me@mutt.org>
153 * To avoid this problem, use a backslash (\) to quote any backticks. We also
154 * need to quote backslashes as well, since you could defeat the above by
155 * doing
156 * From: Michael \`/bin/rm -f ~\` Elkins <me@mutt.org>
157 * since that would get aliased as
158 * alias me Michael \\`/bin/rm -f ~\\` Elkins <me@mutt.org>
159 * which still gets evaluated because the double backslash is not a quote.
160 *
161 * Additionally, we need to quote ' and " characters - otherwise, mutt will
162 * interpret them on the wrong parsing step.
163 *
164 * $ wants to be quoted since it may indicate the start of an environment
165 * variable.
166 */
167
168static void write_safe_address (FILE *fp, char *s)
169{
170 while (*s)
171 {
172 if (*s == '\\' || *s == '`' || *s == '\'' || *s == '"'
173 || *s == '$')
174 fputc ('\\', fp);
175 fputc (*s, fp);
176 s++;
177 }
178}
179
180ADDRESS *mutt_get_address (ENVELOPE *env, char **pfxp)
181{
182 ADDRESS *adr;
183 char *pfx = NULL;
184
185 if (mutt_addr_is_user (env->from))
186 {
187 if (env->to && !mutt_is_mail_list (env->to))
188 {
189 pfx = "To";
190 adr = env->to;
191 }
192 else
193 {
194 pfx = "Cc";
195 adr = env->cc;
196 }
197 }
198 else if (env->reply_to && !mutt_is_mail_list (env->reply_to))
199 {
200 pfx = "Reply-To";
201 adr = env->reply_to;
202 }
203 else
204 {
205 adr = env->from;
206 pfx = "From";
207 }
208
209 if (pfxp) *pfxp = pfx;
210
211 return adr;
212}
213
214static void recode_buf (char *buf, size_t buflen)
215{
216 char *s;
217
218 if (!ConfigCharset || !Charset)
219 return;
220 s = safe_strdup (buf);
221 if (!s)
222 return;
223 if (mutt_convert_string (&s, Charset, ConfigCharset, 0) == 0)
224 strfcpy (buf, s, buflen);
225 FREE(&s);
226}
227
228void mutt_create_alias (ENVELOPE *cur, ADDRESS *iadr)
229{
230 ALIAS *new, *t;
231 char buf[LONG_STRING], tmp[LONG_STRING], prompt[SHORT_STRING], *pc;
232 char *err = NULL;
233 char fixed[LONG_STRING];
234 FILE *rc;
235 ADDRESS *adr = NULL;
236
237 if (cur)
238 {
239 adr = mutt_get_address (cur, NULL);
240 }
241 else if (iadr)
242 {
243 adr = iadr;
244 }
245
246 if (adr && adr->mailbox)
247 {
248 strfcpy (tmp, adr->mailbox, sizeof (tmp));
249 if ((pc = strchr (tmp, '@')))
250 *pc = 0;
251 }
252 else
253 tmp[0] = '\0';
254
255 /* Don't suggest a bad alias name in the event of a strange local part. */
256 mutt_check_alias_name (tmp, buf, sizeof (buf));
257
258retry_name:
259 /* L10N: prompt to add a new alias */
260 if (mutt_get_field (_("Alias as: "), buf, sizeof (buf), 0) != 0 || !buf[0])
261 return;
262
263 /* check to see if the user already has an alias defined */
264 if (mutt_lookup_alias (buf))
265 {
266 mutt_error _("You already have an alias defined with that name!");
267 return;
268 }
269
270 if (mutt_check_alias_name (buf, fixed, sizeof (fixed)))
271 {
272 switch (mutt_yesorno (_("Warning: This alias name may not work. Fix it?"), MUTT_YES))
273 {
274 case MUTT_YES:
275 strfcpy (buf, fixed, sizeof (buf));
276 goto retry_name;
277 case -1:
278 return;
279 }
280 }
281
282 new = safe_calloc (1, sizeof (ALIAS));
283 new->self = new;
284 new->name = safe_strdup (buf);
285
286 mutt_addrlist_to_local (adr);
287
288 if (adr)
289 strfcpy (buf, adr->mailbox, sizeof (buf));
290 else
291 buf[0] = 0;
292
293 mutt_addrlist_to_intl (adr, NULL);
294
295 do
296 {
297 if (mutt_get_field (_("Address: "), buf, sizeof (buf), 0) != 0 || !buf[0])
298 {
299 mutt_free_alias (&new);
300 return;
301 }
302
303 if ((new->addr = rfc822_parse_adrlist (new->addr, buf)) == NULL)
304 BEEP ();
305 if (mutt_addrlist_to_intl (new->addr, &err))
306 {
307 mutt_error (_("Error: '%s' is a bad IDN."), err);
308 FREE (&err);
309 mutt_sleep (2);
310 continue;
311 }
312 }
313 while (new->addr == NULL);
314
315 if (adr && adr->personal && !mutt_is_mail_list (adr))
316 strfcpy (buf, adr->personal, sizeof (buf));
317 else
318 buf[0] = 0;
319
320 if (mutt_get_field (_("Personal name: "), buf, sizeof (buf), 0) != 0)
321 {
322 mutt_free_alias (&new);
323 return;
324 }
325 new->addr->personal = safe_strdup (buf);
326
327 buf[0] = 0;
328 rfc822_write_address (buf, sizeof (buf), new->addr, 1);
329 snprintf (prompt, sizeof (prompt), _("[%s = %s] Accept?"), new->name, buf);
330 if (mutt_yesorno (prompt, MUTT_YES) != MUTT_YES)
331 {
332 mutt_free_alias (&new);
333 return;
334 }
335
336 mutt_alias_add_reverse (new);
337
338 if ((t = Aliases))
339 {
340 while (t->next)
341 t = t->next;
342 t->next = new;
343 }
344 else
345 Aliases = new;
346
347 strfcpy (buf, NONULL (AliasFile), sizeof (buf));
348 if (mutt_get_field (_("Save to file: "), buf, sizeof (buf), MUTT_FILE) != 0)
349 return;
350 mutt_expand_path (buf, sizeof (buf));
351 if ((rc = fopen (buf, "a+")))
352 {
353 /* terminate existing file with \n if necessary */
354 if (fseek (rc, 0, SEEK_END))
355 goto fseek_err;
356 if (ftell(rc) > 0)
357 {
358 if (fseek (rc, -1, SEEK_CUR) < 0)
359 goto fseek_err;
360 if (fread(buf, 1, 1, rc) != 1)
361 {
362 mutt_perror (_("Error reading alias file"));
363 safe_fclose (&rc);
364 return;
365 }
366 if (fseek (rc, 0, SEEK_END) < 0)
367 goto fseek_err;
368 if (buf[0] != '\n')
369 fputc ('\n', rc);
370 }
371
372 if (mutt_check_alias_name (new->name, NULL, 0))
373 mutt_quote_filename (buf, sizeof (buf), new->name);
374 else
375 strfcpy (buf, new->name, sizeof (buf));
376 recode_buf (buf, sizeof (buf));
377 fprintf (rc, "alias %s ", buf);
378 buf[0] = 0;
379 rfc822_write_address (buf, sizeof (buf), new->addr, 0);
380 recode_buf (buf, sizeof (buf));
381 write_safe_address (rc, buf);
382 fputc ('\n', rc);
383 safe_fclose (&rc);
384 mutt_message _("Alias added.");
385 }
386 else
387 mutt_perror (buf);
388
389 return;
390
391fseek_err:
392 mutt_perror (_("Error seeking in alias file"));
393 safe_fclose (&rc);
394 return;
395}
396
397/*
398 * Sanity-check an alias name: Only characters which are non-special to both
399 * the RFC 822 and the mutt configuration parser are permitted.
400 */
401
402int mutt_check_alias_name (const char *s, char *dest, size_t destlen)
403{
404 wchar_t wc;
405 mbstate_t mb;
406 size_t l;
407 int rv = 0, bad = 0, dry = !dest || !destlen;
408
409 memset (&mb, 0, sizeof (mbstate_t));
410
411 if (!dry)
412 destlen--;
413 for (; s && *s && (dry || destlen) &&
414 (l = mbrtowc (&wc, s, MB_CUR_MAX, &mb)) != 0;
415 s += l, destlen -= l)
416 {
417 bad = l == (size_t)(-1) || l == (size_t)(-2); /* conversion error */
418 bad = bad || (!dry && l > destlen); /* too few room for mb char */
419 if (l == 1)
420 bad = bad || (strchr ("-_+=.", *s) == NULL && !iswalnum (wc));
421 else
422 bad = bad || !iswalnum (wc);
423 if (bad)
424 {
425 if (dry)
426 return -1;
427 if (l == (size_t)(-1))
428 memset (&mb, 0, sizeof (mbstate_t));
429 *dest++ = '_';
430 rv = -1;
431 }
432 else if (!dry)
433 {
434 memcpy (dest, s, l);
435 dest += l;
436 }
437 }
438 if (!dry)
439 *dest = 0;
440 return rv;
441}
442
443/*
444 * This routine looks to see if the user has an alias defined for the given
445 * address.
446 */
447ADDRESS *alias_reverse_lookup (ADDRESS *a)
448{
449 if (!a || !a->mailbox)
450 return NULL;
451
452 return hash_find (ReverseAlias, a->mailbox);
453}
454
455void mutt_alias_add_reverse (ALIAS *t)
456{
457 ADDRESS *ap;
458 if (!t)
459 return;
460
461 /* Note that the address mailbox should be converted to intl form
462 * before using as a key in the hash. This is currently done
463 * by all callers, but added here mostly as documentation.. */
464 mutt_addrlist_to_intl (t->addr, NULL);
465
466 for (ap = t->addr; ap; ap = ap->next)
467 {
468 if (!ap->group && ap->mailbox)
469 hash_insert (ReverseAlias, ap->mailbox, ap);
470 }
471}
472
473void mutt_alias_delete_reverse (ALIAS *t)
474{
475 ADDRESS *ap;
476 if (!t)
477 return;
478
479 /* If the alias addresses were converted to local form, they won't
480 * match the hash entries. */
481 mutt_addrlist_to_intl (t->addr, NULL);
482
483 for (ap = t->addr; ap; ap = ap->next)
484 {
485 if (!ap->group && ap->mailbox)
486 hash_delete (ReverseAlias, ap->mailbox, ap, NULL);
487 }
488}
489
490/* alias_complete() -- alias completion routine
491 *
492 * given a partial alias, this routine attempts to fill in the alias
493 * from the alias list as much as possible. if given empty search string
494 * or found nothing, present all aliases
495 */
496int mutt_alias_complete (char *s, size_t buflen)
497{
498 ALIAS *a = Aliases;
499 ALIAS *a_list = NULL, *a_cur = NULL;
500 char bestname[HUGE_STRING];
501 int i;
502
503#ifndef min
504#define min(a,b) ((a<b)?a:b)
505#endif
506
507 if (s[0] != 0) /* avoid empty string as strstr argument */
508 {
509 memset (bestname, 0, sizeof (bestname));
510
511 while (a)
512 {
513 if (a->name && strstr (a->name, s) == a->name)
514 {
515 if (!bestname[0]) /* init */
516 strfcpy (bestname, a->name,
517 min (mutt_strlen (a->name) + 1, sizeof (bestname)));
518 else
519 {
520 for (i = 0 ; a->name[i] && a->name[i] == bestname[i] ; i++)
521 ;
522 bestname[i] = 0;
523 }
524 }
525 a = a->next;
526 }
527
528 if (bestname[0] != 0)
529 {
530 if (mutt_strcmp (bestname, s) != 0)
531 {
532 /* we are adding something to the completion */
533 strfcpy (s, bestname, mutt_strlen (bestname) + 1);
534 return 1;
535 }
536
537 /* build alias list and show it */
538
539 a = Aliases;
540 while (a)
541 {
542 if (a->name && (strstr (a->name, s) == a->name))
543 {
544 if (!a_list) /* init */
545 a_cur = a_list = (ALIAS *) safe_malloc (sizeof (ALIAS));
546 else
547 {
548 a_cur->next = (ALIAS *) safe_malloc (sizeof (ALIAS));
549 a_cur = a_cur->next;
550 }
551 memcpy (a_cur, a, sizeof (ALIAS));
552 a_cur->next = NULL;
553 }
554 a = a->next;
555 }
556 }
557 }
558
559 bestname[0] = 0;
560 mutt_alias_menu (bestname, sizeof(bestname), a_list ? a_list : Aliases);
561 if (bestname[0] != 0)
562 strfcpy (s, bestname, buflen);
563
564 /* free the alias list */
565 while (a_list)
566 {
567 a_cur = a_list;
568 a_list = a_list->next;
569 FREE (&a_cur);
570 }
571
572 /* remove any aliases marked for deletion */
573 a_list = NULL;
574 for (a_cur = Aliases; a_cur;)
575 {
576 if (a_cur->del)
577 {
578 if (a_list)
579 a_list->next = a_cur->next;
580 else
581 Aliases = a_cur->next;
582
583 a_cur->next = NULL;
584 mutt_free_alias (&a_cur);
585
586 if (a_list)
587 a_cur = a_list;
588 else
589 a_cur = Aliases;
590 }
591 else
592 {
593 a_list = a_cur;
594 a_cur = a_cur->next;
595 }
596 }
597
598 return 0;
599}
600
601static int string_is_address(const char *str, const char *u, const char *d)
602{
603 char buf[LONG_STRING];
604
605 snprintf(buf, sizeof(buf), "%s@%s", NONULL(u), NONULL(d));
606 if (ascii_strcasecmp(str, buf) == 0)
607 return 1;
608
609 return 0;
610}
611
612/* returns TRUE if the given address belongs to the user. */
613int mutt_addr_is_user (ADDRESS *addr)
614{
615 const char *fqdn;
616
617 if (!addr)
618 {
619 dprint (5, (debugfile, "mutt_addr_is_user: no, NULL address\n"));
620 return 0;
621 }
622 if (!addr->mailbox)
623 {
624 dprint (5, (debugfile, "mutt_addr_is_user: no, no mailbox\n"));
625 return 0;
626 }
627
628 if (ascii_strcasecmp (addr->mailbox, Username) == 0)
629 {
630 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s\n", addr->mailbox, Username));
631 return 1;
632 }
633 if (string_is_address(addr->mailbox, Username, Hostname))
634 {
635 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, Hostname));
636 return 1;
637 }
638 fqdn = mutt_fqdn (0);
639 if (string_is_address(addr->mailbox, Username, fqdn))
640 {
641 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, NONULL(fqdn)));
642 return 1;
643 }
644 fqdn = mutt_fqdn (1);
645 if (string_is_address(addr->mailbox, Username, fqdn))
646 {
647 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, NONULL(fqdn)));
648 return 1;
649 }
650
651 if (From && !ascii_strcasecmp (From->mailbox, addr->mailbox))
652 {
653 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s\n", addr->mailbox, From->mailbox));
654 return 1;
655 }
656
657 if (mutt_match_rx_list (addr->mailbox, Alternates))
658 {
659 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s matched by alternates.\n", addr->mailbox));
660 if (mutt_match_rx_list (addr->mailbox, UnAlternates))
661 dprint (5, (debugfile, "mutt_addr_is_user: but, %s matched by unalternates.\n", addr->mailbox));
662 else
663 return 1;
664 }
665
666 dprint (5, (debugfile, "mutt_addr_is_user: no, all failed.\n"));
667 return 0;
668}