Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 620 lines (545 sloc) 14.369 kb
14c2920 Imported Upstream version 1.5.18
Antonio Radici authored
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
31 ADDRESS *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
41 static 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);
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
126 ADDRESS *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
136 void 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 backtics 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 backtics. 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
168 static 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
180 ADDRESS *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
214 void mutt_create_alias (ENVELOPE *cur, ADDRESS *iadr)
215 {
216 ALIAS *new, *t;
217 char buf[LONG_STRING], tmp[LONG_STRING], prompt[SHORT_STRING], *pc;
218 char *err = NULL;
219 char fixed[LONG_STRING];
220 FILE *rc;
221 ADDRESS *adr = NULL;
222
223 if (cur)
224 {
225 adr = mutt_get_address (cur, NULL);
226 }
227 else if (iadr)
228 {
229 adr = iadr;
230 }
231
232 if (adr && adr->mailbox)
233 {
234 strfcpy (tmp, adr->mailbox, sizeof (tmp));
235 if ((pc = strchr (tmp, '@')))
236 *pc = 0;
237 }
238 else
239 tmp[0] = '\0';
240
241 /* Don't suggest a bad alias name in the event of a strange local part. */
242 mutt_check_alias_name (tmp, buf, sizeof (buf));
243
244 retry_name:
245 /* add a new alias */
246 if (mutt_get_field (_("Alias as: "), buf, sizeof (buf), 0) != 0 || !buf[0])
247 return;
248
249 /* check to see if the user already has an alias defined */
250 if (mutt_lookup_alias (buf))
251 {
252 mutt_error _("You already have an alias defined with that name!");
253 return;
254 }
255
256 if (mutt_check_alias_name (buf, fixed, sizeof (fixed)))
257 {
258 switch (mutt_yesorno (_("Warning: This alias name may not work. Fix it?"), M_YES))
259 {
260 case M_YES:
261 strfcpy (buf, fixed, sizeof (buf));
262 goto retry_name;
263 case -1:
264 return;
265 }
266 }
267
268 new = safe_calloc (1, sizeof (ALIAS));
269 new->self = new;
270 new->name = safe_strdup (buf);
271
272 mutt_addrlist_to_local (adr);
273
274 if (adr)
275 strfcpy (buf, adr->mailbox, sizeof (buf));
276 else
277 buf[0] = 0;
278
279 mutt_addrlist_to_idna (adr, NULL);
280
281 do
282 {
283 if (mutt_get_field (_("Address: "), buf, sizeof (buf), 0) != 0 || !buf[0])
284 {
285 mutt_free_alias (&new);
286 return;
287 }
288
289 if((new->addr = rfc822_parse_adrlist (new->addr, buf)) == NULL)
290 BEEP ();
291 if (mutt_addrlist_to_idna (new->addr, &err))
292 {
293 mutt_error (_("Error: '%s' is a bad IDN."), err);
294 mutt_sleep (2);
295 continue;
296 }
297 }
298 while(new->addr == NULL);
299
300 if (adr && adr->personal && !mutt_is_mail_list (adr))
301 strfcpy (buf, adr->personal, sizeof (buf));
302 else
303 buf[0] = 0;
304
305 if (mutt_get_field (_("Personal name: "), buf, sizeof (buf), 0) != 0)
306 {
307 mutt_free_alias (&new);
308 return;
309 }
310 new->addr->personal = safe_strdup (buf);
311
312 buf[0] = 0;
313 rfc822_write_address (buf, sizeof (buf), new->addr, 1);
314 snprintf (prompt, sizeof (prompt), _("[%s = %s] Accept?"), new->name, buf);
315 if (mutt_yesorno (prompt, M_YES) != M_YES)
316 {
317 mutt_free_alias (&new);
318 return;
319 }
320
321 if ((t = Aliases))
322 {
323 while (t->next)
324 t = t->next;
325 t->next = new;
326 }
327 else
328 Aliases = new;
329
330 strfcpy (buf, NONULL (AliasFile), sizeof (buf));
331 if (mutt_get_field (_("Save to file: "), buf, sizeof (buf), M_FILE) != 0)
332 return;
333 mutt_expand_path (buf, sizeof (buf));
334 if ((rc = fopen (buf, "a+")))
335 {
336 /* terminate existing file with \n if necessary */
337 if (fseek (rc, 0, SEEK_END))
338 goto fseek_err;
339 if (ftell(rc) > 0)
340 {
341 if (fseek (rc, -1, SEEK_CUR) < 0)
342 goto fseek_err;
343 if (fread(buf, 1, 1, rc) < 0)
344 {
345 mutt_perror (_("Error reading alias file"));
346 return;
347 }
348 if (fseek (rc, 0, SEEK_END) < 0)
349 goto fseek_err;
350 if (buf[0] != '\n')
351 fputc ('\n', rc);
352 }
353
354 if (mutt_check_alias_name (new->name, NULL, 0))
355 mutt_quote_filename (buf, sizeof (buf), new->name);
356 else
357 strfcpy (buf, new->name, sizeof (buf));
358 fprintf (rc, "alias %s ", buf);
359 buf[0] = 0;
360 rfc822_write_address (buf, sizeof (buf), new->addr, 0);
361 write_safe_address (rc, buf);
362 fputc ('\n', rc);
363 fclose (rc);
364 mutt_message _("Alias added.");
365 }
366 else
367 mutt_perror (buf);
368
369 return;
370
371 fseek_err:
372 mutt_perror (_("Error seeking in alias file"));
373 fclose(rc);
374 return;
375 }
376
377 /*
378 * Sanity-check an alias name: Only characters which are non-special to both
379 * the RFC 822 and the mutt configuration parser are permitted.
380 */
381
382 int mutt_check_alias_name (const char *s, char *dest, size_t destlen)
383 {
384 wchar_t wc;
385 mbstate_t mb;
386 size_t l;
387 int rv = 0, bad = 0, dry = !dest || !destlen;
388
389 memset (&mb, 0, sizeof (mbstate_t));
390
391 if (!dry)
392 destlen--;
393 for (; s && *s && (dry || destlen) &&
394 (l = mbrtowc (&wc, s, MB_CUR_MAX, &mb)) != 0;
395 s += l, destlen -= l)
396 {
397 bad = l == (size_t)(-1) || l == (size_t)(-2); /* conversion error */
398 bad = bad || (!dry && l > destlen); /* too few room for mb char */
399 if (l == 1)
400 bad = bad || (strchr ("-_+=.", *s) == NULL && !iswalnum (wc));
401 else
402 bad = bad || !iswalnum (wc);
403 if (bad)
404 {
405 if (dry)
406 return -1;
407 *dest++ = '_';
408 rv = -1;
409 }
410 else if (!dry)
411 {
412 memcpy (dest, s, l);
413 dest += l;
414 }
415 }
416 if (!dry)
417 *dest = 0;
418 return rv;
419 }
420
421 /*
422 * This routine looks to see if the user has an alias defined for the given
423 * address.
424 */
425 ADDRESS *alias_reverse_lookup (ADDRESS *a)
426 {
427 ALIAS *t = Aliases;
428 ADDRESS *ap;
429
430 if (!a || !a->mailbox)
431 return NULL;
432
433 for (; t; t = t->next)
434 {
435 /* cycle through all addresses if this is a group alias */
436 for (ap = t->addr; ap; ap = ap->next)
437 {
438 if (!ap->group && ap->mailbox &&
439 ascii_strcasecmp (ap->mailbox, a->mailbox) == 0)
440 return ap;
441 }
442 }
443 return 0;
444 }
445
446 /* alias_complete() -- alias completion routine
447 *
448 * given a partial alias, this routine attempts to fill in the alias
449 * from the alias list as much as possible. if given empty search string
450 * or found nothing, present all aliases
451 */
452 int mutt_alias_complete (char *s, size_t buflen)
453 {
454 ALIAS *a = Aliases;
455 ALIAS *a_list = NULL, *a_cur = NULL;
456 char bestname[HUGE_STRING];
457 int i;
458
459 #define min(a,b) ((a<b)?a:b)
460
461 if (s[0] != 0) /* avoid empty string as strstr argument */
462 {
463 memset (bestname, 0, sizeof (bestname));
464
465 while (a)
466 {
467 if (a->name && strstr (a->name, s) == a->name)
468 {
469 if (!bestname[0]) /* init */
470 strfcpy (bestname, a->name,
471 min (mutt_strlen (a->name) + 1, sizeof (bestname)));
472 else
473 {
474 for (i = 0 ; a->name[i] && a->name[i] == bestname[i] ; i++)
475 ;
476 bestname[i] = 0;
477 }
478 }
479 a = a->next;
480 }
481
482 if (bestname[0] != 0)
483 {
484 if (mutt_strcmp (bestname, s) != 0)
485 {
486 /* we are adding something to the completion */
487 strfcpy (s, bestname, mutt_strlen (bestname) + 1);
488 return 1;
489 }
490
491 /* build alias list and show it */
492
493 a = Aliases;
494 while (a)
495 {
496 if (a->name && (strstr (a->name, s) == a->name))
497 {
498 if (!a_list) /* init */
499 a_cur = a_list = (ALIAS *) safe_malloc (sizeof (ALIAS));
500 else
501 {
502 a_cur->next = (ALIAS *) safe_malloc (sizeof (ALIAS));
503 a_cur = a_cur->next;
504 }
505 memcpy (a_cur, a, sizeof (ALIAS));
506 a_cur->next = NULL;
507 }
508 a = a->next;
509 }
510 }
511 }
512
513 bestname[0] = 0;
514 mutt_alias_menu (bestname, sizeof(bestname), a_list ? a_list : Aliases);
515 if (bestname[0] != 0)
516 strfcpy (s, bestname, buflen);
517
518 /* free the alias list */
519 while (a_list)
520 {
521 a_cur = a_list;
522 a_list = a_list->next;
523 FREE (&a_cur);
524 }
525
526 /* remove any aliases marked for deletion */
527 a_list = NULL;
528 for (a_cur = Aliases; a_cur;)
529 {
530 if (a_cur->del)
531 {
532 if (a_list)
533 a_list->next = a_cur->next;
534 else
535 Aliases = a_cur->next;
536
537 a_cur->next = NULL;
538 mutt_free_alias (&a_cur);
539
540 if (a_list)
541 a_cur = a_list;
542 else
543 a_cur = Aliases;
544 }
545 else
546 {
547 a_list = a_cur;
548 a_cur = a_cur->next;
549 }
550 }
551
552 return 0;
553 }
554
555 static int string_is_address(const char *str, const char *u, const char *d)
556 {
557 char buf[LONG_STRING];
558
559 snprintf(buf, sizeof(buf), "%s@%s", NONULL(u), NONULL(d));
560 if (ascii_strcasecmp(str, buf) == 0)
561 return 1;
562
563 return 0;
564 }
565
566 /* returns TRUE if the given address belongs to the user. */
567 int mutt_addr_is_user (ADDRESS *addr)
568 {
569 /* NULL address is assumed to be the user. */
570 if (!addr)
571 {
572 dprint (5, (debugfile, "mutt_addr_is_user: yes, NULL address\n"));
573 return 1;
574 }
575 if (!addr->mailbox)
576 {
577 dprint (5, (debugfile, "mutt_addr_is_user: no, no mailbox\n"));
578 return 0;
579 }
580
581 if (ascii_strcasecmp (addr->mailbox, Username) == 0)
582 {
583 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s\n", addr->mailbox, Username));
584 return 1;
585 }
586 if (string_is_address(addr->mailbox, Username, Hostname))
587 {
588 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, Hostname));
589 return 1;
590 }
591 if (string_is_address(addr->mailbox, Username, mutt_fqdn(0)))
592 {
593 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, mutt_fqdn (0)));
594 return 1;
595 }
596 if (string_is_address(addr->mailbox, Username, mutt_fqdn(1)))
597 {
598 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, mutt_fqdn (1)));
599 return 1;
600 }
601
602 if (From && !ascii_strcasecmp (From->mailbox, addr->mailbox))
603 {
604 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s\n", addr->mailbox, From->mailbox));
605 return 1;
606 }
607
608 if (mutt_match_rx_list (addr->mailbox, Alternates))
609 {
610 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s matched by alternates.\n", addr->mailbox));
611 if (mutt_match_rx_list (addr->mailbox, UnAlternates))
612 dprint (5, (debugfile, "mutt_addr_is_user: but, %s matched by unalternates.\n", addr->mailbox));
613 else
614 return 1;
615 }
616
617 dprint (5, (debugfile, "mutt_addr_is_user: no, all failed.\n"));
618 return 0;
619 }
Something went wrong with that request. Please try again.