mutt stable branch with some hacks
at master 3540 lines 83 kB view raw
1/* 2 * Copyright (C) 1996-2002,2010,2013,2016 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 "mapping.h" 25#include "mutt_curses.h" 26#include "mutt_menu.h" 27#include "mutt_regex.h" 28#include "history.h" 29#include "keymap.h" 30#include "mbyte.h" 31#include "charset.h" 32#include "mutt_crypt.h" 33#include "mutt_idna.h" 34#include "group.h" 35 36#if defined(USE_SSL) 37#include "mutt_ssl.h" 38#endif 39 40 41 42#include "mx.h" 43#include "init.h" 44#include "mailbox.h" 45 46#include <ctype.h> 47#include <stdlib.h> 48#include <unistd.h> 49#include <string.h> 50#include <sys/utsname.h> 51#include <errno.h> 52#include <sys/wait.h> 53#include <sys/time.h> 54 55#define CHECK_PAGER \ 56 if ((CurrentMenu == MENU_PAGER) && (idx >= 0) && \ 57 (MuttVars[idx].flags & R_RESORT)) \ 58 { \ 59 snprintf (err->data, err->dsize, \ 60 _("Not available in this menu.")); \ 61 return (-1); \ 62 } 63 64typedef struct myvar 65{ 66 char *name; 67 char *value; 68 struct myvar* next; 69} myvar_t; 70 71static myvar_t* MyVars; 72 73static int var_to_string (int idx, char* val, size_t len); 74 75static void myvar_set (const char* var, const char* val); 76static const char* myvar_get (const char* var); 77static void myvar_del (const char* var); 78 79extern char **envlist; 80 81static void toggle_quadoption (int opt) 82{ 83 int n = opt/4; 84 int b = (opt % 4) * 2; 85 86 QuadOptions[n] ^= (1 << b); 87} 88 89void set_quadoption (int opt, int flag) 90{ 91 int n = opt/4; 92 int b = (opt % 4) * 2; 93 94 QuadOptions[n] &= ~(0x3 << b); 95 QuadOptions[n] |= (flag & 0x3) << b; 96} 97 98int quadoption (int opt) 99{ 100 int n = opt/4; 101 int b = (opt % 4) * 2; 102 103 return (QuadOptions[n] >> b) & 0x3; 104} 105 106int query_quadoption (int opt, const char *prompt) 107{ 108 int v = quadoption (opt); 109 110 switch (v) 111 { 112 case MUTT_YES: 113 case MUTT_NO: 114 return (v); 115 116 default: 117 v = mutt_yesorno (prompt, (v == MUTT_ASKYES)); 118 mutt_window_clearline (MuttMessageWindow, 0); 119 return (v); 120 } 121 122 /* not reached */ 123} 124 125/* given the variable ``s'', return the index into the rc_vars array which 126 matches, or -1 if the variable is not found. */ 127static int mutt_option_index (char *s) 128{ 129 int i; 130 131 for (i = 0; MuttVars[i].option; i++) 132 if (mutt_strcmp (s, MuttVars[i].option) == 0) 133 return (MuttVars[i].type == DT_SYN ? mutt_option_index ((char *) MuttVars[i].data) : i); 134 return (-1); 135} 136 137int mutt_extract_token (BUFFER *dest, BUFFER *tok, int flags) 138{ 139 char ch; 140 char qc = 0; /* quote char */ 141 char *pc; 142 143 /* reset the destination pointer to the beginning of the buffer */ 144 dest->dptr = dest->data; 145 146 SKIPWS (tok->dptr); 147 while ((ch = *tok->dptr)) 148 { 149 if (!qc) 150 { 151 if ((ISSPACE (ch) && !(flags & MUTT_TOKEN_SPACE)) || 152 (ch == '#' && !(flags & MUTT_TOKEN_COMMENT)) || 153 (ch == '=' && (flags & MUTT_TOKEN_EQUAL)) || 154 (ch == ';' && !(flags & MUTT_TOKEN_SEMICOLON)) || 155 ((flags & MUTT_TOKEN_PATTERN) && strchr ("~%=!|", ch))) 156 break; 157 } 158 159 tok->dptr++; 160 161 if (ch == qc) 162 qc = 0; /* end of quote */ 163 else if (!qc && (ch == '\'' || ch == '"') && !(flags & MUTT_TOKEN_QUOTE)) 164 qc = ch; 165 else if (ch == '\\' && qc != '\'') 166 { 167 if (!*tok->dptr) 168 return -1; /* premature end of token */ 169 switch (ch = *tok->dptr++) 170 { 171 case 'c': 172 case 'C': 173 if (!*tok->dptr) 174 return -1; /* premature end of token */ 175 mutt_buffer_addch (dest, (toupper ((unsigned char) *tok->dptr) 176 - '@') & 0x7f); 177 tok->dptr++; 178 break; 179 case 'r': 180 mutt_buffer_addch (dest, '\r'); 181 break; 182 case 'n': 183 mutt_buffer_addch (dest, '\n'); 184 break; 185 case 't': 186 mutt_buffer_addch (dest, '\t'); 187 break; 188 case 'f': 189 mutt_buffer_addch (dest, '\f'); 190 break; 191 case 'e': 192 mutt_buffer_addch (dest, '\033'); 193 break; 194 default: 195 if (isdigit ((unsigned char) ch) && 196 isdigit ((unsigned char) *tok->dptr) && 197 isdigit ((unsigned char) *(tok->dptr + 1))) 198 { 199 200 mutt_buffer_addch (dest, (ch << 6) + (*tok->dptr << 3) + *(tok->dptr + 1) - 3504); 201 tok->dptr += 2; 202 } 203 else 204 mutt_buffer_addch (dest, ch); 205 } 206 } 207 else if (ch == '^' && (flags & MUTT_TOKEN_CONDENSE)) 208 { 209 if (!*tok->dptr) 210 return -1; /* premature end of token */ 211 ch = *tok->dptr++; 212 if (ch == '^') 213 mutt_buffer_addch (dest, ch); 214 else if (ch == '[') 215 mutt_buffer_addch (dest, '\033'); 216 else if (isalpha ((unsigned char) ch)) 217 mutt_buffer_addch (dest, toupper ((unsigned char) ch) - '@'); 218 else 219 { 220 mutt_buffer_addch (dest, '^'); 221 mutt_buffer_addch (dest, ch); 222 } 223 } 224 else if (ch == '`' && (!qc || qc == '"')) 225 { 226 FILE *fp; 227 pid_t pid; 228 char *cmd, *ptr; 229 size_t expnlen; 230 BUFFER expn; 231 int line = 0; 232 233 pc = tok->dptr; 234 do { 235 if ((pc = strpbrk (pc, "\\`"))) 236 { 237 /* skip any quoted chars */ 238 if (*pc == '\\') 239 pc += 2; 240 } 241 } while (pc && *pc != '`'); 242 if (!pc) 243 { 244 dprint (1, (debugfile, "mutt_get_token: mismatched backticks\n")); 245 return (-1); 246 } 247 cmd = mutt_substrdup (tok->dptr, pc); 248 if ((pid = mutt_create_filter (cmd, NULL, &fp, NULL)) < 0) 249 { 250 dprint (1, (debugfile, "mutt_get_token: unable to fork command: %s", cmd)); 251 FREE (&cmd); 252 return (-1); 253 } 254 FREE (&cmd); 255 256 tok->dptr = pc + 1; 257 258 /* read line */ 259 mutt_buffer_init (&expn); 260 expn.data = mutt_read_line (NULL, &expn.dsize, fp, &line, 0); 261 safe_fclose (&fp); 262 mutt_wait_filter (pid); 263 264 /* if we got output, make a new string consisting of the shell output 265 plus whatever else was left on the original line */ 266 /* BUT: If this is inside a quoted string, directly add output to 267 * the token */ 268 if (expn.data && qc) 269 { 270 mutt_buffer_addstr (dest, expn.data); 271 FREE (&expn.data); 272 } 273 else if (expn.data) 274 { 275 expnlen = mutt_strlen (expn.data); 276 tok->dsize = expnlen + mutt_strlen (tok->dptr) + 1; 277 ptr = safe_malloc (tok->dsize); 278 memcpy (ptr, expn.data, expnlen); 279 strcpy (ptr + expnlen, tok->dptr); /* __STRCPY_CHECKED__ */ 280 if (tok->destroy) 281 FREE (&tok->data); 282 tok->data = ptr; 283 tok->dptr = ptr; 284 tok->destroy = 1; /* mark that the caller should destroy this data */ 285 ptr = NULL; 286 FREE (&expn.data); 287 } 288 } 289 else if (ch == '$' && (!qc || qc == '"') && (*tok->dptr == '{' || isalpha ((unsigned char) *tok->dptr))) 290 { 291 const char *env = NULL; 292 char *var = NULL; 293 int idx; 294 295 if (*tok->dptr == '{') 296 { 297 tok->dptr++; 298 if ((pc = strchr (tok->dptr, '}'))) 299 { 300 var = mutt_substrdup (tok->dptr, pc); 301 tok->dptr = pc + 1; 302 } 303 } 304 else 305 { 306 for (pc = tok->dptr; isalnum ((unsigned char) *pc) || *pc == '_'; pc++) 307 ; 308 var = mutt_substrdup (tok->dptr, pc); 309 tok->dptr = pc; 310 } 311 if (var) 312 { 313 if ((env = getenv (var)) || (env = myvar_get (var))) 314 mutt_buffer_addstr (dest, env); 315 else if ((idx = mutt_option_index (var)) != -1) 316 { 317 /* expand settable mutt variables */ 318 char val[LONG_STRING]; 319 320 if (var_to_string (idx, val, sizeof (val))) 321 mutt_buffer_addstr (dest, val); 322 } 323 FREE (&var); 324 } 325 } 326 else 327 mutt_buffer_addch (dest, ch); 328 } 329 mutt_buffer_addch (dest, 0); /* terminate the string */ 330 SKIPWS (tok->dptr); 331 return 0; 332} 333 334static void mutt_free_opt (struct option_t* p) 335{ 336 REGEXP* pp; 337 338 switch (p->type & DT_MASK) 339 { 340 case DT_ADDR: 341 rfc822_free_address ((ADDRESS**)p->data); 342 break; 343 case DT_RX: 344 pp = (REGEXP*)p->data; 345 FREE (&pp->pattern); 346 if (pp->rx) 347 { 348 regfree (pp->rx); 349 FREE (&pp->rx); 350 } 351 break; 352 case DT_PATH: 353 case DT_STR: 354 FREE ((char**)p->data); /* __FREE_CHECKED__ */ 355 break; 356 } 357} 358 359/* clean up before quitting */ 360void mutt_free_opts (void) 361{ 362 int i; 363 364 for (i = 0; MuttVars[i].option; i++) 365 mutt_free_opt (MuttVars + i); 366 367 mutt_free_rx_list (&Alternates); 368 mutt_free_rx_list (&UnAlternates); 369 mutt_free_rx_list (&MailLists); 370 mutt_free_rx_list (&UnMailLists); 371 mutt_free_rx_list (&SubscribedLists); 372 mutt_free_rx_list (&UnSubscribedLists); 373 mutt_free_rx_list (&NoSpamList); 374} 375 376static void add_to_list (LIST **list, const char *str) 377{ 378 LIST *t, *last = NULL; 379 380 /* don't add a NULL or empty string to the list */ 381 if (!str || *str == '\0') 382 return; 383 384 /* check to make sure the item is not already on this list */ 385 for (last = *list; last; last = last->next) 386 { 387 if (ascii_strcasecmp (str, last->data) == 0) 388 { 389 /* already on the list, so just ignore it */ 390 last = NULL; 391 break; 392 } 393 if (!last->next) 394 break; 395 } 396 397 if (!*list || last) 398 { 399 t = (LIST *) safe_calloc (1, sizeof (LIST)); 400 t->data = safe_strdup (str); 401 if (last) 402 { 403 last->next = t; 404 last = last->next; 405 } 406 else 407 *list = last = t; 408 } 409} 410 411int mutt_add_to_rx_list (RX_LIST **list, const char *s, int flags, BUFFER *err) 412{ 413 RX_LIST *t, *last = NULL; 414 REGEXP *rx; 415 416 if (!s || !*s) 417 return 0; 418 419 if (!(rx = mutt_compile_regexp (s, flags))) 420 { 421 snprintf (err->data, err->dsize, "Bad regexp: %s\n", s); 422 return -1; 423 } 424 425 /* check to make sure the item is not already on this list */ 426 for (last = *list; last; last = last->next) 427 { 428 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) 429 { 430 /* already on the list, so just ignore it */ 431 last = NULL; 432 break; 433 } 434 if (!last->next) 435 break; 436 } 437 438 if (!*list || last) 439 { 440 t = mutt_new_rx_list(); 441 t->rx = rx; 442 if (last) 443 { 444 last->next = t; 445 last = last->next; 446 } 447 else 448 *list = last = t; 449 } 450 else /* duplicate */ 451 mutt_free_regexp (&rx); 452 453 return 0; 454} 455 456static int remove_from_spam_list (SPAM_LIST **list, const char *pat); 457 458static int add_to_spam_list (SPAM_LIST **list, const char *pat, const char *templ, BUFFER *err) 459{ 460 SPAM_LIST *t = NULL, *last = NULL; 461 REGEXP *rx; 462 int n; 463 const char *p; 464 465 if (!pat || !*pat || !templ) 466 return 0; 467 468 if (!(rx = mutt_compile_regexp (pat, REG_ICASE))) 469 { 470 snprintf (err->data, err->dsize, _("Bad regexp: %s"), pat); 471 return -1; 472 } 473 474 /* check to make sure the item is not already on this list */ 475 for (last = *list; last; last = last->next) 476 { 477 if (ascii_strcasecmp (rx->pattern, last->rx->pattern) == 0) 478 { 479 /* Already on the list. Formerly we just skipped this case, but 480 * now we're supporting removals, which means we're supporting 481 * re-adds conceptually. So we probably want this to imply a 482 * removal, then do an add. We can achieve the removal by freeing 483 * the template, and leaving t pointed at the current item. 484 */ 485 t = last; 486 FREE(&t->template); 487 break; 488 } 489 if (!last->next) 490 break; 491 } 492 493 /* If t is set, it's pointing into an extant SPAM_LIST* that we want to 494 * update. Otherwise we want to make a new one to link at the list's end. 495 */ 496 if (!t) 497 { 498 t = mutt_new_spam_list(); 499 t->rx = rx; 500 if (last) 501 last->next = t; 502 else 503 *list = t; 504 } 505 506 /* Now t is the SPAM_LIST* that we want to modify. It is prepared. */ 507 t->template = safe_strdup(templ); 508 509 /* Find highest match number in template string */ 510 t->nmatch = 0; 511 for (p = templ; *p;) 512 { 513 if (*p == '%') 514 { 515 n = atoi(++p); 516 if (n > t->nmatch) 517 t->nmatch = n; 518 while (*p && isdigit((int)*p)) 519 ++p; 520 } 521 else 522 ++p; 523 } 524 525 if (t->nmatch > t->rx->rx->re_nsub) 526 { 527 snprintf (err->data, err->dsize, _("Not enough subexpressions for spam " 528 "template")); 529 remove_from_spam_list(list, pat); 530 return -1; 531 } 532 533 t->nmatch++; /* match 0 is always the whole expr */ 534 535 return 0; 536} 537 538static int remove_from_spam_list (SPAM_LIST **list, const char *pat) 539{ 540 SPAM_LIST *spam, *prev; 541 int nremoved = 0; 542 543 /* Being first is a special case. */ 544 spam = *list; 545 if (!spam) 546 return 0; 547 if (spam->rx && !mutt_strcmp(spam->rx->pattern, pat)) 548 { 549 *list = spam->next; 550 mutt_free_regexp(&spam->rx); 551 FREE(&spam->template); 552 FREE(&spam); 553 return 1; 554 } 555 556 prev = spam; 557 for (spam = prev->next; spam;) 558 { 559 if (!mutt_strcmp(spam->rx->pattern, pat)) 560 { 561 prev->next = spam->next; 562 mutt_free_regexp(&spam->rx); 563 FREE(&spam->template); 564 FREE(&spam); 565 spam = prev->next; 566 ++nremoved; 567 } 568 else 569 spam = spam->next; 570 } 571 572 return nremoved; 573} 574 575 576static void remove_from_list (LIST **l, const char *str) 577{ 578 LIST *p, *last = NULL; 579 580 if (mutt_strcmp ("*", str) == 0) 581 mutt_free_list (l); /* ``unCMD *'' means delete all current entries */ 582 else 583 { 584 p = *l; 585 last = NULL; 586 while (p) 587 { 588 if (ascii_strcasecmp (str, p->data) == 0) 589 { 590 FREE (&p->data); 591 if (last) 592 last->next = p->next; 593 else 594 (*l) = p->next; 595 FREE (&p); 596 } 597 else 598 { 599 last = p; 600 p = p->next; 601 } 602 } 603 } 604} 605 606static void free_mbchar_table (mbchar_table **t) 607{ 608 if (!t || !*t) 609 return; 610 611 FREE (&(*t)->chars); 612 FREE (&(*t)->segmented_str); 613 FREE (&(*t)->orig_str); 614 FREE (t); /* __FREE_CHECKED__ */ 615} 616 617static mbchar_table *parse_mbchar_table (const char *s) 618{ 619 mbchar_table *t; 620 size_t slen, k; 621 mbstate_t mbstate; 622 char *d; 623 624 t = safe_calloc (1, sizeof (mbchar_table)); 625 slen = mutt_strlen (s); 626 if (!slen) 627 return t; 628 629 t->orig_str = safe_strdup (s); 630 /* This could be more space efficient. However, being used on tiny 631 * strings (Tochars and StChars), the overhead is not great. */ 632 t->chars = safe_calloc (slen, sizeof (char *)); 633 d = t->segmented_str = safe_calloc (slen * 2, sizeof (char)); 634 635 memset (&mbstate, 0, sizeof (mbstate)); 636 while (slen && (k = mbrtowc (NULL, s, slen, &mbstate))) 637 { 638 if (k == (size_t)(-1) || k == (size_t)(-2)) 639 { 640 dprint (1, (debugfile, 641 "parse_mbchar_table: mbrtowc returned %d converting %s in %s\n", 642 (k == (size_t)(-1)) ? -1 : -2, 643 s, t->orig_str)); 644 if (k == (size_t)(-1)) 645 memset (&mbstate, 0, sizeof (mbstate)); 646 k = (k == (size_t)(-1)) ? 1 : slen; 647 } 648 649 slen -= k; 650 t->chars[t->len++] = d; 651 while (k--) 652 *d++ = *s++; 653 *d++ = '\0'; 654 } 655 656 return t; 657} 658 659static int parse_unignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 660{ 661 do 662 { 663 mutt_extract_token (buf, s, 0); 664 665 /* don't add "*" to the unignore list */ 666 if (strcmp (buf->data, "*")) 667 add_to_list (&UnIgnore, buf->data); 668 669 remove_from_list (&Ignore, buf->data); 670 } 671 while (MoreArgs (s)); 672 673 return 0; 674} 675 676static int parse_ignore (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 677{ 678 do 679 { 680 mutt_extract_token (buf, s, 0); 681 remove_from_list (&UnIgnore, buf->data); 682 add_to_list (&Ignore, buf->data); 683 } 684 while (MoreArgs (s)); 685 686 return 0; 687} 688 689static int parse_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 690{ 691 do 692 { 693 mutt_extract_token (buf, s, 0); 694 add_to_list ((LIST **) data, buf->data); 695 } 696 while (MoreArgs (s)); 697 698 return 0; 699} 700 701static void _alternates_clean (void) 702{ 703 int i; 704 if (Context && Context->msgcount) 705 { 706 for (i = 0; i < Context->msgcount; i++) 707 Context->hdrs[i]->recip_valid = 0; 708 } 709} 710 711static int parse_alternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 712{ 713 group_context_t *gc = NULL; 714 715 _alternates_clean(); 716 717 do 718 { 719 mutt_extract_token (buf, s, 0); 720 721 if (parse_group_context (&gc, buf, s, data, err) == -1) 722 goto bail; 723 724 mutt_remove_from_rx_list (&UnAlternates, buf->data); 725 726 if (mutt_add_to_rx_list (&Alternates, buf->data, REG_ICASE, err) != 0) 727 goto bail; 728 729 if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0) 730 goto bail; 731 } 732 while (MoreArgs (s)); 733 734 mutt_group_context_destroy (&gc); 735 return 0; 736 737 bail: 738 mutt_group_context_destroy (&gc); 739 return -1; 740} 741 742static int parse_unalternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 743{ 744 _alternates_clean(); 745 do 746 { 747 mutt_extract_token (buf, s, 0); 748 mutt_remove_from_rx_list (&Alternates, buf->data); 749 750 if (mutt_strcmp (buf->data, "*") && 751 mutt_add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0) 752 return -1; 753 754 } 755 while (MoreArgs (s)); 756 757 return 0; 758} 759 760static int parse_spam_list (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 761{ 762 BUFFER templ; 763 764 mutt_buffer_init (&templ); 765 766 /* Insist on at least one parameter */ 767 if (!MoreArgs(s)) 768 { 769 if (data == MUTT_SPAM) 770 strfcpy(err->data, _("spam: no matching pattern"), err->dsize); 771 else 772 strfcpy(err->data, _("nospam: no matching pattern"), err->dsize); 773 return -1; 774 } 775 776 /* Extract the first token, a regexp */ 777 mutt_extract_token (buf, s, 0); 778 779 /* data should be either MUTT_SPAM or MUTT_NOSPAM. MUTT_SPAM is for spam commands. */ 780 if (data == MUTT_SPAM) 781 { 782 /* If there's a second parameter, it's a template for the spam tag. */ 783 if (MoreArgs(s)) 784 { 785 mutt_extract_token (&templ, s, 0); 786 787 /* Add to the spam list. */ 788 if (add_to_spam_list (&SpamList, buf->data, templ.data, err) != 0) { 789 FREE(&templ.data); 790 return -1; 791 } 792 FREE(&templ.data); 793 } 794 795 /* If not, try to remove from the nospam list. */ 796 else 797 { 798 mutt_remove_from_rx_list(&NoSpamList, buf->data); 799 } 800 801 return 0; 802 } 803 804 /* MUTT_NOSPAM is for nospam commands. */ 805 else if (data == MUTT_NOSPAM) 806 { 807 /* nospam only ever has one parameter. */ 808 809 /* "*" is a special case. */ 810 if (!mutt_strcmp(buf->data, "*")) 811 { 812 mutt_free_spam_list (&SpamList); 813 mutt_free_rx_list (&NoSpamList); 814 return 0; 815 } 816 817 /* If it's on the spam list, just remove it. */ 818 if (remove_from_spam_list(&SpamList, buf->data) != 0) 819 return 0; 820 821 /* Otherwise, add it to the nospam list. */ 822 if (mutt_add_to_rx_list (&NoSpamList, buf->data, REG_ICASE, err) != 0) 823 return -1; 824 825 return 0; 826 } 827 828 /* This should not happen. */ 829 strfcpy(err->data, "This is no good at all.", err->dsize); 830 return -1; 831} 832 833 834static int parse_unlist (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 835{ 836 do 837 { 838 mutt_extract_token (buf, s, 0); 839 /* 840 * Check for deletion of entire list 841 */ 842 if (mutt_strcmp (buf->data, "*") == 0) 843 { 844 mutt_free_list ((LIST **) data); 845 break; 846 } 847 remove_from_list ((LIST **) data, buf->data); 848 } 849 while (MoreArgs (s)); 850 851 return 0; 852} 853 854static int parse_lists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 855{ 856 group_context_t *gc = NULL; 857 858 do 859 { 860 mutt_extract_token (buf, s, 0); 861 862 if (parse_group_context (&gc, buf, s, data, err) == -1) 863 goto bail; 864 865 mutt_remove_from_rx_list (&UnMailLists, buf->data); 866 867 if (mutt_add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0) 868 goto bail; 869 870 if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0) 871 goto bail; 872 } 873 while (MoreArgs (s)); 874 875 mutt_group_context_destroy (&gc); 876 return 0; 877 878 bail: 879 mutt_group_context_destroy (&gc); 880 return -1; 881} 882 883typedef enum group_state_t { 884 NONE, RX, ADDR 885} group_state_t; 886 887static int parse_group (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 888{ 889 group_context_t *gc = NULL; 890 group_state_t state = NONE; 891 ADDRESS *addr = NULL; 892 char *estr = NULL; 893 894 do 895 { 896 mutt_extract_token (buf, s, 0); 897 if (parse_group_context (&gc, buf, s, data, err) == -1) 898 goto bail; 899 900 if (data == MUTT_UNGROUP && !mutt_strcasecmp (buf->data, "*")) 901 { 902 if (mutt_group_context_clear (&gc) < 0) 903 goto bail; 904 goto out; 905 } 906 907 if (!mutt_strcasecmp (buf->data, "-rx")) 908 state = RX; 909 else if (!mutt_strcasecmp (buf->data, "-addr")) 910 state = ADDR; 911 else 912 { 913 switch (state) 914 { 915 case NONE: 916 snprintf (err->data, err->dsize, _("%sgroup: missing -rx or -addr."), 917 data == MUTT_UNGROUP ? "un" : ""); 918 goto bail; 919 920 case RX: 921 if (data == MUTT_GROUP && 922 mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0) 923 goto bail; 924 else if (data == MUTT_UNGROUP && 925 mutt_group_context_remove_rx (gc, buf->data) < 0) 926 goto bail; 927 break; 928 929 case ADDR: 930 if ((addr = mutt_parse_adrlist (NULL, buf->data)) == NULL) 931 goto bail; 932 if (mutt_addrlist_to_intl (addr, &estr)) 933 { 934 snprintf (err->data, err->dsize, _("%sgroup: warning: bad IDN '%s'.\n"), 935 data == 1 ? "un" : "", estr); 936 goto bail; 937 } 938 if (data == MUTT_GROUP) 939 mutt_group_context_add_adrlist (gc, addr); 940 else if (data == MUTT_UNGROUP) 941 mutt_group_context_remove_adrlist (gc, addr); 942 rfc822_free_address (&addr); 943 break; 944 } 945 } 946 } while (MoreArgs (s)); 947 948out: 949 mutt_group_context_destroy (&gc); 950 return 0; 951 952bail: 953 mutt_group_context_destroy (&gc); 954 return -1; 955} 956 957/* always wise to do what someone else did before */ 958static void _attachments_clean (void) 959{ 960 int i; 961 if (Context && Context->msgcount) 962 { 963 for (i = 0; i < Context->msgcount; i++) 964 Context->hdrs[i]->attach_valid = 0; 965 } 966} 967 968static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err) 969{ 970 ATTACH_MATCH *a; 971 LIST *listp, *lastp; 972 char *p; 973 char *tmpminor; 974 int len; 975 int ret; 976 977 /* Find the last item in the list that data points to. */ 978 lastp = NULL; 979 dprint(5, (debugfile, "parse_attach_list: ldata = %p, *ldata = %p\n", 980 (void *)ldata, (void *)*ldata)); 981 for (listp = *ldata; listp; listp = listp->next) 982 { 983 a = (ATTACH_MATCH *)listp->data; 984 dprint(5, (debugfile, "parse_attach_list: skipping %s/%s\n", 985 a->major, a->minor)); 986 lastp = listp; 987 } 988 989 do 990 { 991 mutt_extract_token (buf, s, 0); 992 993 if (!buf->data || *buf->data == '\0') 994 continue; 995 996 a = safe_malloc(sizeof(ATTACH_MATCH)); 997 998 /* some cheap hacks that I expect to remove */ 999 if (!ascii_strcasecmp(buf->data, "any")) 1000 a->major = safe_strdup("*/.*"); 1001 else if (!ascii_strcasecmp(buf->data, "none")) 1002 a->major = safe_strdup("cheap_hack/this_should_never_match"); 1003 else 1004 a->major = safe_strdup(buf->data); 1005 1006 if ((p = strchr(a->major, '/'))) 1007 { 1008 *p = '\0'; 1009 ++p; 1010 a->minor = p; 1011 } 1012 else 1013 { 1014 a->minor = "unknown"; 1015 } 1016 1017 len = strlen(a->minor); 1018 tmpminor = safe_malloc(len+3); 1019 strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */ 1020 tmpminor[0] = '^'; 1021 tmpminor[len+1] = '$'; 1022 tmpminor[len+2] = '\0'; 1023 1024 a->major_int = mutt_check_mime_type(a->major); 1025 ret = REGCOMP(&a->minor_rx, tmpminor, REG_ICASE); 1026 1027 FREE(&tmpminor); 1028 1029 if (ret) 1030 { 1031 regerror(ret, &a->minor_rx, err->data, err->dsize); 1032 FREE(&a->major); 1033 FREE(&a); 1034 return -1; 1035 } 1036 1037 dprint(5, (debugfile, "parse_attach_list: added %s/%s [%d]\n", 1038 a->major, a->minor, a->major_int)); 1039 1040 listp = safe_malloc(sizeof(LIST)); 1041 listp->data = (char *)a; 1042 listp->next = NULL; 1043 if (lastp) 1044 { 1045 lastp->next = listp; 1046 } 1047 else 1048 { 1049 *ldata = listp; 1050 } 1051 lastp = listp; 1052 } 1053 while (MoreArgs (s)); 1054 1055 _attachments_clean(); 1056 return 0; 1057} 1058 1059static int parse_unattach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err) 1060{ 1061 ATTACH_MATCH *a; 1062 LIST *lp, *lastp, *newlp; 1063 char *tmp; 1064 int major; 1065 char *minor; 1066 1067 do 1068 { 1069 mutt_extract_token (buf, s, 0); 1070 1071 if (!ascii_strcasecmp(buf->data, "any")) 1072 tmp = safe_strdup("*/.*"); 1073 else if (!ascii_strcasecmp(buf->data, "none")) 1074 tmp = safe_strdup("cheap_hack/this_should_never_match"); 1075 else 1076 tmp = safe_strdup(buf->data); 1077 1078 if ((minor = strchr(tmp, '/'))) 1079 { 1080 *minor = '\0'; 1081 ++minor; 1082 } 1083 else 1084 { 1085 minor = "unknown"; 1086 } 1087 major = mutt_check_mime_type(tmp); 1088 1089 /* We must do our own walk here because remove_from_list() will only 1090 * remove the LIST->data, not anything pointed to by the LIST->data. */ 1091 lastp = NULL; 1092 for(lp = *ldata; lp; ) 1093 { 1094 a = (ATTACH_MATCH *)lp->data; 1095 dprint(5, (debugfile, "parse_unattach_list: check %s/%s [%d] : %s/%s [%d]\n", 1096 a->major, a->minor, a->major_int, tmp, minor, major)); 1097 if (a->major_int == major && !mutt_strcasecmp(minor, a->minor)) 1098 { 1099 dprint(5, (debugfile, "parse_unattach_list: removed %s/%s [%d]\n", 1100 a->major, a->minor, a->major_int)); 1101 regfree(&a->minor_rx); 1102 FREE(&a->major); 1103 1104 /* Relink backward */ 1105 if (lastp) 1106 lastp->next = lp->next; 1107 else 1108 *ldata = lp->next; 1109 1110 newlp = lp->next; 1111 FREE(&lp->data); /* same as a */ 1112 FREE(&lp); 1113 lp = newlp; 1114 continue; 1115 } 1116 1117 lastp = lp; 1118 lp = lp->next; 1119 } 1120 1121 } 1122 while (MoreArgs (s)); 1123 1124 FREE(&tmp); 1125 _attachments_clean(); 1126 return 0; 1127} 1128 1129static int print_attach_list (LIST *lp, char op, char *name) 1130{ 1131 while (lp) { 1132 printf("attachments %c%s %s/%s\n", op, name, 1133 ((ATTACH_MATCH *)lp->data)->major, 1134 ((ATTACH_MATCH *)lp->data)->minor); 1135 lp = lp->next; 1136 } 1137 1138 return 0; 1139} 1140 1141 1142static int parse_attachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 1143{ 1144 char op, *category; 1145 LIST **listp; 1146 1147 mutt_extract_token(buf, s, 0); 1148 if (!buf->data || *buf->data == '\0') { 1149 strfcpy(err->data, _("attachments: no disposition"), err->dsize); 1150 return -1; 1151 } 1152 1153 category = buf->data; 1154 op = *category++; 1155 1156 if (op == '?') { 1157 mutt_endwin (NULL); 1158 fflush (stdout); 1159 printf("\nCurrent attachments settings:\n\n"); 1160 print_attach_list(AttachAllow, '+', "A"); 1161 print_attach_list(AttachExclude, '-', "A"); 1162 print_attach_list(InlineAllow, '+', "I"); 1163 print_attach_list(InlineExclude, '-', "I"); 1164 set_option (OPTFORCEREDRAWINDEX); 1165 set_option (OPTFORCEREDRAWPAGER); 1166 mutt_any_key_to_continue (NULL); 1167 return 0; 1168 } 1169 1170 if (op != '+' && op != '-') { 1171 op = '+'; 1172 category--; 1173 } 1174 if (!ascii_strncasecmp(category, "attachment", strlen(category))) { 1175 if (op == '+') 1176 listp = &AttachAllow; 1177 else 1178 listp = &AttachExclude; 1179 } 1180 else if (!ascii_strncasecmp(category, "inline", strlen(category))) { 1181 if (op == '+') 1182 listp = &InlineAllow; 1183 else 1184 listp = &InlineExclude; 1185 } 1186 else { 1187 strfcpy(err->data, _("attachments: invalid disposition"), err->dsize); 1188 return -1; 1189 } 1190 1191 return parse_attach_list(buf, s, listp, err); 1192} 1193 1194static int parse_unattachments (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 1195{ 1196 char op, *p; 1197 LIST **listp; 1198 1199 mutt_extract_token(buf, s, 0); 1200 if (!buf->data || *buf->data == '\0') { 1201 strfcpy(err->data, _("unattachments: no disposition"), err->dsize); 1202 return -1; 1203 } 1204 1205 p = buf->data; 1206 op = *p++; 1207 if (op != '+' && op != '-') { 1208 op = '+'; 1209 p--; 1210 } 1211 if (!ascii_strncasecmp(p, "attachment", strlen(p))) { 1212 if (op == '+') 1213 listp = &AttachAllow; 1214 else 1215 listp = &AttachExclude; 1216 } 1217 else if (!ascii_strncasecmp(p, "inline", strlen(p))) { 1218 if (op == '+') 1219 listp = &InlineAllow; 1220 else 1221 listp = &InlineExclude; 1222 } 1223 else { 1224 strfcpy(err->data, _("unattachments: invalid disposition"), err->dsize); 1225 return -1; 1226 } 1227 1228 return parse_unattach_list(buf, s, listp, err); 1229} 1230 1231static int parse_unlists (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 1232{ 1233 do 1234 { 1235 mutt_extract_token (buf, s, 0); 1236 mutt_remove_from_rx_list (&SubscribedLists, buf->data); 1237 mutt_remove_from_rx_list (&MailLists, buf->data); 1238 1239 if (mutt_strcmp (buf->data, "*") && 1240 mutt_add_to_rx_list (&UnMailLists, buf->data, REG_ICASE, err) != 0) 1241 return -1; 1242 } 1243 while (MoreArgs (s)); 1244 1245 return 0; 1246} 1247 1248static int parse_subscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 1249{ 1250 group_context_t *gc = NULL; 1251 1252 do 1253 { 1254 mutt_extract_token (buf, s, 0); 1255 1256 if (parse_group_context (&gc, buf, s, data, err) == -1) 1257 goto bail; 1258 1259 mutt_remove_from_rx_list (&UnMailLists, buf->data); 1260 mutt_remove_from_rx_list (&UnSubscribedLists, buf->data); 1261 1262 if (mutt_add_to_rx_list (&MailLists, buf->data, REG_ICASE, err) != 0) 1263 goto bail; 1264 if (mutt_add_to_rx_list (&SubscribedLists, buf->data, REG_ICASE, err) != 0) 1265 goto bail; 1266 if (mutt_group_context_add_rx (gc, buf->data, REG_ICASE, err) != 0) 1267 goto bail; 1268 } 1269 while (MoreArgs (s)); 1270 1271 mutt_group_context_destroy (&gc); 1272 return 0; 1273 1274 bail: 1275 mutt_group_context_destroy (&gc); 1276 return -1; 1277} 1278 1279static int parse_unsubscribe (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 1280{ 1281 do 1282 { 1283 mutt_extract_token (buf, s, 0); 1284 mutt_remove_from_rx_list (&SubscribedLists, buf->data); 1285 1286 if (mutt_strcmp (buf->data, "*") && 1287 mutt_add_to_rx_list (&UnSubscribedLists, buf->data, REG_ICASE, err) != 0) 1288 return -1; 1289 } 1290 while (MoreArgs (s)); 1291 1292 return 0; 1293} 1294 1295static int parse_unalias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 1296{ 1297 ALIAS *tmp, *last = NULL; 1298 1299 do 1300 { 1301 mutt_extract_token (buf, s, 0); 1302 1303 if (mutt_strcmp ("*", buf->data) == 0) 1304 { 1305 if (CurrentMenu == MENU_ALIAS) 1306 { 1307 for (tmp = Aliases; tmp ; tmp = tmp->next) 1308 tmp->del = 1; 1309 set_option (OPTFORCEREDRAWINDEX); 1310 } 1311 else 1312 mutt_free_alias (&Aliases); 1313 break; 1314 } 1315 else 1316 for (tmp = Aliases; tmp; tmp = tmp->next) 1317 { 1318 if (mutt_strcasecmp (buf->data, tmp->name) == 0) 1319 { 1320 if (CurrentMenu == MENU_ALIAS) 1321 { 1322 tmp->del = 1; 1323 set_option (OPTFORCEREDRAWINDEX); 1324 break; 1325 } 1326 1327 if (last) 1328 last->next = tmp->next; 1329 else 1330 Aliases = tmp->next; 1331 tmp->next = NULL; 1332 mutt_free_alias (&tmp); 1333 break; 1334 } 1335 last = tmp; 1336 } 1337 } 1338 while (MoreArgs (s)); 1339 return 0; 1340} 1341 1342static int parse_alias (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 1343{ 1344 ALIAS *tmp = Aliases; 1345 ALIAS *last = NULL; 1346 char *estr = NULL; 1347 group_context_t *gc = NULL; 1348 1349 if (!MoreArgs (s)) 1350 { 1351 strfcpy (err->data, _("alias: no address"), err->dsize); 1352 return (-1); 1353 } 1354 1355 mutt_extract_token (buf, s, 0); 1356 1357 if (parse_group_context (&gc, buf, s, data, err) == -1) 1358 return -1; 1359 1360 /* check to see if an alias with this name already exists */ 1361 for (; tmp; tmp = tmp->next) 1362 { 1363 if (!mutt_strcasecmp (tmp->name, buf->data)) 1364 break; 1365 last = tmp; 1366 } 1367 1368 if (!tmp) 1369 { 1370 /* create a new alias */ 1371 tmp = (ALIAS *) safe_calloc (1, sizeof (ALIAS)); 1372 tmp->self = tmp; 1373 tmp->name = safe_strdup (buf->data); 1374 /* give the main addressbook code a chance */ 1375 if (CurrentMenu == MENU_ALIAS) 1376 set_option (OPTMENUCALLER); 1377 } 1378 else 1379 { 1380 mutt_alias_delete_reverse (tmp); 1381 /* override the previous value */ 1382 rfc822_free_address (&tmp->addr); 1383 if (CurrentMenu == MENU_ALIAS) 1384 set_option (OPTFORCEREDRAWINDEX); 1385 } 1386 1387 mutt_extract_token (buf, s, MUTT_TOKEN_QUOTE | MUTT_TOKEN_SPACE | MUTT_TOKEN_SEMICOLON); 1388 dprint (3, (debugfile, "parse_alias: Second token is '%s'.\n", 1389 buf->data)); 1390 1391 tmp->addr = mutt_parse_adrlist (tmp->addr, buf->data); 1392 1393 if (last) 1394 last->next = tmp; 1395 else 1396 Aliases = tmp; 1397 if (mutt_addrlist_to_intl (tmp->addr, &estr)) 1398 { 1399 snprintf (err->data, err->dsize, _("Warning: Bad IDN '%s' in alias '%s'.\n"), 1400 estr, tmp->name); 1401 goto bail; 1402 } 1403 1404 mutt_group_context_add_adrlist (gc, tmp->addr); 1405 mutt_alias_add_reverse (tmp); 1406 1407#ifdef DEBUG 1408 if (debuglevel >= 2) 1409 { 1410 ADDRESS *a; 1411 /* A group is terminated with an empty address, so check a->mailbox */ 1412 for (a = tmp->addr; a && a->mailbox; a = a->next) 1413 { 1414 if (!a->group) 1415 dprint (3, (debugfile, "parse_alias: %s\n", 1416 a->mailbox)); 1417 else 1418 dprint (3, (debugfile, "parse_alias: Group %s\n", 1419 a->mailbox)); 1420 } 1421 } 1422#endif 1423 mutt_group_context_destroy (&gc); 1424 return 0; 1425 1426 bail: 1427 mutt_group_context_destroy (&gc); 1428 return -1; 1429} 1430 1431static int 1432parse_unmy_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 1433{ 1434 LIST *last = NULL; 1435 LIST *tmp = UserHeader; 1436 LIST *ptr; 1437 size_t l; 1438 1439 do 1440 { 1441 mutt_extract_token (buf, s, 0); 1442 if (mutt_strcmp ("*", buf->data) == 0) 1443 mutt_free_list (&UserHeader); 1444 else 1445 { 1446 tmp = UserHeader; 1447 last = NULL; 1448 1449 l = mutt_strlen (buf->data); 1450 if (buf->data[l - 1] == ':') 1451 l--; 1452 1453 while (tmp) 1454 { 1455 if (ascii_strncasecmp (buf->data, tmp->data, l) == 0 && tmp->data[l] == ':') 1456 { 1457 ptr = tmp; 1458 if (last) 1459 last->next = tmp->next; 1460 else 1461 UserHeader = tmp->next; 1462 tmp = tmp->next; 1463 ptr->next = NULL; 1464 mutt_free_list (&ptr); 1465 } 1466 else 1467 { 1468 last = tmp; 1469 tmp = tmp->next; 1470 } 1471 } 1472 } 1473 } 1474 while (MoreArgs (s)); 1475 return 0; 1476} 1477 1478static int parse_my_hdr (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 1479{ 1480 LIST *tmp; 1481 size_t keylen; 1482 char *p; 1483 1484 mutt_extract_token (buf, s, MUTT_TOKEN_SPACE | MUTT_TOKEN_QUOTE); 1485 if ((p = strpbrk (buf->data, ": \t")) == NULL || *p != ':') 1486 { 1487 strfcpy (err->data, _("invalid header field"), err->dsize); 1488 return (-1); 1489 } 1490 keylen = p - buf->data + 1; 1491 1492 if (UserHeader) 1493 { 1494 for (tmp = UserHeader; ; tmp = tmp->next) 1495 { 1496 /* see if there is already a field by this name */ 1497 if (ascii_strncasecmp (buf->data, tmp->data, keylen) == 0) 1498 { 1499 /* replace the old value */ 1500 FREE (&tmp->data); 1501 tmp->data = buf->data; 1502 mutt_buffer_init (buf); 1503 return 0; 1504 } 1505 if (!tmp->next) 1506 break; 1507 } 1508 tmp->next = mutt_new_list (); 1509 tmp = tmp->next; 1510 } 1511 else 1512 { 1513 tmp = mutt_new_list (); 1514 UserHeader = tmp; 1515 } 1516 tmp->data = buf->data; 1517 mutt_buffer_init (buf); 1518 return 0; 1519} 1520 1521static int 1522parse_sort (short *val, const char *s, const struct mapping_t *map, BUFFER *err) 1523{ 1524 int i, flags = 0; 1525 1526 if (mutt_strncmp ("reverse-", s, 8) == 0) 1527 { 1528 s += 8; 1529 flags = SORT_REVERSE; 1530 } 1531 1532 if (mutt_strncmp ("last-", s, 5) == 0) 1533 { 1534 s += 5; 1535 flags |= SORT_LAST; 1536 } 1537 1538 if ((i = mutt_getvaluebyname (s, map)) == -1) 1539 { 1540 snprintf (err->data, err->dsize, _("%s: unknown sorting method"), s); 1541 return (-1); 1542 } 1543 1544 *val = i | flags; 1545 1546 return 0; 1547} 1548 1549static void mutt_set_default (struct option_t *p) 1550{ 1551 switch (p->type & DT_MASK) 1552 { 1553 case DT_STR: 1554 if (!p->init && *((char **) p->data)) 1555 p->init = (unsigned long) safe_strdup (* ((char **) p->data)); 1556 break; 1557 case DT_PATH: 1558 if (!p->init && *((char **) p->data)) 1559 { 1560 char *cp = safe_strdup (*((char **) p->data)); 1561 /* mutt_pretty_mailbox (cp); */ 1562 p->init = (unsigned long) cp; 1563 } 1564 break; 1565 case DT_ADDR: 1566 if (!p->init && *((ADDRESS **) p->data)) 1567 { 1568 char tmp[HUGE_STRING]; 1569 *tmp = '\0'; 1570 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) p->data), 0); 1571 p->init = (unsigned long) safe_strdup (tmp); 1572 } 1573 break; 1574 case DT_RX: 1575 { 1576 REGEXP *pp = (REGEXP *) p->data; 1577 if (!p->init && pp->pattern) 1578 p->init = (unsigned long) safe_strdup (pp->pattern); 1579 break; 1580 } 1581 } 1582} 1583 1584static void mutt_restore_default (struct option_t *p) 1585{ 1586 switch (p->type & DT_MASK) 1587 { 1588 case DT_STR: 1589 mutt_str_replace ((char **) p->data, (char *) p->init); 1590 break; 1591 case DT_MBCHARTBL: 1592 free_mbchar_table ((mbchar_table **)p->data); 1593 *((mbchar_table **) p->data) = parse_mbchar_table ((char *) p->init); 1594 break; 1595 case DT_PATH: 1596 FREE((char **) p->data); /* __FREE_CHECKED__ */ 1597 if (p->init) 1598 { 1599 char path[_POSIX_PATH_MAX]; 1600 strfcpy (path, (char *) p->init, sizeof (path)); 1601 mutt_expand_path (path, sizeof (path)); 1602 *((char **) p->data) = safe_strdup (path); 1603 } 1604 break; 1605 case DT_ADDR: 1606 rfc822_free_address ((ADDRESS **) p->data); 1607 if (p->init) 1608 *((ADDRESS **) p->data) = rfc822_parse_adrlist (NULL, (char *) p->init); 1609 break; 1610 case DT_BOOL: 1611 if (p->init) 1612 set_option (p->data); 1613 else 1614 unset_option (p->data); 1615 break; 1616 case DT_QUAD: 1617 set_quadoption (p->data, p->init); 1618 break; 1619 case DT_NUM: 1620 case DT_SORT: 1621 case DT_MAGIC: 1622 *((short *) p->data) = p->init; 1623 break; 1624 case DT_RX: 1625 { 1626 REGEXP *pp = (REGEXP *) p->data; 1627 int flags = 0; 1628 1629 FREE (&pp->pattern); 1630 if (pp->rx) 1631 { 1632 regfree (pp->rx); 1633 FREE (&pp->rx); 1634 } 1635 1636 if (p->init) 1637 { 1638 char *s = (char *) p->init; 1639 1640 pp->rx = safe_calloc (1, sizeof (regex_t)); 1641 pp->pattern = safe_strdup ((char *) p->init); 1642 if (mutt_strcmp (p->option, "mask") != 0) 1643 flags |= mutt_which_case ((const char *) p->init); 1644 if (mutt_strcmp (p->option, "mask") == 0 && *s == '!') 1645 { 1646 s++; 1647 pp->not = 1; 1648 } 1649 if (REGCOMP (pp->rx, s, flags) != 0) 1650 { 1651 fprintf (stderr, _("mutt_restore_default(%s): error in regexp: %s\n"), 1652 p->option, pp->pattern); 1653 FREE (&pp->pattern); 1654 FREE (&pp->rx); 1655 } 1656 } 1657 } 1658 break; 1659 } 1660 1661 if (p->flags & R_INDEX) 1662 set_option (OPTFORCEREDRAWINDEX); 1663 if (p->flags & R_PAGER) 1664 set_option (OPTFORCEREDRAWPAGER); 1665 if (p->flags & R_RESORT_SUB) 1666 set_option (OPTSORTSUBTHREADS); 1667 if (p->flags & R_RESORT) 1668 set_option (OPTNEEDRESORT); 1669 if (p->flags & R_RESORT_INIT) 1670 set_option (OPTRESORTINIT); 1671 if (p->flags & R_TREE) 1672 set_option (OPTREDRAWTREE); 1673 if (p->flags & R_REFLOW) 1674 mutt_reflow_windows (); 1675#ifdef USE_SIDEBAR 1676 if (p->flags & R_SIDEBAR) 1677 SidebarNeedsRedraw = 1; 1678#endif 1679} 1680 1681static size_t escape_string (char *dst, size_t len, const char* src) 1682{ 1683 char* p = dst; 1684 1685 if (!len) 1686 return 0; 1687 len--; /* save room for \0 */ 1688#define ESC_CHAR(C) do { *p++ = '\\'; if (p - dst < len) *p++ = C; } while(0) 1689 while (p - dst < len && src && *src) 1690 { 1691 switch (*src) 1692 { 1693 case '\n': 1694 ESC_CHAR('n'); 1695 break; 1696 case '\r': 1697 ESC_CHAR('r'); 1698 break; 1699 case '\t': 1700 ESC_CHAR('t'); 1701 break; 1702 default: 1703 if ((*src == '\\' || *src == '"') && p - dst < len - 1) 1704 *p++ = '\\'; 1705 *p++ = *src; 1706 } 1707 src++; 1708 } 1709#undef ESC_CHAR 1710 *p = '\0'; 1711 return p - dst; 1712} 1713 1714static void pretty_var (char *dst, size_t len, const char *option, const char *val) 1715{ 1716 char *p; 1717 1718 if (!len) 1719 return; 1720 1721 strfcpy (dst, option, len); 1722 len--; /* save room for \0 */ 1723 p = dst + mutt_strlen (dst); 1724 1725 if (p - dst < len) 1726 *p++ = '='; 1727 if (p - dst < len) 1728 *p++ = '"'; 1729 p += escape_string (p, len - (p - dst) + 1, val); /* \0 terminate it */ 1730 if (p - dst < len) 1731 *p++ = '"'; 1732 *p = 0; 1733} 1734 1735static int check_charset (struct option_t *opt, const char *val) 1736{ 1737 char *p, *q = NULL, *s = safe_strdup (val); 1738 int rc = 0, strict = strcmp (opt->option, "send_charset") == 0; 1739 1740 if (!s) 1741 return rc; 1742 1743 for (p = strtok_r (s, ":", &q); p; p = strtok_r (NULL, ":", &q)) 1744 { 1745 if (!*p) 1746 continue; 1747 if (mutt_check_charset (p, strict) < 0) 1748 { 1749 rc = -1; 1750 break; 1751 } 1752 } 1753 1754 FREE(&s); 1755 return rc; 1756} 1757 1758static int parse_setenv(BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err) 1759{ 1760 int query, unset, len; 1761 char work[LONG_STRING]; 1762 char **save, **envp = envlist; 1763 int count = 0; 1764 1765 query = 0; 1766 unset = data & MUTT_SET_UNSET; 1767 1768 if (!MoreArgs (s)) 1769 { 1770 strfcpy (err->data, _("too few arguments"), err->dsize); 1771 return -1; 1772 } 1773 1774 if (*s->dptr == '?') 1775 { 1776 query = 1; 1777 s->dptr++; 1778 } 1779 1780 /* get variable name */ 1781 mutt_extract_token (tmp, s, MUTT_TOKEN_EQUAL); 1782 len = strlen (tmp->data); 1783 1784 if (query) 1785 { 1786 int found = 0; 1787 while (envp && *envp) 1788 { 1789 if (!mutt_strncmp (tmp->data, *envp, len)) 1790 { 1791 if (!found) 1792 { 1793 mutt_endwin (NULL); 1794 found = 1; 1795 } 1796 puts (*envp); 1797 } 1798 envp++; 1799 } 1800 1801 if (found) 1802 { 1803 set_option (OPTFORCEREDRAWINDEX); 1804 set_option (OPTFORCEREDRAWPAGER); 1805 mutt_any_key_to_continue (NULL); 1806 return 0; 1807 } 1808 1809 snprintf (err->data, err->dsize, _("%s is unset"), tmp->data); 1810 return -1; 1811 } 1812 1813 if (unset) 1814 { 1815 count = 0; 1816 while (envp && *envp) 1817 { 1818 if (!mutt_strncmp (tmp->data, *envp, len) && (*envp)[len] == '=') 1819 { 1820 /* shuffle down */ 1821 save = envp++; 1822 while (*envp) 1823 { 1824 *save++ = *envp++; 1825 count++; 1826 } 1827 *save = NULL; 1828 safe_realloc (&envlist, sizeof(char *) * (count+1)); 1829 return 0; 1830 } 1831 envp++; 1832 count++; 1833 } 1834 return -1; 1835 } 1836 1837 if (*s->dptr == '=') 1838 { 1839 s->dptr++; 1840 SKIPWS (s->dptr); 1841 } 1842 1843 if (!MoreArgs (s)) 1844 { 1845 strfcpy (err->data, _("too few arguments"), err->dsize); 1846 return -1; 1847 } 1848 1849 /* Look for current slot to overwrite */ 1850 count = 0; 1851 while (envp && *envp) 1852 { 1853 if (!mutt_strncmp (tmp->data, *envp, len) && (*envp)[len] == '=') 1854 { 1855 FREE (envp); /* __FREE_CHECKED__ */ 1856 break; 1857 } 1858 envp++; 1859 count++; 1860 } 1861 1862 /* Format var=value string */ 1863 strfcpy (work, tmp->data, sizeof(work)); 1864 len = strlen (work); 1865 work[len++] = '='; 1866 mutt_extract_token (tmp, s, 0); 1867 strfcpy (&work[len], tmp->data, sizeof(work)-len); 1868 1869 /* If slot found, overwrite */ 1870 if (*envp) 1871 *envp = safe_strdup (work); 1872 1873 /* If not found, add new slot */ 1874 else 1875 { 1876 *envp = safe_strdup (work); 1877 count++; 1878 safe_realloc (&envlist, sizeof(char *) * (count + 1)); 1879 envlist[count] = NULL; 1880 } 1881 1882 return 0; 1883} 1884 1885static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err) 1886{ 1887 int query, unset, inv, reset, r = 0; 1888 int idx = -1; 1889 const char *p; 1890 char scratch[_POSIX_PATH_MAX]; 1891 char* myvar; 1892 1893 while (MoreArgs (s)) 1894 { 1895 /* reset state variables */ 1896 query = 0; 1897 unset = data & MUTT_SET_UNSET; 1898 inv = data & MUTT_SET_INV; 1899 reset = data & MUTT_SET_RESET; 1900 myvar = NULL; 1901 1902 if (*s->dptr == '?') 1903 { 1904 query = 1; 1905 s->dptr++; 1906 } 1907 else if (mutt_strncmp ("no", s->dptr, 2) == 0) 1908 { 1909 s->dptr += 2; 1910 unset = !unset; 1911 } 1912 else if (mutt_strncmp ("inv", s->dptr, 3) == 0) 1913 { 1914 s->dptr += 3; 1915 inv = !inv; 1916 } 1917 else if (*s->dptr == '&') 1918 { 1919 reset = 1; 1920 s->dptr++; 1921 } 1922 1923 /* get the variable name */ 1924 mutt_extract_token (tmp, s, MUTT_TOKEN_EQUAL); 1925 1926 if (!mutt_strncmp ("my_", tmp->data, 3)) 1927 myvar = tmp->data; 1928 else if ((idx = mutt_option_index (tmp->data)) == -1 && 1929 !(reset && !mutt_strcmp ("all", tmp->data))) 1930 { 1931 snprintf (err->data, err->dsize, _("%s: unknown variable"), tmp->data); 1932 return (-1); 1933 } 1934 SKIPWS (s->dptr); 1935 1936 if (reset) 1937 { 1938 if (query || unset || inv) 1939 { 1940 snprintf (err->data, err->dsize, _("prefix is illegal with reset")); 1941 return (-1); 1942 } 1943 1944 if (s && *s->dptr == '=') 1945 { 1946 snprintf (err->data, err->dsize, _("value is illegal with reset")); 1947 return (-1); 1948 } 1949 1950 if (!mutt_strcmp ("all", tmp->data)) 1951 { 1952 if (CurrentMenu == MENU_PAGER) 1953 { 1954 snprintf (err->data, err->dsize, _("Not available in this menu.")); 1955 return (-1); 1956 } 1957 for (idx = 0; MuttVars[idx].option; idx++) 1958 mutt_restore_default (&MuttVars[idx]); 1959 set_option (OPTFORCEREDRAWINDEX); 1960 set_option (OPTFORCEREDRAWPAGER); 1961 set_option (OPTSORTSUBTHREADS); 1962 set_option (OPTNEEDRESORT); 1963 set_option (OPTRESORTINIT); 1964 set_option (OPTREDRAWTREE); 1965 return 0; 1966 } 1967 else 1968 { 1969 CHECK_PAGER; 1970 if (myvar) 1971 myvar_del (myvar); 1972 else 1973 mutt_restore_default (&MuttVars[idx]); 1974 } 1975 } 1976 else if (!myvar && DTYPE (MuttVars[idx].type) == DT_BOOL) 1977 { 1978 if (s && *s->dptr == '=') 1979 { 1980 if (unset || inv || query) 1981 { 1982 snprintf (err->data, err->dsize, _("Usage: set variable=yes|no")); 1983 return (-1); 1984 } 1985 1986 s->dptr++; 1987 mutt_extract_token (tmp, s, 0); 1988 if (ascii_strcasecmp ("yes", tmp->data) == 0) 1989 unset = inv = 0; 1990 else if (ascii_strcasecmp ("no", tmp->data) == 0) 1991 unset = 1; 1992 else 1993 { 1994 snprintf (err->data, err->dsize, _("Usage: set variable=yes|no")); 1995 return (-1); 1996 } 1997 } 1998 1999 if (query) 2000 { 2001 snprintf (err->data, err->dsize, option (MuttVars[idx].data) 2002 ? _("%s is set") : _("%s is unset"), tmp->data); 2003 return 0; 2004 } 2005 2006 CHECK_PAGER; 2007 if (unset) 2008 unset_option (MuttVars[idx].data); 2009 else if (inv) 2010 toggle_option (MuttVars[idx].data); 2011 else 2012 set_option (MuttVars[idx].data); 2013 } 2014 else if (myvar || DTYPE (MuttVars[idx].type) == DT_STR || 2015 DTYPE (MuttVars[idx].type) == DT_PATH || 2016 DTYPE (MuttVars[idx].type) == DT_ADDR || 2017 DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) 2018 { 2019 if (unset) 2020 { 2021 CHECK_PAGER; 2022 if (myvar) 2023 myvar_del (myvar); 2024 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) 2025 rfc822_free_address ((ADDRESS **) MuttVars[idx].data); 2026 else if (DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) 2027 free_mbchar_table ((mbchar_table **) MuttVars[idx].data); 2028 else 2029 /* MuttVars[idx].data is already 'char**' (or some 'void**') or... 2030 * so cast to 'void*' is okay */ 2031 FREE ((void *) MuttVars[idx].data); /* __FREE_CHECKED__ */ 2032 } 2033 else if (query || *s->dptr != '=') 2034 { 2035 char _tmp[LONG_STRING]; 2036 const char *val = NULL; 2037 2038 if (myvar) 2039 { 2040 if ((val = myvar_get (myvar))) 2041 { 2042 pretty_var (err->data, err->dsize, myvar, val); 2043 break; 2044 } 2045 else 2046 { 2047 snprintf (err->data, err->dsize, _("%s: unknown variable"), myvar); 2048 return (-1); 2049 } 2050 } 2051 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) 2052 { 2053 _tmp[0] = '\0'; 2054 rfc822_write_address (_tmp, sizeof (_tmp), *((ADDRESS **) MuttVars[idx].data), 0); 2055 val = _tmp; 2056 } 2057 else if (DTYPE (MuttVars[idx].type) == DT_PATH) 2058 { 2059 _tmp[0] = '\0'; 2060 strfcpy (_tmp, NONULL(*((char **) MuttVars[idx].data)), sizeof (_tmp)); 2061 mutt_pretty_mailbox (_tmp, sizeof (_tmp)); 2062 val = _tmp; 2063 } 2064 else if (DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) 2065 { 2066 mbchar_table *mbt = (*((mbchar_table **) MuttVars[idx].data)); 2067 val = mbt ? NONULL (mbt->orig_str) : ""; 2068 } 2069 else 2070 val = *((char **) MuttVars[idx].data); 2071 2072 /* user requested the value of this variable */ 2073 pretty_var (err->data, err->dsize, MuttVars[idx].option, NONULL(val)); 2074 break; 2075 } 2076 else 2077 { 2078 CHECK_PAGER; 2079 s->dptr++; 2080 2081 if (myvar) 2082 { 2083 /* myvar is a pointer to tmp and will be lost on extract_token */ 2084 myvar = safe_strdup (myvar); 2085 myvar_del (myvar); 2086 } 2087 2088 mutt_extract_token (tmp, s, 0); 2089 2090 if (myvar) 2091 { 2092 myvar_set (myvar, tmp->data); 2093 FREE (&myvar); 2094 myvar="don't resort"; 2095 } 2096 else if (DTYPE (MuttVars[idx].type) == DT_PATH) 2097 { 2098 /* MuttVars[idx].data is already 'char**' (or some 'void**') or... 2099 * so cast to 'void*' is okay */ 2100 FREE ((void *) MuttVars[idx].data); /* __FREE_CHECKED__ */ 2101 2102 strfcpy (scratch, tmp->data, sizeof (scratch)); 2103 mutt_expand_path (scratch, sizeof (scratch)); 2104 *((char **) MuttVars[idx].data) = safe_strdup (scratch); 2105 } 2106 else if (DTYPE (MuttVars[idx].type) == DT_STR) 2107 { 2108 if ((strstr (MuttVars[idx].option, "charset") && 2109 check_charset (&MuttVars[idx], tmp->data) < 0) | 2110 /* $charset can't be empty, others can */ 2111 (strcmp(MuttVars[idx].option, "charset") == 0 && ! *tmp->data)) 2112 { 2113 snprintf (err->data, err->dsize, _("Invalid value for option %s: \"%s\""), 2114 MuttVars[idx].option, tmp->data); 2115 return (-1); 2116 } 2117 2118 FREE ((void *) MuttVars[idx].data); /* __FREE_CHECKED__ */ 2119 *((char **) MuttVars[idx].data) = safe_strdup (tmp->data); 2120 if (mutt_strcmp (MuttVars[idx].option, "charset") == 0) 2121 mutt_set_charset (Charset); 2122 } 2123 else if (DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) 2124 { 2125 free_mbchar_table ((mbchar_table **) MuttVars[idx].data); 2126 *((mbchar_table **) MuttVars[idx].data) = parse_mbchar_table (tmp->data); 2127 } 2128 else 2129 { 2130 rfc822_free_address ((ADDRESS **) MuttVars[idx].data); 2131 *((ADDRESS **) MuttVars[idx].data) = rfc822_parse_adrlist (NULL, tmp->data); 2132 } 2133 } 2134 } 2135 else if (DTYPE(MuttVars[idx].type) == DT_RX) 2136 { 2137 REGEXP *ptr = (REGEXP *) MuttVars[idx].data; 2138 regex_t *rx; 2139 int e, flags = 0; 2140 2141 if (query || *s->dptr != '=') 2142 { 2143 /* user requested the value of this variable */ 2144 pretty_var (err->data, err->dsize, MuttVars[idx].option, NONULL(ptr->pattern)); 2145 break; 2146 } 2147 2148 if (option(OPTATTACHMSG) && !mutt_strcmp(MuttVars[idx].option, "reply_regexp")) 2149 { 2150 snprintf (err->data, err->dsize, "Operation not permitted when in attach-message mode."); 2151 r = -1; 2152 break; 2153 } 2154 2155 CHECK_PAGER; 2156 s->dptr++; 2157 2158 /* copy the value of the string */ 2159 mutt_extract_token (tmp, s, 0); 2160 2161 if (!ptr->pattern || mutt_strcmp (ptr->pattern, tmp->data) != 0) 2162 { 2163 int not = 0; 2164 2165 /* $mask is case-sensitive */ 2166 if (mutt_strcmp (MuttVars[idx].option, "mask") != 0) 2167 flags |= mutt_which_case (tmp->data); 2168 2169 p = tmp->data; 2170 if (mutt_strcmp (MuttVars[idx].option, "mask") == 0) 2171 { 2172 if (*p == '!') 2173 { 2174 not = 1; 2175 p++; 2176 } 2177 } 2178 2179 rx = (regex_t *) safe_malloc (sizeof (regex_t)); 2180 if ((e = REGCOMP (rx, p, flags)) != 0) 2181 { 2182 regerror (e, rx, err->data, err->dsize); 2183 FREE (&rx); 2184 break; 2185 } 2186 2187 /* get here only if everything went smootly */ 2188 if (ptr->pattern) 2189 { 2190 FREE (&ptr->pattern); 2191 regfree ((regex_t *) ptr->rx); 2192 FREE (&ptr->rx); 2193 } 2194 2195 ptr->pattern = safe_strdup (tmp->data); 2196 ptr->rx = rx; 2197 ptr->not = not; 2198 2199 /* $reply_regexp and $alterantes require special treatment */ 2200 2201 if (Context && Context->msgcount && 2202 mutt_strcmp (MuttVars[idx].option, "reply_regexp") == 0) 2203 { 2204 regmatch_t pmatch[1]; 2205 int i; 2206 2207#define CUR_ENV Context->hdrs[i]->env 2208 for (i = 0; i < Context->msgcount; i++) 2209 { 2210 if (CUR_ENV && CUR_ENV->subject) 2211 { 2212 CUR_ENV->real_subj = (regexec (ReplyRegexp.rx, 2213 CUR_ENV->subject, 1, pmatch, 0)) ? 2214 CUR_ENV->subject : 2215 CUR_ENV->subject + pmatch[0].rm_eo; 2216 } 2217 } 2218#undef CUR_ENV 2219 } 2220 } 2221 } 2222 else if (DTYPE(MuttVars[idx].type) == DT_MAGIC) 2223 { 2224 if (query || *s->dptr != '=') 2225 { 2226 switch (DefaultMagic) 2227 { 2228 case MUTT_MBOX: 2229 p = "mbox"; 2230 break; 2231 case MUTT_MMDF: 2232 p = "MMDF"; 2233 break; 2234 case MUTT_MH: 2235 p = "MH"; 2236 break; 2237 case MUTT_MAILDIR: 2238 p = "Maildir"; 2239 break; 2240 default: 2241 p = "unknown"; 2242 break; 2243 } 2244 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, p); 2245 break; 2246 } 2247 2248 CHECK_PAGER; 2249 s->dptr++; 2250 2251 /* copy the value of the string */ 2252 mutt_extract_token (tmp, s, 0); 2253 if (mx_set_magic (tmp->data)) 2254 { 2255 snprintf (err->data, err->dsize, _("%s: invalid mailbox type"), tmp->data); 2256 r = -1; 2257 break; 2258 } 2259 } 2260 else if (DTYPE(MuttVars[idx].type) == DT_NUM) 2261 { 2262 short *ptr = (short *) MuttVars[idx].data; 2263 short val; 2264 int rc; 2265 2266 if (query || *s->dptr != '=') 2267 { 2268 val = *ptr; 2269 /* compatibility alias */ 2270 if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0) 2271 val = *ptr < 0 ? -*ptr : 0; 2272 2273 /* user requested the value of this variable */ 2274 snprintf (err->data, err->dsize, "%s=%d", MuttVars[idx].option, val); 2275 break; 2276 } 2277 2278 CHECK_PAGER; 2279 s->dptr++; 2280 2281 mutt_extract_token (tmp, s, 0); 2282 rc = mutt_atos (tmp->data, (short *) &val); 2283 2284 if (rc < 0 || !*tmp->data) 2285 { 2286 snprintf (err->data, err->dsize, _("%s: invalid value (%s)"), tmp->data, 2287 rc == -1 ? _("format error") : _("number overflow")); 2288 r = -1; 2289 break; 2290 } 2291 else 2292 *ptr = val; 2293 2294 /* these ones need a sanity check */ 2295 if (mutt_strcmp (MuttVars[idx].option, "history") == 0) 2296 { 2297 if (*ptr < 0) 2298 *ptr = 0; 2299 mutt_init_history (); 2300 } 2301 else if (mutt_strcmp (MuttVars[idx].option, "pager_index_lines") == 0) 2302 { 2303 if (*ptr < 0) 2304 *ptr = 0; 2305 } 2306 else if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0) 2307 { 2308 if (*ptr < 0) 2309 *ptr = 0; 2310 else 2311 *ptr = -*ptr; 2312 } 2313#ifdef USE_IMAP 2314 else if (mutt_strcmp (MuttVars[idx].option, "imap_pipeline_depth") == 0) 2315 { 2316 if (*ptr < 0) 2317 *ptr = 0; 2318 } 2319#endif 2320 } 2321 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) 2322 { 2323 if (query) 2324 { 2325 static const char * const vals[] = { "no", "yes", "ask-no", "ask-yes" }; 2326 2327 snprintf (err->data, err->dsize, "%s=%s", MuttVars[idx].option, 2328 vals [ quadoption (MuttVars[idx].data) ]); 2329 break; 2330 } 2331 2332 CHECK_PAGER; 2333 if (*s->dptr == '=') 2334 { 2335 s->dptr++; 2336 mutt_extract_token (tmp, s, 0); 2337 if (ascii_strcasecmp ("yes", tmp->data) == 0) 2338 set_quadoption (MuttVars[idx].data, MUTT_YES); 2339 else if (ascii_strcasecmp ("no", tmp->data) == 0) 2340 set_quadoption (MuttVars[idx].data, MUTT_NO); 2341 else if (ascii_strcasecmp ("ask-yes", tmp->data) == 0) 2342 set_quadoption (MuttVars[idx].data, MUTT_ASKYES); 2343 else if (ascii_strcasecmp ("ask-no", tmp->data) == 0) 2344 set_quadoption (MuttVars[idx].data, MUTT_ASKNO); 2345 else 2346 { 2347 snprintf (err->data, err->dsize, _("%s: invalid value"), tmp->data); 2348 r = -1; 2349 break; 2350 } 2351 } 2352 else 2353 { 2354 if (inv) 2355 toggle_quadoption (MuttVars[idx].data); 2356 else if (unset) 2357 set_quadoption (MuttVars[idx].data, MUTT_NO); 2358 else 2359 set_quadoption (MuttVars[idx].data, MUTT_YES); 2360 } 2361 } 2362 else if (DTYPE (MuttVars[idx].type) == DT_SORT) 2363 { 2364 const struct mapping_t *map = NULL; 2365 2366 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) 2367 { 2368 case DT_SORT_ALIAS: 2369 map = SortAliasMethods; 2370 break; 2371 case DT_SORT_BROWSER: 2372 map = SortBrowserMethods; 2373 break; 2374 case DT_SORT_KEYS: 2375 if ((WithCrypto & APPLICATION_PGP)) 2376 map = SortKeyMethods; 2377 break; 2378 case DT_SORT_AUX: 2379 map = SortAuxMethods; 2380 break; 2381 case DT_SORT_SIDEBAR: 2382 map = SortSidebarMethods; 2383 break; 2384 default: 2385 map = SortMethods; 2386 break; 2387 } 2388 2389 if (!map) 2390 { 2391 snprintf (err->data, err->dsize, _("%s: Unknown type."), MuttVars[idx].option); 2392 r = -1; 2393 break; 2394 } 2395 2396 if (query || *s->dptr != '=') 2397 { 2398 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map); 2399 2400 snprintf (err->data, err->dsize, "%s=%s%s%s", MuttVars[idx].option, 2401 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "", 2402 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "", 2403 p); 2404 return 0; 2405 } 2406 CHECK_PAGER; 2407 s->dptr++; 2408 mutt_extract_token (tmp, s , 0); 2409 2410 if (parse_sort ((short *) MuttVars[idx].data, tmp->data, map, err) == -1) 2411 { 2412 r = -1; 2413 break; 2414 } 2415 } 2416 else 2417 { 2418 snprintf (err->data, err->dsize, _("%s: unknown type"), MuttVars[idx].option); 2419 r = -1; 2420 break; 2421 } 2422 2423 if (!myvar) 2424 { 2425 if (MuttVars[idx].flags & R_INDEX) 2426 set_option (OPTFORCEREDRAWINDEX); 2427 if (MuttVars[idx].flags & R_PAGER) 2428 set_option (OPTFORCEREDRAWPAGER); 2429 if (MuttVars[idx].flags & R_RESORT_SUB) 2430 set_option (OPTSORTSUBTHREADS); 2431 if (MuttVars[idx].flags & R_RESORT) 2432 set_option (OPTNEEDRESORT); 2433 if (MuttVars[idx].flags & R_RESORT_INIT) 2434 set_option (OPTRESORTINIT); 2435 if (MuttVars[idx].flags & R_TREE) 2436 set_option (OPTREDRAWTREE); 2437 if (MuttVars[idx].flags & R_REFLOW) 2438 mutt_reflow_windows (); 2439#ifdef USE_SIDEBAR 2440 if (MuttVars[idx].flags & R_SIDEBAR) 2441 SidebarNeedsRedraw = 1; 2442#endif 2443 } 2444 } 2445 return (r); 2446} 2447 2448#define MAXERRS 128 2449 2450/* reads the specified initialization file. returns -1 if errors were found 2451 so that we can pause to let the user know... */ 2452static int source_rc (const char *rcfile, BUFFER *err) 2453{ 2454 FILE *f; 2455 int line = 0, rc = 0, conv = 0; 2456 BUFFER token; 2457 char *linebuf = NULL; 2458 char *currentline = NULL; 2459 size_t buflen; 2460 pid_t pid; 2461 2462 dprint (2, (debugfile, "Reading configuration file '%s'.\n", 2463 rcfile)); 2464 2465 if ((f = mutt_open_read (rcfile, &pid)) == NULL) 2466 { 2467 snprintf (err->data, err->dsize, "%s: %s", rcfile, strerror (errno)); 2468 return (-1); 2469 } 2470 2471 mutt_buffer_init (&token); 2472 while ((linebuf = mutt_read_line (linebuf, &buflen, f, &line, MUTT_CONT)) != NULL) 2473 { 2474 conv=ConfigCharset && (*ConfigCharset) && Charset; 2475 if (conv) 2476 { 2477 currentline=safe_strdup(linebuf); 2478 if (!currentline) continue; 2479 mutt_convert_string(&currentline, ConfigCharset, Charset, 0); 2480 } 2481 else 2482 currentline=linebuf; 2483 2484 if (mutt_parse_rc_line (currentline, &token, err) == -1) 2485 { 2486 mutt_error (_("Error in %s, line %d: %s"), rcfile, line, err->data); 2487 if (--rc < -MAXERRS) 2488 { 2489 if (conv) FREE(&currentline); 2490 break; 2491 } 2492 } 2493 else 2494 { 2495 if (rc < 0) 2496 rc = -1; 2497 } 2498 if (conv) 2499 FREE(&currentline); 2500 } 2501 FREE (&token.data); 2502 FREE (&linebuf); 2503 safe_fclose (&f); 2504 if (pid != -1) 2505 mutt_wait_filter (pid); 2506 if (rc) 2507 { 2508 /* the muttrc source keyword */ 2509 snprintf (err->data, err->dsize, rc >= -MAXERRS ? _("source: errors in %s") 2510 : _("source: reading aborted due to too many errors in %s"), rcfile); 2511 rc = -1; 2512 } 2513 return (rc); 2514} 2515 2516#undef MAXERRS 2517 2518static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err) 2519{ 2520 char path[_POSIX_PATH_MAX]; 2521 2522 if (mutt_extract_token (tmp, s, 0) != 0) 2523 { 2524 snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr); 2525 return (-1); 2526 } 2527 if (MoreArgs (s)) 2528 { 2529 strfcpy (err->data, _("source: too many arguments"), err->dsize); 2530 return (-1); 2531 } 2532 strfcpy (path, tmp->data, sizeof (path)); 2533 mutt_expand_path (path, sizeof (path)); 2534 return (source_rc (path, err)); 2535} 2536 2537/* line command to execute 2538 2539 token scratch buffer to be used by parser. caller should free 2540 token->data when finished. the reason for this variable is 2541 to avoid having to allocate and deallocate a lot of memory 2542 if we are parsing many lines. the caller can pass in the 2543 memory to use, which avoids having to create new space for 2544 every call to this function. 2545 2546 err where to write error messages */ 2547int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err) 2548{ 2549 int i, r = -1; 2550 BUFFER expn; 2551 2552 if (!line || !*line) 2553 return 0; 2554 2555 mutt_buffer_init (&expn); 2556 expn.data = expn.dptr = line; 2557 expn.dsize = mutt_strlen (line); 2558 2559 *err->data = 0; 2560 2561 SKIPWS (expn.dptr); 2562 while (*expn.dptr) 2563 { 2564 if (*expn.dptr == '#') 2565 break; /* rest of line is a comment */ 2566 if (*expn.dptr == ';') 2567 { 2568 expn.dptr++; 2569 continue; 2570 } 2571 mutt_extract_token (token, &expn, 0); 2572 for (i = 0; Commands[i].name; i++) 2573 { 2574 if (!mutt_strcmp (token->data, Commands[i].name)) 2575 { 2576 if (Commands[i].func (token, &expn, Commands[i].data, err) != 0) 2577 goto finish; 2578 break; 2579 } 2580 } 2581 if (!Commands[i].name) 2582 { 2583 snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data)); 2584 goto finish; 2585 } 2586 } 2587 r = 0; 2588finish: 2589 if (expn.destroy) 2590 FREE (&expn.data); 2591 return (r); 2592} 2593 2594 2595#define NUMVARS (sizeof (MuttVars)/sizeof (MuttVars[0])) 2596#define NUMCOMMANDS (sizeof (Commands)/sizeof (Commands[0])) 2597/* initial string that starts completion. No telling how much crap 2598 * the user has typed so far. Allocate LONG_STRING just to be sure! */ 2599static char User_typed [LONG_STRING] = {0}; 2600 2601static int Num_matched = 0; /* Number of matches for completion */ 2602static char Completed [STRING] = {0}; /* completed string (command or variable) */ 2603static const char **Matches; 2604/* this is a lie until mutt_init runs: */ 2605static int Matches_listsize = MAX(NUMVARS,NUMCOMMANDS) + 10; 2606 2607static void matches_ensure_morespace(int current) 2608{ 2609 int base_space, extra_space, space; 2610 2611 if (current > Matches_listsize - 2) 2612 { 2613 base_space = MAX(NUMVARS,NUMCOMMANDS) + 1; 2614 extra_space = Matches_listsize - base_space; 2615 extra_space *= 2; 2616 space = base_space + extra_space; 2617 safe_realloc (&Matches, space * sizeof (char *)); 2618 memset (&Matches[current + 1], 0, space - current); 2619 Matches_listsize = space; 2620 } 2621} 2622 2623/* helper function for completion. Changes the dest buffer if 2624 necessary/possible to aid completion. 2625 dest == completion result gets here. 2626 src == candidate for completion. 2627 try == user entered data for completion. 2628 len == length of dest buffer. 2629*/ 2630static void candidate (char *dest, char *try, const char *src, int len) 2631{ 2632 int l; 2633 2634 if (strstr (src, try) == src) 2635 { 2636 matches_ensure_morespace (Num_matched); 2637 Matches[Num_matched++] = src; 2638 if (dest[0] == 0) 2639 strfcpy (dest, src, len); 2640 else 2641 { 2642 for (l = 0; src[l] && src[l] == dest[l]; l++); 2643 dest[l] = 0; 2644 } 2645 } 2646} 2647 2648int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs) 2649{ 2650 char *pt = buffer; 2651 int num; 2652 int spaces; /* keep track of the number of leading spaces on the line */ 2653 myvar_t *myv; 2654 2655 SKIPWS (buffer); 2656 spaces = buffer - pt; 2657 2658 pt = buffer + pos - spaces; 2659 while ((pt > buffer) && !isspace ((unsigned char) *pt)) 2660 pt--; 2661 2662 if (pt == buffer) /* complete cmd */ 2663 { 2664 /* first TAB. Collect all the matches */ 2665 if (numtabs == 1) 2666 { 2667 Num_matched = 0; 2668 strfcpy (User_typed, pt, sizeof (User_typed)); 2669 memset (Matches, 0, Matches_listsize); 2670 memset (Completed, 0, sizeof (Completed)); 2671 for (num = 0; Commands[num].name; num++) 2672 candidate (Completed, User_typed, Commands[num].name, sizeof (Completed)); 2673 matches_ensure_morespace (Num_matched); 2674 Matches[Num_matched++] = User_typed; 2675 2676 /* All matches are stored. Longest non-ambiguous string is "" 2677 * i.e. don't change 'buffer'. Fake successful return this time */ 2678 if (User_typed[0] == 0) 2679 return 1; 2680 } 2681 2682 if (Completed[0] == 0 && User_typed[0]) 2683 return 0; 2684 2685 /* Num_matched will _always_ be at least 1 since the initial 2686 * user-typed string is always stored */ 2687 if (numtabs == 1 && Num_matched == 2) 2688 snprintf(Completed, sizeof(Completed),"%s", Matches[0]); 2689 else if (numtabs > 1 && Num_matched > 2) 2690 /* cycle thru all the matches */ 2691 snprintf(Completed, sizeof(Completed), "%s", 2692 Matches[(numtabs - 2) % Num_matched]); 2693 2694 /* return the completed command */ 2695 strncpy (buffer, Completed, len - spaces); 2696 } 2697 else if (!mutt_strncmp (buffer, "set", 3) 2698 || !mutt_strncmp (buffer, "unset", 5) 2699 || !mutt_strncmp (buffer, "reset", 5) 2700 || !mutt_strncmp (buffer, "toggle", 6)) 2701 { /* complete variables */ 2702 static const char * const prefixes[] = { "no", "inv", "?", "&", 0 }; 2703 2704 pt++; 2705 /* loop through all the possible prefixes (no, inv, ...) */ 2706 if (!mutt_strncmp (buffer, "set", 3)) 2707 { 2708 for (num = 0; prefixes[num]; num++) 2709 { 2710 if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num]))) 2711 { 2712 pt += mutt_strlen (prefixes[num]); 2713 break; 2714 } 2715 } 2716 } 2717 2718 /* first TAB. Collect all the matches */ 2719 if (numtabs == 1) 2720 { 2721 Num_matched = 0; 2722 strfcpy (User_typed, pt, sizeof (User_typed)); 2723 memset (Matches, 0, Matches_listsize); 2724 memset (Completed, 0, sizeof (Completed)); 2725 for (num = 0; MuttVars[num].option; num++) 2726 candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed)); 2727 for (myv = MyVars; myv; myv = myv->next) 2728 candidate (Completed, User_typed, myv->name, sizeof (Completed)); 2729 matches_ensure_morespace (Num_matched); 2730 Matches[Num_matched++] = User_typed; 2731 2732 /* All matches are stored. Longest non-ambiguous string is "" 2733 * i.e. don't change 'buffer'. Fake successful return this time */ 2734 if (User_typed[0] == 0) 2735 return 1; 2736 } 2737 2738 if (Completed[0] == 0 && User_typed[0]) 2739 return 0; 2740 2741 /* Num_matched will _always_ be at least 1 since the initial 2742 * user-typed string is always stored */ 2743 if (numtabs == 1 && Num_matched == 2) 2744 snprintf(Completed, sizeof(Completed),"%s", Matches[0]); 2745 else if (numtabs > 1 && Num_matched > 2) 2746 /* cycle thru all the matches */ 2747 snprintf(Completed, sizeof(Completed), "%s", 2748 Matches[(numtabs - 2) % Num_matched]); 2749 2750 strncpy (pt, Completed, buffer + len - pt - spaces); 2751 } 2752 else if (!mutt_strncmp (buffer, "exec", 4)) 2753 { 2754 const struct binding_t *menu = km_get_table (CurrentMenu); 2755 2756 if (!menu && CurrentMenu != MENU_PAGER) 2757 menu = OpGeneric; 2758 2759 pt++; 2760 /* first TAB. Collect all the matches */ 2761 if (numtabs == 1) 2762 { 2763 Num_matched = 0; 2764 strfcpy (User_typed, pt, sizeof (User_typed)); 2765 memset (Matches, 0, Matches_listsize); 2766 memset (Completed, 0, sizeof (Completed)); 2767 for (num = 0; menu[num].name; num++) 2768 candidate (Completed, User_typed, menu[num].name, sizeof (Completed)); 2769 /* try the generic menu */ 2770 if (Completed[0] == 0 && CurrentMenu != MENU_PAGER) 2771 { 2772 menu = OpGeneric; 2773 for (num = 0; menu[num].name; num++) 2774 candidate (Completed, User_typed, menu[num].name, sizeof (Completed)); 2775 } 2776 matches_ensure_morespace (Num_matched); 2777 Matches[Num_matched++] = User_typed; 2778 2779 /* All matches are stored. Longest non-ambiguous string is "" 2780 * i.e. don't change 'buffer'. Fake successful return this time */ 2781 if (User_typed[0] == 0) 2782 return 1; 2783 } 2784 2785 if (Completed[0] == 0 && User_typed[0]) 2786 return 0; 2787 2788 /* Num_matched will _always_ be at least 1 since the initial 2789 * user-typed string is always stored */ 2790 if (numtabs == 1 && Num_matched == 2) 2791 snprintf(Completed, sizeof(Completed),"%s", Matches[0]); 2792 else if (numtabs > 1 && Num_matched > 2) 2793 /* cycle thru all the matches */ 2794 snprintf(Completed, sizeof(Completed), "%s", 2795 Matches[(numtabs - 2) % Num_matched]); 2796 2797 strncpy (pt, Completed, buffer + len - pt - spaces); 2798 } 2799 else 2800 return 0; 2801 2802 return 1; 2803} 2804 2805int mutt_var_value_complete (char *buffer, size_t len, int pos) 2806{ 2807 char var[STRING], *pt = buffer; 2808 int spaces; 2809 2810 if (buffer[0] == 0) 2811 return 0; 2812 2813 SKIPWS (buffer); 2814 spaces = buffer - pt; 2815 2816 pt = buffer + pos - spaces; 2817 while ((pt > buffer) && !isspace ((unsigned char) *pt)) 2818 pt--; 2819 pt++; /* move past the space */ 2820 if (*pt == '=') /* abort if no var before the '=' */ 2821 return 0; 2822 2823 if (mutt_strncmp (buffer, "set", 3) == 0) 2824 { 2825 int idx; 2826 char val[LONG_STRING]; 2827 const char *myvarval; 2828 2829 strfcpy (var, pt, sizeof (var)); 2830 /* ignore the trailing '=' when comparing */ 2831 var[mutt_strlen (var) - 1] = 0; 2832 if ((idx = mutt_option_index (var)) == -1) 2833 { 2834 if ((myvarval = myvar_get(var)) != NULL) 2835 { 2836 pretty_var (pt, len - (pt - buffer), var, myvarval); 2837 return 1; 2838 } 2839 return 0; /* no such variable. */ 2840 } 2841 else if (var_to_string (idx, val, sizeof (val))) 2842 { 2843 snprintf (pt, len - (pt - buffer), "%s=\"%s\"", var, val); 2844 return 1; 2845 } 2846 } 2847 return 0; 2848} 2849 2850static int var_to_string (int idx, char* val, size_t len) 2851{ 2852 char tmp[LONG_STRING]; 2853 static const char * const vals[] = { "no", "yes", "ask-no", "ask-yes" }; 2854 2855 tmp[0] = '\0'; 2856 2857 if ((DTYPE(MuttVars[idx].type) == DT_STR) || 2858 (DTYPE(MuttVars[idx].type) == DT_PATH) || 2859 (DTYPE(MuttVars[idx].type) == DT_RX)) 2860 { 2861 strfcpy (tmp, NONULL (*((char **) MuttVars[idx].data)), sizeof (tmp)); 2862 if (DTYPE (MuttVars[idx].type) == DT_PATH) 2863 mutt_pretty_mailbox (tmp, sizeof (tmp)); 2864 } 2865 else if (DTYPE (MuttVars[idx].type) == DT_MBCHARTBL) 2866 { 2867 mbchar_table *mbt = (*((mbchar_table **) MuttVars[idx].data)); 2868 strfcpy (tmp, mbt ? NONULL (mbt->orig_str) : "", sizeof (tmp)); 2869 } 2870 else if (DTYPE (MuttVars[idx].type) == DT_ADDR) 2871 { 2872 rfc822_write_address (tmp, sizeof (tmp), *((ADDRESS **) MuttVars[idx].data), 0); 2873 } 2874 else if (DTYPE (MuttVars[idx].type) == DT_QUAD) 2875 strfcpy (tmp, vals[quadoption (MuttVars[idx].data)], sizeof (tmp)); 2876 else if (DTYPE (MuttVars[idx].type) == DT_NUM) 2877 { 2878 short sval = *((short *) MuttVars[idx].data); 2879 2880 /* avert your eyes, gentle reader */ 2881 if (mutt_strcmp (MuttVars[idx].option, "wrapmargin") == 0) 2882 sval = sval > 0 ? 0 : -sval; 2883 2884 snprintf (tmp, sizeof (tmp), "%d", sval); 2885 } 2886 else if (DTYPE (MuttVars[idx].type) == DT_SORT) 2887 { 2888 const struct mapping_t *map; 2889 const char *p; 2890 2891 switch (MuttVars[idx].type & DT_SUBTYPE_MASK) 2892 { 2893 case DT_SORT_ALIAS: 2894 map = SortAliasMethods; 2895 break; 2896 case DT_SORT_BROWSER: 2897 map = SortBrowserMethods; 2898 break; 2899 case DT_SORT_KEYS: 2900 if ((WithCrypto & APPLICATION_PGP)) 2901 map = SortKeyMethods; 2902 else 2903 map = SortMethods; 2904 break; 2905 default: 2906 map = SortMethods; 2907 break; 2908 } 2909 p = mutt_getnamebyvalue (*((short *) MuttVars[idx].data) & SORT_MASK, map); 2910 snprintf (tmp, sizeof (tmp), "%s%s%s", 2911 (*((short *) MuttVars[idx].data) & SORT_REVERSE) ? "reverse-" : "", 2912 (*((short *) MuttVars[idx].data) & SORT_LAST) ? "last-" : "", 2913 p); 2914 } 2915 else if (DTYPE (MuttVars[idx].type) == DT_MAGIC) 2916 { 2917 char *p; 2918 2919 switch (DefaultMagic) 2920 { 2921 case MUTT_MBOX: 2922 p = "mbox"; 2923 break; 2924 case MUTT_MMDF: 2925 p = "MMDF"; 2926 break; 2927 case MUTT_MH: 2928 p = "MH"; 2929 break; 2930 case MUTT_MAILDIR: 2931 p = "Maildir"; 2932 break; 2933 default: 2934 p = "unknown"; 2935 } 2936 strfcpy (tmp, p, sizeof (tmp)); 2937 } 2938 else if (DTYPE (MuttVars[idx].type) == DT_BOOL) 2939 strfcpy (tmp, option (MuttVars[idx].data) ? "yes" : "no", sizeof (tmp)); 2940 else 2941 return 0; 2942 2943 escape_string (val, len - 1, tmp); 2944 2945 return 1; 2946} 2947 2948/* Implement the -Q command line flag */ 2949int mutt_query_variables (LIST *queries) 2950{ 2951 LIST *p; 2952 2953 char command[STRING]; 2954 2955 BUFFER err, token; 2956 2957 mutt_buffer_init (&err); 2958 mutt_buffer_init (&token); 2959 2960 err.dsize = STRING; 2961 err.data = safe_malloc (err.dsize); 2962 2963 for (p = queries; p; p = p->next) 2964 { 2965 snprintf (command, sizeof (command), "set ?%s\n", p->data); 2966 if (mutt_parse_rc_line (command, &token, &err) == -1) 2967 { 2968 fprintf (stderr, "%s\n", err.data); 2969 FREE (&token.data); 2970 FREE (&err.data); 2971 2972 return 1; 2973 } 2974 printf ("%s\n", err.data); 2975 } 2976 2977 FREE (&token.data); 2978 FREE (&err.data); 2979 2980 return 0; 2981} 2982 2983/* dump out the value of all the variables we have */ 2984int mutt_dump_variables (void) 2985{ 2986 int i; 2987 2988 char command[STRING]; 2989 2990 BUFFER err, token; 2991 2992 mutt_buffer_init (&err); 2993 mutt_buffer_init (&token); 2994 2995 err.dsize = STRING; 2996 err.data = safe_malloc (err.dsize); 2997 2998 for (i = 0; MuttVars[i].option; i++) 2999 { 3000 if (MuttVars[i].type == DT_SYN) 3001 continue; 3002 3003 snprintf (command, sizeof (command), "set ?%s\n", MuttVars[i].option); 3004 if (mutt_parse_rc_line (command, &token, &err) == -1) 3005 { 3006 fprintf (stderr, "%s\n", err.data); 3007 FREE (&token.data); 3008 FREE (&err.data); 3009 3010 return 1; 3011 } 3012 printf("%s\n", err.data); 3013 } 3014 3015 FREE (&token.data); 3016 FREE (&err.data); 3017 3018 return 0; 3019} 3020 3021const char *mutt_getnamebyvalue (int val, const struct mapping_t *map) 3022{ 3023 int i; 3024 3025 for (i=0; map[i].name; i++) 3026 if (map[i].value == val) 3027 return (map[i].name); 3028 return NULL; 3029} 3030 3031int mutt_getvaluebyname (const char *name, const struct mapping_t *map) 3032{ 3033 int i; 3034 3035 for (i = 0; map[i].name; i++) 3036 if (ascii_strcasecmp (map[i].name, name) == 0) 3037 return (map[i].value); 3038 return (-1); 3039} 3040 3041#ifdef DEBUG 3042static void start_debug (void) 3043{ 3044 int i; 3045 char buf[_POSIX_PATH_MAX]; 3046 char buf2[_POSIX_PATH_MAX]; 3047 3048 /* rotate the old debug logs */ 3049 for (i=3; i>=0; i--) 3050 { 3051 snprintf (buf, sizeof(buf), "%s/.muttdebug%d", NONULL(Homedir), i); 3052 snprintf (buf2, sizeof(buf2), "%s/.muttdebug%d", NONULL(Homedir), i+1); 3053 rename (buf, buf2); 3054 } 3055 if ((debugfile = safe_fopen(buf, "w")) != NULL) 3056 { 3057 setbuf (debugfile, NULL); /* don't buffer the debugging output! */ 3058 dprint(1,(debugfile,"Mutt/%s (%s) debugging at level %d\n", 3059 MUTT_VERSION, ReleaseDate, debuglevel)); 3060 } 3061} 3062#endif 3063 3064static int mutt_execute_commands (LIST *p) 3065{ 3066 BUFFER err, token; 3067 3068 mutt_buffer_init (&err); 3069 err.dsize = STRING; 3070 err.data = safe_malloc (err.dsize); 3071 mutt_buffer_init (&token); 3072 for (; p; p = p->next) 3073 { 3074 if (mutt_parse_rc_line (p->data, &token, &err) != 0) 3075 { 3076 fprintf (stderr, _("Error in command line: %s\n"), err.data); 3077 FREE (&token.data); 3078 FREE (&err.data); 3079 3080 return -1; 3081 } 3082 } 3083 FREE (&token.data); 3084 FREE (&err.data); 3085 3086 return 0; 3087} 3088 3089static void mutt_srandom (void) 3090{ 3091 struct timeval tv; 3092 unsigned seed; 3093 3094 gettimeofday(&tv, NULL); 3095 /* POSIX.1-2008 states that seed is 'unsigned' without specifying its width. 3096 * Use as many of the lower order bits from the current time of day as the seed. 3097 * If the upper bound is truncated, that is fine. 3098 * 3099 * tv_sec is integral of type integer or float. Cast to 'long long' before 3100 * bitshift in case it is a float. 3101 */ 3102 seed = ((LONGLONG) tv.tv_sec << 20) | tv.tv_usec; 3103 srandom(seed); 3104} 3105 3106static char* mutt_find_cfg (const char *home, const char *xdg_cfg_home) 3107{ 3108 const char* names[] = 3109 { 3110 "muttrc-" MUTT_VERSION, 3111 "muttrc", 3112 NULL, 3113 }; 3114 3115 const char* locations[][2] = 3116 { 3117 { home, ".", }, 3118 { home, ".mutt/" }, 3119 { xdg_cfg_home, "mutt/", }, 3120 { NULL, NULL }, 3121 }; 3122 3123 int i; 3124 3125 for (i = 0; locations[i][0] || locations[i][1]; i++) 3126 { 3127 int j; 3128 3129 if (!locations[i][0]) 3130 continue; 3131 3132 for (j = 0; names[j]; j++) 3133 { 3134 char buffer[STRING]; 3135 3136 snprintf (buffer, sizeof (buffer), 3137 "%s/%s%s", locations[i][0], locations[i][1], names[j]); 3138 if (access (buffer, F_OK) == 0) 3139 return safe_strdup(buffer); 3140 } 3141 } 3142 3143 return NULL; 3144} 3145 3146void mutt_init (int skip_sys_rc, LIST *commands) 3147{ 3148 struct passwd *pw; 3149 struct utsname utsname; 3150 char *p, buffer[STRING]; 3151 char *domain = NULL; 3152 int i, need_pause = 0; 3153 BUFFER err; 3154 3155 mutt_buffer_init (&err); 3156 err.dsize = STRING; 3157 err.data = safe_malloc(err.dsize); 3158 err.dptr = err.data; 3159 3160 Groups = hash_create (1031, 0); 3161 ReverseAlias = hash_create (1031, 1); 3162 3163 mutt_menu_init (); 3164 mutt_srandom (); 3165 3166 /* 3167 * XXX - use something even more difficult to predict? 3168 */ 3169 snprintf (AttachmentMarker, sizeof (AttachmentMarker), 3170 "\033]9;%ld\a", (long) time (NULL)); 3171 3172 /* on one of the systems I use, getcwd() does not return the same prefix 3173 as is listed in the passwd file */ 3174 if ((p = getenv ("HOME"))) 3175 Homedir = safe_strdup (p); 3176 3177 /* Get some information about the user */ 3178 if ((pw = getpwuid (getuid ()))) 3179 { 3180 char rnbuf[STRING]; 3181 3182 Username = safe_strdup (pw->pw_name); 3183 if (!Homedir) 3184 Homedir = safe_strdup (pw->pw_dir); 3185 3186 Realname = safe_strdup (mutt_gecos_name (rnbuf, sizeof (rnbuf), pw)); 3187 Shell = safe_strdup (pw->pw_shell); 3188 endpwent (); 3189 } 3190 else 3191 { 3192 if (!Homedir) 3193 { 3194 mutt_endwin (NULL); 3195 fputs (_("unable to determine home directory"), stderr); 3196 exit (1); 3197 } 3198 if ((p = getenv ("USER"))) 3199 Username = safe_strdup (p); 3200 else 3201 { 3202 mutt_endwin (NULL); 3203 fputs (_("unable to determine username"), stderr); 3204 exit (1); 3205 } 3206 Shell = safe_strdup ((p = getenv ("SHELL")) ? p : "/bin/sh"); 3207 } 3208 3209#ifdef DEBUG 3210 /* Start up debugging mode if requested */ 3211 if (debuglevel > 0) 3212 start_debug (); 3213#endif 3214 3215 /* And about the host... */ 3216 3217#ifdef DOMAIN 3218 domain = safe_strdup (DOMAIN); 3219#endif /* DOMAIN */ 3220 3221 /* 3222 * The call to uname() shouldn't fail, but if it does, the system is horribly 3223 * broken, and the system's networking configuration is in an unreliable 3224 * state. We should bail. 3225 */ 3226 if ((uname (&utsname)) == -1) 3227 { 3228 mutt_endwin (NULL); 3229 perror (_("unable to determine nodename via uname()")); 3230 exit (1); 3231 } 3232 3233 /* some systems report the FQDN instead of just the hostname */ 3234 if ((p = strchr (utsname.nodename, '.'))) 3235 Hostname = mutt_substrdup (utsname.nodename, p); 3236 else 3237 Hostname = safe_strdup (utsname.nodename); 3238 3239 /* now get FQDN. Use configured domain first, DNS next, then uname */ 3240 if (domain) 3241 { 3242 /* we have a compile-time domain name, use that for Fqdn */ 3243 Fqdn = safe_malloc (mutt_strlen (domain) + mutt_strlen (Hostname) + 2); 3244 sprintf (Fqdn, "%s.%s", NONULL(Hostname), domain); /* __SPRINTF_CHECKED__ */ 3245 } 3246 else if (!(getdnsdomainname (buffer, sizeof buffer))) 3247 { 3248 Fqdn = safe_malloc (mutt_strlen (buffer) + mutt_strlen (Hostname) + 2); 3249 sprintf (Fqdn, "%s.%s", NONULL(Hostname), buffer); /* __SPRINTF_CHECKED__ */ 3250 } 3251 else 3252 /* 3253 * DNS failed, use the nodename. Whether or not the nodename had a '.' in 3254 * it, we can use the nodename as the FQDN. On hosts where DNS is not 3255 * being used, e.g. small network that relies on hosts files, a short host 3256 * name is all that is required for SMTP to work correctly. It could be 3257 * wrong, but we've done the best we can, at this point the onus is on the 3258 * user to provide the correct hostname if the nodename won't work in their 3259 * network. 3260 */ 3261 Fqdn = safe_strdup(utsname.nodename); 3262 3263 3264 if ((p = getenv ("MAIL"))) 3265 Spoolfile = safe_strdup (p); 3266 else if ((p = getenv ("MAILDIR"))) 3267 Spoolfile = safe_strdup (p); 3268 else 3269 { 3270#ifdef HOMESPOOL 3271 mutt_concat_path (buffer, NONULL (Homedir), MAILPATH, sizeof (buffer)); 3272#else 3273 mutt_concat_path (buffer, MAILPATH, NONULL(Username), sizeof (buffer)); 3274#endif 3275 Spoolfile = safe_strdup (buffer); 3276 } 3277 3278 if ((p = getenv ("MAILCAPS"))) 3279 MailcapPath = safe_strdup (p); 3280 else 3281 { 3282 /* Default search path from RFC1524 */ 3283 MailcapPath = safe_strdup ("~/.mailcap:" PKGDATADIR "/mailcap:" SYSCONFDIR "/mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap"); 3284 } 3285 3286 Tempdir = safe_strdup ((p = getenv ("TMPDIR")) ? p : "/tmp"); 3287 3288 p = getenv ("VISUAL"); 3289 if (!p) 3290 { 3291 p = getenv ("EDITOR"); 3292 if (!p) 3293 p = "vi"; 3294 } 3295 Editor = safe_strdup (p); 3296 Visual = safe_strdup (p); 3297 3298 if ((p = getenv ("REPLYTO")) != NULL) 3299 { 3300 BUFFER buf, token; 3301 3302 snprintf (buffer, sizeof (buffer), "Reply-To: %s", p); 3303 3304 mutt_buffer_init (&buf); 3305 buf.data = buf.dptr = buffer; 3306 buf.dsize = mutt_strlen (buffer); 3307 3308 mutt_buffer_init (&token); 3309 parse_my_hdr (&token, &buf, 0, &err); 3310 FREE (&token.data); 3311 } 3312 3313 if ((p = getenv ("EMAIL")) != NULL) 3314 From = rfc822_parse_adrlist (NULL, p); 3315 3316 mutt_set_langinfo_charset (); 3317 mutt_set_charset (Charset); 3318 3319 Matches = safe_calloc (Matches_listsize, sizeof (char *)); 3320 3321 /* Set standard defaults */ 3322 for (i = 0; MuttVars[i].option; i++) 3323 { 3324 mutt_set_default (&MuttVars[i]); 3325 mutt_restore_default (&MuttVars[i]); 3326 } 3327 3328 CurrentMenu = MENU_MAIN; 3329 3330 3331#ifndef LOCALES_HACK 3332 /* Do we have a locale definition? */ 3333 if (((p = getenv ("LC_ALL")) != NULL && p[0]) || 3334 ((p = getenv ("LANG")) != NULL && p[0]) || 3335 ((p = getenv ("LC_CTYPE")) != NULL && p[0])) 3336 set_option (OPTLOCALES); 3337#endif 3338 3339#ifdef HAVE_GETSID 3340 /* Unset suspend by default if we're the session leader */ 3341 if (getsid(0) == getpid()) 3342 unset_option (OPTSUSPEND); 3343#endif 3344 3345 mutt_init_history (); 3346 3347 /* RFC2368, "4. Unsafe headers" 3348 * The creator of a mailto URL cannot expect the resolver of a URL to 3349 * understand more than the "subject" and "body" headers. Clients that 3350 * resolve mailto URLs into mail messages should be able to correctly 3351 * create RFC 822-compliant mail messages using the "subject" and "body" 3352 * headers. 3353 */ 3354 add_to_list(&MailtoAllow, "body"); 3355 add_to_list(&MailtoAllow, "subject"); 3356 3357 3358 3359 /* 3360 * 3361 * BIG FAT WARNING 3362 * 3363 * When changing the code which looks for a configuration file, 3364 * please also change the corresponding code in muttbug.sh.in. 3365 * 3366 * 3367 */ 3368 3369 if (!Muttrc) 3370 { 3371 char *xdg_cfg_home = getenv ("XDG_CONFIG_HOME"); 3372 3373 if (!xdg_cfg_home && Homedir) 3374 { 3375 snprintf (buffer, sizeof (buffer), "%s/.config", Homedir); 3376 xdg_cfg_home = buffer; 3377 } 3378 3379 Muttrc = mutt_find_cfg (Homedir, xdg_cfg_home); 3380 } 3381 else 3382 { 3383 strfcpy (buffer, Muttrc, sizeof (buffer)); 3384 FREE (&Muttrc); 3385 mutt_expand_path (buffer, sizeof (buffer)); 3386 Muttrc = safe_strdup (buffer); 3387 if (access (Muttrc, F_OK)) 3388 { 3389 snprintf (buffer, sizeof (buffer), "%s: %s", Muttrc, strerror (errno)); 3390 mutt_endwin (buffer); 3391 exit (1); 3392 } 3393 } 3394 3395 if (Muttrc) 3396 { 3397 FREE (&AliasFile); 3398 AliasFile = safe_strdup (Muttrc); 3399 } 3400 3401 /* Process the global rc file if it exists and the user hasn't explicity 3402 requested not to via "-n". */ 3403 if (!skip_sys_rc) 3404 { 3405 snprintf (buffer, sizeof(buffer), "%s/Muttrc-%s", SYSCONFDIR, MUTT_VERSION); 3406 if (access (buffer, F_OK) == -1) 3407 snprintf (buffer, sizeof(buffer), "%s/Muttrc", SYSCONFDIR); 3408 if (access (buffer, F_OK) == -1) 3409 snprintf (buffer, sizeof (buffer), "%s/Muttrc-%s", PKGDATADIR, MUTT_VERSION); 3410 if (access (buffer, F_OK) == -1) 3411 snprintf (buffer, sizeof (buffer), "%s/Muttrc", PKGDATADIR); 3412 if (access (buffer, F_OK) != -1) 3413 { 3414 if (source_rc (buffer, &err) != 0) 3415 { 3416 fputs (err.data, stderr); 3417 fputc ('\n', stderr); 3418 need_pause = 1; 3419 } 3420 } 3421 } 3422 3423 /* Read the user's initialization file. */ 3424 if (Muttrc) 3425 { 3426 if (!option (OPTNOCURSES)) 3427 endwin (); 3428 if (source_rc (Muttrc, &err) != 0) 3429 { 3430 fputs (err.data, stderr); 3431 fputc ('\n', stderr); 3432 need_pause = 1; 3433 } 3434 } 3435 3436 if (mutt_execute_commands (commands) != 0) 3437 need_pause = 1; 3438 3439 if (need_pause && !option (OPTNOCURSES)) 3440 { 3441 if (mutt_any_key_to_continue (NULL) == -1) 3442 mutt_exit(1); 3443 } 3444 3445 mutt_read_histfile (); 3446 3447#if 0 3448 set_option (OPTWEED); /* turn weeding on by default */ 3449#endif 3450 3451 FREE (&err.data); 3452} 3453 3454int mutt_get_hook_type (const char *name) 3455{ 3456 const struct command_t *c; 3457 3458 for (c = Commands ; c->name ; c++) 3459 if (c->func == mutt_parse_hook && ascii_strcasecmp (c->name, name) == 0) 3460 return c->data; 3461 return 0; 3462} 3463 3464static int parse_group_context (group_context_t **ctx, BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err) 3465{ 3466 while (!mutt_strcasecmp (buf->data, "-group")) 3467 { 3468 if (!MoreArgs (s)) 3469 { 3470 strfcpy (err->data, _("-group: no group name"), err->dsize); 3471 goto bail; 3472 } 3473 3474 mutt_extract_token (buf, s, 0); 3475 3476 mutt_group_context_add (ctx, mutt_pattern_group (buf->data)); 3477 3478 if (!MoreArgs (s)) 3479 { 3480 strfcpy (err->data, _("out of arguments"), err->dsize); 3481 goto bail; 3482 } 3483 3484 mutt_extract_token (buf, s, 0); 3485 } 3486 3487 return 0; 3488 3489 bail: 3490 mutt_group_context_destroy (ctx); 3491 return -1; 3492} 3493 3494static void myvar_set (const char* var, const char* val) 3495{ 3496 myvar_t** cur; 3497 3498 for (cur = &MyVars; *cur; cur = &((*cur)->next)) 3499 if (!mutt_strcmp ((*cur)->name, var)) 3500 break; 3501 3502 if (!*cur) 3503 *cur = safe_calloc (1, sizeof (myvar_t)); 3504 3505 if (!(*cur)->name) 3506 (*cur)->name = safe_strdup (var); 3507 3508 mutt_str_replace (&(*cur)->value, val); 3509} 3510 3511static void myvar_del (const char* var) 3512{ 3513 myvar_t **cur; 3514 myvar_t *tmp; 3515 3516 3517 for (cur = &MyVars; *cur; cur = &((*cur)->next)) 3518 if (!mutt_strcmp ((*cur)->name, var)) 3519 break; 3520 3521 if (*cur) 3522 { 3523 tmp = (*cur)->next; 3524 FREE (&(*cur)->name); 3525 FREE (&(*cur)->value); 3526 FREE (cur); /* __FREE_CHECKED__ */ 3527 *cur = tmp; 3528 } 3529} 3530 3531static const char* myvar_get (const char* var) 3532{ 3533 myvar_t* cur; 3534 3535 for (cur = MyVars; cur; cur = cur->next) 3536 if (!mutt_strcmp (cur->name, var)) 3537 return NONULL(cur->value); 3538 3539 return NULL; 3540}