mutt stable branch with some hacks
at master 659 lines 15 kB view raw
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 || !*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 mutt_sleep (2); 309 continue; 310 } 311 } 312 while(new->addr == NULL); 313 314 if (adr && adr->personal && !mutt_is_mail_list (adr)) 315 strfcpy (buf, adr->personal, sizeof (buf)); 316 else 317 buf[0] = 0; 318 319 if (mutt_get_field (_("Personal name: "), buf, sizeof (buf), 0) != 0) 320 { 321 mutt_free_alias (&new); 322 return; 323 } 324 new->addr->personal = safe_strdup (buf); 325 326 buf[0] = 0; 327 rfc822_write_address (buf, sizeof (buf), new->addr, 1); 328 snprintf (prompt, sizeof (prompt), _("[%s = %s] Accept?"), new->name, buf); 329 if (mutt_yesorno (prompt, MUTT_YES) != MUTT_YES) 330 { 331 mutt_free_alias (&new); 332 return; 333 } 334 335 mutt_alias_add_reverse (new); 336 337 if ((t = Aliases)) 338 { 339 while (t->next) 340 t = t->next; 341 t->next = new; 342 } 343 else 344 Aliases = new; 345 346 strfcpy (buf, NONULL (AliasFile), sizeof (buf)); 347 if (mutt_get_field (_("Save to file: "), buf, sizeof (buf), MUTT_FILE) != 0) 348 return; 349 mutt_expand_path (buf, sizeof (buf)); 350 if ((rc = fopen (buf, "a+"))) 351 { 352 /* terminate existing file with \n if necessary */ 353 if (fseek (rc, 0, SEEK_END)) 354 goto fseek_err; 355 if (ftell(rc) > 0) 356 { 357 if (fseek (rc, -1, SEEK_CUR) < 0) 358 goto fseek_err; 359 if (fread(buf, 1, 1, rc) != 1) 360 { 361 mutt_perror (_("Error reading alias file")); 362 safe_fclose (&rc); 363 return; 364 } 365 if (fseek (rc, 0, SEEK_END) < 0) 366 goto fseek_err; 367 if (buf[0] != '\n') 368 fputc ('\n', rc); 369 } 370 371 if (mutt_check_alias_name (new->name, NULL, 0)) 372 mutt_quote_filename (buf, sizeof (buf), new->name); 373 else 374 strfcpy (buf, new->name, sizeof (buf)); 375 recode_buf (buf, sizeof (buf)); 376 fprintf (rc, "alias %s ", buf); 377 buf[0] = 0; 378 rfc822_write_address (buf, sizeof (buf), new->addr, 0); 379 recode_buf (buf, sizeof (buf)); 380 write_safe_address (rc, buf); 381 fputc ('\n', rc); 382 safe_fclose (&rc); 383 mutt_message _("Alias added."); 384 } 385 else 386 mutt_perror (buf); 387 388 return; 389 390 fseek_err: 391 mutt_perror (_("Error seeking in alias file")); 392 safe_fclose (&rc); 393 return; 394} 395 396/* 397 * Sanity-check an alias name: Only characters which are non-special to both 398 * the RFC 822 and the mutt configuration parser are permitted. 399 */ 400 401int mutt_check_alias_name (const char *s, char *dest, size_t destlen) 402{ 403 wchar_t wc; 404 mbstate_t mb; 405 size_t l; 406 int rv = 0, bad = 0, dry = !dest || !destlen; 407 408 memset (&mb, 0, sizeof (mbstate_t)); 409 410 if (!dry) 411 destlen--; 412 for (; s && *s && (dry || destlen) && 413 (l = mbrtowc (&wc, s, MB_CUR_MAX, &mb)) != 0; 414 s += l, destlen -= l) 415 { 416 bad = l == (size_t)(-1) || l == (size_t)(-2); /* conversion error */ 417 bad = bad || (!dry && l > destlen); /* too few room for mb char */ 418 if (l == 1) 419 bad = bad || (strchr ("-_+=.", *s) == NULL && !iswalnum (wc)); 420 else 421 bad = bad || !iswalnum (wc); 422 if (bad) 423 { 424 if (dry) 425 return -1; 426 if (l == (size_t)(-1)) 427 memset (&mb, 0, sizeof (mbstate_t)); 428 *dest++ = '_'; 429 rv = -1; 430 } 431 else if (!dry) 432 { 433 memcpy (dest, s, l); 434 dest += l; 435 } 436 } 437 if (!dry) 438 *dest = 0; 439 return rv; 440} 441 442/* 443 * This routine looks to see if the user has an alias defined for the given 444 * address. 445 */ 446ADDRESS *alias_reverse_lookup (ADDRESS *a) 447{ 448 if (!a || !a->mailbox) 449 return NULL; 450 451 return hash_find (ReverseAlias, a->mailbox); 452} 453 454void mutt_alias_add_reverse (ALIAS *t) 455{ 456 ADDRESS *ap; 457 if (!t) 458 return; 459 460 for (ap = t->addr; ap; ap = ap->next) 461 { 462 if (!ap->group && ap->mailbox) 463 hash_insert (ReverseAlias, ap->mailbox, ap, 1); 464 } 465} 466 467void mutt_alias_delete_reverse (ALIAS *t) 468{ 469 ADDRESS *ap; 470 if (!t) 471 return; 472 473 for (ap = t->addr; ap; ap = ap->next) 474 { 475 if (!ap->group && ap->mailbox) 476 hash_delete (ReverseAlias, ap->mailbox, ap, NULL); 477 } 478} 479 480/* alias_complete() -- alias completion routine 481 * 482 * given a partial alias, this routine attempts to fill in the alias 483 * from the alias list as much as possible. if given empty search string 484 * or found nothing, present all aliases 485 */ 486int mutt_alias_complete (char *s, size_t buflen) 487{ 488 ALIAS *a = Aliases; 489 ALIAS *a_list = NULL, *a_cur = NULL; 490 char bestname[HUGE_STRING]; 491 int i; 492 493#ifndef min 494#define min(a,b) ((a<b)?a:b) 495#endif 496 497 if (s[0] != 0) /* avoid empty string as strstr argument */ 498 { 499 memset (bestname, 0, sizeof (bestname)); 500 501 while (a) 502 { 503 if (a->name && strstr (a->name, s) == a->name) 504 { 505 if (!bestname[0]) /* init */ 506 strfcpy (bestname, a->name, 507 min (mutt_strlen (a->name) + 1, sizeof (bestname))); 508 else 509 { 510 for (i = 0 ; a->name[i] && a->name[i] == bestname[i] ; i++) 511 ; 512 bestname[i] = 0; 513 } 514 } 515 a = a->next; 516 } 517 518 if (bestname[0] != 0) 519 { 520 if (mutt_strcmp (bestname, s) != 0) 521 { 522 /* we are adding something to the completion */ 523 strfcpy (s, bestname, mutt_strlen (bestname) + 1); 524 return 1; 525 } 526 527 /* build alias list and show it */ 528 529 a = Aliases; 530 while (a) 531 { 532 if (a->name && (strstr (a->name, s) == a->name)) 533 { 534 if (!a_list) /* init */ 535 a_cur = a_list = (ALIAS *) safe_malloc (sizeof (ALIAS)); 536 else 537 { 538 a_cur->next = (ALIAS *) safe_malloc (sizeof (ALIAS)); 539 a_cur = a_cur->next; 540 } 541 memcpy (a_cur, a, sizeof (ALIAS)); 542 a_cur->next = NULL; 543 } 544 a = a->next; 545 } 546 } 547 } 548 549 bestname[0] = 0; 550 mutt_alias_menu (bestname, sizeof(bestname), a_list ? a_list : Aliases); 551 if (bestname[0] != 0) 552 strfcpy (s, bestname, buflen); 553 554 /* free the alias list */ 555 while (a_list) 556 { 557 a_cur = a_list; 558 a_list = a_list->next; 559 FREE (&a_cur); 560 } 561 562 /* remove any aliases marked for deletion */ 563 a_list = NULL; 564 for (a_cur = Aliases; a_cur;) 565 { 566 if (a_cur->del) 567 { 568 if (a_list) 569 a_list->next = a_cur->next; 570 else 571 Aliases = a_cur->next; 572 573 a_cur->next = NULL; 574 mutt_free_alias (&a_cur); 575 576 if (a_list) 577 a_cur = a_list; 578 else 579 a_cur = Aliases; 580 } 581 else 582 { 583 a_list = a_cur; 584 a_cur = a_cur->next; 585 } 586 } 587 588 return 0; 589} 590 591static int string_is_address(const char *str, const char *u, const char *d) 592{ 593 char buf[LONG_STRING]; 594 595 snprintf(buf, sizeof(buf), "%s@%s", NONULL(u), NONULL(d)); 596 if (ascii_strcasecmp(str, buf) == 0) 597 return 1; 598 599 return 0; 600} 601 602/* returns TRUE if the given address belongs to the user. */ 603int mutt_addr_is_user (ADDRESS *addr) 604{ 605 const char *fqdn; 606 607 /* NULL address is assumed to be the user. */ 608 if (!addr) 609 { 610 dprint (5, (debugfile, "mutt_addr_is_user: yes, NULL address\n")); 611 return 1; 612 } 613 if (!addr->mailbox) 614 { 615 dprint (5, (debugfile, "mutt_addr_is_user: no, no mailbox\n")); 616 return 0; 617 } 618 619 if (ascii_strcasecmp (addr->mailbox, Username) == 0) 620 { 621 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s\n", addr->mailbox, Username)); 622 return 1; 623 } 624 if (string_is_address(addr->mailbox, Username, Hostname)) 625 { 626 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, Hostname)); 627 return 1; 628 } 629 fqdn = mutt_fqdn (0); 630 if (string_is_address(addr->mailbox, Username, fqdn)) 631 { 632 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, NONULL(fqdn))); 633 return 1; 634 } 635 fqdn = mutt_fqdn (1); 636 if (string_is_address(addr->mailbox, Username, fqdn)) 637 { 638 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s @ %s \n", addr->mailbox, Username, NONULL(fqdn))); 639 return 1; 640 } 641 642 if (From && !ascii_strcasecmp (From->mailbox, addr->mailbox)) 643 { 644 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s = %s\n", addr->mailbox, From->mailbox)); 645 return 1; 646 } 647 648 if (mutt_match_rx_list (addr->mailbox, Alternates)) 649 { 650 dprint (5, (debugfile, "mutt_addr_is_user: yes, %s matched by alternates.\n", addr->mailbox)); 651 if (mutt_match_rx_list (addr->mailbox, UnAlternates)) 652 dprint (5, (debugfile, "mutt_addr_is_user: but, %s matched by unalternates.\n", addr->mailbox)); 653 else 654 return 1; 655 } 656 657 dprint (5, (debugfile, "mutt_addr_is_user: no, all failed.\n")); 658 return 0; 659}