mutt stable branch with some hacks
at master 1292 lines 32 kB view raw
1/* 2 * Copyright (C) 1996-2000,2002,2007,2010 Michael R. Elkins <me@mutt.org> 3 * Copyright (C) 1999-2006 Thomas Roessler <roessler@does-not-exist.org> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 */ 19 20#if HAVE_CONFIG_H 21# include "config.h" 22#endif 23 24#include "mutt.h" 25#include "mutt_curses.h" 26#include "mutt_menu.h" 27#include "rfc1524.h" 28#include "mime.h" 29#include "mailbox.h" 30#include "attach.h" 31#include "mapping.h" 32#include "mx.h" 33#include "mutt_crypt.h" 34 35#include <ctype.h> 36#include <stdlib.h> 37#include <unistd.h> 38#include <sys/wait.h> 39#include <sys/stat.h> 40#include <string.h> 41#include <errno.h> 42 43static const char *Mailbox_is_read_only = N_("Mailbox is read-only."); 44 45#define CHECK_READONLY if (Context->readonly) \ 46{\ 47 mutt_flushinp (); \ 48 mutt_error _(Mailbox_is_read_only); \ 49 break; \ 50} 51 52static const struct mapping_t AttachHelp[] = { 53 { N_("Exit"), OP_EXIT }, 54 { N_("Save"), OP_SAVE }, 55 { N_("Pipe"), OP_PIPE }, 56 { N_("Print"), OP_PRINT }, 57 { N_("Help"), OP_HELP }, 58 { NULL, 0 } 59}; 60 61void mutt_update_tree (ATTACHPTR **idx, short idxlen) 62{ 63 char buf[STRING]; 64 char *s; 65 int x; 66 67 for (x = 0; x < idxlen; x++) 68 { 69 idx[x]->num = x; 70 if (2 * (idx[x]->level + 2) < sizeof (buf)) 71 { 72 if (idx[x]->level) 73 { 74 s = buf + 2 * (idx[x]->level - 1); 75 *s++ = (idx[x]->content->next) ? MUTT_TREE_LTEE : MUTT_TREE_LLCORNER; 76 *s++ = MUTT_TREE_HLINE; 77 *s++ = MUTT_TREE_RARROW; 78 } 79 else 80 s = buf; 81 *s = 0; 82 } 83 84 if (idx[x]->tree) 85 { 86 if (mutt_strcmp (idx[x]->tree, buf) != 0) 87 mutt_str_replace (&idx[x]->tree, buf); 88 } 89 else 90 idx[x]->tree = safe_strdup (buf); 91 92 if (2 * (idx[x]->level + 2) < sizeof (buf) && idx[x]->level) 93 { 94 s = buf + 2 * (idx[x]->level - 1); 95 *s++ = (idx[x]->content->next) ? '\005' : '\006'; 96 *s++ = '\006'; 97 } 98 } 99} 100 101ATTACHPTR **mutt_gen_attach_list (BODY *m, 102 int parent_type, 103 ATTACHPTR **idx, 104 short *idxlen, 105 short *idxmax, 106 int level, 107 int compose) 108{ 109 ATTACHPTR *new; 110 int i; 111 112 for (; m; m = m->next) 113 { 114 if (*idxlen == *idxmax) 115 { 116 safe_realloc (&idx, sizeof (ATTACHPTR *) * ((*idxmax) += 5)); 117 for (i = *idxlen; i < *idxmax; i++) 118 idx[i] = NULL; 119 } 120 121 if (m->type == TYPEMULTIPART && m->parts 122 && (compose || (parent_type == -1 && ascii_strcasecmp ("alternative", m->subtype))) 123 && (!(WithCrypto & APPLICATION_PGP) || !mutt_is_multipart_encrypted(m)) 124 ) 125 { 126 idx = mutt_gen_attach_list (m->parts, m->type, idx, idxlen, idxmax, level, compose); 127 } 128 else 129 { 130 if (!idx[*idxlen]) 131 idx[*idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); 132 133 new = idx[(*idxlen)++]; 134 new->content = m; 135 m->aptr = new; 136 new->parent_type = parent_type; 137 new->level = level; 138 139 /* We don't support multipart messages in the compose menu yet */ 140 if (!compose && !m->collapsed && 141 ((m->type == TYPEMULTIPART 142 && (!(WithCrypto & APPLICATION_PGP) 143 || !mutt_is_multipart_encrypted (m)) 144 ) 145 || mutt_is_message_type(m->type, m->subtype))) 146 { 147 idx = mutt_gen_attach_list (m->parts, m->type, idx, idxlen, idxmax, level + 1, compose); 148 } 149 } 150 } 151 152 if (level == 0) 153 mutt_update_tree (idx, *idxlen); 154 155 return (idx); 156} 157 158/* %c = character set: convert? 159 * %C = character set 160 * %D = deleted flag 161 * %d = description 162 * %e = MIME content-transfer-encoding 163 * %F = filename for content-disposition header 164 * %f = filename 165 * %I = content-disposition, either I (inline) or A (attachment) 166 * %t = tagged flag 167 * %T = tree chars 168 * %m = major MIME type 169 * %M = MIME subtype 170 * %n = attachment number 171 * %s = size 172 * %u = unlink 173 */ 174const char *mutt_attach_fmt (char *dest, 175 size_t destlen, 176 size_t col, 177 int cols, 178 char op, 179 const char *src, 180 const char *prefix, 181 const char *ifstring, 182 const char *elsestring, 183 unsigned long data, 184 format_flag flags) 185{ 186 char fmt[16]; 187 char tmp[SHORT_STRING]; 188 char charset[SHORT_STRING]; 189 ATTACHPTR *aptr = (ATTACHPTR *) data; 190 int optional = (flags & MUTT_FORMAT_OPTIONAL); 191 size_t l; 192 193 switch (op) 194 { 195 case 'C': 196 if (!optional) 197 { 198 if (mutt_is_text_part (aptr->content) && 199 mutt_get_body_charset (charset, sizeof (charset), aptr->content)) 200 mutt_format_s (dest, destlen, prefix, charset); 201 else 202 mutt_format_s (dest, destlen, prefix, ""); 203 } 204 else if (!mutt_is_text_part (aptr->content) || 205 !mutt_get_body_charset (charset, sizeof (charset), aptr->content)) 206 optional = 0; 207 break; 208 case 'c': 209 /* XXX */ 210 if (!optional) 211 { 212 snprintf (fmt, sizeof (fmt), "%%%sc", prefix); 213 snprintf (dest, destlen, fmt, aptr->content->type != TYPETEXT || 214 aptr->content->noconv ? 'n' : 'c'); 215 } 216 else if (aptr->content->type != TYPETEXT || aptr->content->noconv) 217 optional = 0; 218 break; 219 case 'd': 220 if(!optional) 221 { 222 if (aptr->content->description) 223 { 224 mutt_format_s (dest, destlen, prefix, aptr->content->description); 225 break; 226 } 227 if (mutt_is_message_type(aptr->content->type, aptr->content->subtype) && 228 MsgFmt && aptr->content->hdr) 229 { 230 char s[SHORT_STRING]; 231 _mutt_make_string (s, sizeof (s), MsgFmt, NULL, aptr->content->hdr, 232 MUTT_FORMAT_FORCESUBJ | MUTT_FORMAT_MAKEPRINT | MUTT_FORMAT_ARROWCURSOR); 233 if (*s) 234 { 235 mutt_format_s (dest, destlen, prefix, s); 236 break; 237 } 238 } 239 if (!aptr->content->d_filename && !aptr->content->filename) 240 { 241 mutt_format_s (dest, destlen, prefix, "<no description>"); 242 break; 243 } 244 } 245 else if(aptr->content->description || 246 (mutt_is_message_type (aptr->content->type, aptr->content->subtype) 247 && MsgFmt && aptr->content->hdr)) 248 break; 249 /* FALLS THROUGH TO 'F' */ 250 case 'F': 251 if (!optional) 252 { 253 if (aptr->content->d_filename) 254 { 255 mutt_format_s (dest, destlen, prefix, aptr->content->d_filename); 256 break; 257 } 258 } 259 else if (!aptr->content->d_filename && !aptr->content->filename) 260 { 261 optional = 0; 262 break; 263 } 264 /* FALLS THROUGH TO 'f' */ 265 case 'f': 266 if(!optional) 267 { 268 if (aptr->content->filename && *aptr->content->filename == '/') 269 { 270 char path[_POSIX_PATH_MAX]; 271 272 strfcpy (path, aptr->content->filename, sizeof (path)); 273 mutt_pretty_mailbox (path, sizeof (path)); 274 mutt_format_s (dest, destlen, prefix, path); 275 } 276 else 277 mutt_format_s (dest, destlen, prefix, NONULL (aptr->content->filename)); 278 } 279 else if(!aptr->content->filename) 280 optional = 0; 281 break; 282 case 'D': 283 if(!optional) 284 snprintf (dest, destlen, "%c", aptr->content->deleted ? 'D' : ' '); 285 else if(!aptr->content->deleted) 286 optional = 0; 287 break; 288 case 'e': 289 if(!optional) 290 mutt_format_s (dest, destlen, prefix, 291 ENCODING (aptr->content->encoding)); 292 break; 293 case 'I': 294 if (!optional) 295 { 296 const char dispchar[] = { 'I', 'A', 'F', '-' }; 297 char ch; 298 299 if (aptr->content->disposition < sizeof(dispchar)) 300 ch = dispchar[aptr->content->disposition]; 301 else 302 { 303 dprint(1, (debugfile, "ERROR: invalid content-disposition %d\n", aptr->content->disposition)); 304 ch = '!'; 305 } 306 snprintf (dest, destlen, "%c", ch); 307 } 308 break; 309 case 'm': 310 if(!optional) 311 mutt_format_s (dest, destlen, prefix, TYPE (aptr->content)); 312 break; 313 case 'M': 314 if(!optional) 315 mutt_format_s (dest, destlen, prefix, aptr->content->subtype); 316 else if(!aptr->content->subtype) 317 optional = 0; 318 break; 319 case 'n': 320 if(!optional) 321 { 322 snprintf (fmt, sizeof (fmt), "%%%sd", prefix); 323 snprintf (dest, destlen, fmt, aptr->num + 1); 324 } 325 break; 326 case 'Q': 327 if (optional) 328 optional = aptr->content->attach_qualifies; 329 else { 330 snprintf (fmt, sizeof (fmt), "%%%sc", prefix); 331 mutt_format_s (dest, destlen, fmt, "Q"); 332 } 333 break; 334 case 's': 335 if (flags & MUTT_FORMAT_STAT_FILE) 336 { 337 struct stat st; 338 stat (aptr->content->filename, &st); 339 l = st.st_size; 340 } 341 else 342 l = aptr->content->length; 343 344 if(!optional) 345 { 346 mutt_pretty_size (tmp, sizeof(tmp), l); 347 mutt_format_s (dest, destlen, prefix, tmp); 348 } 349 else if (l == 0) 350 optional = 0; 351 352 break; 353 case 't': 354 if(!optional) 355 snprintf (dest, destlen, "%c", aptr->content->tagged ? '*' : ' '); 356 else if(!aptr->content->tagged) 357 optional = 0; 358 break; 359 case 'T': 360 if(!optional) 361 mutt_format_s_tree (dest, destlen, prefix, NONULL (aptr->tree)); 362 else if (!aptr->tree) 363 optional = 0; 364 break; 365 case 'u': 366 if(!optional) 367 snprintf (dest, destlen, "%c", aptr->content->unlink ? '-' : ' '); 368 else if (!aptr->content->unlink) 369 optional = 0; 370 break; 371 case 'X': 372 if (optional) 373 optional = (aptr->content->attach_count + aptr->content->attach_qualifies) != 0; 374 else 375 { 376 snprintf (fmt, sizeof (fmt), "%%%sd", prefix); 377 snprintf (dest, destlen, fmt, aptr->content->attach_count + aptr->content->attach_qualifies); 378 } 379 break; 380 default: 381 *dest = 0; 382 } 383 384 if (optional) 385 mutt_FormatString (dest, destlen, col, cols, ifstring, mutt_attach_fmt, data, 0); 386 else if (flags & MUTT_FORMAT_OPTIONAL) 387 mutt_FormatString (dest, destlen, col, cols, elsestring, mutt_attach_fmt, data, 0); 388 return (src); 389} 390 391static void attach_entry (char *b, size_t blen, MUTTMENU *menu, int num) 392{ 393 mutt_FormatString (b, blen, 0, MuttIndexWindow->cols, NONULL (AttachFormat), mutt_attach_fmt, (unsigned long) (((ATTACHPTR **)menu->data)[num]), MUTT_FORMAT_ARROWCURSOR); 394} 395 396int mutt_tag_attach (MUTTMENU *menu, int n, int m) 397{ 398 BODY *cur = ((ATTACHPTR **) menu->data)[n]->content; 399 int ot = cur->tagged; 400 401 cur->tagged = (m >= 0 ? m : !cur->tagged); 402 return cur->tagged - ot; 403} 404 405int mutt_is_message_type (int type, const char *subtype) 406{ 407 if (type != TYPEMESSAGE) 408 return 0; 409 410 subtype = NONULL(subtype); 411 return (ascii_strcasecmp (subtype, "rfc822") == 0 || ascii_strcasecmp (subtype, "news") == 0); 412} 413 414static void prepend_curdir (char *dst, size_t dstlen) 415{ 416 size_t l; 417 418 if (!dst || !*dst || *dst == '/' || dstlen < 3 || 419 /* XXX bad modularization, these are special to mutt_expand_path() */ 420 !strchr ("~=+@<>!-^", *dst)) 421 return; 422 423 dstlen -= 3; 424 l = strlen (dst) + 2; 425 l = (l > dstlen ? dstlen : l); 426 memmove (dst + 2, dst, l); 427 dst[0] = '.'; 428 dst[1] = '/'; 429 dst[l + 2] = 0; 430} 431 432static int mutt_query_save_attachment (FILE *fp, BODY *body, HEADER *hdr, char **directory) 433{ 434 char *prompt; 435 char buf[_POSIX_PATH_MAX], tfile[_POSIX_PATH_MAX]; 436 int is_message; 437 int append = 0; 438 int rc; 439 440 if (body->filename) 441 { 442 if (directory && *directory) 443 mutt_concat_path (buf, *directory, mutt_basename (body->filename), sizeof (buf)); 444 else 445 strfcpy (buf, body->filename, sizeof (buf)); 446 } 447 else if(body->hdr && 448 body->encoding != ENCBASE64 && 449 body->encoding != ENCQUOTEDPRINTABLE && 450 mutt_is_message_type(body->type, body->subtype)) 451 mutt_default_save(buf, sizeof(buf), body->hdr); 452 else 453 buf[0] = 0; 454 455 prepend_curdir (buf, sizeof (buf)); 456 457 prompt = _("Save to file: "); 458 while (prompt) 459 { 460 if (mutt_get_field (prompt, buf, sizeof (buf), MUTT_FILE | MUTT_CLEAR) != 0 461 || !buf[0]) 462 { 463 mutt_clear_error (); 464 return -1; 465 } 466 467 prompt = NULL; 468 mutt_expand_path (buf, sizeof (buf)); 469 470 is_message = (fp && 471 body->hdr && 472 body->encoding != ENCBASE64 && 473 body->encoding != ENCQUOTEDPRINTABLE && 474 mutt_is_message_type (body->type, body->subtype)); 475 476 if (is_message) 477 { 478 struct stat st; 479 480 /* check to make sure that this file is really the one the user wants */ 481 if ((rc = mutt_save_confirm (buf, &st)) == 1) 482 { 483 prompt = _("Save to file: "); 484 continue; 485 } 486 else if (rc == -1) 487 return -1; 488 strfcpy(tfile, buf, sizeof(tfile)); 489 } 490 else 491 { 492 if ((rc = mutt_check_overwrite (body->filename, buf, tfile, sizeof (tfile), &append, directory)) == -1) 493 return -1; 494 else if (rc == 1) 495 { 496 prompt = _("Save to file: "); 497 continue; 498 } 499 } 500 501 mutt_message _("Saving..."); 502 if (mutt_save_attachment (fp, body, tfile, append, (hdr || !is_message) ? hdr : body->hdr) == 0) 503 { 504 mutt_message _("Attachment saved."); 505 return 0; 506 } 507 else 508 { 509 prompt = _("Save to file: "); 510 continue; 511 } 512 } 513 return 0; 514} 515 516void mutt_save_attachment_list (FILE *fp, int tag, BODY *top, HEADER *hdr, MUTTMENU *menu) 517{ 518 char buf[_POSIX_PATH_MAX], tfile[_POSIX_PATH_MAX]; 519 char *directory = NULL; 520 int rc = 1; 521 int last = menu ? menu->current : -1; 522 FILE *fpout; 523 524 buf[0] = 0; 525 526 for (; top; top = top->next) 527 { 528 if (!tag || top->tagged) 529 { 530 if (!option (OPTATTACHSPLIT)) 531 { 532 if (!buf[0]) 533 { 534 int append = 0; 535 536 strfcpy (buf, mutt_basename (NONULL (top->filename)), sizeof (buf)); 537 prepend_curdir (buf, sizeof (buf)); 538 539 if (mutt_get_field (_("Save to file: "), buf, sizeof (buf), 540 MUTT_FILE | MUTT_CLEAR) != 0 || !buf[0]) 541 return; 542 mutt_expand_path (buf, sizeof (buf)); 543 if (mutt_check_overwrite (top->filename, buf, tfile, 544 sizeof (tfile), &append, NULL)) 545 return; 546 rc = mutt_save_attachment (fp, top, tfile, append, hdr); 547 if (rc == 0 && AttachSep && (fpout = fopen (tfile,"a")) != NULL) 548 { 549 fprintf(fpout, "%s", AttachSep); 550 safe_fclose (&fpout); 551 } 552 } 553 else 554 { 555 rc = mutt_save_attachment (fp, top, tfile, MUTT_SAVE_APPEND, hdr); 556 if (rc == 0 && AttachSep && (fpout = fopen (tfile,"a")) != NULL) 557 { 558 fprintf(fpout, "%s", AttachSep); 559 safe_fclose (&fpout); 560 } 561 } 562 } 563 else 564 { 565 if (tag && menu && top->aptr) 566 { 567 menu->oldcurrent = menu->current; 568 menu->current = top->aptr->num; 569 menu_check_recenter (menu); 570 menu->redraw |= REDRAW_MOTION; 571 572 menu_redraw (menu); 573 } 574 if (mutt_query_save_attachment (fp, top, hdr, &directory) == -1) 575 break; 576 } 577 } 578 else if (top->parts) 579 mutt_save_attachment_list (fp, 1, top->parts, hdr, menu); 580 if (!tag) 581 break; 582 } 583 584 FREE (&directory); 585 586 if (tag && menu) 587 { 588 menu->oldcurrent = menu->current; 589 menu->current = last; 590 menu_check_recenter (menu); 591 menu->redraw |= REDRAW_MOTION; 592 } 593 594 if (!option (OPTATTACHSPLIT) && (rc == 0)) 595 mutt_message _("Attachment saved."); 596} 597 598static void 599mutt_query_pipe_attachment (char *command, FILE *fp, BODY *body, int filter) 600{ 601 char tfile[_POSIX_PATH_MAX]; 602 char warning[STRING+_POSIX_PATH_MAX]; 603 604 if (filter) 605 { 606 snprintf (warning, sizeof (warning), 607 _("WARNING! You are about to overwrite %s, continue?"), 608 body->filename); 609 if (mutt_yesorno (warning, MUTT_NO) != MUTT_YES) { 610 mutt_window_clearline (MuttMessageWindow, 0); 611 return; 612 } 613 mutt_mktemp (tfile, sizeof (tfile)); 614 } 615 else 616 tfile[0] = 0; 617 618 if (mutt_pipe_attachment (fp, body, command, tfile)) 619 { 620 if (filter) 621 { 622 mutt_unlink (body->filename); 623 mutt_rename_file (tfile, body->filename); 624 mutt_update_encoding (body); 625 mutt_message _("Attachment filtered."); 626 } 627 } 628 else 629 { 630 if (filter && tfile[0]) 631 mutt_unlink (tfile); 632 } 633} 634 635static void pipe_attachment (FILE *fp, BODY *b, STATE *state) 636{ 637 FILE *ifp; 638 639 if (fp) 640 { 641 state->fpin = fp; 642 mutt_decode_attachment (b, state); 643 if (AttachSep) 644 state_puts (AttachSep, state); 645 } 646 else 647 { 648 if ((ifp = fopen (b->filename, "r")) == NULL) 649 { 650 mutt_perror ("fopen"); 651 return; 652 } 653 mutt_copy_stream (ifp, state->fpout); 654 safe_fclose (&ifp); 655 if (AttachSep) 656 state_puts (AttachSep, state); 657 } 658} 659 660static void 661pipe_attachment_list (char *command, FILE *fp, int tag, BODY *top, int filter, 662 STATE *state) 663{ 664 for (; top; top = top->next) 665 { 666 if (!tag || top->tagged) 667 { 668 if (!filter && !option (OPTATTACHSPLIT)) 669 pipe_attachment (fp, top, state); 670 else 671 mutt_query_pipe_attachment (command, fp, top, filter); 672 } 673 else if (top->parts) 674 pipe_attachment_list (command, fp, tag, top->parts, filter, state); 675 if (!tag) 676 break; 677 } 678} 679 680void mutt_pipe_attachment_list (FILE *fp, int tag, BODY *top, int filter) 681{ 682 STATE state; 683 char buf[SHORT_STRING]; 684 pid_t thepid; 685 686 if (fp) 687 filter = 0; /* sanity check: we can't filter in the recv case yet */ 688 689 buf[0] = 0; 690 memset (&state, 0, sizeof (STATE)); 691 /* perform charset conversion on text attachments when piping */ 692 state.flags = MUTT_CHARCONV; 693 694 if (mutt_get_field ((filter ? _("Filter through: ") : _("Pipe to: ")), 695 buf, sizeof (buf), MUTT_CMD) != 0 || !buf[0]) 696 return; 697 698 mutt_expand_path (buf, sizeof (buf)); 699 700 if (!filter && !option (OPTATTACHSPLIT)) 701 { 702 mutt_endwin (NULL); 703 thepid = mutt_create_filter (buf, &state.fpout, NULL, NULL); 704 pipe_attachment_list (buf, fp, tag, top, filter, &state); 705 safe_fclose (&state.fpout); 706 if (mutt_wait_filter (thepid) != 0 || option (OPTWAITKEY)) 707 mutt_any_key_to_continue (NULL); 708 } 709 else 710 pipe_attachment_list (buf, fp, tag, top, filter, &state); 711} 712 713static int can_print (BODY *top, int tag) 714{ 715 char type [STRING]; 716 717 for (; top; top = top->next) 718 { 719 snprintf (type, sizeof (type), "%s/%s", TYPE (top), top->subtype); 720 if (!tag || top->tagged) 721 { 722 if (!rfc1524_mailcap_lookup (top, type, NULL, MUTT_PRINT)) 723 { 724 if (ascii_strcasecmp ("text/plain", top->subtype) && 725 ascii_strcasecmp ("application/postscript", top->subtype)) 726 { 727 if (!mutt_can_decode (top)) 728 { 729 mutt_error (_("I don't know how to print %s attachments!"), type); 730 return (0); 731 } 732 } 733 } 734 } 735 else if (top->parts) 736 return (can_print (top->parts, tag)); 737 if (!tag) 738 break; 739 } 740 return (1); 741} 742 743static void print_attachment_list (FILE *fp, int tag, BODY *top, STATE *state) 744{ 745 char type [STRING]; 746 747 748 for (; top; top = top->next) 749 { 750 if (!tag || top->tagged) 751 { 752 snprintf (type, sizeof (type), "%s/%s", TYPE (top), top->subtype); 753 if (!option (OPTATTACHSPLIT) && !rfc1524_mailcap_lookup (top, type, NULL, MUTT_PRINT)) 754 { 755 if (!ascii_strcasecmp ("text/plain", top->subtype) || 756 !ascii_strcasecmp ("application/postscript", top->subtype)) 757 pipe_attachment (fp, top, state); 758 else if (mutt_can_decode (top)) 759 { 760 /* decode and print */ 761 762 char newfile[_POSIX_PATH_MAX] = ""; 763 FILE *ifp; 764 765 mutt_mktemp (newfile, sizeof (newfile)); 766 if (mutt_decode_save_attachment (fp, top, newfile, MUTT_PRINTING, 0) == 0) 767 { 768 if ((ifp = fopen (newfile, "r")) != NULL) 769 { 770 mutt_copy_stream (ifp, state->fpout); 771 safe_fclose (&ifp); 772 if (AttachSep) 773 state_puts (AttachSep, state); 774 } 775 } 776 mutt_unlink (newfile); 777 } 778 } 779 else 780 mutt_print_attachment (fp, top); 781 } 782 else if (top->parts) 783 print_attachment_list (fp, tag, top->parts, state); 784 if (!tag) 785 return; 786 } 787} 788 789void mutt_print_attachment_list (FILE *fp, int tag, BODY *top) 790{ 791 STATE state; 792 793 pid_t thepid; 794 if (query_quadoption (OPT_PRINT, tag ? _("Print tagged attachment(s)?") : _("Print attachment?")) != MUTT_YES) 795 return; 796 797 if (!option (OPTATTACHSPLIT)) 798 { 799 if (!can_print (top, tag)) 800 return; 801 mutt_endwin (NULL); 802 memset (&state, 0, sizeof (STATE)); 803 thepid = mutt_create_filter (NONULL (PrintCmd), &state.fpout, NULL, NULL); 804 print_attachment_list (fp, tag, top, &state); 805 safe_fclose (&state.fpout); 806 if (mutt_wait_filter (thepid) != 0 || option (OPTWAITKEY)) 807 mutt_any_key_to_continue (NULL); 808 } 809 else 810 print_attachment_list (fp, tag, top, &state); 811} 812 813static void 814mutt_update_attach_index (BODY *cur, ATTACHPTR ***idxp, 815 short *idxlen, short *idxmax, 816 MUTTMENU *menu) 817{ 818 ATTACHPTR **idx = *idxp; 819 while (--(*idxlen) >= 0) 820 idx[(*idxlen)]->content = NULL; 821 *idxlen = 0; 822 823 idx = *idxp = mutt_gen_attach_list (cur, -1, idx, idxlen, idxmax, 0, 0); 824 825 menu->max = *idxlen; 826 menu->data = *idxp; 827 828 if (menu->current >= menu->max) 829 menu->current = menu->max - 1; 830 menu_check_recenter (menu); 831 menu->redraw |= REDRAW_INDEX; 832 833} 834 835 836int 837mutt_attach_display_loop (MUTTMENU *menu, int op, FILE *fp, HEADER *hdr, 838 BODY *cur, ATTACHPTR ***idxp, short *idxlen, short *idxmax, 839 int recv) 840{ 841 ATTACHPTR **idx = *idxp; 842#if 0 843 int old_optweed = option (OPTWEED); 844 set_option (OPTWEED); 845#endif 846 847 do 848 { 849 switch (op) 850 { 851 case OP_DISPLAY_HEADERS: 852 toggle_option (OPTWEED); 853 /* fall through */ 854 855 case OP_VIEW_ATTACH: 856 op = mutt_view_attachment (fp, idx[menu->current]->content, MUTT_REGULAR, 857 hdr, idx, *idxlen); 858 break; 859 860 case OP_NEXT_ENTRY: 861 case OP_MAIN_NEXT_UNDELETED: /* hack */ 862 if (menu->current < menu->max - 1) 863 { 864 menu->current++; 865 op = OP_VIEW_ATTACH; 866 } 867 else 868 op = OP_NULL; 869 break; 870 case OP_PREV_ENTRY: 871 case OP_MAIN_PREV_UNDELETED: /* hack */ 872 if (menu->current > 0) 873 { 874 menu->current--; 875 op = OP_VIEW_ATTACH; 876 } 877 else 878 op = OP_NULL; 879 break; 880 case OP_EDIT_TYPE: 881 /* when we edit the content-type, we should redisplay the attachment 882 immediately */ 883 mutt_edit_content_type (hdr, idx[menu->current]->content, fp); 884 if (idxmax) 885 { 886 mutt_update_attach_index (cur, idxp, idxlen, idxmax, menu); 887 idx = *idxp; 888 } 889 op = OP_VIEW_ATTACH; 890 break; 891 /* functions which are passed through from the pager */ 892 case OP_CHECK_TRADITIONAL: 893 if (!(WithCrypto & APPLICATION_PGP) || (hdr && hdr->security & PGP_TRADITIONAL_CHECKED)) 894 { 895 op = OP_NULL; 896 break; 897 } 898 /* fall through */ 899 case OP_ATTACH_COLLAPSE: 900 if (recv) 901 return op; 902 default: 903 op = OP_NULL; 904 } 905 } 906 while (op != OP_NULL); 907 908#if 0 909 if (option (OPTWEED) != old_optweed) 910 toggle_option (OPTWEED); 911#endif 912 return op; 913} 914 915static void attach_collapse (BODY *b, short collapse, short init, short just_one) 916{ 917 short i; 918 for (; b; b = b->next) 919 { 920 i = init || b->collapsed; 921 if (i && option (OPTDIGESTCOLLAPSE) && b->type == TYPEMULTIPART 922 && !ascii_strcasecmp (b->subtype, "digest")) 923 attach_collapse (b->parts, 1, 1, 0); 924 else if (b->type == TYPEMULTIPART || mutt_is_message_type (b->type, b->subtype)) 925 attach_collapse (b->parts, collapse, i, 0); 926 b->collapsed = collapse; 927 if (just_one) 928 return; 929 } 930} 931 932void mutt_attach_init (BODY *b) 933{ 934 for (; b; b = b->next) 935 { 936 b->tagged = 0; 937 b->collapsed = 0; 938 if (b->parts) 939 mutt_attach_init (b->parts); 940 } 941} 942 943static const char *Function_not_permitted = N_("Function not permitted in attach-message mode."); 944 945#define CHECK_ATTACH if(option(OPTATTACHMSG)) \ 946 {\ 947 mutt_flushinp (); \ 948 mutt_error _(Function_not_permitted); \ 949 break; \ 950 } 951 952 953 954 955void mutt_view_attachments (HEADER *hdr) 956{ 957 int secured = 0; 958 int need_secured = 0; 959 960 char helpstr[LONG_STRING]; 961 MUTTMENU *menu; 962 BODY *cur = NULL; 963 MESSAGE *msg; 964 FILE *fp; 965 ATTACHPTR **idx = NULL; 966 short idxlen = 0; 967 short idxmax = 0; 968 int flags = 0; 969 int op = OP_NULL; 970 971 /* make sure we have parsed this message */ 972 mutt_parse_mime_message (Context, hdr); 973 974 mutt_message_hook (Context, hdr, MUTT_MESSAGEHOOK); 975 976 if ((msg = mx_open_message (Context, hdr->msgno)) == NULL) 977 return; 978 979 980 if (WithCrypto && ((hdr->security & ENCRYPT) || 981 (mutt_is_application_smime(hdr->content) & SMIMEOPAQUE))) 982 { 983 need_secured = 1; 984 985 if ((hdr->security & ENCRYPT) && !crypt_valid_passphrase(hdr->security)) 986 { 987 mx_close_message (Context, &msg); 988 return; 989 } 990 if ((WithCrypto & APPLICATION_SMIME) && (hdr->security & APPLICATION_SMIME)) 991 { 992 if (hdr->env) 993 crypt_smime_getkeys (hdr->env); 994 995 if (mutt_is_application_smime(hdr->content)) 996 { 997 secured = ! crypt_smime_decrypt_mime (msg->fp, &fp, 998 hdr->content, &cur); 999 1000 /* S/MIME nesting */ 1001 if ((mutt_is_application_smime (cur) & SMIMEOPAQUE)) 1002 { 1003 BODY *_cur = cur; 1004 FILE *_fp = fp; 1005 1006 fp = NULL; cur = NULL; 1007 secured = !crypt_smime_decrypt_mime (_fp, &fp, _cur, &cur); 1008 1009 mutt_free_body (&_cur); 1010 safe_fclose (&_fp); 1011 } 1012 } 1013 else 1014 need_secured = 0; 1015 } 1016 if ((WithCrypto & APPLICATION_PGP) && (hdr->security & APPLICATION_PGP)) 1017 { 1018 if (mutt_is_multipart_encrypted(hdr->content) || 1019 mutt_is_malformed_multipart_pgp_encrypted(hdr->content)) 1020 secured = !crypt_pgp_decrypt_mime (msg->fp, &fp, hdr->content, &cur); 1021 else 1022 need_secured = 0; 1023 } 1024 1025 if (need_secured && !secured) 1026 { 1027 mx_close_message (Context, &msg); 1028 mutt_error _("Can't decrypt encrypted message!"); 1029 return; 1030 } 1031 } 1032 1033 if (!WithCrypto || !need_secured) 1034 { 1035 fp = msg->fp; 1036 cur = hdr->content; 1037 } 1038 1039 menu = mutt_new_menu (MENU_ATTACH); 1040 menu->title = _("Attachments"); 1041 menu->make_entry = attach_entry; 1042 menu->tag = mutt_tag_attach; 1043 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_ATTACH, AttachHelp); 1044 1045 mutt_attach_init (cur); 1046 attach_collapse (cur, 0, 1, 0); 1047 mutt_update_attach_index (cur, &idx, &idxlen, &idxmax, menu); 1048 1049 FOREVER 1050 { 1051 if (op == OP_NULL) 1052 op = mutt_menuLoop (menu); 1053 switch (op) 1054 { 1055 case OP_ATTACH_VIEW_MAILCAP: 1056 mutt_view_attachment (fp, idx[menu->current]->content, MUTT_MAILCAP, 1057 hdr, idx, idxlen); 1058 menu->redraw = REDRAW_FULL; 1059 break; 1060 1061 case OP_ATTACH_VIEW_TEXT: 1062 mutt_view_attachment (fp, idx[menu->current]->content, MUTT_AS_TEXT, 1063 hdr, idx, idxlen); 1064 menu->redraw = REDRAW_FULL; 1065 break; 1066 1067 case OP_DISPLAY_HEADERS: 1068 case OP_VIEW_ATTACH: 1069 op = mutt_attach_display_loop (menu, op, fp, hdr, cur, &idx, &idxlen, &idxmax, 1); 1070 menu->redraw = REDRAW_FULL; 1071 continue; 1072 1073 case OP_ATTACH_COLLAPSE: 1074 if (!idx[menu->current]->content->parts) 1075 { 1076 mutt_error _("There are no subparts to show!"); 1077 break; 1078 } 1079 if (!idx[menu->current]->content->collapsed) 1080 attach_collapse (idx[menu->current]->content, 1, 0, 1); 1081 else 1082 attach_collapse (idx[menu->current]->content, 0, 1, 1); 1083 mutt_update_attach_index (cur, &idx, &idxlen, &idxmax, menu); 1084 break; 1085 1086 case OP_FORGET_PASSPHRASE: 1087 crypt_forget_passphrase (); 1088 break; 1089 1090 case OP_EXTRACT_KEYS: 1091 if ((WithCrypto & APPLICATION_PGP)) 1092 { 1093 crypt_pgp_extract_keys_from_attachment_list (fp, menu->tagprefix, 1094 menu->tagprefix ? cur : idx[menu->current]->content); 1095 menu->redraw = REDRAW_FULL; 1096 } 1097 break; 1098 1099 case OP_CHECK_TRADITIONAL: 1100 if ((WithCrypto & APPLICATION_PGP) 1101 && crypt_pgp_check_traditional (fp, menu->tagprefix ? cur 1102 : idx[menu->current]->content, 1103 menu->tagprefix)) 1104 { 1105 hdr->security = crypt_query (cur); 1106 menu->redraw = REDRAW_FULL; 1107 } 1108 break; 1109 1110 case OP_PRINT: 1111 mutt_print_attachment_list (fp, menu->tagprefix, 1112 menu->tagprefix ? cur : idx[menu->current]->content); 1113 break; 1114 1115 case OP_PIPE: 1116 mutt_pipe_attachment_list (fp, menu->tagprefix, 1117 menu->tagprefix ? cur : idx[menu->current]->content, 0); 1118 break; 1119 1120 case OP_SAVE: 1121 mutt_save_attachment_list (fp, menu->tagprefix, 1122 menu->tagprefix ? cur : idx[menu->current]->content, hdr, menu); 1123 1124 if (!menu->tagprefix && option (OPTRESOLVE) && menu->current < menu->max - 1) 1125 menu->current++; 1126 1127 menu->redraw = REDRAW_MOTION_RESYNCH | REDRAW_FULL; 1128 break; 1129 1130 case OP_DELETE: 1131 CHECK_READONLY; 1132 1133#ifdef USE_POP 1134 if (Context->magic == MUTT_POP) 1135 { 1136 mutt_flushinp (); 1137 mutt_error _("Can't delete attachment from POP server."); 1138 break; 1139 } 1140#endif 1141 1142 if (WithCrypto && (hdr->security & ENCRYPT)) 1143 { 1144 mutt_message _( 1145 "Deletion of attachments from encrypted messages is unsupported."); 1146 break; 1147 } 1148 if (WithCrypto && (hdr->security & (SIGN | PARTSIGN))) 1149 { 1150 mutt_message _( 1151 "Deletion of attachments from signed messages may invalidate the signature."); 1152 } 1153 if (!menu->tagprefix) 1154 { 1155 if (idx[menu->current]->parent_type == TYPEMULTIPART) 1156 { 1157 idx[menu->current]->content->deleted = 1; 1158 if (option (OPTRESOLVE) && menu->current < menu->max - 1) 1159 { 1160 menu->current++; 1161 menu->redraw = REDRAW_MOTION_RESYNCH; 1162 } 1163 else 1164 menu->redraw = REDRAW_CURRENT; 1165 } 1166 else 1167 mutt_message _( 1168 "Only deletion of multipart attachments is supported."); 1169 } 1170 else 1171 { 1172 int x; 1173 1174 for (x = 0; x < menu->max; x++) 1175 { 1176 if (idx[x]->content->tagged) 1177 { 1178 if (idx[x]->parent_type == TYPEMULTIPART) 1179 { 1180 idx[x]->content->deleted = 1; 1181 menu->redraw = REDRAW_INDEX; 1182 } 1183 else 1184 mutt_message _( 1185 "Only deletion of multipart attachments is supported."); 1186 } 1187 } 1188 } 1189 break; 1190 1191 case OP_UNDELETE: 1192 CHECK_READONLY; 1193 if (!menu->tagprefix) 1194 { 1195 idx[menu->current]->content->deleted = 0; 1196 if (option (OPTRESOLVE) && menu->current < menu->max - 1) 1197 { 1198 menu->current++; 1199 menu->redraw = REDRAW_MOTION_RESYNCH; 1200 } 1201 else 1202 menu->redraw = REDRAW_CURRENT; 1203 } 1204 else 1205 { 1206 int x; 1207 1208 for (x = 0; x < menu->max; x++) 1209 { 1210 if (idx[x]->content->tagged) 1211 { 1212 idx[x]->content->deleted = 0; 1213 menu->redraw = REDRAW_INDEX; 1214 } 1215 } 1216 } 1217 break; 1218 1219 case OP_RESEND: 1220 CHECK_ATTACH; 1221 mutt_attach_resend (fp, hdr, idx, idxlen, 1222 menu->tagprefix ? NULL : idx[menu->current]->content); 1223 menu->redraw = REDRAW_FULL; 1224 break; 1225 1226 case OP_BOUNCE_MESSAGE: 1227 CHECK_ATTACH; 1228 mutt_attach_bounce (fp, hdr, idx, idxlen, 1229 menu->tagprefix ? NULL : idx[menu->current]->content); 1230 menu->redraw = REDRAW_FULL; 1231 break; 1232 1233 case OP_FORWARD_MESSAGE: 1234 CHECK_ATTACH; 1235 mutt_attach_forward (fp, hdr, idx, idxlen, 1236 menu->tagprefix ? NULL : idx[menu->current]->content); 1237 menu->redraw = REDRAW_FULL; 1238 break; 1239 1240 case OP_REPLY: 1241 case OP_GROUP_REPLY: 1242 case OP_LIST_REPLY: 1243 1244 CHECK_ATTACH; 1245 1246 flags = SENDREPLY | 1247 (op == OP_GROUP_REPLY ? SENDGROUPREPLY : 0) | 1248 (op == OP_LIST_REPLY ? SENDLISTREPLY : 0); 1249 mutt_attach_reply (fp, hdr, idx, idxlen, 1250 menu->tagprefix ? NULL : idx[menu->current]->content, flags); 1251 menu->redraw = REDRAW_FULL; 1252 break; 1253 1254 case OP_EDIT_TYPE: 1255 mutt_edit_content_type (hdr, idx[menu->current]->content, fp); 1256 mutt_update_attach_index (cur, &idx, &idxlen, &idxmax, menu); 1257 break; 1258 1259 case OP_EXIT: 1260 mx_close_message (Context, &msg); 1261 hdr->attach_del = 0; 1262 while (idxmax-- > 0) 1263 { 1264 if (!idx[idxmax]) 1265 continue; 1266 if (idx[idxmax]->content && idx[idxmax]->content->deleted) 1267 hdr->attach_del = 1; 1268 if (idx[idxmax]->content) 1269 idx[idxmax]->content->aptr = NULL; 1270 FREE (&idx[idxmax]->tree); 1271 FREE (&idx[idxmax]); 1272 } 1273 if (hdr->attach_del) 1274 hdr->changed = 1; 1275 FREE (&idx); 1276 idxmax = 0; 1277 1278 if (WithCrypto && need_secured && secured) 1279 { 1280 safe_fclose (&fp); 1281 mutt_free_body (&cur); 1282 } 1283 1284 mutt_menuDestroy (&menu); 1285 return; 1286 } 1287 1288 op = OP_NULL; 1289 } 1290 1291 /* not reached */ 1292}