mutt stable branch with some hacks
at jcs 668 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 || !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}