mutt stable branch with some hacks
at master 1845 lines 44 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 /* Yes, fall through (it wasn't a <<, so this char is first in TAG) */ 858 case TAG : 859 if (wc == (wchar_t) '>') 860 { 861 tag[tag_len] = (wchar_t) '\0'; 862 enriched_set_flags (tag, &stte); 863 state = TEXT; 864 } 865 else if (tag_len < LONG_STRING) /* ignore overly long tags */ 866 tag[tag_len++] = wc; 867 else 868 state = BOGUS_TAG; 869 break; 870 871 case BOGUS_TAG : 872 if (wc == (wchar_t) '>') 873 state = TEXT; 874 break; 875 876 case NEWLINE : 877 if (wc == (wchar_t) '\n') 878 enriched_flush (&stte, 1); 879 else 880 { 881 ungetwc (wc, s->fpin); 882 bytes++; 883 state = TEXT; 884 } 885 break; 886 887 case ST_EOF : 888 enriched_putwc ((wchar_t) '\0', &stte); 889 enriched_flush (&stte, 1); 890 state = DONE; 891 break; 892 893 case DONE: /* not reached, but gcc complains if this is absent */ 894 break; 895 } 896 } 897 898 state_putc ('\n', s); /* add a final newline */ 899 900 FREE (&(stte.buffer)); 901 FREE (&(stte.line)); 902 FREE (&(stte.param)); 903 904 return 0; 905} 906 907/* for compatibility with metamail */ 908static int is_mmnoask (const char *buf) 909{ 910 char tmp[LONG_STRING], *p, *q; 911 int lng; 912 913 if ((p = getenv ("MM_NOASK")) != NULL && *p) 914 { 915 if (mutt_strcmp (p, "1") == 0) 916 return (1); 917 918 strfcpy (tmp, p, sizeof (tmp)); 919 p = tmp; 920 921 while ((p = strtok (p, ",")) != NULL) 922 { 923 if ((q = strrchr (p, '/')) != NULL) 924 { 925 if (*(q+1) == '*') 926 { 927 if (ascii_strncasecmp (buf, p, q-p) == 0) 928 return (1); 929 } 930 else 931 { 932 if (ascii_strcasecmp (buf, p) == 0) 933 return (1); 934 } 935 } 936 else 937 { 938 lng = mutt_strlen (p); 939 if (buf[lng] == '/' && mutt_strncasecmp (buf, p, lng) == 0) 940 return (1); 941 } 942 943 p = NULL; 944 } 945 } 946 947 return (0); 948} 949 950/* 951 * Returns: 952 * 1 if the body part should be filtered by a mailcap entry prior to viewing inline. 953 * 954 * 0 otherwise 955 */ 956static int mutt_is_autoview (BODY *b) 957{ 958 char type[SHORT_STRING]; 959 int is_autoview = 0; 960 961 snprintf (type, sizeof (type), "%s/%s", TYPE (b), b->subtype); 962 963 if (option(OPTIMPLICITAUTOVIEW)) 964 { 965 /* $implicit_autoview is essentially the same as "auto_view *" */ 966 is_autoview = 1; 967 } 968 else 969 { 970 /* determine if this type is on the user's auto_view list */ 971 LIST *t = AutoViewList; 972 973 mutt_check_lookup_list (b, type, sizeof (type)); 974 for (; t; t = t->next) { 975 int i = mutt_strlen (t->data) - 1; 976 if ((i > 0 && t->data[i-1] == '/' && t->data[i] == '*' && 977 ascii_strncasecmp (type, t->data, i) == 0) || 978 ascii_strcasecmp (type, t->data) == 0) 979 is_autoview = 1; 980 } 981 982 if (is_mmnoask (type)) 983 is_autoview = 1; 984 } 985 986 /* determine if there is a mailcap entry suitable for auto_view 987 * 988 * WARNING: type is altered by this call as a result of `mime_lookup' support */ 989 if (is_autoview) 990 return rfc1524_mailcap_lookup(b, type, NULL, MUTT_AUTOVIEW); 991 992 return 0; 993} 994 995#define TXTHTML 1 996#define TXTPLAIN 2 997#define TXTENRICHED 3 998 999static int alternative_handler (BODY *a, STATE *s) 1000{ 1001 BODY *choice = NULL; 1002 BODY *b; 1003 LIST *t; 1004 int type = 0; 1005 int mustfree = 0; 1006 int rc = 0; 1007 1008 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE || 1009 a->encoding == ENCUUENCODED) 1010 { 1011 struct stat st; 1012 mustfree = 1; 1013 fstat (fileno (s->fpin), &st); 1014 b = mutt_new_body (); 1015 b->length = (long) st.st_size; 1016 b->parts = mutt_parse_multipart (s->fpin, 1017 mutt_get_parameter ("boundary", a->parameter), 1018 (long) st.st_size, ascii_strcasecmp ("digest", a->subtype) == 0); 1019 } 1020 else 1021 b = a; 1022 1023 a = b; 1024 1025 /* First, search list of preferred types */ 1026 t = AlternativeOrderList; 1027 while (t && !choice) 1028 { 1029 char *c; 1030 int btlen; /* length of basetype */ 1031 int wild; /* do we have a wildcard to match all subtypes? */ 1032 1033 c = strchr (t->data, '/'); 1034 if (c) 1035 { 1036 wild = (c[1] == '*' && c[2] == 0); 1037 btlen = c - t->data; 1038 } 1039 else 1040 { 1041 wild = 1; 1042 btlen = mutt_strlen (t->data); 1043 } 1044 1045 if (a && a->parts) 1046 b = a->parts; 1047 else 1048 b = a; 1049 while (b) 1050 { 1051 const char *bt = TYPE(b); 1052 if (!ascii_strncasecmp (bt, t->data, btlen) && bt[btlen] == 0) 1053 { 1054 /* the basetype matches */ 1055 if (wild || !ascii_strcasecmp (t->data + btlen + 1, b->subtype)) 1056 { 1057 choice = b; 1058 } 1059 } 1060 b = b->next; 1061 } 1062 t = t->next; 1063 } 1064 1065 /* Next, look for an autoviewable type */ 1066 if (!choice) 1067 { 1068 if (a && a->parts) 1069 b = a->parts; 1070 else 1071 b = a; 1072 while (b) 1073 { 1074 if (mutt_is_autoview (b)) 1075 choice = b; 1076 b = b->next; 1077 } 1078 } 1079 1080 /* Then, look for a text entry */ 1081 if (!choice) 1082 { 1083 if (a && a->parts) 1084 b = a->parts; 1085 else 1086 b = a; 1087 while (b) 1088 { 1089 if (b->type == TYPETEXT) 1090 { 1091 if (! ascii_strcasecmp ("plain", b->subtype) && type <= TXTPLAIN) 1092 { 1093 choice = b; 1094 type = TXTPLAIN; 1095 } 1096 else if (! ascii_strcasecmp ("enriched", b->subtype) && type <= TXTENRICHED) 1097 { 1098 choice = b; 1099 type = TXTENRICHED; 1100 } 1101 else if (! ascii_strcasecmp ("html", b->subtype) && type <= TXTHTML) 1102 { 1103 choice = b; 1104 type = TXTHTML; 1105 } 1106 } 1107 b = b->next; 1108 } 1109 } 1110 1111 /* Finally, look for other possibilities */ 1112 if (!choice) 1113 { 1114 if (a && a->parts) 1115 b = a->parts; 1116 else 1117 b = a; 1118 while (b) 1119 { 1120 if (mutt_can_decode (b)) 1121 choice = b; 1122 b = b->next; 1123 } 1124 } 1125 1126 if (choice) 1127 { 1128 if (s->flags & MUTT_DISPLAY && !option (OPTWEED)) 1129 { 1130 fseeko (s->fpin, choice->hdr_offset, 0); 1131 mutt_copy_bytes(s->fpin, s->fpout, choice->offset-choice->hdr_offset); 1132 } 1133 mutt_body_handler (choice, s); 1134 } 1135 else if (s->flags & MUTT_DISPLAY) 1136 { 1137 /* didn't find anything that we could display! */ 1138 state_mark_attach (s); 1139 state_puts(_("[-- Error: Could not display any parts of Multipart/Alternative! --]\n"), s); 1140 rc = -1; 1141 } 1142 1143 if (mustfree) 1144 mutt_free_body(&a); 1145 1146 return rc; 1147} 1148 1149/* handles message/rfc822 body parts */ 1150static int message_handler (BODY *a, STATE *s) 1151{ 1152 struct stat st; 1153 BODY *b; 1154 LOFF_T off_start; 1155 int rc = 0; 1156 1157 off_start = ftello (s->fpin); 1158 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE || 1159 a->encoding == ENCUUENCODED) 1160 { 1161 fstat (fileno (s->fpin), &st); 1162 b = mutt_new_body (); 1163 b->length = (LOFF_T) st.st_size; 1164 b->parts = mutt_parse_messageRFC822 (s->fpin, b); 1165 } 1166 else 1167 b = a; 1168 1169 if (b->parts) 1170 { 1171 mutt_copy_hdr (s->fpin, s->fpout, off_start, b->parts->offset, 1172 (((s->flags & MUTT_WEED) || ((s->flags & (MUTT_DISPLAY|MUTT_PRINTING)) && option (OPTWEED))) ? (CH_WEED | CH_REORDER) : 0) | 1173 (s->prefix ? CH_PREFIX : 0) | CH_DECODE | CH_FROM | 1174 ((s->flags & MUTT_DISPLAY) ? CH_DISPLAY : 0), s->prefix); 1175 1176 if (s->prefix) 1177 state_puts (s->prefix, s); 1178 state_putc ('\n', s); 1179 1180 rc = mutt_body_handler (b->parts, s); 1181 } 1182 1183 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE || 1184 a->encoding == ENCUUENCODED) 1185 mutt_free_body (&b); 1186 1187 return rc; 1188} 1189 1190/* returns 1 if decoding the attachment will produce output */ 1191int mutt_can_decode (BODY *a) 1192{ 1193 if (mutt_is_autoview (a)) 1194 return 1; 1195 else if (a->type == TYPETEXT) 1196 return (1); 1197 else if (a->type == TYPEMESSAGE) 1198 return (1); 1199 else if (a->type == TYPEMULTIPART) 1200 { 1201 BODY *p; 1202 1203 if (WithCrypto) 1204 { 1205 if (ascii_strcasecmp (a->subtype, "signed") == 0 || 1206 ascii_strcasecmp (a->subtype, "encrypted") == 0) 1207 return (1); 1208 } 1209 1210 for (p = a->parts; p; p = p->next) 1211 { 1212 if (mutt_can_decode (p)) 1213 return (1); 1214 } 1215 1216 } 1217 else if (WithCrypto && a->type == TYPEAPPLICATION) 1218 { 1219 if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp(a)) 1220 return (1); 1221 if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(a)) 1222 return (1); 1223 } 1224 1225 return (0); 1226} 1227 1228static int multipart_handler (BODY *a, STATE *s) 1229{ 1230 BODY *b, *p; 1231 char length[5]; 1232 struct stat st; 1233 int count; 1234 int rc = 0; 1235 1236 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE || 1237 a->encoding == ENCUUENCODED) 1238 { 1239 fstat (fileno (s->fpin), &st); 1240 b = mutt_new_body (); 1241 b->length = (long) st.st_size; 1242 b->parts = mutt_parse_multipart (s->fpin, 1243 mutt_get_parameter ("boundary", a->parameter), 1244 (long) st.st_size, ascii_strcasecmp ("digest", a->subtype) == 0); 1245 } 1246 else 1247 b = a; 1248 1249 for (p = b->parts, count = 1; p; p = p->next, count++) 1250 { 1251 if (s->flags & MUTT_DISPLAY) 1252 { 1253 state_mark_attach (s); 1254 state_printf (s, _("[-- Attachment #%d"), count); 1255 if (p->description || p->filename || p->form_name) 1256 { 1257 state_puts (": ", s); 1258 state_puts (p->description ? p->description : 1259 p->filename ? p->filename : p->form_name, s); 1260 } 1261 state_puts (" --]\n", s); 1262 1263 mutt_pretty_size (length, sizeof (length), p->length); 1264 1265 state_mark_attach (s); 1266 state_printf (s, _("[-- Type: %s/%s, Encoding: %s, Size: %s --]\n"), 1267 TYPE (p), p->subtype, ENCODING (p->encoding), length); 1268 if (!option (OPTWEED)) 1269 { 1270 fseeko (s->fpin, p->hdr_offset, 0); 1271 mutt_copy_bytes(s->fpin, s->fpout, p->offset-p->hdr_offset); 1272 } 1273 else 1274 state_putc ('\n', s); 1275 } 1276 1277 rc = mutt_body_handler (p, s); 1278 state_putc ('\n', s); 1279 1280 if (rc) 1281 { 1282 mutt_error (_("One or more parts of this message could not be displayed")); 1283 dprint (1, (debugfile, "Failed on attachment #%d, type %s/%s.\n", count, TYPE(p), NONULL (p->subtype))); 1284 } 1285 1286 if ((s->flags & MUTT_REPLYING) 1287 && (option (OPTINCLUDEONLYFIRST)) && (s->flags & MUTT_FIRSTDONE)) 1288 break; 1289 } 1290 1291 if (a->encoding == ENCBASE64 || a->encoding == ENCQUOTEDPRINTABLE || 1292 a->encoding == ENCUUENCODED) 1293 mutt_free_body (&b); 1294 1295 /* make failure of a single part non-fatal */ 1296 if (rc < 0) 1297 rc = 1; 1298 return rc; 1299} 1300 1301static int autoview_handler (BODY *a, STATE *s) 1302{ 1303 rfc1524_entry *entry = rfc1524_new_entry (); 1304 char buffer[LONG_STRING]; 1305 char type[STRING]; 1306 char command[LONG_STRING]; 1307 char tempfile[_POSIX_PATH_MAX] = ""; 1308 char *fname; 1309 FILE *fpin = NULL; 1310 FILE *fpout = NULL; 1311 FILE *fperr = NULL; 1312 int piped = FALSE; 1313 pid_t thepid; 1314 int rc = 0; 1315 1316 snprintf (type, sizeof (type), "%s/%s", TYPE (a), a->subtype); 1317 rfc1524_mailcap_lookup (a, type, entry, MUTT_AUTOVIEW); 1318 1319 fname = safe_strdup (a->filename); 1320 mutt_sanitize_filename (fname, 1); 1321 rfc1524_expand_filename (entry->nametemplate, fname, tempfile, sizeof (tempfile)); 1322 FREE (&fname); 1323 1324 if (entry->command) 1325 { 1326 strfcpy (command, entry->command, sizeof (command)); 1327 1328 /* rfc1524_expand_command returns 0 if the file is required */ 1329 piped = rfc1524_expand_command (a, tempfile, type, command, sizeof (command)); 1330 1331 if (s->flags & MUTT_DISPLAY) 1332 { 1333 state_mark_attach (s); 1334 state_printf (s, _("[-- Autoview using %s --]\n"), command); 1335 mutt_message(_("Invoking autoview command: %s"),command); 1336 } 1337 1338 if ((fpin = safe_fopen (tempfile, "w+")) == NULL) 1339 { 1340 mutt_perror ("fopen"); 1341 rfc1524_free_entry (&entry); 1342 return -1; 1343 } 1344 1345 mutt_copy_bytes (s->fpin, fpin, a->length); 1346 1347 if(!piped) 1348 { 1349 safe_fclose (&fpin); 1350 thepid = mutt_create_filter (command, NULL, &fpout, &fperr); 1351 } 1352 else 1353 { 1354 unlink (tempfile); 1355 fflush (fpin); 1356 rewind (fpin); 1357 thepid = mutt_create_filter_fd (command, NULL, &fpout, &fperr, 1358 fileno(fpin), -1, -1); 1359 } 1360 1361 if (thepid < 0) 1362 { 1363 mutt_perror _("Can't create filter"); 1364 if (s->flags & MUTT_DISPLAY) 1365 { 1366 state_mark_attach (s); 1367 state_printf (s, _("[-- Can't run %s. --]\n"), command); 1368 } 1369 rc = -1; 1370 goto bail; 1371 } 1372 1373 if (s->prefix) 1374 { 1375 while (fgets (buffer, sizeof(buffer), fpout) != NULL) 1376 { 1377 state_puts (s->prefix, s); 1378 state_puts (buffer, s); 1379 } 1380 /* check for data on stderr */ 1381 if (fgets (buffer, sizeof(buffer), fperr)) 1382 { 1383 if (s->flags & MUTT_DISPLAY) 1384 { 1385 state_mark_attach (s); 1386 state_printf (s, _("[-- Autoview stderr of %s --]\n"), command); 1387 } 1388 1389 state_puts (s->prefix, s); 1390 state_puts (buffer, s); 1391 while (fgets (buffer, sizeof(buffer), fperr) != NULL) 1392 { 1393 state_puts (s->prefix, s); 1394 state_puts (buffer, s); 1395 } 1396 } 1397 } 1398 else 1399 { 1400 mutt_copy_stream (fpout, s->fpout); 1401 /* Check for stderr messages */ 1402 if (fgets (buffer, sizeof(buffer), fperr)) 1403 { 1404 if (s->flags & MUTT_DISPLAY) 1405 { 1406 state_mark_attach (s); 1407 state_printf (s, _("[-- Autoview stderr of %s --]\n"), 1408 command); 1409 } 1410 1411 state_puts (buffer, s); 1412 mutt_copy_stream (fperr, s->fpout); 1413 } 1414 } 1415 1416 bail: 1417 safe_fclose (&fpout); 1418 safe_fclose (&fperr); 1419 1420 mutt_wait_filter (thepid); 1421 if (piped) 1422 safe_fclose (&fpin); 1423 else 1424 mutt_unlink (tempfile); 1425 1426 if (s->flags & MUTT_DISPLAY) 1427 mutt_clear_error (); 1428 } 1429 rfc1524_free_entry (&entry); 1430 1431 return rc; 1432} 1433 1434static int external_body_handler (BODY *b, STATE *s) 1435{ 1436 const char *access_type; 1437 const char *expiration; 1438 time_t expire; 1439 1440 access_type = mutt_get_parameter ("access-type", b->parameter); 1441 if (!access_type) 1442 { 1443 if (s->flags & MUTT_DISPLAY) 1444 { 1445 state_mark_attach (s); 1446 state_puts (_("[-- Error: message/external-body has no access-type parameter --]\n"), s); 1447 return 0; 1448 } 1449 else 1450 return -1; 1451 } 1452 1453 expiration = mutt_get_parameter ("expiration", b->parameter); 1454 if (expiration) 1455 expire = mutt_parse_date (expiration, NULL); 1456 else 1457 expire = -1; 1458 1459 if (!ascii_strcasecmp (access_type, "x-mutt-deleted")) 1460 { 1461 if (s->flags & (MUTT_DISPLAY|MUTT_PRINTING)) 1462 { 1463 char *length; 1464 char pretty_size[10]; 1465 1466 state_mark_attach (s); 1467 state_printf (s, _("[-- This %s/%s attachment "), 1468 TYPE(b->parts), b->parts->subtype); 1469 length = mutt_get_parameter ("length", b->parameter); 1470 if (length) 1471 { 1472 mutt_pretty_size (pretty_size, sizeof (pretty_size), 1473 strtol (length, NULL, 10)); 1474 state_printf (s, _("(size %s bytes) "), pretty_size); 1475 } 1476 state_puts (_("has been deleted --]\n"), s); 1477 1478 if (expire != -1) 1479 { 1480 state_mark_attach (s); 1481 state_printf (s, _("[-- on %s --]\n"), expiration); 1482 } 1483 if (b->parts->filename) 1484 { 1485 state_mark_attach (s); 1486 state_printf (s, _("[-- name: %s --]\n"), b->parts->filename); 1487 } 1488 1489 mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset, 1490 (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) | 1491 CH_DECODE , NULL); 1492 } 1493 } 1494 else if(expiration && expire < time(NULL)) 1495 { 1496 if (s->flags & MUTT_DISPLAY) 1497 { 1498 state_mark_attach (s); 1499 state_printf (s, _("[-- This %s/%s attachment is not included, --]\n"), 1500 TYPE(b->parts), b->parts->subtype); 1501 state_attach_puts (_("[-- and the indicated external source has --]\n" 1502 "[-- expired. --]\n"), s); 1503 1504 mutt_copy_hdr(s->fpin, s->fpout, ftello (s->fpin), b->parts->offset, 1505 (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) | 1506 CH_DECODE | CH_DISPLAY, NULL); 1507 } 1508 } 1509 else 1510 { 1511 if (s->flags & MUTT_DISPLAY) 1512 { 1513 state_mark_attach (s); 1514 state_printf (s, 1515 _("[-- This %s/%s attachment is not included, --]\n"), 1516 TYPE (b->parts), b->parts->subtype); 1517 state_mark_attach (s); 1518 state_printf (s, 1519 _("[-- and the indicated access-type %s is unsupported --]\n"), 1520 access_type); 1521 mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset, 1522 (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) | 1523 CH_DECODE | CH_DISPLAY, NULL); 1524 } 1525 } 1526 1527 return 0; 1528} 1529 1530void mutt_decode_attachment (BODY *b, STATE *s) 1531{ 1532 int istext = mutt_is_text_part (b); 1533 iconv_t cd = (iconv_t)(-1); 1534 1535 if (istext && s->flags & MUTT_CHARCONV) 1536 { 1537 char *charset = mutt_get_parameter ("charset", b->parameter); 1538 if (!charset && AssumedCharset && *AssumedCharset) 1539 charset = mutt_get_default_charset (); 1540 if (charset && Charset) 1541 cd = mutt_iconv_open (Charset, charset, MUTT_ICONV_HOOK_FROM); 1542 } 1543 else if (istext && b->charset) 1544 cd = mutt_iconv_open (Charset, b->charset, MUTT_ICONV_HOOK_FROM); 1545 1546 fseeko (s->fpin, b->offset, 0); 1547 switch (b->encoding) 1548 { 1549 case ENCQUOTEDPRINTABLE: 1550 mutt_decode_quoted (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd); 1551 break; 1552 case ENCBASE64: 1553 mutt_decode_base64 (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd); 1554 break; 1555 case ENCUUENCODED: 1556 mutt_decode_uuencoded (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd); 1557 break; 1558 default: 1559 mutt_decode_xbit (s, b->length, istext || ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)), cd); 1560 break; 1561 } 1562 1563 if (cd != (iconv_t)(-1)) 1564 iconv_close (cd); 1565} 1566 1567/* when generating format=flowed ($text_flowed is set) from format=fixed, 1568 * strip all trailing spaces to improve interoperability; 1569 * if $text_flowed is unset, simply verbatim copy input 1570 */ 1571static int text_plain_handler (BODY *b, STATE *s) 1572{ 1573 char *buf = NULL; 1574 size_t l = 0, sz = 0; 1575 1576 while ((buf = mutt_read_line (buf, &sz, s->fpin, NULL, 0))) 1577 { 1578 if (mutt_strcmp (buf, "-- ") != 0 && option (OPTTEXTFLOWED)) 1579 { 1580 l = mutt_strlen (buf); 1581 while (l > 0 && buf[l-1] == ' ') 1582 buf[--l] = 0; 1583 } 1584 if (s->prefix) 1585 state_puts (s->prefix, s); 1586 state_puts (buf, s); 1587 state_putc ('\n', s); 1588 } 1589 1590 FREE (&buf); 1591 return 0; 1592} 1593 1594static int run_decode_and_handler (BODY *b, STATE *s, handler_t handler, int plaintext) 1595{ 1596 int origType; 1597 char *savePrefix = NULL; 1598 FILE *fp = NULL; 1599 char tempfile[_POSIX_PATH_MAX]; 1600 size_t tmplength = 0; 1601 LOFF_T tmpoffset = 0; 1602 int decode = 0; 1603 int rc = 0; 1604 1605 fseeko (s->fpin, b->offset, 0); 1606 1607 /* see if we need to decode this part before processing it */ 1608 if (b->encoding == ENCBASE64 || b->encoding == ENCQUOTEDPRINTABLE || 1609 b->encoding == ENCUUENCODED || plaintext || 1610 mutt_is_text_part (b)) /* text subtypes may 1611 * require character 1612 * set conversion even 1613 * with 8bit encoding. 1614 */ 1615 { 1616 origType = b->type; 1617 1618 if (!plaintext) 1619 { 1620 /* decode to a tempfile, saving the original destination */ 1621 fp = s->fpout; 1622 mutt_mktemp (tempfile, sizeof (tempfile)); 1623 if ((s->fpout = safe_fopen (tempfile, "w")) == NULL) 1624 { 1625 mutt_error _("Unable to open temporary file!"); 1626 dprint (1, (debugfile, "Can't open %s.\n", tempfile)); 1627 return -1; 1628 } 1629 /* decoding the attachment changes the size and offset, so save a copy 1630 * of the "real" values now, and restore them after processing 1631 */ 1632 tmplength = b->length; 1633 tmpoffset = b->offset; 1634 1635 /* if we are decoding binary bodies, we don't want to prefix each 1636 * line with the prefix or else the data will get corrupted. 1637 */ 1638 savePrefix = s->prefix; 1639 s->prefix = NULL; 1640 1641 decode = 1; 1642 } 1643 else 1644 b->type = TYPETEXT; 1645 1646 mutt_decode_attachment (b, s); 1647 1648 if (decode) 1649 { 1650 b->length = ftello (s->fpout); 1651 b->offset = 0; 1652 safe_fclose (&s->fpout); 1653 1654 /* restore final destination and substitute the tempfile for input */ 1655 s->fpout = fp; 1656 fp = s->fpin; 1657 s->fpin = fopen (tempfile, "r"); 1658 unlink (tempfile); 1659 1660 /* restore the prefix */ 1661 s->prefix = savePrefix; 1662 } 1663 1664 b->type = origType; 1665 } 1666 1667 /* process the (decoded) body part */ 1668 if (handler) 1669 { 1670 rc = handler (b, s); 1671 1672 if (rc) 1673 { 1674 dprint (1, (debugfile, "Failed on attachment of type %s/%s.\n", TYPE(b), NONULL (b->subtype))); 1675 } 1676 1677 if (decode) 1678 { 1679 b->length = tmplength; 1680 b->offset = tmpoffset; 1681 1682 /* restore the original source stream */ 1683 safe_fclose (&s->fpin); 1684 s->fpin = fp; 1685 } 1686 } 1687 s->flags |= MUTT_FIRSTDONE; 1688 1689 return rc; 1690} 1691 1692static int valid_pgp_encrypted_handler (BODY *b, STATE *s) 1693{ 1694 int rc; 1695 BODY *octetstream; 1696 1697 octetstream = b->parts->next; 1698 rc = crypt_pgp_encrypted_handler (octetstream, s); 1699 b->goodsig |= octetstream->goodsig; 1700 1701 return rc; 1702} 1703 1704static int malformed_pgp_encrypted_handler (BODY *b, STATE *s) 1705{ 1706 int rc; 1707 BODY *octetstream; 1708 1709 octetstream = b->parts->next->next; 1710 /* exchange encodes the octet-stream, so re-run it through the decoder */ 1711 rc = run_decode_and_handler (octetstream, s, crypt_pgp_encrypted_handler, 0); 1712 b->goodsig |= octetstream->goodsig; 1713 1714 return rc; 1715} 1716 1717int mutt_body_handler (BODY *b, STATE *s) 1718{ 1719 int plaintext = 0; 1720 handler_t handler = NULL; 1721 int rc = 0; 1722 1723 int oflags = s->flags; 1724 1725 /* first determine which handler to use to process this part */ 1726 1727 if (mutt_is_autoview (b)) 1728 { 1729 handler = autoview_handler; 1730 s->flags &= ~MUTT_CHARCONV; 1731 } 1732 else if (b->type == TYPETEXT) 1733 { 1734 if (ascii_strcasecmp ("plain", b->subtype) == 0) 1735 { 1736 /* avoid copying this part twice since removing the transfer-encoding is 1737 * the only operation needed. 1738 */ 1739 if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)) 1740 handler = crypt_pgp_application_pgp_handler; 1741 else if (option(OPTREFLOWTEXT) && ascii_strcasecmp ("flowed", mutt_get_parameter ("format", b->parameter)) == 0) 1742 handler = rfc3676_handler; 1743 else 1744 handler = text_plain_handler; 1745 } 1746 else if (ascii_strcasecmp ("enriched", b->subtype) == 0) 1747 handler = text_enriched_handler; 1748 else /* text body type without a handler */ 1749 plaintext = 1; 1750 } 1751 else if (b->type == TYPEMESSAGE) 1752 { 1753 if(mutt_is_message_type(b->type, b->subtype)) 1754 handler = message_handler; 1755 else if (!ascii_strcasecmp ("delivery-status", b->subtype)) 1756 plaintext = 1; 1757 else if (!ascii_strcasecmp ("external-body", b->subtype)) 1758 handler = external_body_handler; 1759 } 1760 else if (b->type == TYPEMULTIPART) 1761 { 1762 char *p; 1763 1764 if (ascii_strcasecmp ("alternative", b->subtype) == 0) 1765 handler = alternative_handler; 1766 else if (WithCrypto && ascii_strcasecmp ("signed", b->subtype) == 0) 1767 { 1768 p = mutt_get_parameter ("protocol", b->parameter); 1769 1770 if (!p) 1771 mutt_error _("Error: multipart/signed has no protocol."); 1772 else if (s->flags & MUTT_VERIFY) 1773 handler = mutt_signed_handler; 1774 } 1775 else if (mutt_is_valid_multipart_pgp_encrypted (b)) 1776 handler = valid_pgp_encrypted_handler; 1777 else if (mutt_is_malformed_multipart_pgp_encrypted (b)) 1778 handler = malformed_pgp_encrypted_handler; 1779 1780 if (!handler) 1781 handler = multipart_handler; 1782 1783 if (b->encoding != ENC7BIT && b->encoding != ENC8BIT 1784 && b->encoding != ENCBINARY) 1785 { 1786 dprint (1, (debugfile, "Bad encoding type %d for multipart entity, " 1787 "assuming 7 bit\n", b->encoding)); 1788 b->encoding = ENC7BIT; 1789 } 1790 } 1791 else if (WithCrypto && b->type == TYPEAPPLICATION) 1792 { 1793 if (option (OPTDONTHANDLEPGPKEYS) 1794 && !ascii_strcasecmp("pgp-keys", b->subtype)) 1795 { 1796 /* pass raw part through for key extraction */ 1797 plaintext = 1; 1798 } 1799 else if ((WithCrypto & APPLICATION_PGP) && mutt_is_application_pgp (b)) 1800 handler = crypt_pgp_application_pgp_handler; 1801 else if ((WithCrypto & APPLICATION_SMIME) && mutt_is_application_smime(b)) 1802 handler = crypt_smime_application_smime_handler; 1803 } 1804 1805 /* only respect disposition == attachment if we're not 1806 displaying from the attachment menu (i.e. pager) */ 1807 if ((!option (OPTHONORDISP) || (b->disposition != DISPATTACH || 1808 option(OPTVIEWATTACH))) && 1809 (plaintext || handler)) 1810 { 1811 rc = run_decode_and_handler (b, s, handler, plaintext); 1812 } 1813 /* print hint to use attachment menu for disposition == attachment 1814 if we're not already being called from there */ 1815 else if ((s->flags & MUTT_DISPLAY) || (b->disposition == DISPATTACH && 1816 !option (OPTVIEWATTACH) && 1817 option (OPTHONORDISP) && 1818 (plaintext || handler))) 1819 { 1820 state_mark_attach (s); 1821 if (option (OPTHONORDISP) && b->disposition == DISPATTACH) 1822 fputs (_("[-- This is an attachment "), s->fpout); 1823 else 1824 state_printf (s, _("[-- %s/%s is unsupported "), TYPE (b), b->subtype); 1825 if (!option (OPTVIEWATTACH)) 1826 { 1827 char keystroke[SHORT_STRING]; 1828 1829 if (km_expand_key (keystroke, sizeof(keystroke), 1830 km_find_func (MENU_PAGER, OP_VIEW_ATTACHMENTS))) 1831 fprintf (s->fpout, _("(use '%s' to view this part)"), keystroke); 1832 else 1833 fputs (_("(need 'view-attachments' bound to key!)"), s->fpout); 1834 } 1835 fputs (" --]\n", s->fpout); 1836 } 1837 1838 s->flags = oflags | (s->flags & MUTT_FIRSTDONE); 1839 if (rc) 1840 { 1841 dprint (1, (debugfile, "Bailing on attachment of type %s/%s.\n", TYPE(b), NONULL (b->subtype))); 1842 } 1843 1844 return rc; 1845}