mutt stable branch with some hacks
at master 1018 lines 25 kB view raw
1/* 2 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org> 3 * Copyright (C) 2000-2004,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 "mime.h" 28#include "sort.h" 29#include "mailbox.h" 30#include "copy.h" 31#include "mx.h" 32#include "pager.h" 33#include "mutt_crypt.h" 34#include "mutt_idna.h" 35#include <sys/types.h> 36#include <sys/stat.h> 37#include <fcntl.h> 38 39#ifdef USE_IMAP 40#include "imap.h" 41#endif 42 43#include "buffy.h" 44 45#include <errno.h> 46#include <unistd.h> 47#include <stdlib.h> 48#include <string.h> 49#include <sys/wait.h> 50#include <sys/stat.h> 51#include <sys/types.h> 52#include <utime.h> 53 54static const char *ExtPagerProgress = "all"; 55 56/* The folder the user last saved to. Used by ci_save_message() */ 57static char LastSaveFolder[_POSIX_PATH_MAX] = ""; 58 59int mutt_display_message (HEADER *cur) 60{ 61 char tempfile[_POSIX_PATH_MAX], buf[LONG_STRING]; 62 int rc = 0, builtin = 0; 63 int cmflags = MUTT_CM_DECODE | MUTT_CM_DISPLAY | MUTT_CM_CHARCONV; 64 FILE *fpout = NULL; 65 FILE *fpfilterout = NULL; 66 pid_t filterpid = -1; 67 int res; 68 69 snprintf (buf, sizeof (buf), "%s/%s", TYPE (cur->content), 70 cur->content->subtype); 71 72 mutt_parse_mime_message (Context, cur); 73 mutt_message_hook (Context, cur, MUTT_MESSAGEHOOK); 74 75 /* see if crypto is needed for this message. if so, we should exit curses */ 76 if (WithCrypto && cur->security) 77 { 78 if (cur->security & ENCRYPT) 79 { 80 if (cur->security & APPLICATION_SMIME) 81 crypt_smime_getkeys (cur->env); 82 if(!crypt_valid_passphrase(cur->security)) 83 return 0; 84 85 cmflags |= MUTT_CM_VERIFY; 86 } 87 else if (cur->security & SIGN) 88 { 89 /* find out whether or not the verify signature */ 90 if (query_quadoption (OPT_VERIFYSIG, _("Verify PGP signature?")) == MUTT_YES) 91 { 92 cmflags |= MUTT_CM_VERIFY; 93 } 94 } 95 } 96 97 if (cmflags & MUTT_CM_VERIFY || cur->security & ENCRYPT) 98 { 99 if (cur->security & APPLICATION_PGP) 100 { 101 if (cur->env->from) 102 crypt_pgp_invoke_getkeys (cur->env->from); 103 104 crypt_invoke_message (APPLICATION_PGP); 105 } 106 107 if (cur->security & APPLICATION_SMIME) 108 crypt_invoke_message (APPLICATION_SMIME); 109 } 110 111 112 mutt_mktemp (tempfile, sizeof (tempfile)); 113 if ((fpout = safe_fopen (tempfile, "w")) == NULL) 114 { 115 mutt_error _("Could not create temporary file!"); 116 return (0); 117 } 118 119 if (DisplayFilter && *DisplayFilter) 120 { 121 fpfilterout = fpout; 122 fpout = NULL; 123 /* mutt_endwin (NULL); */ 124 filterpid = mutt_create_filter_fd (DisplayFilter, &fpout, NULL, NULL, 125 -1, fileno(fpfilterout), -1); 126 if (filterpid < 0) 127 { 128 mutt_error (_("Cannot create display filter")); 129 safe_fclose (&fpfilterout); 130 unlink (tempfile); 131 return 0; 132 } 133 } 134 135 if (!Pager || mutt_strcmp (Pager, "builtin") == 0) 136 builtin = 1; 137 else 138 { 139 struct hdr_format_info hfi; 140 hfi.ctx = Context; 141 hfi.pager_progress = ExtPagerProgress; 142 hfi.hdr = cur; 143 mutt_make_string_info (buf, sizeof (buf), MuttIndexWindow->cols, NONULL(PagerFmt), &hfi, MUTT_FORMAT_MAKEPRINT); 144 fputs (buf, fpout); 145 fputs ("\n\n", fpout); 146 } 147 148 res = mutt_copy_message (fpout, Context, cur, cmflags, 149 (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) | CH_DECODE | CH_FROM | CH_DISPLAY); 150 if ((safe_fclose (&fpout) != 0 && errno != EPIPE) || res < 0) 151 { 152 mutt_error (_("Could not copy message")); 153 if (fpfilterout != NULL) 154 { 155 mutt_wait_filter (filterpid); 156 safe_fclose (&fpfilterout); 157 } 158 mutt_unlink (tempfile); 159 return 0; 160 } 161 162 if (fpfilterout != NULL && mutt_wait_filter (filterpid) != 0) 163 mutt_any_key_to_continue (NULL); 164 165 safe_fclose (&fpfilterout); /* XXX - check result? */ 166 167 168 if (WithCrypto) 169 { 170 /* update crypto information for this message */ 171 cur->security &= ~(GOODSIGN|BADSIGN); 172 cur->security |= crypt_query (cur->content); 173 174 /* Remove color cache for this message, in case there 175 are color patterns for both ~g and ~V */ 176 cur->pair = 0; 177 } 178 179 if (builtin) 180 { 181 pager_t info; 182 183 if (WithCrypto 184 && (cur->security & APPLICATION_SMIME) && (cmflags & MUTT_CM_VERIFY)) 185 { 186 if (cur->security & GOODSIGN) 187 { 188 if (!crypt_smime_verify_sender(cur)) 189 mutt_message ( _("S/MIME signature successfully verified.")); 190 else 191 mutt_error ( _("S/MIME certificate owner does not match sender.")); 192 } 193 else if (cur->security & PARTSIGN) 194 mutt_message (_("Warning: Part of this message has not been signed.")); 195 else if (cur->security & SIGN || cur->security & BADSIGN) 196 mutt_error ( _("S/MIME signature could NOT be verified.")); 197 } 198 199 if (WithCrypto 200 && (cur->security & APPLICATION_PGP) && (cmflags & MUTT_CM_VERIFY)) 201 { 202 if (cur->security & GOODSIGN) 203 mutt_message (_("PGP signature successfully verified.")); 204 else if (cur->security & PARTSIGN) 205 mutt_message (_("Warning: Part of this message has not been signed.")); 206 else if (cur->security & SIGN) 207 mutt_message (_("PGP signature could NOT be verified.")); 208 } 209 210 /* Invoke the builtin pager */ 211 memset (&info, 0, sizeof (pager_t)); 212 info.hdr = cur; 213 info.ctx = Context; 214 rc = mutt_pager (NULL, tempfile, MUTT_PAGER_MESSAGE, &info); 215 } 216 else 217 { 218 int r; 219 220 mutt_endwin (NULL); 221 snprintf (buf, sizeof (buf), "%s %s", NONULL(Pager), tempfile); 222 if ((r = mutt_system (buf)) == -1) 223 mutt_error (_("Error running \"%s\"!"), buf); 224 unlink (tempfile); 225 if (!option (OPTNOCURSES)) 226 keypad (stdscr, TRUE); 227 if (r != -1) 228 mutt_set_flag (Context, cur, MUTT_READ, 1); 229 if (r != -1 && option (OPTPROMPTAFTER)) 230 { 231 mutt_unget_event (mutt_any_key_to_continue _("Command: "), 0); 232 rc = km_dokey (MENU_PAGER); 233 } 234 else 235 rc = 0; 236 } 237 238 return rc; 239} 240 241void ci_bounce_message (HEADER *h, int *redraw) 242{ 243 char prompt[SHORT_STRING]; 244 char scratch[SHORT_STRING]; 245 char buf[HUGE_STRING] = { 0 }; 246 ADDRESS *adr = NULL; 247 char *err = NULL; 248 int rc; 249 250 /* RfC 5322 mandates a From: header, so warn before bouncing 251 * messages without one */ 252 if (h) 253 { 254 if (!h->env->from) 255 { 256 mutt_error _("Warning: message contains no From: header"); 257 mutt_sleep (2); 258 } 259 } 260 else if (Context) 261 { 262 for (rc = 0; rc < Context->msgcount; rc++) 263 { 264 if (Context->hdrs[rc]->tagged && !Context->hdrs[rc]->env->from) 265 { 266 mutt_error _("Warning: message contains no From: header"); 267 mutt_sleep (2); 268 break; 269 } 270 } 271 } 272 273 if(h) 274 strfcpy(prompt, _("Bounce message to: "), sizeof(prompt)); 275 else 276 strfcpy(prompt, _("Bounce tagged messages to: "), sizeof(prompt)); 277 278 rc = mutt_get_field (prompt, buf, sizeof (buf), MUTT_ALIAS); 279 280 if (option (OPTNEEDREDRAW)) 281 { 282 unset_option (OPTNEEDREDRAW); 283 *redraw = REDRAW_FULL; 284 } 285 286 if (rc || !buf[0]) 287 return; 288 289 if (!(adr = mutt_parse_adrlist (adr, buf))) 290 { 291 mutt_error _("Error parsing address!"); 292 return; 293 } 294 295 adr = mutt_expand_aliases (adr); 296 297 if (mutt_addrlist_to_intl (adr, &err) < 0) 298 { 299 mutt_error (_("Bad IDN: '%s'"), err); 300 FREE (&err); 301 rfc822_free_address (&adr); 302 return; 303 } 304 305 buf[0] = 0; 306 rfc822_write_address (buf, sizeof (buf), adr, 1); 307 308#define extra_space (15 + 7 + 2) 309 snprintf (scratch, sizeof (scratch), 310 (h ? _("Bounce message to %s") : _("Bounce messages to %s")), buf); 311 312 if (mutt_strwidth (prompt) > MuttMessageWindow->cols - extra_space) 313 { 314 mutt_format_string (prompt, sizeof (prompt), 315 0, MuttMessageWindow->cols-extra_space, FMT_LEFT, 0, 316 scratch, sizeof (scratch), 0); 317 safe_strcat (prompt, sizeof (prompt), "...?"); 318 } 319 else 320 snprintf (prompt, sizeof (prompt), "%s?", scratch); 321 322 if (query_quadoption (OPT_BOUNCE, prompt) != MUTT_YES) 323 { 324 rfc822_free_address (&adr); 325 mutt_window_clearline (MuttMessageWindow, 0); 326 mutt_message (h ? _("Message not bounced.") : _("Messages not bounced.")); 327 return; 328 } 329 330 mutt_window_clearline (MuttMessageWindow, 0); 331 332 rc = mutt_bounce_message (NULL, h, adr); 333 rfc822_free_address (&adr); 334 /* If no error, or background, display message. */ 335 if ((rc == 0) || (rc == S_BKG)) 336 mutt_message (h ? _("Message bounced.") : _("Messages bounced.")); 337} 338 339static void pipe_set_flags (int decode, int print, int *cmflags, int *chflags) 340{ 341 if (decode) 342 { 343 *cmflags |= MUTT_CM_DECODE | MUTT_CM_CHARCONV; 344 *chflags |= CH_DECODE | CH_REORDER; 345 346 if (option (OPTWEED)) 347 { 348 *chflags |= CH_WEED; 349 *cmflags |= MUTT_CM_WEED; 350 } 351 } 352 353 if (print) 354 *cmflags |= MUTT_CM_PRINTING; 355 356} 357 358static void pipe_msg (HEADER *h, FILE *fp, int decode, int print) 359{ 360 int cmflags = 0; 361 int chflags = CH_FROM; 362 363 pipe_set_flags (decode, print, &cmflags, &chflags); 364 365 if (WithCrypto && decode && h->security & ENCRYPT) 366 { 367 if(!crypt_valid_passphrase(h->security)) 368 return; 369 endwin (); 370 } 371 372 if (decode) 373 mutt_parse_mime_message (Context, h); 374 375 mutt_copy_message (fp, Context, h, cmflags, chflags); 376} 377 378 379/* the following code is shared between printing and piping */ 380 381static int _mutt_pipe_message (HEADER *h, char *cmd, 382 int decode, 383 int print, 384 int split, 385 char *sep) 386{ 387 388 int i, rc = 0; 389 pid_t thepid; 390 FILE *fpout; 391 392/* mutt_endwin (NULL); 393 394 is this really needed here ? 395 it makes the screen flicker on pgp and s/mime messages, 396 before asking for a passphrase... 397 Oliver Ehli */ 398 if (h) 399 { 400 401 mutt_message_hook (Context, h, MUTT_MESSAGEHOOK); 402 403 if (WithCrypto && decode) 404 { 405 mutt_parse_mime_message (Context, h); 406 if(h->security & ENCRYPT && !crypt_valid_passphrase(h->security)) 407 return 1; 408 } 409 mutt_endwin (NULL); 410 411 if ((thepid = mutt_create_filter (cmd, &fpout, NULL, NULL)) < 0) 412 { 413 mutt_perror _("Can't create filter process"); 414 return 1; 415 } 416 417 pipe_msg (h, fpout, decode, print); 418 safe_fclose (&fpout); 419 rc = mutt_wait_filter (thepid); 420 } 421 else 422 { /* handle tagged messages */ 423 424 if (WithCrypto && decode) 425 { 426 for (i = 0; i < Context->vcount; i++) 427 if(Context->hdrs[Context->v2r[i]]->tagged) 428 { 429 mutt_message_hook (Context, Context->hdrs[Context->v2r[i]], MUTT_MESSAGEHOOK); 430 mutt_parse_mime_message(Context, Context->hdrs[Context->v2r[i]]); 431 if (Context->hdrs[Context->v2r[i]]->security & ENCRYPT && 432 !crypt_valid_passphrase(Context->hdrs[Context->v2r[i]]->security)) 433 return 1; 434 } 435 } 436 437 if (split) 438 { 439 for (i = 0; i < Context->vcount; i++) 440 { 441 if (Context->hdrs[Context->v2r[i]]->tagged) 442 { 443 mutt_message_hook (Context, Context->hdrs[Context->v2r[i]], MUTT_MESSAGEHOOK); 444 mutt_endwin (NULL); 445 if ((thepid = mutt_create_filter (cmd, &fpout, NULL, NULL)) < 0) 446 { 447 mutt_perror _("Can't create filter process"); 448 return 1; 449 } 450 pipe_msg (Context->hdrs[Context->v2r[i]], fpout, decode, print); 451 /* add the message separator */ 452 if (sep) fputs (sep, fpout); 453 safe_fclose (&fpout); 454 if (mutt_wait_filter (thepid) != 0) 455 rc = 1; 456 } 457 } 458 } 459 else 460 { 461 mutt_endwin (NULL); 462 if ((thepid = mutt_create_filter (cmd, &fpout, NULL, NULL)) < 0) 463 { 464 mutt_perror _("Can't create filter process"); 465 return 1; 466 } 467 for (i = 0; i < Context->vcount; i++) 468 { 469 if (Context->hdrs[Context->v2r[i]]->tagged) 470 { 471 mutt_message_hook (Context, Context->hdrs[Context->v2r[i]], MUTT_MESSAGEHOOK); 472 pipe_msg (Context->hdrs[Context->v2r[i]], fpout, decode, print); 473 /* add the message separator */ 474 if (sep) fputs (sep, fpout); 475 } 476 } 477 safe_fclose (&fpout); 478 if (mutt_wait_filter (thepid) != 0) 479 rc = 1; 480 } 481 } 482 483 if (rc || option (OPTWAITKEY)) 484 mutt_any_key_to_continue (NULL); 485 return rc; 486} 487 488void mutt_pipe_message (HEADER *h) 489{ 490 char buffer[LONG_STRING]; 491 492 buffer[0] = 0; 493 if (mutt_get_field (_("Pipe to command: "), buffer, sizeof (buffer), MUTT_CMD) 494 != 0 || !buffer[0]) 495 return; 496 497 mutt_expand_path (buffer, sizeof (buffer)); 498 _mutt_pipe_message (h, buffer, 499 option (OPTPIPEDECODE), 500 0, 501 option (OPTPIPESPLIT), 502 PipeSep); 503} 504 505void mutt_print_message (HEADER *h) 506{ 507 508 if (quadoption (OPT_PRINT) && (!PrintCmd || !*PrintCmd)) 509 { 510 mutt_message (_("No printing command has been defined.")); 511 return; 512 } 513 514 if (query_quadoption (OPT_PRINT, 515 h ? _("Print message?") : _("Print tagged messages?")) 516 != MUTT_YES) 517 return; 518 519 if (_mutt_pipe_message (h, PrintCmd, 520 option (OPTPRINTDECODE), 521 1, 522 option (OPTPRINTSPLIT), 523 "\f") == 0) 524 mutt_message (h ? _("Message printed") : _("Messages printed")); 525 else 526 mutt_message (h ? _("Message could not be printed") : 527 _("Messages could not be printed")); 528} 529 530 531int mutt_select_sort (int reverse) 532{ 533 int method = Sort; /* save the current method in case of abort */ 534 535 switch (mutt_multi_choice (reverse ? 536 _("Rev-Sort (d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/(u)nsort/si(z)e/s(c)ore/s(p)am?: ") : 537 _("Sort (d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/(u)nsort/si(z)e/s(c)ore/s(p)am?: "), 538 _("dfrsotuzcp"))) 539 { 540 case -1: /* abort - don't resort */ 541 return -1; 542 543 case 1: /* (d)ate */ 544 Sort = SORT_DATE; 545 break; 546 547 case 2: /* (f)rm */ 548 Sort = SORT_FROM; 549 break; 550 551 case 3: /* (r)ecv */ 552 Sort = SORT_RECEIVED; 553 break; 554 555 case 4: /* (s)ubj */ 556 Sort = SORT_SUBJECT; 557 break; 558 559 case 5: /* t(o) */ 560 Sort = SORT_TO; 561 break; 562 563 case 6: /* (t)hread */ 564 Sort = SORT_THREADS; 565 break; 566 567 case 7: /* (u)nsort */ 568 Sort = SORT_ORDER; 569 break; 570 571 case 8: /* si(z)e */ 572 Sort = SORT_SIZE; 573 break; 574 575 case 9: /* s(c)ore */ 576 Sort = SORT_SCORE; 577 break; 578 579 case 10: /* s(p)am */ 580 Sort = SORT_SPAM; 581 break; 582 } 583 if (reverse) 584 Sort |= SORT_REVERSE; 585 586 return (Sort != method ? 0 : -1); /* no need to resort if it's the same */ 587} 588 589/* invoke a command in a subshell */ 590void mutt_shell_escape (void) 591{ 592 char buf[LONG_STRING]; 593 594 buf[0] = 0; 595 if (mutt_get_field (_("Shell command: "), buf, sizeof (buf), MUTT_CMD) == 0) 596 { 597 if (!buf[0] && Shell) 598 strfcpy (buf, Shell, sizeof (buf)); 599 if(buf[0]) 600 { 601 mutt_window_clearline (MuttMessageWindow, 0); 602 mutt_endwin (NULL); 603 fflush (stdout); 604 if (mutt_system (buf) != 0 || option (OPTWAITKEY)) 605 mutt_any_key_to_continue (NULL); 606 } 607 } 608} 609 610/* enter a mutt command */ 611void mutt_enter_command (void) 612{ 613 BUFFER err, token; 614 char buffer[LONG_STRING]; 615 int r; 616 617 buffer[0] = 0; 618 if (mutt_get_field (":", buffer, sizeof (buffer), MUTT_COMMAND) != 0 || !buffer[0]) 619 return; 620 mutt_buffer_init (&err); 621 err.dsize = STRING; 622 err.data = safe_malloc(err.dsize); 623 mutt_buffer_init (&token); 624 r = mutt_parse_rc_line (buffer, &token, &err); 625 FREE (&token.data); 626 if (err.data[0]) 627 { 628 /* since errbuf could potentially contain printf() sequences in it, 629 we must call mutt_error() in this fashion so that vsprintf() 630 doesn't expect more arguments that we passed */ 631 if (r == 0) 632 mutt_message ("%s", err.data); 633 else 634 mutt_error ("%s", err.data); 635 } 636 637 FREE (&err.data); 638} 639 640void mutt_display_address (ENVELOPE *env) 641{ 642 char *pfx = NULL; 643 char buf[SHORT_STRING]; 644 ADDRESS *adr = NULL; 645 646 adr = mutt_get_address (env, &pfx); 647 648 if (!adr) return; 649 650 /* 651 * Note: We don't convert IDNA to local representation this time. 652 * That is intentional, so the user has an opportunity to copy & 653 * paste the on-the-wire form of the address to other, IDN-unable 654 * software. 655 */ 656 657 buf[0] = 0; 658 rfc822_write_address (buf, sizeof (buf), adr, 0); 659 mutt_message ("%s: %s", pfx, buf); 660} 661 662static void set_copy_flags (HEADER *hdr, int decode, int decrypt, int *cmflags, int *chflags) 663{ 664 *cmflags = 0; 665 *chflags = CH_UPDATE_LEN; 666 667 if (WithCrypto && !decode && decrypt && (hdr->security & ENCRYPT)) 668 { 669 if ((WithCrypto & APPLICATION_PGP) 670 && mutt_is_multipart_encrypted(hdr->content)) 671 { 672 *chflags = CH_NONEWLINE | CH_XMIT | CH_MIME; 673 *cmflags = MUTT_CM_DECODE_PGP; 674 } 675 else if ((WithCrypto & APPLICATION_PGP) 676 && mutt_is_application_pgp (hdr->content) & ENCRYPT) 677 decode = 1; 678 else if ((WithCrypto & APPLICATION_SMIME) 679 && mutt_is_application_smime(hdr->content) & ENCRYPT) 680 { 681 *chflags = CH_NONEWLINE | CH_XMIT | CH_MIME; 682 *cmflags = MUTT_CM_DECODE_SMIME; 683 } 684 } 685 686 if (decode) 687 { 688 *chflags = CH_XMIT | CH_MIME | CH_TXTPLAIN; 689 *cmflags = MUTT_CM_DECODE | MUTT_CM_CHARCONV; 690 691 if (!decrypt) /* If decode doesn't kick in for decrypt, */ 692 { 693 *chflags |= CH_DECODE; /* then decode RFC 2047 headers, */ 694 695 if (option (OPTWEED)) 696 { 697 *chflags |= CH_WEED; /* and respect $weed. */ 698 *cmflags |= MUTT_CM_WEED; 699 } 700 } 701 } 702} 703 704int _mutt_save_message (HEADER *h, CONTEXT *ctx, int delete, int decode, int decrypt) 705{ 706 int cmflags, chflags; 707 int rc; 708 709 set_copy_flags (h, decode, decrypt, &cmflags, &chflags); 710 711 if (decode || decrypt) 712 mutt_parse_mime_message (Context, h); 713 714 if ((rc = mutt_append_message (ctx, Context, h, cmflags, chflags)) != 0) 715 return rc; 716 717 if (delete) 718 { 719 mutt_set_flag (Context, h, MUTT_DELETE, 1); 720 mutt_set_flag (Context, h, MUTT_PURGE, 1); 721 if (option (OPTDELETEUNTAG)) 722 mutt_set_flag (Context, h, MUTT_TAG, 0); 723 } 724 725 return 0; 726} 727 728/* returns 0 if the copy/save was successful, or -1 on error/abort */ 729int mutt_save_message (HEADER *h, int delete, 730 int decode, int decrypt, int *redraw) 731{ 732 int i, need_buffy_cleanup; 733 int need_passphrase = 0, app=0; 734 char prompt[SHORT_STRING], buf[_POSIX_PATH_MAX]; 735 CONTEXT ctx; 736 struct stat st; 737 738 *redraw = 0; 739 740 741 snprintf (prompt, sizeof (prompt), 742 decode ? (delete ? _("Decode-save%s to mailbox") : 743 _("Decode-copy%s to mailbox")) : 744 (decrypt ? (delete ? _("Decrypt-save%s to mailbox") : 745 _("Decrypt-copy%s to mailbox")) : 746 (delete ? _("Save%s to mailbox") : _("Copy%s to mailbox"))), 747 h ? "" : _(" tagged")); 748 749 750 if (h) 751 { 752 if (WithCrypto) 753 { 754 need_passphrase = h->security & ENCRYPT; 755 app = h->security; 756 } 757 mutt_message_hook (Context, h, MUTT_MESSAGEHOOK); 758 mutt_default_save (buf, sizeof (buf), h); 759 } 760 else 761 { 762 /* look for the first tagged message */ 763 764 for (i = 0; i < Context->vcount; i++) 765 { 766 if (Context->hdrs[Context->v2r[i]]->tagged) 767 { 768 h = Context->hdrs[Context->v2r[i]]; 769 break; 770 } 771 } 772 773 774 if (h) 775 { 776 mutt_message_hook (Context, h, MUTT_MESSAGEHOOK); 777 mutt_default_save (buf, sizeof (buf), h); 778 if (WithCrypto) 779 { 780 need_passphrase = h->security & ENCRYPT; 781 app = h->security; 782 } 783 h = NULL; 784 } 785 } 786 787 mutt_pretty_mailbox (buf, sizeof (buf)); 788 if (mutt_enter_fname (prompt, buf, sizeof (buf), redraw, 0) == -1) 789 return (-1); 790 791 if (*redraw != REDRAW_FULL) 792 { 793 if (!h) 794 *redraw = REDRAW_INDEX | REDRAW_STATUS; 795 else 796 *redraw = REDRAW_STATUS; 797 } 798 799 if (!buf[0]) 800 return (-1); 801 802 /* This is an undocumented feature of ELM pointed out to me by Felix von 803 * Leitner <leitner@prz.fu-berlin.de> 804 */ 805 if (mutt_strcmp (buf, ".") == 0) 806 strfcpy (buf, LastSaveFolder, sizeof (buf)); 807 else 808 strfcpy (LastSaveFolder, buf, sizeof (LastSaveFolder)); 809 810 mutt_expand_path (buf, sizeof (buf)); 811 812 /* check to make sure that this file is really the one the user wants */ 813 if (mutt_save_confirm (buf, &st) != 0) 814 return -1; 815 816 if (WithCrypto && need_passphrase && (decode || decrypt) 817 && !crypt_valid_passphrase(app)) 818 return -1; 819 820 mutt_message (_("Copying to %s..."), buf); 821 822#ifdef USE_IMAP 823 if (Context->magic == MUTT_IMAP && 824 !(decode || decrypt) && mx_is_imap (buf)) 825 { 826 switch (imap_copy_messages (Context, h, buf, delete)) 827 { 828 /* success */ 829 case 0: mutt_clear_error (); return 0; 830 /* non-fatal error: fall through to fetch/append */ 831 case 1: break; 832 /* fatal error, abort */ 833 case -1: return -1; 834 } 835 } 836#endif 837 838 if (mx_open_mailbox (buf, MUTT_APPEND, &ctx) != NULL) 839 { 840 if (h) 841 { 842 if (_mutt_save_message(h, &ctx, delete, decode, decrypt) != 0) 843 { 844 mx_close_mailbox (&ctx, NULL); 845 return -1; 846 } 847 } 848 else 849 { 850 for (i = 0; i < Context->vcount; i++) 851 { 852 if (Context->hdrs[Context->v2r[i]]->tagged) 853 { 854 mutt_message_hook (Context, Context->hdrs[Context->v2r[i]], MUTT_MESSAGEHOOK); 855 if (_mutt_save_message(Context->hdrs[Context->v2r[i]], 856 &ctx, delete, decode, decrypt) != 0) 857 { 858 mx_close_mailbox (&ctx, NULL); 859 return -1; 860 } 861 } 862 } 863 } 864 865 need_buffy_cleanup = (ctx.magic == MUTT_MBOX || ctx.magic == MUTT_MMDF); 866 867 mx_close_mailbox (&ctx, NULL); 868 869 if (need_buffy_cleanup) 870 mutt_buffy_cleanup (buf, &st); 871 872 mutt_clear_error (); 873 return (0); 874 } 875 876 return -1; 877} 878 879 880void mutt_version (void) 881{ 882 mutt_message ("Mutt %s (%s)", MUTT_VERSION, ReleaseDate); 883} 884 885void mutt_edit_content_type (HEADER *h, BODY *b, FILE *fp) 886{ 887 char buf[LONG_STRING]; 888 char obuf[LONG_STRING]; 889 char tmp[STRING]; 890 PARAMETER *p; 891 892 char charset[STRING]; 893 char *cp; 894 895 short charset_changed = 0; 896 short type_changed = 0; 897 898 cp = mutt_get_parameter ("charset", b->parameter); 899 strfcpy (charset, NONULL (cp), sizeof (charset)); 900 901 snprintf (buf, sizeof (buf), "%s/%s", TYPE (b), b->subtype); 902 strfcpy (obuf, buf, sizeof (obuf)); 903 if (b->parameter) 904 { 905 size_t l; 906 907 for (p = b->parameter; p; p = p->next) 908 { 909 l = strlen (buf); 910 911 rfc822_cat (tmp, sizeof (tmp), p->value, MimeSpecials); 912 snprintf (buf + l, sizeof (buf) - l, "; %s=%s", p->attribute, tmp); 913 } 914 } 915 916 if (mutt_get_field ("Content-Type: ", buf, sizeof (buf), 0) != 0 || 917 buf[0] == 0) 918 return; 919 920 /* clean up previous junk */ 921 mutt_free_parameter (&b->parameter); 922 FREE (&b->subtype); 923 924 mutt_parse_content_type (buf, b); 925 926 927 snprintf (tmp, sizeof (tmp), "%s/%s", TYPE (b), NONULL (b->subtype)); 928 type_changed = ascii_strcasecmp (tmp, obuf); 929 charset_changed = ascii_strcasecmp (charset, mutt_get_parameter ("charset", b->parameter)); 930 931 /* if in send mode, check for conversion - current setting is default. */ 932 933 if (!h && b->type == TYPETEXT && charset_changed) 934 { 935 int r; 936 snprintf (tmp, sizeof (tmp), _("Convert to %s upon sending?"), 937 mutt_get_parameter ("charset", b->parameter)); 938 if ((r = mutt_yesorno (tmp, !b->noconv)) != -1) 939 b->noconv = (r == MUTT_NO); 940 } 941 942 /* inform the user */ 943 944 snprintf (tmp, sizeof (tmp), "%s/%s", TYPE (b), NONULL (b->subtype)); 945 if (type_changed) 946 mutt_message (_("Content-Type changed to %s."), tmp); 947 if (b->type == TYPETEXT && charset_changed) 948 { 949 if (type_changed) 950 mutt_sleep (1); 951 mutt_message (_("Character set changed to %s; %s."), 952 mutt_get_parameter ("charset", b->parameter), 953 b->noconv ? _("not converting") : _("converting")); 954 } 955 956 b->force_charset |= charset_changed ? 1 : 0; 957 958 if (!is_multipart (b) && b->parts) 959 mutt_free_body (&b->parts); 960 if (!mutt_is_message_type (b->type, b->subtype) && b->hdr) 961 { 962 b->hdr->content = NULL; 963 mutt_free_header (&b->hdr); 964 } 965 966 if (fp && (is_multipart (b) || mutt_is_message_type (b->type, b->subtype))) 967 mutt_parse_part (fp, b); 968 969 if (WithCrypto && h) 970 { 971 if (h->content == b) 972 h->security = 0; 973 974 h->security |= crypt_query (b); 975 } 976} 977 978 979static int _mutt_check_traditional_pgp (HEADER *h, int *redraw) 980{ 981 MESSAGE *msg; 982 int rv = 0; 983 984 h->security |= PGP_TRADITIONAL_CHECKED; 985 986 mutt_parse_mime_message (Context, h); 987 if ((msg = mx_open_message (Context, h->msgno)) == NULL) 988 return 0; 989 if (crypt_pgp_check_traditional (msg->fp, h->content, 0)) 990 { 991 h->security = crypt_query (h->content); 992 *redraw |= REDRAW_FULL; 993 rv = 1; 994 } 995 996 h->security |= PGP_TRADITIONAL_CHECKED; 997 mx_close_message (Context, &msg); 998 return rv; 999} 1000 1001int mutt_check_traditional_pgp (HEADER *h, int *redraw) 1002{ 1003 int i; 1004 int rv = 0; 1005 if (h && !(h->security & PGP_TRADITIONAL_CHECKED)) 1006 rv = _mutt_check_traditional_pgp (h, redraw); 1007 else 1008 { 1009 for (i = 0; i < Context->vcount; i++) 1010 if (Context->hdrs[Context->v2r[i]]->tagged && 1011 !(Context->hdrs[Context->v2r[i]]->security & PGP_TRADITIONAL_CHECKED)) 1012 rv = _mutt_check_traditional_pgp (Context->hdrs[Context->v2r[i]], redraw) 1013 || rv; 1014 } 1015 return rv; 1016} 1017 1018