mutt stable branch with some hacks
at jcs 1820 lines 47 kB view raw
1/* 2 * Copyright (C) 1996-2000,2012-2013 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 "mailbox.h" 26#include "mime.h" 27#include "rfc2047.h" 28#include "rfc2231.h" 29#include "mutt_crypt.h" 30#include "url.h" 31 32#ifdef USE_AUTOCRYPT 33#include "autocrypt/autocrypt.h" 34#endif 35 36#include <string.h> 37#include <ctype.h> 38#include <sys/stat.h> 39#include <stdlib.h> 40 41/* Reads an arbitrarily long header field, and looks ahead for continuation 42 * lines. ``line'' must point to a dynamically allocated string; it is 43 * increased if more space is required to fit the whole line. 44 */ 45char *mutt_read_rfc822_line (FILE *f, char *line, size_t *linelen) 46{ 47 char *buf = line; 48 int ch; 49 size_t offset = 0; 50 size_t len = 0; 51 52 FOREVER 53 { 54 if (fgets (buf, *linelen - offset, f) == NULL || /* end of file or */ 55 (ISSPACE (*line) && !offset)) /* end of headers */ 56 { 57 *line = 0; 58 return (line); 59 } 60 61 len = mutt_strlen (buf); 62 if (! len) 63 return (line); 64 65 buf += len - 1; 66 if (*buf == '\n') 67 { 68 /* we did get a full line. remove trailing space */ 69 while (ISSPACE (*buf)) 70 *buf-- = 0; /* we cannot come beyond line's beginning because 71 * it begins with a non-space */ 72 73 /* check to see if the next line is a continuation line */ 74 if ((ch = fgetc (f)) != ' ' && ch != '\t') 75 { 76 ungetc (ch, f); 77 return (line); /* next line is a separate header field or EOH */ 78 } 79 80 /* eat tabs and spaces from the beginning of the continuation line */ 81 while ((ch = fgetc (f)) == ' ' || ch == '\t') 82 ; 83 ungetc (ch, f); 84 *++buf = ' '; /* string is still terminated because we removed 85 at least one whitespace char above */ 86 } 87 88 buf++; 89 offset = buf - line; 90 if (*linelen < offset + STRING) 91 { 92 /* grow the buffer */ 93 *linelen += STRING; 94 safe_realloc (&line, *linelen); 95 buf = line + offset; 96 } 97 } 98 /* not reached */ 99} 100 101static LIST *mutt_parse_references (char *s, int in_reply_to) 102{ 103 LIST *t, *lst = NULL; 104 char *m; 105 const char *sp; 106 107 m = mutt_extract_message_id (s, &sp); 108 while (m) 109 { 110 t = safe_malloc (sizeof (LIST)); 111 t->data = m; 112 t->next = lst; 113 lst = t; 114 115 m = mutt_extract_message_id (NULL, &sp); 116 } 117 118 return lst; 119} 120 121int mutt_check_encoding (const char *c) 122{ 123 if (ascii_strncasecmp ("7bit", c, sizeof ("7bit")-1) == 0) 124 return (ENC7BIT); 125 else if (ascii_strncasecmp ("8bit", c, sizeof ("8bit")-1) == 0) 126 return (ENC8BIT); 127 else if (ascii_strncasecmp ("binary", c, sizeof ("binary")-1) == 0) 128 return (ENCBINARY); 129 else if (ascii_strncasecmp ("quoted-printable", c, sizeof ("quoted-printable")-1) == 0) 130 return (ENCQUOTEDPRINTABLE); 131 else if (ascii_strncasecmp ("base64", c, sizeof("base64")-1) == 0) 132 return (ENCBASE64); 133 else if (ascii_strncasecmp ("x-uuencode", c, sizeof("x-uuencode")-1) == 0) 134 return (ENCUUENCODED); 135#ifdef SUN_ATTACHMENT 136 else if (ascii_strncasecmp ("uuencode", c, sizeof("uuencode")-1) == 0) 137 return (ENCUUENCODED); 138#endif 139 else 140 return (ENCOTHER); 141} 142 143/* Performs rfc2231 parameter parsing on s. 144 * 145 * Autocrypt defines an irregular parameter format that doesn't follow the 146 * rfc. It splits keydata across multiple lines without parameter continuations. 147 * The allow_value_spaces parameter allows parsing those values which 148 * are split by spaces when unfolded. 149 */ 150static PARAMETER *parse_parameters (const char *s, int allow_value_spaces) 151{ 152 PARAMETER *head = 0, *cur = 0, *new; 153 BUFFER *buffer = NULL; 154 const char *p; 155 size_t i; 156 157 buffer = mutt_buffer_pool_get (); 158 /* allow_value_spaces, especially with autocrypt keydata, can result 159 * in quite large parameter values. avoid frequent reallocs by 160 * pre-sizing */ 161 if (allow_value_spaces) 162 mutt_buffer_increase_size (buffer, mutt_strlen (s)); 163 164 dprint (2, (debugfile, "parse_parameters: `%s'\n", s)); 165 166 while (*s) 167 { 168 mutt_buffer_clear (buffer); 169 170 if ((p = strpbrk (s, "=;")) == NULL) 171 { 172 dprint(1, (debugfile, "parse_parameters: malformed parameter: %s\n", s)); 173 goto bail; 174 } 175 176 /* if we hit a ; now the parameter has no value, just skip it */ 177 if (*p != ';') 178 { 179 i = p - s; 180 /* remove whitespace from the end of the attribute name */ 181 while (i > 0 && is_email_wsp(s[i-1])) 182 --i; 183 184 /* the check for the missing parameter token is here so that we can skip 185 * over any quoted value that may be present. 186 */ 187 if (i == 0) 188 { 189 dprint(1, (debugfile, "parse_parameters: missing attribute: %s\n", s)); 190 new = NULL; 191 } 192 else 193 { 194 new = mutt_new_parameter (); 195 new->attribute = mutt_substrdup(s, s + i); 196 } 197 198 do 199 { 200 s = skip_email_wsp(p + 1); /* skip over the =, or space if we loop */ 201 202 if (*s == '"') 203 { 204 int state_ascii = 1; 205 s++; 206 for (; *s; s++) 207 { 208 if (AssumedCharset) 209 { 210 /* As iso-2022-* has a character of '"' with non-ascii state, 211 * ignore it. */ 212 if (*s == 0x1b) 213 { 214 if (s[1] == '(' && (s[2] == 'B' || s[2] == 'J')) 215 state_ascii = 1; 216 else 217 state_ascii = 0; 218 } 219 } 220 if (state_ascii && *s == '"') 221 break; 222 if (*s == '\\') 223 { 224 if (s[1]) 225 { 226 s++; 227 /* Quote the next character */ 228 mutt_buffer_addch (buffer, *s); 229 } 230 } 231 else 232 mutt_buffer_addch (buffer, *s); 233 } 234 if (*s) 235 s++; /* skip over the " */ 236 } 237 else 238 { 239 for (; *s && *s != ' ' && *s != ';'; s++) 240 mutt_buffer_addch (buffer, *s); 241 } 242 243 p = s; 244 } while (allow_value_spaces && (*s == ' ')); 245 246 /* if the attribute token was missing, 'new' will be NULL */ 247 if (new) 248 { 249 new->value = safe_strdup (mutt_b2s (buffer)); 250 251 dprint (2, (debugfile, "parse_parameter: `%s' = `%s'\n", 252 new->attribute ? new->attribute : "", 253 new->value ? new->value : "")); 254 255 /* Add this parameter to the list */ 256 if (head) 257 { 258 cur->next = new; 259 cur = cur->next; 260 } 261 else 262 head = cur = new; 263 } 264 } 265 else 266 { 267 dprint (1, (debugfile, "parse_parameters(): parameter with no value: %s\n", s)); 268 s = p; 269 } 270 271 /* Find the next parameter */ 272 if (*s != ';' && (s = strchr (s, ';')) == NULL) 273 break; /* no more parameters */ 274 275 do 276 { 277 /* Move past any leading whitespace. the +1 skips over the semicolon */ 278 s = skip_email_wsp(s + 1); 279 } 280 while (*s == ';'); /* skip empty parameters */ 281 } 282 283bail: 284 285 rfc2231_decode_parameters (&head); 286 mutt_buffer_pool_release (&buffer); 287 return (head); 288} 289 290int mutt_check_mime_type (const char *s) 291{ 292 if (ascii_strcasecmp ("text", s) == 0) 293 return TYPETEXT; 294 else if (ascii_strcasecmp ("multipart", s) == 0) 295 return TYPEMULTIPART; 296#ifdef SUN_ATTACHMENT 297 else if (ascii_strcasecmp ("x-sun-attachment", s) == 0) 298 return TYPEMULTIPART; 299#endif 300 else if (ascii_strcasecmp ("application", s) == 0) 301 return TYPEAPPLICATION; 302 else if (ascii_strcasecmp ("message", s) == 0) 303 return TYPEMESSAGE; 304 else if (ascii_strcasecmp ("image", s) == 0) 305 return TYPEIMAGE; 306 else if (ascii_strcasecmp ("audio", s) == 0) 307 return TYPEAUDIO; 308 else if (ascii_strcasecmp ("video", s) == 0) 309 return TYPEVIDEO; 310 else if (ascii_strcasecmp ("model", s) == 0) 311 return TYPEMODEL; 312 else if (ascii_strcasecmp ("*", s) == 0) 313 return TYPEANY; 314 else if (ascii_strcasecmp (".*", s) == 0) 315 return TYPEANY; 316 else 317 return TYPEOTHER; 318} 319 320void mutt_parse_content_type (char *s, BODY *ct) 321{ 322 char *pc; 323 char *subtype; 324 325 FREE (&ct->subtype); 326 mutt_free_parameter(&ct->parameter); 327 328 /* First extract any existing parameters */ 329 if ((pc = strchr(s, ';')) != NULL) 330 { 331 *pc++ = 0; 332 while (*pc && ISSPACE (*pc)) 333 pc++; 334 ct->parameter = parse_parameters(pc, 0); 335 336 /* Some pre-RFC1521 gateways still use the "name=filename" convention, 337 * but if a filename has already been set in the content-disposition, 338 * let that take precedence, and don't set it here */ 339 if ((pc = mutt_get_parameter( "name", ct->parameter)) && !ct->filename) 340 ct->filename = safe_strdup(pc); 341 342#ifdef SUN_ATTACHMENT 343 /* this is deep and utter perversion */ 344 if ((pc = mutt_get_parameter ("conversions", ct->parameter))) 345 ct->encoding = mutt_check_encoding (pc); 346#endif 347 348 } 349 350 /* Now get the subtype */ 351 if ((subtype = strchr(s, '/'))) 352 { 353 *subtype++ = '\0'; 354 for (pc = subtype; *pc && !ISSPACE(*pc) && *pc != ';'; pc++) 355 ; 356 *pc = '\0'; 357 ct->subtype = safe_strdup (subtype); 358 } 359 360 /* Finally, get the major type */ 361 ct->type = mutt_check_mime_type (s); 362 363#ifdef SUN_ATTACHMENT 364 if (ascii_strcasecmp ("x-sun-attachment", s) == 0) 365 ct->subtype = safe_strdup ("x-sun-attachment"); 366#endif 367 368 if (ct->type == TYPEOTHER) 369 { 370 ct->xtype = safe_strdup (s); 371 } 372 373 if (ct->subtype == NULL) 374 { 375 /* Some older non-MIME mailers (i.e., mailtool, elm) have a content-type 376 * field, so we can attempt to convert the type to BODY here. 377 */ 378 if (ct->type == TYPETEXT) 379 ct->subtype = safe_strdup ("plain"); 380 else if (ct->type == TYPEAUDIO) 381 ct->subtype = safe_strdup ("basic"); 382 else if (ct->type == TYPEMESSAGE) 383 ct->subtype = safe_strdup ("rfc822"); 384 else if (ct->type == TYPEOTHER) 385 { 386 char buffer[SHORT_STRING]; 387 388 ct->type = TYPEAPPLICATION; 389 snprintf (buffer, sizeof (buffer), "x-%s", s); 390 ct->subtype = safe_strdup (buffer); 391 } 392 else 393 ct->subtype = safe_strdup ("x-unknown"); 394 } 395 396 /* Default character set for text types. */ 397 if (ct->type == TYPETEXT) 398 { 399 if (!(pc = mutt_get_parameter ("charset", ct->parameter))) 400 mutt_set_parameter ("charset", AssumedCharset ? 401 (const char *) mutt_get_default_charset () 402 : "us-ascii", &ct->parameter); 403 else 404 { 405 /* Microsoft Outlook seems to think it is necessary to repeat 406 * charset=, strip it off not to confuse ourselves */ 407 if (ascii_strncasecmp (pc, "charset=", sizeof ("charset=") - 1) == 0) 408 mutt_set_parameter ("charset", pc + (sizeof ("charset=") - 1), 409 &ct->parameter); 410 } 411 } 412 413} 414 415static void parse_content_disposition (const char *s, BODY *ct) 416{ 417 PARAMETER *parms; 418 419 if (!ascii_strncasecmp ("inline", s, 6)) 420 ct->disposition = DISPINLINE; 421 else if (!ascii_strncasecmp ("form-data", s, 9)) 422 ct->disposition = DISPFORMDATA; 423 else 424 ct->disposition = DISPATTACH; 425 426 /* Check to see if a default filename was given */ 427 if ((s = strchr (s, ';')) != NULL) 428 { 429 s = skip_email_wsp(s + 1); 430 if ((s = mutt_get_parameter ("filename", (parms = parse_parameters (s, 0))))) 431 mutt_str_replace (&ct->filename, s); 432 if ((s = mutt_get_parameter ("name", parms))) 433 ct->form_name = safe_strdup (s); 434 mutt_free_parameter (&parms); 435 } 436} 437 438#ifdef USE_AUTOCRYPT 439static AUTOCRYPTHDR *parse_autocrypt (AUTOCRYPTHDR *head, const char *s) 440{ 441 AUTOCRYPTHDR *autocrypt; 442 PARAMETER *params = NULL, *param; 443 444 autocrypt = mutt_new_autocrypthdr (); 445 autocrypt->next = head; 446 447 param = params = parse_parameters (s, 1); 448 if (!params) 449 { 450 autocrypt->invalid = 1; 451 goto cleanup; 452 } 453 454 while (param) 455 { 456 if (!ascii_strcasecmp (param->attribute, "addr")) 457 { 458 if (autocrypt->addr) 459 { 460 autocrypt->invalid = 1; 461 goto cleanup; 462 } 463 autocrypt->addr = param->value; 464 param->value = NULL; 465 } 466 else if (!ascii_strcasecmp (param->attribute, "prefer-encrypt")) 467 { 468 if (!ascii_strcasecmp (param->value, "mutual")) 469 autocrypt->prefer_encrypt = 1; 470 } 471 else if (!ascii_strcasecmp (param->attribute, "keydata")) 472 { 473 if (autocrypt->keydata) 474 { 475 autocrypt->invalid = 1; 476 goto cleanup; 477 } 478 autocrypt->keydata = param->value; 479 param->value = NULL; 480 } 481 else if (param->attribute && (param->attribute[0] != '_')) 482 { 483 autocrypt->invalid = 1; 484 goto cleanup; 485 } 486 487 param = param->next; 488 } 489 490 /* Checking the addr against From, and for multiple valid headers 491 * occurs later, after all the headers are parsed. */ 492 if (!autocrypt->addr || !autocrypt->keydata) 493 autocrypt->invalid = 1; 494 495cleanup: 496 mutt_free_parameter (&params); 497 return autocrypt; 498} 499#endif 500 501/* args: 502 * fp stream to read from 503 * 504 * digest 1 if reading subparts of a multipart/digest, 0 505 * otherwise 506 */ 507 508BODY *mutt_read_mime_header (FILE *fp, int digest) 509{ 510 BODY *p = mutt_new_body(); 511 ENVELOPE *e = mutt_new_envelope (); 512 char *c; 513 char *line = safe_malloc (LONG_STRING); 514 size_t linelen = LONG_STRING; 515 516 p->hdr_offset = ftello (fp); 517 518 p->encoding = ENC7BIT; /* default from RFC1521 */ 519 p->type = digest ? TYPEMESSAGE : TYPETEXT; 520 p->disposition = DISPINLINE; 521 522 while (*(line = mutt_read_rfc822_line (fp, line, &linelen)) != 0) 523 { 524 /* Find the value of the current header */ 525 if ((c = strchr (line, ':'))) 526 { 527 *c = 0; 528 c = skip_email_wsp(c + 1); 529 if (!*c) 530 { 531 dprint (1, (debugfile, "mutt_read_mime_header(): skipping empty header field: %s\n", line)); 532 continue; 533 } 534 } 535 else 536 { 537 dprint (1, (debugfile, "read_mime_header: bogus MIME header: %s\n", line)); 538 break; 539 } 540 541 if (!ascii_strncasecmp ("content-", line, 8)) 542 { 543 if (!ascii_strcasecmp ("type", line + 8)) 544 mutt_parse_content_type (c, p); 545 else if (!ascii_strcasecmp ("transfer-encoding", line + 8)) 546 p->encoding = mutt_check_encoding (c); 547 else if (!ascii_strcasecmp ("disposition", line + 8)) 548 parse_content_disposition (c, p); 549 else if (!ascii_strcasecmp ("description", line + 8)) 550 { 551 mutt_str_replace (&p->description, c); 552 rfc2047_decode (&p->description); 553 } 554 } 555#ifdef SUN_ATTACHMENT 556 else if (!ascii_strncasecmp ("x-sun-", line, 6)) 557 { 558 if (!ascii_strcasecmp ("data-type", line + 6)) 559 mutt_parse_content_type (c, p); 560 else if (!ascii_strcasecmp ("encoding-info", line + 6)) 561 p->encoding = mutt_check_encoding (c); 562 else if (!ascii_strcasecmp ("content-lines", line + 6)) 563 mutt_set_parameter ("content-lines", c, &(p->parameter)); 564 else if (!ascii_strcasecmp ("data-description", line + 6)) 565 { 566 mutt_str_replace (&p->description, c); 567 rfc2047_decode (&p->description); 568 } 569 } 570#endif 571 else 572 { 573 if (mutt_parse_rfc822_line (e, NULL, line, c, 0, 0, 0, NULL)) 574 p->mime_headers = e; 575 } 576 } 577 p->offset = ftello (fp); /* Mark the start of the real data */ 578 if (p->type == TYPETEXT && !p->subtype) 579 p->subtype = safe_strdup ("plain"); 580 else if (p->type == TYPEMESSAGE && !p->subtype) 581 p->subtype = safe_strdup ("rfc822"); 582 583 FREE (&line); 584 585 if (p->mime_headers) 586 rfc2047_decode_envelope (p->mime_headers); 587 else 588 mutt_free_envelope (&e); 589 590 return (p); 591} 592 593void mutt_parse_part (FILE *fp, BODY *b) 594{ 595 char *bound = 0; 596 597 switch (b->type) 598 { 599 case TYPEMULTIPART: 600#ifdef SUN_ATTACHMENT 601 if ( !ascii_strcasecmp (b->subtype, "x-sun-attachment") ) 602 bound = "--------"; 603 else 604#endif 605 bound = mutt_get_parameter ("boundary", b->parameter); 606 607 fseeko (fp, b->offset, SEEK_SET); 608 b->parts = mutt_parse_multipart (fp, bound, 609 b->offset + b->length, 610 ascii_strcasecmp ("digest", b->subtype) == 0); 611 break; 612 613 case TYPEMESSAGE: 614 if (b->subtype) 615 { 616 fseeko (fp, b->offset, SEEK_SET); 617 if (mutt_is_message_type(b->type, b->subtype)) 618 b->parts = mutt_parse_messageRFC822 (fp, b); 619 else if (ascii_strcasecmp (b->subtype, "external-body") == 0) 620 b->parts = mutt_read_mime_header (fp, 0); 621 else 622 return; 623 } 624 break; 625 626 default: 627 return; 628 } 629 630 /* try to recover from parsing error */ 631 if (!b->parts) 632 { 633 b->type = TYPETEXT; 634 mutt_str_replace (&b->subtype, "plain"); 635 } 636} 637 638/* parse a MESSAGE/RFC822 body 639 * 640 * args: 641 * fp stream to read from 642 * 643 * parent structure which contains info about the message/rfc822 644 * body part 645 * 646 * NOTE: this assumes that `parent->length' has been set! 647 */ 648 649BODY *mutt_parse_messageRFC822 (FILE *fp, BODY *parent) 650{ 651 BODY *msg; 652 653 parent->hdr = mutt_new_header (); 654 parent->hdr->offset = ftello (fp); 655 parent->hdr->env = mutt_read_rfc822_header (fp, parent->hdr, 0, 0); 656 msg = parent->hdr->content; 657 658 /* ignore the length given in the content-length since it could be wrong 659 and we already have the info to calculate the correct length */ 660 /* if (msg->length == -1) */ 661 msg->length = parent->length - (msg->offset - parent->offset); 662 663 /* if body of this message is empty, we can end up with a negative length */ 664 if (msg->length < 0) 665 msg->length = 0; 666 667 mutt_parse_part(fp, msg); 668 return (msg); 669} 670 671/* parse a multipart structure 672 * 673 * args: 674 * fp stream to read from 675 * 676 * boundary body separator 677 * 678 * end_off length of the multipart body (used when the final 679 * boundary is missing to avoid reading too far) 680 * 681 * digest 1 if reading a multipart/digest, 0 otherwise 682 */ 683 684BODY *mutt_parse_multipart (FILE *fp, const char *boundary, LOFF_T end_off, int digest) 685{ 686#ifdef SUN_ATTACHMENT 687 int lines; 688#endif 689 int blen, len, crlf = 0; 690 char buffer[LONG_STRING]; 691 BODY *head = 0, *last = 0, *new = 0; 692 int i; 693 int final = 0; /* did we see the ending boundary? */ 694 695 if (!boundary) 696 { 697 mutt_error _("multipart message has no boundary parameter!"); 698 return (NULL); 699 } 700 701 blen = mutt_strlen (boundary); 702 while (ftello (fp) < end_off && fgets (buffer, LONG_STRING, fp) != NULL) 703 { 704 len = mutt_strlen (buffer); 705 706 crlf = (len > 1 && buffer[len - 2] == '\r') ? 1 : 0; 707 708 if (buffer[0] == '-' && buffer[1] == '-' && 709 mutt_strncmp (buffer + 2, boundary, blen) == 0) 710 { 711 if (last) 712 { 713 last->length = ftello (fp) - last->offset - len - 1 - crlf; 714 if (last->parts && last->parts->length == 0) 715 last->parts->length = ftello (fp) - last->parts->offset - len - 1 - crlf; 716 /* if the body is empty, we can end up with a -1 length */ 717 if (last->length < 0) 718 last->length = 0; 719 } 720 721 /* Remove any trailing whitespace, up to the length of the boundary */ 722 for (i = len - 1; ISSPACE (buffer[i]) && i >= blen + 2; i--) 723 buffer[i] = 0; 724 725 /* Check for the end boundary */ 726 if (mutt_strcmp (buffer + blen + 2, "--") == 0) 727 { 728 final = 1; 729 break; /* done parsing */ 730 } 731 else if (buffer[2 + blen] == 0) 732 { 733 new = mutt_read_mime_header (fp, digest); 734 735#ifdef SUN_ATTACHMENT 736 if (mutt_get_parameter ("content-lines", new->parameter)) 737 { 738 mutt_atoi (mutt_get_parameter ("content-lines", new->parameter), &lines); 739 for ( ; lines; lines-- ) 740 if (ftello (fp) >= end_off || fgets (buffer, LONG_STRING, fp) == NULL) 741 break; 742 } 743#endif 744 745 /* 746 * Consistency checking - catch 747 * bad attachment end boundaries 748 */ 749 750 if (new->offset > end_off) 751 { 752 mutt_free_body(&new); 753 break; 754 } 755 if (head) 756 { 757 last->next = new; 758 last = new; 759 } 760 else 761 last = head = new; 762 } 763 } 764 } 765 766 /* in case of missing end boundary, set the length to something reasonable */ 767 if (last && last->length == 0 && !final) 768 last->length = end_off - last->offset; 769 770 /* parse recursive MIME parts */ 771 for (last = head; last; last = last->next) 772 mutt_parse_part(fp, last); 773 774 return (head); 775} 776 777static const char *uncomment_timezone (char *buf, size_t buflen, const char *tz) 778{ 779 char *p; 780 size_t len; 781 782 if (*tz != '(') 783 return tz; /* no need to do anything */ 784 tz = skip_email_wsp(tz + 1); 785 if ((p = strpbrk (tz, " )")) == NULL) 786 return tz; 787 len = p - tz; 788 if (len > buflen - 1) 789 len = buflen - 1; 790 memcpy (buf, tz, len); 791 buf[len] = 0; 792 return buf; 793} 794 795static const struct tz_t 796{ 797 char tzname[5]; 798 unsigned char zhours; 799 unsigned char zminutes; 800 unsigned char zoccident; /* west of UTC? */ 801} 802TimeZones[] = 803{ 804 { "aat", 1, 0, 1 }, /* Atlantic Africa Time */ 805 { "adt", 4, 0, 0 }, /* Arabia DST */ 806 { "ast", 3, 0, 0 }, /* Arabia */ 807/*{ "ast", 4, 0, 1 },*/ /* Atlantic */ 808 { "bst", 1, 0, 0 }, /* British DST */ 809 { "cat", 1, 0, 0 }, /* Central Africa */ 810 { "cdt", 5, 0, 1 }, 811 { "cest", 2, 0, 0 }, /* Central Europe DST */ 812 { "cet", 1, 0, 0 }, /* Central Europe */ 813 { "cst", 6, 0, 1 }, 814/*{ "cst", 8, 0, 0 },*/ /* China */ 815/*{ "cst", 9, 30, 0 },*/ /* Australian Central Standard Time */ 816 { "eat", 3, 0, 0 }, /* East Africa */ 817 { "edt", 4, 0, 1 }, 818 { "eest", 3, 0, 0 }, /* Eastern Europe DST */ 819 { "eet", 2, 0, 0 }, /* Eastern Europe */ 820 { "egst", 0, 0, 0 }, /* Eastern Greenland DST */ 821 { "egt", 1, 0, 1 }, /* Eastern Greenland */ 822 { "est", 5, 0, 1 }, 823 { "gmt", 0, 0, 0 }, 824 { "gst", 4, 0, 0 }, /* Presian Gulf */ 825 { "hkt", 8, 0, 0 }, /* Hong Kong */ 826 { "ict", 7, 0, 0 }, /* Indochina */ 827 { "idt", 3, 0, 0 }, /* Israel DST */ 828 { "ist", 2, 0, 0 }, /* Israel */ 829/*{ "ist", 5, 30, 0 },*/ /* India */ 830 { "jst", 9, 0, 0 }, /* Japan */ 831 { "kst", 9, 0, 0 }, /* Korea */ 832 { "mdt", 6, 0, 1 }, 833 { "met", 1, 0, 0 }, /* this is now officially CET */ 834 { "msd", 4, 0, 0 }, /* Moscow DST */ 835 { "msk", 3, 0, 0 }, /* Moscow */ 836 { "mst", 7, 0, 1 }, 837 { "nzdt", 13, 0, 0 }, /* New Zealand DST */ 838 { "nzst", 12, 0, 0 }, /* New Zealand */ 839 { "pdt", 7, 0, 1 }, 840 { "pst", 8, 0, 1 }, 841 { "sat", 2, 0, 0 }, /* South Africa */ 842 { "smt", 4, 0, 0 }, /* Seychelles */ 843 { "sst", 11, 0, 1 }, /* Samoa */ 844/*{ "sst", 8, 0, 0 },*/ /* Singapore */ 845 { "utc", 0, 0, 0 }, 846 { "wat", 0, 0, 0 }, /* West Africa */ 847 { "west", 1, 0, 0 }, /* Western Europe DST */ 848 { "wet", 0, 0, 0 }, /* Western Europe */ 849 { "wgst", 2, 0, 1 }, /* Western Greenland DST */ 850 { "wgt", 3, 0, 1 }, /* Western Greenland */ 851 { "wst", 8, 0, 0 }, /* Western Australia */ 852}; 853 854/* parses a date string in RFC822 format: 855 * 856 * Date: [ weekday , ] day-of-month month year hour:minute:second timezone 857 * 858 * This routine assumes that `h' has been initialized to 0. the `timezone' 859 * field is optional, defaulting to +0000 if missing. 860 */ 861time_t mutt_parse_date (const char *s, HEADER *h) 862{ 863 int count = 0; 864 char *t; 865 int hour, min, sec; 866 struct tm tm; 867 int i; 868 int tz_offset = 0; 869 int zhours = 0; 870 int zminutes = 0; 871 int zoccident = 0; 872 const char *ptz; 873 char tzstr[SHORT_STRING]; 874 char scratch[SHORT_STRING]; 875 876 /* Don't modify our argument. Fixed-size buffer is ok here since 877 * the date format imposes a natural limit. 878 */ 879 880 strfcpy (scratch, s, sizeof (scratch)); 881 882 /* kill the day of the week, if it exists. */ 883 if ((t = strchr (scratch, ','))) 884 t++; 885 else 886 t = scratch; 887 t = skip_email_wsp(t); 888 889 memset (&tm, 0, sizeof (tm)); 890 891 while ((t = strtok (t, " \t")) != NULL) 892 { 893 switch (count) 894 { 895 case 0: /* day of the month */ 896 if (mutt_atoi (t, &tm.tm_mday) < 0 || tm.tm_mday < 0) 897 return (-1); 898 if (tm.tm_mday > 31) 899 return (-1); 900 break; 901 902 case 1: /* month of the year */ 903 if ((i = mutt_check_month (t)) < 0) 904 return (-1); 905 tm.tm_mon = i; 906 break; 907 908 case 2: /* year */ 909 if (mutt_atoi (t, &tm.tm_year) < 0 || tm.tm_year < 0) 910 return (-1); 911 if (tm.tm_year < 50) 912 tm.tm_year += 100; 913 else if (tm.tm_year >= 1900) 914 tm.tm_year -= 1900; 915 break; 916 917 case 3: /* time of day */ 918 if (sscanf (t, "%d:%d:%d", &hour, &min, &sec) == 3) 919 ; 920 else if (sscanf (t, "%d:%d", &hour, &min) == 2) 921 sec = 0; 922 else 923 { 924 dprint(1, (debugfile, "parse_date: could not process time format: %s\n", t)); 925 return(-1); 926 } 927 tm.tm_hour = hour; 928 tm.tm_min = min; 929 tm.tm_sec = sec; 930 break; 931 932 case 4: /* timezone */ 933 /* sometimes we see things like (MST) or (-0700) so attempt to 934 * compensate by uncommenting the string if non-RFC822 compliant 935 */ 936 ptz = uncomment_timezone (tzstr, sizeof (tzstr), t); 937 938 if (*ptz == '+' || *ptz == '-') 939 { 940 if (ptz[1] && ptz[2] && ptz[3] && ptz[4] 941 && isdigit ((unsigned char) ptz[1]) && isdigit ((unsigned char) ptz[2]) 942 && isdigit ((unsigned char) ptz[3]) && isdigit ((unsigned char) ptz[4])) 943 { 944 zhours = (ptz[1] - '0') * 10 + (ptz[2] - '0'); 945 zminutes = (ptz[3] - '0') * 10 + (ptz[4] - '0'); 946 947 if (ptz[0] == '-') 948 zoccident = 1; 949 } 950 } 951 else 952 { 953 struct tz_t *tz; 954 955 tz = bsearch (ptz, TimeZones, sizeof TimeZones/sizeof (struct tz_t), 956 sizeof (struct tz_t), 957 (int (*)(const void *, const void *)) ascii_strcasecmp 958 /* This is safe to do: A pointer to a struct equals 959 * a pointer to its first element*/); 960 961 if (tz) 962 { 963 zhours = tz->zhours; 964 zminutes = tz->zminutes; 965 zoccident = tz->zoccident; 966 } 967 968 /* ad hoc support for the European MET (now officially CET) TZ */ 969 if (ascii_strcasecmp (t, "MET") == 0) 970 { 971 if ((t = strtok (NULL, " \t")) != NULL) 972 { 973 if (!ascii_strcasecmp (t, "DST")) 974 zhours++; 975 } 976 } 977 } 978 tz_offset = zhours * 3600 + zminutes * 60; 979 if (!zoccident) 980 tz_offset = -tz_offset; 981 break; 982 } 983 count++; 984 t = 0; 985 } 986 987 if (count < 4) /* don't check for missing timezone */ 988 { 989 dprint(1,(debugfile, "parse_date(): error parsing date format, using received time\n")); 990 return (-1); 991 } 992 993 if (h) 994 { 995 h->zhours = zhours; 996 h->zminutes = zminutes; 997 h->zoccident = zoccident; 998 } 999 1000 return (mutt_mktime (&tm, 0) + tz_offset); 1001} 1002 1003/* extract the first substring that looks like a message-id. 1004 * call back with NULL for more (like strtok). 1005 */ 1006char *mutt_extract_message_id (const char *s, const char **saveptr) 1007{ 1008 const char *o, *onull, *p; 1009 char *ret = NULL; 1010 1011 if (s) 1012 p = s; 1013 else if (saveptr) 1014 p = *saveptr; 1015 else 1016 return NULL; 1017 1018 for (s = NULL, o = NULL, onull = NULL; 1019 (p = strpbrk (p, "<> \t;")) != NULL; ++p) 1020 { 1021 if (*p == '<') 1022 { 1023 s = p; 1024 o = onull = NULL; 1025 continue; 1026 } 1027 1028 if (!s) 1029 continue; 1030 1031 if (*p == '>') 1032 { 1033 size_t olen = onull - o, slen = p - s + 1; 1034 ret = safe_malloc (olen + slen + 1); 1035 if (o) 1036 memcpy (ret, o, olen); 1037 memcpy (ret + olen, s, slen); 1038 ret[olen + slen] = '\0'; 1039 if (saveptr) 1040 *saveptr = p + 1; /* next call starts after '>' */ 1041 return ret; 1042 } 1043 1044 /* some idiotic clients break their message-ids between lines */ 1045 if (s == p) 1046 /* step past another whitespace */ 1047 s = p + 1; 1048 else if (o) 1049 /* more than two lines, give up */ 1050 s = o = onull = NULL; 1051 else 1052 { 1053 /* remember the first line, start looking for the second */ 1054 o = s; 1055 onull = p; 1056 s = p + 1; 1057 } 1058 } 1059 1060 return NULL; 1061} 1062 1063void mutt_parse_mime_message (CONTEXT *ctx, HEADER *cur) 1064{ 1065 MESSAGE *msg; 1066 1067 do 1068 { 1069 if (cur->content->type != TYPEMESSAGE && 1070 cur->content->type != TYPEMULTIPART) 1071 break; /* nothing to do */ 1072 1073 if (cur->content->parts) 1074 break; /* The message was parsed earlier. */ 1075 1076 if ((msg = mx_open_message (ctx, cur->msgno))) 1077 { 1078 mutt_parse_part (msg->fp, cur->content); 1079 1080 if (WithCrypto) 1081 cur->security = crypt_query (cur->content); 1082 1083 mx_close_message (ctx, &msg); 1084 } 1085 } while (0); 1086 1087 cur->attach_valid = 0; 1088} 1089 1090void mutt_auto_subscribe (const char *mailto) 1091{ 1092 ENVELOPE *lpenv; 1093 1094 if (!AutoSubscribeCache) 1095 AutoSubscribeCache = hash_create (200, MUTT_HASH_STRCASECMP | MUTT_HASH_STRDUP_KEYS); 1096 1097 if (!mailto || hash_find (AutoSubscribeCache, mailto)) 1098 return; 1099 1100 hash_insert (AutoSubscribeCache, mailto, AutoSubscribeCache); 1101 1102 lpenv = mutt_new_envelope (); /* parsed envelope from the List-Post mailto: URL */ 1103 1104 if ((url_parse_mailto (lpenv, NULL, mailto) != -1) && 1105 lpenv->to && lpenv->to->mailbox && 1106 !mutt_match_rx_list (lpenv->to->mailbox, SubscribedLists) && 1107 !mutt_match_rx_list (lpenv->to->mailbox, UnMailLists) && 1108 !mutt_match_rx_list (lpenv->to->mailbox, UnSubscribedLists)) 1109 { 1110 BUFFER err; 1111 char errbuf[STRING]; 1112 1113 memset (&err, 0, sizeof(err)); 1114 err.data = errbuf; 1115 err.dsize = sizeof(errbuf); 1116 1117 /* mutt_add_to_rx_list() detects duplicates, so it is safe to 1118 * try to add here without any checks. */ 1119 mutt_add_to_rx_list (&MailLists, lpenv->to->mailbox, REG_ICASE, &err); 1120 mutt_add_to_rx_list (&SubscribedLists, lpenv->to->mailbox, REG_ICASE, &err); 1121 } 1122 mutt_free_envelope (&lpenv); 1123} 1124 1125int mutt_parse_rfc822_line (ENVELOPE *e, HEADER *hdr, char *line, char *p, short user_hdrs, short weed, 1126 short do_2047, LIST **lastp) 1127{ 1128 int matched = 0; 1129 1130 switch (ascii_tolower (line[0])) 1131 { 1132 case 'a': 1133 if (ascii_strcasecmp (line+1, "pparently-to") == 0) 1134 { 1135 e->to = rfc822_parse_adrlist (e->to, p); 1136 matched = 1; 1137 } 1138 else if (ascii_strcasecmp (line+1, "pparently-from") == 0) 1139 { 1140 e->from = rfc822_parse_adrlist (e->from, p); 1141 matched = 1; 1142 } 1143#ifdef USE_AUTOCRYPT 1144 else if (ascii_strcasecmp (line+1, "utocrypt") == 0) 1145 { 1146 if (option (OPTAUTOCRYPT)) 1147 { 1148 e->autocrypt = parse_autocrypt (e->autocrypt, p); 1149 matched = 1; 1150 } 1151 } 1152 else if (ascii_strcasecmp (line+1, "utocrypt-gossip") == 0) 1153 { 1154 if (option (OPTAUTOCRYPT)) 1155 { 1156 e->autocrypt_gossip = parse_autocrypt (e->autocrypt_gossip, p); 1157 matched = 1; 1158 } 1159 } 1160#endif 1161 break; 1162 1163 case 'b': 1164 if (ascii_strcasecmp (line+1, "cc") == 0) 1165 { 1166 e->bcc = rfc822_parse_adrlist (e->bcc, p); 1167 matched = 1; 1168 } 1169 break; 1170 1171 case 'c': 1172 if (ascii_strcasecmp (line+1, "c") == 0) 1173 { 1174 e->cc = rfc822_parse_adrlist (e->cc, p); 1175 matched = 1; 1176 } 1177 else if (ascii_strncasecmp (line + 1, "ontent-", 7) == 0) 1178 { 1179 if (ascii_strcasecmp (line+8, "type") == 0) 1180 { 1181 if (hdr) 1182 mutt_parse_content_type (p, hdr->content); 1183 matched = 1; 1184 } 1185 else if (ascii_strcasecmp (line+8, "transfer-encoding") == 0) 1186 { 1187 if (hdr) 1188 hdr->content->encoding = mutt_check_encoding (p); 1189 matched = 1; 1190 } 1191 else if (ascii_strcasecmp (line+8, "length") == 0) 1192 { 1193 if (hdr) 1194 { 1195 if ((hdr->content->length = atol (p)) < 0) 1196 hdr->content->length = -1; 1197 } 1198 matched = 1; 1199 } 1200 else if (ascii_strcasecmp (line+8, "description") == 0) 1201 { 1202 if (hdr) 1203 { 1204 mutt_str_replace (&hdr->content->description, p); 1205 rfc2047_decode (&hdr->content->description); 1206 } 1207 matched = 1; 1208 } 1209 else if (ascii_strcasecmp (line+8, "disposition") == 0) 1210 { 1211 if (hdr) 1212 parse_content_disposition (p, hdr->content); 1213 matched = 1; 1214 } 1215 } 1216 break; 1217 1218 case 'd': 1219 if (!ascii_strcasecmp ("ate", line + 1)) 1220 { 1221 mutt_str_replace (&e->date, p); 1222 if (hdr) 1223 hdr->date_sent = mutt_parse_date (p, hdr); 1224 matched = 1; 1225 } 1226 break; 1227 1228 case 'e': 1229 if (!ascii_strcasecmp ("xpires", line + 1) && 1230 hdr && mutt_parse_date (p, NULL) < time (NULL)) 1231 hdr->expired = 1; 1232 break; 1233 1234 case 'f': 1235 if (!ascii_strcasecmp ("rom", line + 1)) 1236 { 1237 e->from = rfc822_parse_adrlist (e->from, p); 1238 matched = 1; 1239 } 1240 break; 1241 1242 case 'i': 1243 if (!ascii_strcasecmp (line+1, "n-reply-to")) 1244 { 1245 mutt_free_list (&e->in_reply_to); 1246 e->in_reply_to = mutt_parse_references (p, 1); 1247 matched = 1; 1248 } 1249 break; 1250 1251 case 'l': 1252 if (!ascii_strcasecmp (line + 1, "ines")) 1253 { 1254 if (hdr) 1255 { 1256 /* 1257 * HACK - mutt has, for a very short time, produced negative 1258 * Lines header values. Ignore them. 1259 */ 1260 if (mutt_atoi (p, &hdr->lines) < 0 || hdr->lines < 0) 1261 hdr->lines = 0; 1262 } 1263 1264 matched = 1; 1265 } 1266 else if (!ascii_strcasecmp (line + 1, "ist-Post")) 1267 { 1268 /* RFC 2369. FIXME: We should ignore whitespace, but don't. */ 1269 if (strncmp (p, "NO", 2)) 1270 { 1271 char *beg, *end; 1272 for (beg = strchr (p, '<'); beg; beg = strchr (end, ',')) 1273 { 1274 ++beg; 1275 if (!(end = strchr (beg, '>'))) 1276 break; 1277 1278 /* Take the first mailto URL */ 1279 if (url_check_scheme (beg) == U_MAILTO) 1280 { 1281 FREE (&e->list_post); 1282 e->list_post = mutt_substrdup (beg, end); 1283 if (option (OPTAUTOSUBSCRIBE)) 1284 mutt_auto_subscribe (e->list_post); 1285 break; 1286 } 1287 } 1288 } 1289 matched = 1; 1290 } 1291 break; 1292 1293 case 'm': 1294 if (!ascii_strcasecmp (line + 1, "ime-version")) 1295 { 1296 if (hdr) 1297 hdr->mime = 1; 1298 matched = 1; 1299 } 1300 else if (!ascii_strcasecmp (line + 1, "essage-id")) 1301 { 1302 /* We add a new "Message-ID:" when building a message */ 1303 FREE (&e->message_id); 1304 e->message_id = mutt_extract_message_id (p, NULL); 1305 matched = 1; 1306 } 1307 else if (!ascii_strncasecmp (line + 1, "ail-", 4)) 1308 { 1309 if (!ascii_strcasecmp (line + 5, "reply-to")) 1310 { 1311 /* override the Reply-To: field */ 1312 rfc822_free_address (&e->reply_to); 1313 e->reply_to = rfc822_parse_adrlist (e->reply_to, p); 1314 matched = 1; 1315 } 1316 else if (!ascii_strcasecmp (line + 5, "followup-to")) 1317 { 1318 e->mail_followup_to = rfc822_parse_adrlist (e->mail_followup_to, p); 1319 matched = 1; 1320 } 1321 } 1322 break; 1323 1324 case 'r': 1325 if (!ascii_strcasecmp (line + 1, "eferences")) 1326 { 1327 mutt_free_list (&e->references); 1328 e->references = mutt_parse_references (p, 0); 1329 matched = 1; 1330 } 1331 else if (!ascii_strcasecmp (line + 1, "eply-to")) 1332 { 1333 e->reply_to = rfc822_parse_adrlist (e->reply_to, p); 1334 matched = 1; 1335 } 1336 else if (!ascii_strcasecmp (line + 1, "eturn-path")) 1337 { 1338 e->return_path = rfc822_parse_adrlist (e->return_path, p); 1339 matched = 1; 1340 } 1341 else if (!ascii_strcasecmp (line + 1, "eceived")) 1342 { 1343 if (hdr && !hdr->received) 1344 { 1345 char *d = strrchr (p, ';'); 1346 1347 if (d) 1348 hdr->received = mutt_parse_date (d + 1, NULL); 1349 } 1350 } 1351 break; 1352 1353 case 's': 1354 if (!ascii_strcasecmp (line + 1, "ubject")) 1355 { 1356 if (!e->subject) 1357 e->subject = safe_strdup (p); 1358 matched = 1; 1359 } 1360 else if (!ascii_strcasecmp (line + 1, "ender")) 1361 { 1362 e->sender = rfc822_parse_adrlist (e->sender, p); 1363 matched = 1; 1364 } 1365 else if (!ascii_strcasecmp (line + 1, "tatus")) 1366 { 1367 if (hdr) 1368 { 1369 while (*p) 1370 { 1371 switch (*p) 1372 { 1373 case 'r': 1374 hdr->replied = 1; 1375 break; 1376 case 'O': 1377 hdr->old = 1; 1378 break; 1379 case 'R': 1380 hdr->read = 1; 1381 break; 1382 } 1383 p++; 1384 } 1385 } 1386 matched = 1; 1387 } 1388 else if ((!ascii_strcasecmp ("upersedes", line + 1) || 1389 !ascii_strcasecmp ("upercedes", line + 1)) && hdr) 1390 { 1391 FREE(&e->supersedes); 1392 e->supersedes = safe_strdup (p); 1393 } 1394 break; 1395 1396 case 't': 1397 if (ascii_strcasecmp (line+1, "o") == 0) 1398 { 1399 e->to = rfc822_parse_adrlist (e->to, p); 1400 matched = 1; 1401 } 1402 break; 1403 1404 case 'x': 1405 if (ascii_strcasecmp (line+1, "-status") == 0) 1406 { 1407 if (hdr) 1408 { 1409 while (*p) 1410 { 1411 switch (*p) 1412 { 1413 case 'A': 1414 hdr->replied = 1; 1415 break; 1416 case 'D': 1417 hdr->deleted = 1; 1418 break; 1419 case 'F': 1420 hdr->flagged = 1; 1421 break; 1422 default: 1423 break; 1424 } 1425 p++; 1426 } 1427 } 1428 matched = 1; 1429 } 1430 else if (ascii_strcasecmp (line+1, "-label") == 0) 1431 { 1432 FREE(&e->x_label); 1433 e->x_label = safe_strdup(p); 1434 matched = 1; 1435 } 1436 1437 default: 1438 break; 1439 } 1440 1441 /* Keep track of the user-defined headers */ 1442 if (!matched && user_hdrs) 1443 { 1444 LIST *last = NULL; 1445 1446 if (lastp) 1447 last = *lastp; 1448 1449 /* restore the original line */ 1450 line[strlen (line)] = ':'; 1451 1452 if (weed && option (OPTWEED) && mutt_matches_ignore (line, Ignore) 1453 && !mutt_matches_ignore (line, UnIgnore)) 1454 goto done; 1455 1456 if (last) 1457 { 1458 last->next = mutt_new_list (); 1459 last = last->next; 1460 } 1461 else 1462 last = e->userhdrs = mutt_new_list (); 1463 last->data = safe_strdup (line); 1464 if (do_2047) 1465 rfc2047_decode (&last->data); 1466 1467 if (lastp) 1468 *lastp = last; 1469 } 1470 1471done: 1472 return matched; 1473} 1474 1475 1476/* mutt_read_rfc822_header() -- parses a RFC822 header 1477 * 1478 * Args: 1479 * 1480 * f stream to read from 1481 * 1482 * hdr header structure of current message (optional). 1483 * 1484 * user_hdrs If set, store user headers. Used for recall-message and 1485 * postpone modes. 1486 * 1487 * weed If this parameter is set and the user has activated the 1488 * $weed option, honor the header weed list for user headers. 1489 * Used for recall-message. 1490 * 1491 * Returns: newly allocated envelope structure. You should free it by 1492 * mutt_free_envelope() when envelope stay unneeded. 1493 */ 1494ENVELOPE *mutt_read_rfc822_header (FILE *f, HEADER *hdr, short user_hdrs, 1495 short weed) 1496{ 1497 ENVELOPE *e = mutt_new_envelope(); 1498 LIST *last = NULL; 1499 char *line = safe_malloc (LONG_STRING); 1500 char *p; 1501 LOFF_T loc; 1502 size_t linelen = LONG_STRING; 1503 char buf[LONG_STRING+1]; 1504 1505 if (hdr) 1506 { 1507 if (hdr->content == NULL) 1508 { 1509 hdr->content = mutt_new_body (); 1510 1511 /* set the defaults from RFC1521 */ 1512 hdr->content->type = TYPETEXT; 1513 hdr->content->subtype = safe_strdup ("plain"); 1514 hdr->content->encoding = ENC7BIT; 1515 hdr->content->length = -1; 1516 1517 /* RFC 2183 says this is arbitrary */ 1518 hdr->content->disposition = DISPINLINE; 1519 } 1520 } 1521 1522 while ((loc = ftello (f)), 1523 *(line = mutt_read_rfc822_line (f, line, &linelen)) != 0) 1524 { 1525 if ((p = strpbrk (line, ": \t")) == NULL || *p != ':') 1526 { 1527 char return_path[LONG_STRING]; 1528 time_t t; 1529 1530 /* some bogus MTAs will quote the original "From " line */ 1531 if (mutt_strncmp (">From ", line, 6) == 0) 1532 continue; /* just ignore */ 1533 else if (is_from (line, return_path, sizeof (return_path), &t)) 1534 { 1535 /* MH sometimes has the From_ line in the middle of the header! */ 1536 if (hdr && !hdr->received) 1537 hdr->received = t - mutt_local_tz (t); 1538 continue; 1539 } 1540 1541 fseeko (f, loc, 0); 1542 break; /* end of header */ 1543 } 1544 1545 *buf = '\0'; 1546 1547 if (mutt_match_spam_list(line, SpamList, buf, sizeof(buf))) 1548 { 1549 if (!mutt_match_rx_list(line, NoSpamList)) 1550 { 1551 1552 /* if spam tag already exists, figure out how to amend it */ 1553 if (e->spam && *buf) 1554 { 1555 /* If SpamSep defined, append with separator */ 1556 if (SpamSep) 1557 { 1558 mutt_buffer_addstr(e->spam, SpamSep); 1559 mutt_buffer_addstr(e->spam, buf); 1560 } 1561 1562 /* else overwrite */ 1563 else 1564 { 1565 mutt_buffer_clear (e->spam); 1566 mutt_buffer_addstr(e->spam, buf); 1567 } 1568 } 1569 1570 /* spam tag is new, and match expr is non-empty; copy */ 1571 else if (!e->spam && *buf) 1572 { 1573 e->spam = mutt_buffer_from (buf); 1574 } 1575 1576 /* match expr is empty; plug in null string if no existing tag */ 1577 else if (!e->spam) 1578 { 1579 e->spam = mutt_buffer_from(""); 1580 } 1581 1582 if (e->spam && e->spam->data) 1583 dprint(5, (debugfile, "p822: spam = %s\n", e->spam->data)); 1584 } 1585 } 1586 1587 *p = 0; 1588 p = skip_email_wsp(p + 1); 1589 if (!*p) 1590 continue; /* skip empty header fields */ 1591 1592 mutt_parse_rfc822_line (e, hdr, line, p, user_hdrs, weed, 1, &last); 1593 } 1594 1595 FREE (&line); 1596 1597 if (hdr) 1598 { 1599 hdr->content->hdr_offset = hdr->offset; 1600 hdr->content->offset = ftello (f); 1601 1602 rfc2047_decode_envelope (e); 1603 1604 if (e->subject) 1605 { 1606 regmatch_t pmatch[1]; 1607 1608 if (regexec (ReplyRegexp.rx, e->subject, 1, pmatch, 0) == 0) 1609 e->real_subj = e->subject + pmatch[0].rm_eo; 1610 else 1611 e->real_subj = e->subject; 1612 } 1613 1614 if (hdr->received < 0) 1615 { 1616 dprint(1,(debugfile,"read_rfc822_header(): resetting invalid received time to 0\n")); 1617 hdr->received = 0; 1618 } 1619 1620 /* check for missing or invalid date */ 1621 if (hdr->date_sent <= 0) 1622 { 1623 dprint(1,(debugfile,"read_rfc822_header(): no date found, using received time from msg separator\n")); 1624 hdr->date_sent = hdr->received; 1625 } 1626 1627#ifdef USE_AUTOCRYPT 1628 if (option (OPTAUTOCRYPT)) 1629 { 1630 mutt_autocrypt_process_autocrypt_header (hdr, e); 1631 /* No sense in taking up memory after the header is processed */ 1632 mutt_free_autocrypthdr (&e->autocrypt); 1633 } 1634#endif 1635 } 1636 1637 return (e); 1638} 1639 1640ADDRESS *mutt_parse_adrlist (ADDRESS *p, const char *s) 1641{ 1642 const char *q; 1643 1644 /* check for a simple whitespace separated list of addresses */ 1645 if ((q = strpbrk (s, "\"<>():;,\\")) == NULL) 1646 { 1647 BUFFER *tmp; 1648 char *r; 1649 1650 tmp = mutt_buffer_pool_get (); 1651 mutt_buffer_strcpy (tmp, s); 1652 r = tmp->data; 1653 while ((r = strtok (r, " \t")) != NULL) 1654 { 1655 p = rfc822_parse_adrlist (p, r); 1656 r = NULL; 1657 } 1658 mutt_buffer_pool_release (&tmp); 1659 } 1660 else 1661 p = rfc822_parse_adrlist (p, s); 1662 1663 return p; 1664} 1665 1666/* Compares mime types to the ok and except lists */ 1667static int count_body_parts_check(LIST **checklist, BODY *b, int dflt) 1668{ 1669 LIST *type; 1670 ATTACH_MATCH *a; 1671 1672 /* If list is null, use default behavior. */ 1673 if (! *checklist) 1674 { 1675 /*return dflt;*/ 1676 return 0; 1677 } 1678 1679 for (type = *checklist; type; type = type->next) 1680 { 1681 a = (ATTACH_MATCH *)type->data; 1682 dprint(5, (debugfile, "cbpc: %s %d/%s ?? %s/%s [%d]... ", 1683 dflt ? "[OK] " : "[EXCL] ", 1684 b->type, b->subtype, a->major, a->minor, a->major_int)); 1685 if ((a->major_int == TYPEANY || a->major_int == b->type) && 1686 !regexec(&a->minor_rx, b->subtype, 0, NULL, 0)) 1687 { 1688 dprint(5, (debugfile, "yes\n")); 1689 return 1; 1690 } 1691 else 1692 { 1693 dprint(5, (debugfile, "no\n")); 1694 } 1695 } 1696 1697 return 0; 1698} 1699 1700#define AT_COUNT(why) { shallcount = 1; } 1701#define AT_NOCOUNT(why) { shallcount = 0; } 1702 1703static int count_body_parts (BODY *body, int flags) 1704{ 1705 int count = 0; 1706 int shallcount, shallrecurse; 1707 BODY *bp; 1708 1709 if (body == NULL) 1710 return 0; 1711 1712 for (bp = body; bp != NULL; bp = bp->next) 1713 { 1714 /* Initial disposition is to count and not to recurse this part. */ 1715 AT_COUNT("default"); 1716 shallrecurse = 0; 1717 1718 dprint(5, (debugfile, "bp: desc=\"%s\"; fn=\"%s\", type=\"%d/%s\"\n", 1719 bp->description ? bp->description : ("none"), 1720 bp->filename ? bp->filename : 1721 bp->d_filename ? bp->d_filename : "(none)", 1722 bp->type, bp->subtype ? bp->subtype : "*")); 1723 1724 if (bp->type == TYPEMESSAGE) 1725 { 1726 shallrecurse = 1; 1727 1728 /* If it's an external body pointer, don't recurse it. */ 1729 if (!ascii_strcasecmp (bp->subtype, "external-body")) 1730 shallrecurse = 0; 1731 1732 /* Don't count containers if they're top-level. */ 1733 if (flags & MUTT_PARTS_TOPLEVEL) 1734 AT_NOCOUNT("top-level message/*"); 1735 } 1736 else if (bp->type == TYPEMULTIPART) 1737 { 1738 /* Always recurse multiparts, except multipart/alternative. */ 1739 shallrecurse = 1; 1740 if (!ascii_strcasecmp(bp->subtype, "alternative")) 1741 shallrecurse = option (OPTCOUNTALTERNATIVES); 1742 1743 /* Don't count containers if they're top-level. */ 1744 if (flags & MUTT_PARTS_TOPLEVEL) 1745 AT_NOCOUNT("top-level multipart"); 1746 } 1747 1748 if (bp->disposition == DISPINLINE && 1749 bp->type != TYPEMULTIPART && bp->type != TYPEMESSAGE && bp == body) 1750 AT_NOCOUNT("ignore fundamental inlines"); 1751 1752 /* If this body isn't scheduled for enumeration already, don't bother 1753 * profiling it further. 1754 */ 1755 if (shallcount) 1756 { 1757 /* Turn off shallcount if message type is not in ok list, 1758 * or if it is in except list. Check is done separately for 1759 * inlines vs. attachments. 1760 */ 1761 1762 if (bp->disposition == DISPATTACH) 1763 { 1764 if (!count_body_parts_check(&AttachAllow, bp, 1)) 1765 AT_NOCOUNT("attach not allowed"); 1766 if (count_body_parts_check(&AttachExclude, bp, 0)) 1767 AT_NOCOUNT("attach excluded"); 1768 } 1769 else 1770 { 1771 if (!count_body_parts_check(&InlineAllow, bp, 1)) 1772 AT_NOCOUNT("inline not allowed"); 1773 if (count_body_parts_check(&InlineExclude, bp, 0)) 1774 AT_NOCOUNT("excluded"); 1775 } 1776 } 1777 1778 if (shallcount) 1779 count++; 1780 bp->attach_qualifies = shallcount ? 1 : 0; 1781 1782 dprint(5, (debugfile, "cbp: %p shallcount = %d\n", (void *)bp, shallcount)); 1783 1784 if (shallrecurse) 1785 { 1786 dprint(5, (debugfile, "cbp: %p pre count = %d\n", (void *)bp, count)); 1787 bp->attach_count = count_body_parts(bp->parts, flags & ~MUTT_PARTS_TOPLEVEL); 1788 count += bp->attach_count; 1789 dprint(5, (debugfile, "cbp: %p post count = %d\n", (void *)bp, count)); 1790 } 1791 } 1792 1793 dprint(5, (debugfile, "bp: return %d\n", count < 0 ? 0 : count)); 1794 return count < 0 ? 0 : count; 1795} 1796 1797int mutt_count_body_parts (CONTEXT *ctx, HEADER *hdr) 1798{ 1799 short keep_parts = 0; 1800 1801 if (hdr->attach_valid) 1802 return hdr->attach_total; 1803 1804 if (hdr->content->parts) 1805 keep_parts = 1; 1806 else 1807 mutt_parse_mime_message (ctx, hdr); 1808 1809 if (AttachAllow || AttachExclude || InlineAllow || InlineExclude) 1810 hdr->attach_total = count_body_parts(hdr->content, MUTT_PARTS_TOPLEVEL); 1811 else 1812 hdr->attach_total = 0; 1813 1814 hdr->attach_valid = 1; 1815 1816 if (!keep_parts) 1817 mutt_free_body (&hdr->content->parts); 1818 1819 return hdr->attach_total; 1820}