mutt stable branch with some hacks
at jcs 1902 lines 46 kB view raw
1/* 2 * Copyright (C) 1996-2000,2002,2010,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 <stdlib.h> 24#include <string.h> 25#include <unistd.h> 26#include <ctype.h> 27#include <sys/wait.h> 28#include <sys/stat.h> 29 30#include "mutt.h" 31#include "mutt_curses.h" 32#include "rfc1524.h" 33#include "keymap.h" 34#include "mime.h" 35#include "copy.h" 36#include "charset.h" 37#include "mutt_crypt.h" 38#include "rfc3676.h" 39 40#define BUFI_SIZE 1000 41#define BUFO_SIZE 2000 42 43 44typedef int (*handler_t) (BODY *, STATE *); 45 46const int Index_hex[128] = { 47 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 48 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 49 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 50 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1, 51 -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, 52 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 53 -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1, 54 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1 55}; 56 57const int Index_64[128] = { 58 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 59 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 60 -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, 61 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, 62 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 63 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, 64 -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 65 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 66}; 67 68static void state_prefix_put (const char *d, size_t dlen, STATE *s) 69{ 70 if (s->prefix) 71 while (dlen--) 72 state_prefix_putc (*d++, s); 73 else 74 fwrite (d, dlen, 1, s->fpout); 75} 76 77static void mutt_convert_to_state(iconv_t cd, char *bufi, size_t *l, STATE *s) 78{ 79 char bufo[BUFO_SIZE]; 80 ICONV_CONST char *ib; 81 char *ob; 82 size_t ibl, obl; 83 84 if (!bufi) 85 { 86 if (cd != (iconv_t)(-1)) 87 { 88 ob = bufo, obl = sizeof (bufo); 89 iconv (cd, 0, 0, &ob, &obl); 90 if (ob != bufo) 91 state_prefix_put (bufo, ob - bufo, s); 92 } 93 return; 94 } 95 96 if (cd == (iconv_t)(-1)) 97 { 98 state_prefix_put (bufi, *l, s); 99 *l = 0; 100 return; 101 } 102 103 ib = bufi, ibl = *l; 104 for (;;) 105 { 106 ob = bufo, obl = sizeof (bufo); 107 mutt_iconv (cd, &ib, &ibl, &ob, &obl, 0, "?"); 108 if (ob == bufo) 109 break; 110 state_prefix_put (bufo, ob - bufo, s); 111 } 112 memmove (bufi, ib, ibl); 113 *l = ibl; 114} 115 116static void mutt_decode_xbit (STATE *s, long len, int istext, iconv_t cd) 117{ 118 int c, ch; 119 char bufi[BUFI_SIZE]; 120 size_t l = 0; 121 122 if (istext) 123 { 124 state_set_prefix(s); 125 126 while ((c = fgetc(s->fpin)) != EOF && len--) 127 { 128 if (c == '\r' && len) 129 { 130 if ((ch = fgetc(s->fpin)) == '\n') 131 { 132 c = ch; 133 len--; 134 } 135 else 136 ungetc(ch, s->fpin); 137 } 138 139 bufi[l++] = c; 140 if (l == sizeof (bufi)) 141 mutt_convert_to_state (cd, bufi, &l, s); 142 } 143 144 mutt_convert_to_state (cd, bufi, &l, s); 145 mutt_convert_to_state (cd, 0, 0, s); 146 147 state_reset_prefix (s); 148 } 149 else 150 mutt_copy_bytes (s->fpin, s->fpout, len); 151} 152 153static int qp_decode_triple (char *s, char *d) 154{ 155 /* soft line break */ 156 if (*s == '=' && !(*(s+1))) 157 return 1; 158 159 /* quoted-printable triple */ 160 if (*s == '=' && 161 isxdigit ((unsigned char) *(s+1)) && 162 isxdigit ((unsigned char) *(s+2))) 163 { 164 *d = (hexval (*(s+1)) << 4) | hexval (*(s+2)); 165 return 0; 166 } 167 168 /* something else */ 169 return -1; 170} 171 172static void qp_decode_line (char *dest, char *src, size_t *l, 173 int last) 174{ 175 char *d, *s; 176 char c = 0; 177 178 int kind = -1; 179 int soft = 0; 180 181 /* decode the line */ 182 183 for (d = dest, s = src; *s;) 184 { 185 switch ((kind = qp_decode_triple (s, &c))) 186 { 187 case 0: *d++ = c; s += 3; break; /* qp triple */ 188 case -1: *d++ = *s++; break; /* single character */ 189 case 1: soft = 1; s++; break; /* soft line break */ 190 } 191 } 192 193 if (!soft && last == '\n') 194 { 195 /* neither \r nor \n as part of line-terminating CRLF 196 * may be qp-encoded, so remove \r and \n-terminate; 197 * see RfC2045, sect. 6.7, (1): General 8bit representation */ 198 if (kind == 0 && c == '\r') 199 *(d-1) = '\n'; 200 else 201 *d++ = '\n'; 202 } 203 204 *d = '\0'; 205 *l = d - dest; 206} 207 208/* 209 * Decode an attachment encoded with quoted-printable. 210 * 211 * Why doesn't this overflow any buffers? First, it's guaranteed 212 * that the length of a line grows when you _en_-code it to 213 * quoted-printable. That means that we always can store the 214 * result in a buffer of at most the _same_ size. 215 * 216 * Now, we don't special-case if the line we read with fgets() 217 * isn't terminated. We don't care about this, since STRING > 78, 218 * so corrupted input will just be corrupted a bit more. That 219 * implies that STRING+1 bytes are always sufficient to store the 220 * result of qp_decode_line. 221 * 222 * Finally, at soft line breaks, some part of a multibyte character 223 * may have been left over by mutt_convert_to_state(). This shouldn't 224 * be more than 6 characters, so STRING + 7 should be sufficient 225 * memory to store the decoded data. 226 * 227 * Just to make sure that I didn't make some off-by-one error 228 * above, we just use STRING*2 for the target buffer's size. 229 * 230 */ 231 232static void mutt_decode_quoted (STATE *s, long len, int istext, iconv_t cd) 233{ 234 char line[STRING]; 235 char decline[2*STRING]; 236 size_t l = 0; 237 size_t linelen; /* number of input bytes in `line' */ 238 size_t l3; 239 240 int last; /* store the last character in the input line */ 241 242 if (istext) 243 state_set_prefix(s); 244 245 while (len > 0) 246 { 247 last = 0; 248 249 /* 250 * It's ok to use a fixed size buffer for input, even if the line turns 251 * out to be longer than this. Just process the line in chunks. This 252 * really shouldn't happen according the MIME spec, since Q-P encoded 253 * lines are at most 76 characters, but we should be liberal about what 254 * we accept. 255 */ 256 if (fgets (line, MIN ((ssize_t)sizeof (line), len + 1), s->fpin) == NULL) 257 break; 258 259 linelen = strlen(line); 260 len -= linelen; 261 262 /* 263 * inspect the last character we read so we can tell if we got the 264 * entire line. 265 */ 266 last = linelen ? line[linelen - 1] : 0; 267 268 /* chop trailing whitespace if we got the full line */ 269 if (last == '\n') 270 { 271 while (linelen > 0 && ISSPACE (line[linelen-1])) 272 linelen--; 273 line[linelen]=0; 274 } 275 276 /* decode and do character set conversion */ 277 qp_decode_line (decline + l, line, &l3, last); 278 l += l3; 279 mutt_convert_to_state (cd, decline, &l, s); 280 } 281 282 mutt_convert_to_state (cd, 0, 0, s); 283 state_reset_prefix(s); 284} 285 286void mutt_decode_base64 (STATE *s, long len, int istext, iconv_t cd) 287{ 288 char buf[5]; 289 int c1, c2, c3, c4, ch, cr = 0, i; 290 char bufi[BUFI_SIZE]; 291 size_t l = 0; 292 293 buf[4] = 0; 294 295 if (istext) 296 state_set_prefix(s); 297 298 while (len > 0) 299 { 300 for (i = 0 ; i < 4 && len > 0 ; len--) 301 { 302 if ((ch = fgetc (s->fpin)) == EOF) 303 break; 304 if (ch >= 0 && ch < 128 && (base64val(ch) != -1 || ch == '=')) 305 buf[i++] = ch; 306 } 307 if (i != 4) 308 { 309 /* "i" may be zero if there is trailing whitespace, which is not an error */ 310 if (i != 0) 311 dprint (2, (debugfile, "%s:%d [mutt_decode_base64()]: " 312 "didn't get a multiple of 4 chars.\n", __FILE__, __LINE__)); 313 break; 314 } 315 316 c1 = base64val (buf[0]); 317 c2 = base64val (buf[1]); 318 ch = (c1 << 2) | (c2 >> 4); 319 320 if (cr && ch != '\n') 321 bufi[l++] = '\r'; 322 323 cr = 0; 324 325 if (istext && ch == '\r') 326 cr = 1; 327 else 328 bufi[l++] = ch; 329 330 if (buf[2] == '=') 331 break; 332 c3 = base64val (buf[2]); 333 ch = ((c2 & 0xf) << 4) | (c3 >> 2); 334 335 if (cr && ch != '\n') 336 bufi[l++] = '\r'; 337 338 cr = 0; 339 340 if (istext && ch == '\r') 341 cr = 1; 342 else 343 bufi[l++] = ch; 344 345 if (buf[3] == '=') break; 346 c4 = base64val (buf[3]); 347 ch = ((c3 & 0x3) << 6) | c4; 348 349 if (cr && ch != '\n') 350 bufi[l++] = '\r'; 351 cr = 0; 352 353 if (istext && ch == '\r') 354 cr = 1; 355 else 356 bufi[l++] = ch; 357 358 if (l + 8 >= sizeof (bufi)) 359 mutt_convert_to_state (cd, bufi, &l, s); 360 } 361 362 if (cr) bufi[l++] = '\r'; 363 364 mutt_convert_to_state (cd, bufi, &l, s); 365 mutt_convert_to_state (cd, 0, 0, s); 366 367 state_reset_prefix(s); 368} 369 370static unsigned char decode_byte (char ch) 371{ 372 if (ch == 96) 373 return 0; 374 return ch - 32; 375} 376 377static void mutt_decode_uuencoded (STATE *s, long len, int istext, iconv_t cd) 378{ 379 char tmps[SHORT_STRING]; 380 char linelen, c, l, out; 381 char *pt; 382 char bufi[BUFI_SIZE]; 383 size_t k = 0; 384 385 if (istext) 386 state_set_prefix(s); 387 388 while (len > 0) 389 { 390 if ((fgets(tmps, sizeof(tmps), s->fpin)) == NULL) 391 return; 392 len -= mutt_strlen(tmps); 393 if ((!mutt_strncmp (tmps, "begin", 5)) && ISSPACE (tmps[5])) 394 break; 395 } 396 while (len > 0) 397 { 398 if ((fgets(tmps, sizeof(tmps), s->fpin)) == NULL) 399 return; 400 len -= mutt_strlen(tmps); 401 if (!mutt_strncmp (tmps, "end", 3)) 402 break; 403 pt = tmps; 404 linelen = decode_byte (*pt); 405 pt++; 406 for (c = 0; c < linelen;) 407 { 408 for (l = 2; l <= 6; l += 2) 409 { 410 out = decode_byte (*pt) << l; 411 pt++; 412 out |= (decode_byte (*pt) >> (6 - l)); 413 bufi[k++] = out; 414 c++; 415 if (c == linelen) 416 break; 417 } 418 mutt_convert_to_state (cd, bufi, &k, s); 419 pt++; 420 } 421 } 422 423 mutt_convert_to_state (cd, bufi, &k, s); 424 mutt_convert_to_state (cd, 0, 0, s); 425 426 state_reset_prefix(s); 427} 428 429/* ---------------------------------------------------------------------------- 430 * A (not so) minimal implementation of RFC1563. 431 */ 432 433#define IndentSize (4) 434 435enum { RICH_PARAM=0, RICH_BOLD, RICH_UNDERLINE, RICH_ITALIC, RICH_NOFILL, 436 RICH_INDENT, RICH_INDENT_RIGHT, RICH_EXCERPT, RICH_CENTER, RICH_FLUSHLEFT, 437 RICH_FLUSHRIGHT, RICH_COLOR, RICH_LAST_TAG }; 438 439static const struct { 440 const wchar_t *tag_name; 441 int index; 442} EnrichedTags[] = { 443 { L"param", RICH_PARAM }, 444 { L"bold", RICH_BOLD }, 445 { L"italic", RICH_ITALIC }, 446 { L"underline", RICH_UNDERLINE }, 447 { L"nofill", RICH_NOFILL }, 448 { L"excerpt", RICH_EXCERPT }, 449 { L"indent", RICH_INDENT }, 450 { L"indentright", RICH_INDENT_RIGHT }, 451 { L"center", RICH_CENTER }, 452 { L"flushleft", RICH_FLUSHLEFT }, 453 { L"flushright", RICH_FLUSHRIGHT }, 454 { L"flushboth", RICH_FLUSHLEFT }, 455 { L"color", RICH_COLOR }, 456 { L"x-color", RICH_COLOR }, 457 { NULL, -1 } 458}; 459 460struct enriched_state 461{ 462 wchar_t *buffer; 463 wchar_t *line; 464 wchar_t *param; 465 size_t buff_len; 466 size_t line_len; 467 size_t line_used; 468 size_t line_max; 469 size_t indent_len; 470 size_t word_len; 471 size_t buff_used; 472 size_t param_used; 473 size_t param_len; 474 int tag_level[RICH_LAST_TAG]; 475 int WrapMargin; 476 STATE *s; 477}; 478 479static void enriched_wrap (struct enriched_state *stte) 480{ 481 int x; 482 int extra; 483 484 if (stte->line_len) 485 { 486 if (stte->tag_level[RICH_CENTER] || stte->tag_level[RICH_FLUSHRIGHT]) 487 { 488 /* Strip trailing white space */ 489 size_t y = stte->line_used - 1; 490 491 while (y && iswspace (stte->line[y])) 492 { 493 stte->line[y] = (wchar_t) '\0'; 494 y--; 495 stte->line_used--; 496 stte->line_len--; 497 } 498 if (stte->tag_level[RICH_CENTER]) 499 { 500 /* Strip leading whitespace */ 501 y = 0; 502 503 while (stte->line[y] && iswspace (stte->line[y])) 504 y++; 505 if (y) 506 { 507 size_t z; 508 509 for (z = y ; z <= stte->line_used; z++) 510 { 511 stte->line[z - y] = stte->line[z]; 512 } 513 514 stte->line_len -= y; 515 stte->line_used -= y; 516 } 517 } 518 } 519 520 extra = stte->WrapMargin - stte->line_len - stte->indent_len - 521 (stte->tag_level[RICH_INDENT_RIGHT] * IndentSize); 522 if (extra > 0) 523 { 524 if (stte->tag_level[RICH_CENTER]) 525 { 526 x = extra / 2; 527 while (x) 528 { 529 state_putc (' ', stte->s); 530 x--; 531 } 532 } 533 else if (stte->tag_level[RICH_FLUSHRIGHT]) 534 { 535 x = extra-1; 536 while (x) 537 { 538 state_putc (' ', stte->s); 539 x--; 540 } 541 } 542 } 543 state_putws ((const wchar_t*) stte->line, stte->s); 544 } 545 546 state_putc ('\n', stte->s); 547 stte->line[0] = (wchar_t) '\0'; 548 stte->line_len = 0; 549 stte->line_used = 0; 550 stte->indent_len = 0; 551 if (stte->s->prefix) 552 { 553 state_puts (stte->s->prefix, stte->s); 554 stte->indent_len += mutt_strlen (stte->s->prefix); 555 } 556 557 if (stte->tag_level[RICH_EXCERPT]) 558 { 559 x = stte->tag_level[RICH_EXCERPT]; 560 while (x) 561 { 562 if (stte->s->prefix) 563 { 564 state_puts (stte->s->prefix, stte->s); 565 stte->indent_len += mutt_strlen (stte->s->prefix); 566 } 567 else 568 { 569 state_puts ("> ", stte->s); 570 stte->indent_len += mutt_strlen ("> "); 571 } 572 x--; 573 } 574 } 575 else 576 stte->indent_len = 0; 577 if (stte->tag_level[RICH_INDENT]) 578 { 579 x = stte->tag_level[RICH_INDENT] * IndentSize; 580 stte->indent_len += x; 581 while (x) 582 { 583 state_putc (' ', stte->s); 584 x--; 585 } 586 } 587} 588 589static void enriched_flush (struct enriched_state *stte, int wrap) 590{ 591 if (!stte->tag_level[RICH_NOFILL] && (stte->line_len + stte->word_len > 592 (stte->WrapMargin - (stte->tag_level[RICH_INDENT_RIGHT] * IndentSize) - 593 stte->indent_len))) 594 enriched_wrap (stte); 595 596 if (stte->buff_used) 597 { 598 stte->buffer[stte->buff_used] = (wchar_t) '\0'; 599 stte->line_used += stte->buff_used; 600 if (stte->line_used > stte->line_max) 601 { 602 stte->line_max = stte->line_used; 603 safe_realloc (&stte->line, (stte->line_max + 1) * sizeof (wchar_t)); 604 } 605 wcscat (stte->line, stte->buffer); 606 stte->line_len += stte->word_len; 607 stte->word_len = 0; 608 stte->buff_used = 0; 609 } 610 if (wrap) 611 enriched_wrap(stte); 612 fflush (stte->s->fpout); 613} 614 615 616static void enriched_putwc (wchar_t c, struct enriched_state *stte) 617{ 618 if (stte->tag_level[RICH_PARAM]) 619 { 620 if (stte->tag_level[RICH_COLOR]) 621 { 622 if (stte->param_used + 1 >= stte->param_len) 623 safe_realloc (&stte->param, (stte->param_len += STRING) * sizeof (wchar_t)); 624 625 stte->param[stte->param_used++] = c; 626 } 627 return; /* nothing to do */ 628 } 629 630 /* see if more space is needed (plus extra for possible rich characters) */ 631 if (stte->buff_len < stte->buff_used + 3) 632 { 633 stte->buff_len += LONG_STRING; 634 safe_realloc (&stte->buffer, (stte->buff_len + 1) * sizeof (wchar_t)); 635 } 636 637 if ((!stte->tag_level[RICH_NOFILL] && iswspace (c)) || c == (wchar_t) '\0') 638 { 639 if (c == (wchar_t) '\t') 640 stte->word_len += 8 - (stte->line_len + stte->word_len) % 8; 641 else 642 stte->word_len++; 643 644 stte->buffer[stte->buff_used++] = c; 645 enriched_flush (stte, 0); 646 } 647 else 648 { 649 if (stte->s->flags & MUTT_DISPLAY) 650 { 651 if (stte->tag_level[RICH_BOLD]) 652 { 653 stte->buffer[stte->buff_used++] = c; 654 stte->buffer[stte->buff_used++] = (wchar_t) '\010'; 655 stte->buffer[stte->buff_used++] = c; 656 } 657 else if (stte->tag_level[RICH_UNDERLINE]) 658 { 659 660 stte->buffer[stte->buff_used++] = '_'; 661 stte->buffer[stte->buff_used++] = (wchar_t) '\010'; 662 stte->buffer[stte->buff_used++] = c; 663 } 664 else if (stte->tag_level[RICH_ITALIC]) 665 { 666 stte->buffer[stte->buff_used++] = c; 667 stte->buffer[stte->buff_used++] = (wchar_t) '\010'; 668 stte->buffer[stte->buff_used++] = '_'; 669 } 670 else 671 { 672 stte->buffer[stte->buff_used++] = c; 673 } 674 } 675 else 676 { 677 stte->buffer[stte->buff_used++] = c; 678 } 679 stte->word_len++; 680 } 681} 682 683static void enriched_puts (const char *s, struct enriched_state *stte) 684{ 685 const char *c; 686 687 if (stte->buff_len < stte->buff_used + mutt_strlen (s)) 688 { 689 stte->buff_len += LONG_STRING; 690 safe_realloc (&stte->buffer, (stte->buff_len + 1) * sizeof (wchar_t)); 691 } 692 c = s; 693 while (*c) 694 { 695 stte->buffer[stte->buff_used++] = (wchar_t) *c; 696 c++; 697 } 698} 699 700static void enriched_set_flags (const wchar_t *tag, struct enriched_state *stte) 701{ 702 const wchar_t *tagptr = tag; 703 int i, j; 704 705 if (*tagptr == (wchar_t) '/') 706 tagptr++; 707 708 for (i = 0, j = -1; EnrichedTags[i].tag_name; i++) 709 if (wcscasecmp (EnrichedTags[i].tag_name, tagptr) == 0) 710 { 711 j = EnrichedTags[i].index; 712 break; 713 } 714 715 if (j != -1) 716 { 717 if (j == RICH_CENTER || j == RICH_FLUSHLEFT || j == RICH_FLUSHRIGHT) 718 enriched_flush (stte, 1); 719 720 if (*tag == (wchar_t) '/') 721 { 722 if (stte->tag_level[j]) /* make sure not to go negative */ 723 stte->tag_level[j]--; 724 if ((stte->s->flags & MUTT_DISPLAY) && j == RICH_PARAM && stte->tag_level[RICH_COLOR]) 725 { 726 stte->param[stte->param_used] = (wchar_t) '\0'; 727 if (!wcscasecmp(L"black", stte->param)) 728 { 729 enriched_puts("\033[30m", stte); 730 } 731 else if (!wcscasecmp(L"red", stte->param)) 732 { 733 enriched_puts("\033[31m", stte); 734 } 735 else if (!wcscasecmp(L"green", stte->param)) 736 { 737 enriched_puts("\033[32m", stte); 738 } 739 else if (!wcscasecmp(L"yellow", stte->param)) 740 { 741 enriched_puts("\033[33m", stte); 742 } 743 else if (!wcscasecmp(L"blue", stte->param)) 744 { 745 enriched_puts("\033[34m", stte); 746 } 747 else if (!wcscasecmp(L"magenta", stte->param)) 748 { 749 enriched_puts("\033[35m", stte); 750 } 751 else if (!wcscasecmp(L"cyan", stte->param)) 752 { 753 enriched_puts("\033[36m", stte); 754 } 755 else if (!wcscasecmp(L"white", stte->param)) 756 { 757 enriched_puts("\033[37m", stte); 758 } 759 } 760 if ((stte->s->flags & MUTT_DISPLAY) && j == RICH_COLOR) 761 { 762 enriched_puts("\033[0m", stte); 763 } 764 765 /* flush parameter buffer when closing the tag */ 766 if (j == RICH_PARAM) 767 { 768 stte->param_used = 0; 769 stte->param[0] = (wchar_t) '\0'; 770 } 771 } 772 else 773 stte->tag_level[j]++; 774 775 if (j == RICH_EXCERPT) 776 enriched_flush(stte, 1); 777 } 778} 779 780static int text_enriched_handler (BODY *a, STATE *s) 781{ 782 enum { 783 TEXT, LANGLE, TAG, BOGUS_TAG, NEWLINE, ST_EOF, DONE 784 } state = TEXT; 785 786 long bytes = a->length; 787 struct enriched_state stte; 788 wchar_t wc = 0; 789 int tag_len = 0; 790 wchar_t tag[LONG_STRING + 1]; 791 792 memset (&stte, 0, sizeof (stte)); 793 stte.s = s; 794 stte.WrapMargin = ((s->flags & MUTT_DISPLAY) ? (MuttIndexWindow->cols-4) : 795 ((MuttIndexWindow->cols-4)<72)?(MuttIndexWindow->cols-4):72); 796 stte.line_max = stte.WrapMargin * 4; 797 stte.line = (wchar_t *) safe_calloc (1, (stte.line_max + 1) * sizeof (wchar_t)); 798 stte.param = (wchar_t *) safe_calloc (1, (STRING) * sizeof (wchar_t)); 799 800 stte.param_len = STRING; 801 stte.param_used = 0; 802 803 if (s->prefix) 804 { 805 state_puts (s->prefix, s); 806 stte.indent_len += mutt_strlen (s->prefix); 807 } 808 809 while (state != DONE) 810 { 811 if (state != ST_EOF) 812 { 813 if (!bytes || (wc = fgetwc (s->fpin)) == WEOF) 814 state = ST_EOF; 815 else 816 bytes--; 817 } 818 819 switch (state) 820 { 821 case TEXT : 822 switch (wc) 823 { 824 case '<' : 825 state = LANGLE; 826 break; 827 828 case '\n' : 829 if (stte.tag_level[RICH_NOFILL]) 830 { 831 enriched_flush (&stte, 1); 832 } 833 else 834 { 835 enriched_putwc ((wchar_t) ' ', &stte); 836 state = NEWLINE; 837 } 838 break; 839 840 default: 841 enriched_putwc (wc, &stte); 842 } 843 break; 844 845 case LANGLE : 846 if (wc == (wchar_t) '<') 847 { 848 enriched_putwc (wc, &stte); 849 state = TEXT; 850 break; 851 } 852 else 853 { 854 tag_len = 0; 855 state = TAG; 856 } 857 /* fall through */ 858 /* it wasn't a <<, so this char is first in TAG */ 859 case TAG : 860 if (wc == (wchar_t) '>') 861 { 862 tag[tag_len] = (wchar_t) '\0'; 863 enriched_set_flags (tag, &stte); 864 state = TEXT; 865 } 866 else if (tag_len < LONG_STRING) /* ignore overly long tags */ 867 tag[tag_len++] = wc; 868 else 869 state = BOGUS_TAG; 870 break; 871 872 case BOGUS_TAG : 873 if (wc == (wchar_t) '>') 874 state = TEXT; 875 break; 876 877 case NEWLINE : 878 if (wc == (wchar_t) '\n') 879 enriched_flush (&stte, 1); 880 else 881 { 882 ungetwc (wc, s->fpin); 883 bytes++; 884 state = TEXT; 885 } 886 break; 887 888 case ST_EOF : 889 enriched_putwc ((wchar_t) '\0', &stte); 890 enriched_flush (&stte, 1); 891 state = DONE; 892 break; 893 894 case DONE: /* not reached, but gcc complains if this is absent */ 895 break; 896 } 897 } 898 899 state_putc ('\n', s); /* add a final newline */ 900 901 FREE (&(stte.buffer)); 902 FREE (&(stte.line)); 903 FREE (&(stte.param)); 904 905 return 0; 906} 907 908/* for compatibility with metamail */ 909static int is_mmnoask (const char *buf) 910{ 911 char tmp[LONG_STRING], *p, *q; 912 int lng; 913 914 if ((p = getenv ("MM_NOASK")) != NULL && *p) 915 { 916 if (mutt_strcmp (p, "1") == 0) 917 return (1); 918 919 strfcpy (tmp, p, sizeof (tmp)); 920 p = tmp; 921 922 while ((p = strtok (p, ",")) != NULL) 923 { 924 if ((q = strrchr (p, '/')) != NULL) 925 { 926 if (*(q+1) == '*') 927 { 928 if (ascii_strncasecmp (buf, p, q-p) == 0) 929 return (1); 930 } 931 else 932 { 933 if (ascii_strcasecmp (buf, p) == 0) 934 return (1); 935 } 936 } 937 else 938 { 939 lng = mutt_strlen (p); 940 if (buf[lng] == '/' && mutt_strncasecmp (buf, p, lng) == 0) 941 return (1); 942 } 943 944 p = NULL; 945 } 946 } 947 948 return (0); 949} 950 951/* 952 * Returns: 953 * 1 if the body part should be filtered by a mailcap entry prior to viewing inline. 954 * 955 * 0 otherwise 956 */ 957static int mutt_is_autoview (BODY *b) 958{ 959 char type[STRING]; 960 int is_autoview = 0; 961 962 snprintf (type, sizeof (type), "%s/%s", TYPE (b), b->subtype); 963 964 if (option(OPTIMPLICITAUTOVIEW)) 965 { 966 /* $implicit_autoview is essentially the same as "auto_view *" */ 967 is_autoview = 1; 968 } 969 else 970 { 971 /* determine if this type is on the user's auto_view list */ 972 LIST *t = AutoViewList; 973 974 mutt_check_lookup_list (b, type, sizeof (type)); 975 for (; t; t = t->next) 976 { 977 int i = mutt_strlen (t->data) - 1; 978 if ((i > 0 && t->data[i-1] == '/' && t->data[i] == '*' && 979 ascii_strncasecmp (type, t->data, i) == 0) || 980 ascii_strcasecmp (type, t->data) == 0) 981 is_autoview = 1; 982 } 983 984 if (is_mmnoask (type)) 985 is_autoview = 1; 986 } 987 988 /* determine if there is a mailcap entry suitable for auto_view 989 * 990 * WARNING: type is altered by this call as a result of `mime_lookup' support */ 991 if (is_autoview) 992 return rfc1524_mailcap_lookup(b, type, sizeof(type), NULL, MUTT_AUTOVIEW); 993 994 return 0; 995} 996 997#define TXTHTML 1 998#define TXTPLAIN 2 999#define TXTENRICHED 3 1000 1001static int alternative_handler (BODY *a, STATE *s) 1002{ 1003 BODY *choice = NULL; 1004 BODY *b; 1005 LIST *t; 1006 int type = 0; 1007 int mustfree = 0; 1008 int rc = 0; 1009 1010 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE || 1011 a->encoding == ENCUUENCODED) 1012 { 1013 struct stat st; 1014 mustfree = 1; 1015 fstat (fileno (s->fpin), &st); 1016 b = mutt_new_body (); 1017 b->length = (long) st.st_size; 1018 b->parts = mutt_parse_multipart (s->fpin, 1019 mutt_get_parameter ("boundary", a->parameter), 1020 (long) st.st_size, 1021 ascii_strcasecmp ("digest", a->subtype) == 0); 1022 } 1023 else 1024 b = a; 1025 1026 a = b; 1027 1028 /* First, search list of preferred types */ 1029 t = AlternativeOrderList; 1030 while (t && !choice) 1031 { 1032 char *c; 1033 int btlen; /* length of basetype */ 1034 int wild; /* do we have a wildcard to match all subtypes? */ 1035 1036 c = strchr (t->data, '/'); 1037 if (c) 1038 { 1039 wild = (c[1] == '*' && c[2] == 0); 1040 btlen = c - t->data; 1041 } 1042 else 1043 { 1044 wild = 1; 1045 btlen = mutt_strlen (t->data); 1046 } 1047 1048 if (a && a->parts) 1049 b = a->parts; 1050 else 1051 b = a; 1052 while (b) 1053 { 1054 const char *bt = TYPE(b); 1055 if (!ascii_strncasecmp (bt, t->data, btlen) && bt[btlen] == 0) 1056 { 1057 /* the basetype matches */ 1058 if (wild || !ascii_strcasecmp (t->data + btlen + 1, b->subtype)) 1059 { 1060 choice = b; 1061 } 1062 } 1063 b = b->next; 1064 } 1065 t = t->next; 1066 } 1067 1068 /* Next, look for an autoviewable type */ 1069 if (!choice) 1070 { 1071 if (a && a->parts) 1072 b = a->parts; 1073 else 1074 b = a; 1075 while (b) 1076 { 1077 if (mutt_is_autoview (b)) 1078 choice = b; 1079 b = b->next; 1080 } 1081 } 1082 1083 /* Then, look for a text entry */ 1084 if (!choice) 1085 { 1086 if (a && a->parts) 1087 b = a->parts; 1088 else 1089 b = a; 1090 while (b) 1091 { 1092 if (b->type == TYPETEXT) 1093 { 1094 if (! ascii_strcasecmp ("plain", b->subtype) && type <= TXTPLAIN) 1095 { 1096 choice = b; 1097 type = TXTPLAIN; 1098 } 1099 else if (! ascii_strcasecmp ("enriched", b->subtype) && type <= TXTENRICHED) 1100 { 1101 choice = b; 1102 type = TXTENRICHED; 1103 } 1104 else if (! ascii_strcasecmp ("html", b->subtype) && type <= TXTHTML) 1105 { 1106 choice = b; 1107 type = TXTHTML; 1108 } 1109 } 1110 b = b->next; 1111 } 1112 } 1113 1114 /* Finally, look for other possibilities */ 1115 if (!choice) 1116 { 1117 if (a && a->parts) 1118 b = a->parts; 1119 else 1120 b = a; 1121 while (b) 1122 { 1123 if (mutt_can_decode (b)) 1124 choice = b; 1125 b = b->next; 1126 } 1127 } 1128 1129 if (choice) 1130 { 1131 if (s->flags & MUTT_DISPLAY && !option (OPTWEED)) 1132 { 1133 fseeko (s->fpin, choice->hdr_offset, 0); 1134 mutt_copy_bytes(s->fpin, s->fpout, choice->offset-choice->hdr_offset); 1135 } 1136 mutt_body_handler (choice, s); 1137 } 1138 else if (s->flags & MUTT_DISPLAY) 1139 { 1140 /* didn't find anything that we could display! */ 1141 state_mark_attach (s); 1142 state_puts(_("[-- Error: Could not display any parts of Multipart/Alternative! --]\n"), s); 1143 rc = -1; 1144 } 1145 1146 if (mustfree) 1147 mutt_free_body(&a); 1148 1149 return rc; 1150} 1151 1152/* handles message/rfc822 body parts */ 1153static int message_handler (BODY *a, STATE *s) 1154{ 1155 struct stat st; 1156 BODY *b; 1157 LOFF_T off_start; 1158 int rc = 0; 1159 1160 off_start = ftello (s->fpin); 1161 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE || 1162 a->encoding == ENCUUENCODED) 1163 { 1164 fstat (fileno (s->fpin), &st); 1165 b = mutt_new_body (); 1166 b->length = (LOFF_T) st.st_size; 1167 b->parts = mutt_parse_messageRFC822 (s->fpin, b); 1168 } 1169 else 1170 b = a; 1171 1172 if (b->parts) 1173 { 1174 mutt_copy_hdr (s->fpin, s->fpout, off_start, b->parts->offset, 1175 (((s->flags & MUTT_WEED) || ((s->flags & (MUTT_DISPLAY|MUTT_PRINTING)) && option (OPTWEED))) ? (CH_WEED | CH_REORDER) : 0) | 1176 (s->prefix ? CH_PREFIX : 0) | CH_DECODE | CH_FROM | 1177 ((s->flags & MUTT_DISPLAY) ? CH_DISPLAY : 0), s->prefix); 1178 1179 if (s->prefix) 1180 state_puts (s->prefix, s); 1181 state_putc ('\n', s); 1182 1183 rc = mutt_body_handler (b->parts, s); 1184 } 1185 1186 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE || 1187 a->encoding == ENCUUENCODED) 1188 mutt_free_body (&b); 1189 1190 return rc; 1191} 1192 1193/* returns 1 if decoding the attachment will produce output */ 1194int mutt_can_decode (BODY *a) 1195{ 1196 if (mutt_is_autoview (a)) 1197 return 1; 1198 else if (a->type == TYPETEXT) 1199 return (1); 1200 else if (a->type == TYPEMESSAGE) 1201 return (1); 1202 else if (a->type == TYPEMULTIPART) 1203 { 1204 BODY *p; 1205 1206 if (WithCrypto) 1207 { 1208 if (ascii_strcasecmp (a->subtype, "signed") == 0 || 1209 ascii_strcasecmp (a->subtype, "encrypted") == 0) 1210 return (1); 1211 } 1212 1213 for (p = a->parts; p; p = p->next) 1214 { 1215 if (mutt_can_decode (p)) 1216 return (1); 1217 } 1218 1219 } 1220 else if (WithCrypto && a->type == TYPEAPPLICATION) 1221 { 1222 if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp(a)) 1223 return (1); 1224 if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(a)) 1225 return (1); 1226 } 1227 1228 return (0); 1229} 1230 1231static int multipart_handler (BODY *a, STATE *s) 1232{ 1233 BODY *b, *p; 1234 char length[5]; 1235 struct stat st; 1236 int count; 1237 int rc = 0; 1238 1239 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE || 1240 a->encoding == ENCUUENCODED) 1241 { 1242 fstat (fileno (s->fpin), &st); 1243 b = mutt_new_body (); 1244 b->length = (long) st.st_size; 1245 b->parts = mutt_parse_multipart (s->fpin, 1246 mutt_get_parameter ("boundary", a->parameter), 1247 (long) st.st_size, 1248 ascii_strcasecmp ("digest", a->subtype) == 0); 1249 } 1250 else 1251 b = a; 1252 1253 for (p = b->parts, count = 1; p; p = p->next, count++) 1254 { 1255 if (s->flags & MUTT_DISPLAY) 1256 { 1257 state_mark_attach (s); 1258 state_printf (s, _("[-- Attachment #%d"), count); 1259 if (p->description || p->filename || p->form_name) 1260 { 1261 state_puts (": ", s); 1262 state_puts (p->description ? p->description : 1263 p->filename ? p->filename : p->form_name, s); 1264 } 1265 state_puts (" --]\n", s); 1266 1267 mutt_pretty_size (length, sizeof (length), p->length); 1268 1269 state_mark_attach (s); 1270 state_printf (s, _("[-- Type: %s/%s, Encoding: %s, Size: %s --]\n"), 1271 TYPE (p), p->subtype, ENCODING (p->encoding), length); 1272 if (!option (OPTWEED)) 1273 { 1274 fseeko (s->fpin, p->hdr_offset, 0); 1275 mutt_copy_bytes(s->fpin, s->fpout, p->offset-p->hdr_offset); 1276 } 1277 else 1278 state_putc ('\n', s); 1279 } 1280 1281 rc = mutt_body_handler (p, s); 1282 state_putc ('\n', s); 1283 1284 if (rc) 1285 { 1286 mutt_error (_("One or more parts of this message could not be displayed")); 1287 dprint (1, (debugfile, "Failed on attachment #%d, type %s/%s.\n", count, TYPE(p), NONULL (p->subtype))); 1288 } 1289 1290 if ((s->flags & MUTT_REPLYING) 1291 && (option (OPTINCLUDEONLYFIRST)) && (s->flags & MUTT_FIRSTDONE)) 1292 break; 1293 } 1294 1295 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE || 1296 a->encoding == ENCUUENCODED) 1297 mutt_free_body (&b); 1298 1299 /* make failure of a single part non-fatal */ 1300 if (rc < 0) 1301 rc = 1; 1302 return rc; 1303} 1304 1305static int autoview_handler (BODY *a, STATE *s) 1306{ 1307 rfc1524_entry *entry = rfc1524_new_entry (); 1308 char buffer[LONG_STRING]; 1309 char type[STRING]; 1310 BUFFER *command = NULL; 1311 BUFFER *tempfile = NULL; 1312 char *fname; 1313 FILE *fpin = NULL; 1314 FILE *fpout = NULL; 1315 FILE *fperr = NULL; 1316 int piped = FALSE; 1317 pid_t thepid; 1318 int rc = 0; 1319 1320 command = mutt_buffer_pool_get (); 1321 tempfile = mutt_buffer_pool_get (); 1322 1323 snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype); 1324 rfc1524_mailcap_lookup (a, type, sizeof(type), entry, MUTT_AUTOVIEW); 1325 1326 fname = safe_strdup (a->filename); 1327 mutt_sanitize_filename (fname, 1); 1328 mutt_rfc1524_expand_filename (entry->nametemplate, fname, tempfile); 1329 FREE (&fname); 1330 1331 if (entry->command) 1332 { 1333 mutt_buffer_strcpy (command, entry->command); 1334 1335 /* rfc1524_expand_command returns 0 if the file is required */ 1336 piped = mutt_rfc1524_expand_command (a, mutt_b2s (tempfile), type, command); 1337 1338 if (s->flags & MUTT_DISPLAY) 1339 { 1340 state_mark_attach (s); 1341 state_printf (s, _("[-- Autoview using %s --]\n"), mutt_b2s (command)); 1342 mutt_message(_("Invoking autoview command: %s"), mutt_b2s (command)); 1343 } 1344 1345 if ((fpin = safe_fopen (mutt_b2s (tempfile), "w+")) == NULL) 1346 { 1347 mutt_perror ("fopen"); 1348 rc = -1; 1349 goto cleanup; 1350 } 1351 1352 mutt_copy_bytes (s->fpin, fpin, a->length); 1353 1354 if (!piped) 1355 { 1356 safe_fclose (&fpin); 1357 thepid = mutt_create_filter (mutt_b2s (command), NULL, &fpout, &fperr); 1358 } 1359 else 1360 { 1361 unlink (mutt_b2s (tempfile)); 1362 fflush (fpin); 1363 rewind (fpin); 1364 thepid = mutt_create_filter_fd (mutt_b2s (command), NULL, &fpout, &fperr, 1365 fileno(fpin), -1, -1); 1366 } 1367 1368 if (thepid < 0) 1369 { 1370 mutt_perror _("Can't create filter"); 1371 if (s->flags & MUTT_DISPLAY) 1372 { 1373 state_mark_attach (s); 1374 state_printf (s, _("[-- Can't run %s. --]\n"), mutt_b2s (command)); 1375 } 1376 rc = -1; 1377 goto bail; 1378 } 1379 1380 if (s->prefix) 1381 { 1382 while (fgets (buffer, sizeof(buffer), fpout) != NULL) 1383 { 1384 state_puts (s->prefix, s); 1385 state_puts (buffer, s); 1386 } 1387 /* check for data on stderr */ 1388 if (fgets (buffer, sizeof(buffer), fperr)) 1389 { 1390 if (s->flags & MUTT_DISPLAY) 1391 { 1392 state_mark_attach (s); 1393 state_printf (s, _("[-- Autoview stderr of %s --]\n"), mutt_b2s (command)); 1394 } 1395 1396 state_puts (s->prefix, s); 1397 state_puts (buffer, s); 1398 while (fgets (buffer, sizeof(buffer), fperr) != NULL) 1399 { 1400 state_puts (s->prefix, s); 1401 state_puts (buffer, s); 1402 } 1403 } 1404 } 1405 else 1406 { 1407 mutt_copy_stream (fpout, s->fpout); 1408 /* Check for stderr messages */ 1409 if (fgets (buffer, sizeof(buffer), fperr)) 1410 { 1411 if (s->flags & MUTT_DISPLAY) 1412 { 1413 state_mark_attach (s); 1414 state_printf (s, _("[-- Autoview stderr of %s --]\n"), 1415 mutt_b2s (command)); 1416 } 1417 1418 state_puts (buffer, s); 1419 mutt_copy_stream (fperr, s->fpout); 1420 } 1421 } 1422 1423 bail: 1424 safe_fclose (&fpout); 1425 safe_fclose (&fperr); 1426 1427 mutt_wait_filter (thepid); 1428 if (piped) 1429 safe_fclose (&fpin); 1430 else 1431 mutt_unlink (mutt_b2s (tempfile)); 1432 1433 if (s->flags & MUTT_DISPLAY) 1434 mutt_clear_error (); 1435 } 1436 1437cleanup: 1438 rfc1524_free_entry (&entry); 1439 1440 mutt_buffer_pool_release (&command); 1441 mutt_buffer_pool_release (&tempfile); 1442 1443 return rc; 1444} 1445 1446static int external_body_handler (BODY *b, STATE *s) 1447{ 1448 const char *access_type; 1449 const char *expiration; 1450 time_t expire; 1451 1452 access_type = mutt_get_parameter ("access-type", b->parameter); 1453 if (!access_type) 1454 { 1455 if (s->flags & MUTT_DISPLAY) 1456 { 1457 state_mark_attach (s); 1458 state_puts (_("[-- Error: message/external-body has no access-type parameter --]\n"), s); 1459 return 0; 1460 } 1461 else 1462 return -1; 1463 } 1464 1465 expiration = mutt_get_parameter ("expiration", b->parameter); 1466 if (expiration) 1467 expire = mutt_parse_date (expiration, NULL); 1468 else 1469 expire = -1; 1470 1471 if (!ascii_strcasecmp (access_type, "x-mutt-deleted")) 1472 { 1473 if (s->flags & (MUTT_DISPLAY|MUTT_PRINTING)) 1474 { 1475 char *length; 1476 char pretty_size[10]; 1477 1478 state_mark_attach (s); 1479 state_printf (s, _("[-- This %s/%s attachment "), 1480 TYPE(b->parts), b->parts->subtype); 1481 length = mutt_get_parameter ("length", b->parameter); 1482 if (length) 1483 { 1484 mutt_pretty_size (pretty_size, sizeof (pretty_size), 1485 strtol (length, NULL, 10)); 1486 state_printf (s, _("(size %s bytes) "), pretty_size); 1487 } 1488 state_puts (_("has been deleted --]\n"), s); 1489 1490 if (expire != -1) 1491 { 1492 state_mark_attach (s); 1493 state_printf (s, _("[-- on %s --]\n"), expiration); 1494 } 1495 if (b->parts->filename) 1496 { 1497 state_mark_attach (s); 1498 state_printf (s, _("[-- name: %s --]\n"), b->parts->filename); 1499 } 1500 1501 mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset, 1502 (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) | 1503 CH_DECODE , NULL); 1504 } 1505 } 1506 else if (expiration && expire < time(NULL)) 1507 { 1508 if (s->flags & MUTT_DISPLAY) 1509 { 1510 state_mark_attach (s); 1511 state_printf (s, _("[-- This %s/%s attachment is not included, --]\n"), 1512 TYPE(b->parts), b->parts->subtype); 1513 state_attach_puts (_("[-- and the indicated external source has --]\n" 1514 "[-- expired. --]\n"), s); 1515 1516 mutt_copy_hdr(s->fpin, s->fpout, ftello (s->fpin), b->parts->offset, 1517 (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) | 1518 CH_DECODE | CH_DISPLAY, NULL); 1519 } 1520 } 1521 else 1522 { 1523 if (s->flags & MUTT_DISPLAY) 1524 { 1525 state_mark_attach (s); 1526 state_printf (s, 1527 _("[-- This %s/%s attachment is not included, --]\n"), 1528 TYPE (b->parts), b->parts->subtype); 1529 state_mark_attach (s); 1530 state_printf (s, 1531 _("[-- and the indicated access-type %s is unsupported --]\n"), 1532 access_type); 1533 mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset, 1534 (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) | 1535 CH_DECODE | CH_DISPLAY, NULL); 1536 } 1537 } 1538 1539 return 0; 1540} 1541 1542void mutt_decode_attachment (BODY *b, STATE *s) 1543{ 1544 int istext = mutt_is_text_part (b); 1545 iconv_t cd = (iconv_t)(-1); 1546 1547 if (istext && s->flags & MUTT_CHARCONV) 1548 { 1549 char *charset = mutt_get_parameter ("charset", b->parameter); 1550 if (!charset && AssumedCharset) 1551 charset = mutt_get_default_charset (); 1552 if (charset && Charset) 1553 cd = mutt_iconv_open (Charset, charset, MUTT_ICONV_HOOK_FROM); 1554 } 1555 else if (istext && b->charset) 1556 cd = mutt_iconv_open (Charset, b->charset, MUTT_ICONV_HOOK_FROM); 1557 1558 fseeko (s->fpin, b->offset, 0); 1559 switch (b->encoding) 1560 { 1561 case ENCQUOTEDPRINTABLE: 1562 mutt_decode_quoted (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd); 1563 break; 1564 case ENCBASE64: 1565 mutt_decode_base64 (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd); 1566 break; 1567 case ENCUUENCODED: 1568 mutt_decode_uuencoded (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd); 1569 break; 1570 default: 1571 mutt_decode_xbit (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd); 1572 break; 1573 } 1574 1575 if (cd != (iconv_t)(-1)) 1576 iconv_close (cd); 1577} 1578 1579/* when generating format=flowed ($text_flowed is set) from format=fixed, 1580 * strip all trailing spaces to improve interoperability; 1581 * if $text_flowed is unset, simply verbatim copy input 1582 */ 1583static int text_plain_handler (BODY *b, STATE *s) 1584{ 1585 char *buf = NULL; 1586 size_t l = 0, sz = 0; 1587 1588 while ((buf = mutt_read_line (buf, &sz, s->fpin, NULL, 0))) 1589 { 1590 if (mutt_strcmp (buf, "-- ") != 0 && option (OPTTEXTFLOWED)) 1591 { 1592 l = mutt_strlen (buf); 1593 while (l > 0 && buf[l-1] == ' ') 1594 buf[--l] = 0; 1595 } 1596 if (s->prefix) 1597 state_puts (s->prefix, s); 1598 state_puts (buf, s); 1599 state_putc ('\n', s); 1600 } 1601 1602 FREE (&buf); 1603 return 0; 1604} 1605 1606static int run_decode_and_handler (BODY *b, STATE *s, handler_t handler, int plaintext) 1607{ 1608 int origType; 1609 char *savePrefix = NULL; 1610 FILE *fp = NULL; 1611 BUFFER *tempfile = NULL; 1612 size_t tmplength = 0; 1613 LOFF_T tmpoffset = 0; 1614 int decode = 0; 1615 int rc = 0; 1616 1617 fseeko (s->fpin, b->offset, 0); 1618 1619 /* see if we need to decode this part before processing it */ 1620 if (b->encoding == ENCBASE64 || b->encoding == ENCQUOTEDPRINTABLE || 1621 b->encoding == ENCUUENCODED || plaintext || 1622 mutt_is_text_part (b)) /* text subtypes may 1623 * require character 1624 * set conversion even 1625 * with 8bit encoding. 1626 */ 1627 { 1628 origType = b->type; 1629 1630 if (!plaintext) 1631 { 1632 /* decode to a tempfile, saving the original destination */ 1633 fp = s->fpout; 1634 tempfile = mutt_buffer_pool_get (); 1635 mutt_buffer_mktemp (tempfile); 1636 if ((s->fpout = safe_fopen (mutt_b2s (tempfile), "w")) == NULL) 1637 { 1638 mutt_error _("Unable to open temporary file!"); 1639 dprint (1, (debugfile, "Can't open %s.\n", mutt_b2s (tempfile))); 1640 mutt_buffer_pool_release (&tempfile); 1641 return -1; 1642 } 1643 /* decoding the attachment changes the size and offset, so save a copy 1644 * of the "real" values now, and restore them after processing 1645 */ 1646 tmplength = b->length; 1647 tmpoffset = b->offset; 1648 1649 /* if we are decoding binary bodies, we don't want to prefix each 1650 * line with the prefix or else the data will get corrupted. 1651 */ 1652 savePrefix = s->prefix; 1653 s->prefix = NULL; 1654 1655 decode = 1; 1656 } 1657 else 1658 b->type = TYPETEXT; 1659 1660 mutt_decode_attachment (b, s); 1661 1662 if (decode) 1663 { 1664 b->length = ftello (s->fpout); 1665 b->offset = 0; 1666 safe_fclose (&s->fpout); 1667 1668 /* restore final destination and substitute the tempfile for input */ 1669 s->fpout = fp; 1670 fp = s->fpin; 1671 s->fpin = fopen (mutt_b2s (tempfile), "r"); 1672 unlink (mutt_b2s (tempfile)); 1673 mutt_buffer_pool_release (&tempfile); 1674 1675 /* restore the prefix */ 1676 s->prefix = savePrefix; 1677 } 1678 1679 b->type = origType; 1680 } 1681 1682 /* process the (decoded) body part */ 1683 if (handler) 1684 { 1685 rc = handler (b, s); 1686 1687 if (rc) 1688 { 1689 dprint (1, (debugfile, "Failed on attachment of type %s/%s.\n", TYPE(b), NONULL (b->subtype))); 1690 } 1691 1692 if (decode) 1693 { 1694 b->length = tmplength; 1695 b->offset = tmpoffset; 1696 1697 /* restore the original source stream */ 1698 safe_fclose (&s->fpin); 1699 s->fpin = fp; 1700 } 1701 } 1702 s->flags |= MUTT_FIRSTDONE; 1703 1704 return rc; 1705} 1706 1707static int valid_pgp_encrypted_handler (BODY *b, STATE *s) 1708{ 1709 int rc; 1710 BODY *octetstream; 1711 1712 octetstream = b->parts->next; 1713 1714 /* clear out any mime headers before the handler, so they can't be 1715 * spoofed. */ 1716 mutt_free_envelope (&b->mime_headers); 1717 mutt_free_envelope (&octetstream->mime_headers); 1718 1719 /* Some clients improperly encode the octetstream part. */ 1720 if (octetstream->encoding != ENC7BIT) 1721 rc = run_decode_and_handler (octetstream, s, crypt_pgp_encrypted_handler, 0); 1722 else 1723 rc = crypt_pgp_encrypted_handler (octetstream, s); 1724 b->goodsig |= octetstream->goodsig; 1725#ifdef USE_AUTOCRYPT 1726 b->is_autocrypt |= octetstream->is_autocrypt; 1727#endif 1728 1729 /* Relocate protected headers onto the multipart/encrypted part */ 1730 if (!rc && octetstream->mime_headers) 1731 { 1732 b->mime_headers = octetstream->mime_headers; 1733 octetstream->mime_headers = NULL; 1734 } 1735 1736 return rc; 1737} 1738 1739static int malformed_pgp_encrypted_handler (BODY *b, STATE *s) 1740{ 1741 int rc; 1742 BODY *octetstream; 1743 1744 octetstream = b->parts->next->next; 1745 1746 /* clear out any mime headers before the handler, so they can't be 1747 * spoofed. */ 1748 mutt_free_envelope (&b->mime_headers); 1749 mutt_free_envelope (&octetstream->mime_headers); 1750 1751 /* exchange encodes the octet-stream, so re-run it through the decoder */ 1752 rc = run_decode_and_handler (octetstream, s, crypt_pgp_encrypted_handler, 0); 1753 b->goodsig |= octetstream->goodsig; 1754#ifdef USE_AUTOCRYPT 1755 b->is_autocrypt |= octetstream->is_autocrypt; 1756#endif 1757 1758 /* Relocate protected headers onto the multipart/encrypted part */ 1759 if (!rc && octetstream->mime_headers) 1760 { 1761 b->mime_headers = octetstream->mime_headers; 1762 octetstream->mime_headers = NULL; 1763 } 1764 1765 return rc; 1766} 1767 1768int mutt_body_handler (BODY *b, STATE *s) 1769{ 1770 int plaintext = 0; 1771 handler_t handler = NULL, encrypted_handler = NULL; 1772 int rc = 0; 1773 1774 int oflags = s->flags; 1775 1776 /* first determine which handler to use to process this part */ 1777 1778 if (mutt_is_autoview (b)) 1779 { 1780 handler = autoview_handler; 1781 s->flags &= ~MUTT_CHARCONV; 1782 } 1783 else if (b->type == TYPETEXT) 1784 { 1785 if (ascii_strcasecmp ("plain", b->subtype) == 0) 1786 { 1787 /* avoid copying this part twice since removing the transfer-encoding is 1788 * the only operation needed. 1789 */ 1790 if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)) 1791 encrypted_handler = handler = crypt_pgp_application_pgp_handler; 1792 else if (option(OPTREFLOWTEXT) && ascii_strcasecmp ("flowed", mutt_get_parameter ("format", b->parameter)) == 0) 1793 handler = rfc3676_handler; 1794 else 1795 handler = text_plain_handler; 1796 } 1797 else if (ascii_strcasecmp ("enriched", b->subtype) == 0) 1798 handler = text_enriched_handler; 1799 else /* text body type without a handler */ 1800 plaintext = 1; 1801 } 1802 else if (b->type == TYPEMESSAGE) 1803 { 1804 if (mutt_is_message_type(b->type, b->subtype)) 1805 handler = message_handler; 1806 else if (!ascii_strcasecmp ("delivery-status", b->subtype)) 1807 plaintext = 1; 1808 else if (!ascii_strcasecmp ("external-body", b->subtype)) 1809 handler = external_body_handler; 1810 } 1811 else if (b->type == TYPEMULTIPART) 1812 { 1813 char *p; 1814 1815 if (ascii_strcasecmp ("alternative", b->subtype) == 0) 1816 handler = alternative_handler; 1817 else if (WithCrypto && ascii_strcasecmp ("signed", b->subtype) == 0) 1818 { 1819 p = mutt_get_parameter ("protocol", b->parameter); 1820 1821 if (!p) 1822 mutt_error _("Error: multipart/signed has no protocol."); 1823 else if (s->flags & MUTT_VERIFY) 1824 handler = mutt_signed_handler; 1825 } 1826 else if (mutt_is_valid_multipart_pgp_encrypted (b)) 1827 encrypted_handler = handler = valid_pgp_encrypted_handler; 1828 else if (mutt_is_malformed_multipart_pgp_encrypted (b)) 1829 encrypted_handler = handler = malformed_pgp_encrypted_handler; 1830 1831 if (!handler) 1832 handler = multipart_handler; 1833 1834 if (b->encoding != ENC7BIT && b->encoding != ENC8BIT 1835 && b->encoding != ENCBINARY) 1836 { 1837 dprint (1, (debugfile, "Bad encoding type %d for multipart entity, " 1838 "assuming 7 bit\n", b->encoding)); 1839 b->encoding = ENC7BIT; 1840 } 1841 } 1842 else if (WithCrypto && b->type == TYPEAPPLICATION) 1843 { 1844 if (option (OPTDONTHANDLEPGPKEYS) 1845 && !ascii_strcasecmp("pgp-keys", b->subtype)) 1846 { 1847 /* pass raw part through for key extraction */ 1848 plaintext = 1; 1849 } 1850 else if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)) 1851 encrypted_handler = handler = crypt_pgp_application_pgp_handler; 1852 else if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(b)) 1853 encrypted_handler = handler = crypt_smime_application_smime_handler; 1854 } 1855 1856 /* only respect disposition == attachment if we're not 1857 displaying from the attachment menu (i.e. pager) */ 1858 if ((!option (OPTHONORDISP) || (b->disposition != DISPATTACH || 1859 option(OPTVIEWATTACH))) && 1860 (plaintext || handler)) 1861 { 1862 /* Prevent encrypted attachments from being included in replies 1863 * unless $include_encrypted is set. */ 1864 if ((s->flags & MUTT_REPLYING) && 1865 (s->flags & MUTT_FIRSTDONE) && 1866 encrypted_handler && 1867 !option (OPTINCLUDEENCRYPTED)) 1868 goto cleanup; 1869 1870 rc = run_decode_and_handler (b, s, handler, plaintext); 1871 } 1872 /* print hint to use attachment menu for disposition == attachment 1873 if we're not already being called from there */ 1874 else if (s->flags & MUTT_DISPLAY) 1875 { 1876 state_mark_attach (s); 1877 if (option (OPTHONORDISP) && b->disposition == DISPATTACH) 1878 fputs (_("[-- This is an attachment "), s->fpout); 1879 else 1880 state_printf (s, _("[-- %s/%s is unsupported "), TYPE (b), b->subtype); 1881 if (!option (OPTVIEWATTACH)) 1882 { 1883 char keystroke[SHORT_STRING]; 1884 1885 if (km_expand_key (keystroke, sizeof(keystroke), 1886 km_find_func (MENU_PAGER, OP_VIEW_ATTACHMENTS))) 1887 fprintf (s->fpout, _("(use '%s' to view this part)"), keystroke); 1888 else 1889 fputs (_("(need 'view-attachments' bound to key!)"), s->fpout); 1890 } 1891 fputs (" --]\n", s->fpout); 1892 } 1893 1894cleanup: 1895 s->flags = oflags | (s->flags & MUTT_FIRSTDONE); 1896 if (rc) 1897 { 1898 dprint (1, (debugfile, "Bailing on attachment of type %s/%s.\n", TYPE(b), NONULL (b->subtype))); 1899 } 1900 1901 return rc; 1902}