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