mutt stable branch with some hacks
at jcs 856 lines 22 kB view raw
1/* 2 * Copyright (C) 1996-2002,2012-2013 Michael R. Elkins <me@mutt.org> 3 * Copyright (C) 1999-2002,2004 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_menu.h" 26#include "mime.h" 27#include "mailbox.h" 28#include "mapping.h" 29#include "sort.h" 30#ifdef USE_IMAP 31#include "imap.h" 32#endif 33#include "mutt_crypt.h" 34#include "rfc3676.h" 35 36#include <ctype.h> 37#include <unistd.h> 38#include <string.h> 39#include <sys/stat.h> 40 41static const struct mapping_t PostponeHelp[] = { 42 { N_("Exit"), OP_EXIT }, 43 { N_("Del"), OP_DELETE }, 44 { N_("Undel"), OP_UNDELETE }, 45 { N_("Help"), OP_HELP }, 46 { NULL, 0 } 47}; 48 49 50 51static short PostCount = 0; 52static CONTEXT *PostContext = NULL; 53static short UpdateNumPostponed = 0; 54 55/* Return the number of postponed messages. 56 * if force is 0, use a cached value if it is costly to get a fresh 57 * count (IMAP) - else check. 58 */ 59int mutt_num_postponed (int force) 60{ 61 struct stat st; 62 CONTEXT ctx; 63 64 static time_t LastModify = 0; 65 static char *OldPostponed = NULL; 66 67 if (UpdateNumPostponed) 68 { 69 UpdateNumPostponed = 0; 70 force = 1; 71 } 72 73 if (mutt_strcmp (Postponed, OldPostponed)) 74 { 75 FREE (&OldPostponed); 76 OldPostponed = safe_strdup (Postponed); 77 LastModify = 0; 78 force = 1; 79 } 80 81 if (!Postponed) 82 return 0; 83 84#ifdef USE_IMAP 85 /* LastModify is useless for IMAP */ 86 if (mx_is_imap (Postponed)) 87 { 88 if (force) 89 { 90 short newpc; 91 92 newpc = imap_status (Postponed, 0); 93 if (newpc >= 0) 94 { 95 PostCount = newpc; 96 dprint (3, (debugfile, "mutt_num_postponed: %d postponed IMAP messages found.\n", PostCount)); 97 } 98 else 99 dprint (3, (debugfile, "mutt_num_postponed: using old IMAP postponed count.\n")); 100 } 101 return PostCount; 102 } 103#endif 104 105 if (stat (Postponed, &st) == -1) 106 { 107 PostCount = 0; 108 LastModify = 0; 109 return (0); 110 } 111 112 if (S_ISDIR (st.st_mode)) 113 { 114 /* if we have a maildir mailbox, we need to stat the "new" dir */ 115 116 BUFFER *buf; 117 118 buf = mutt_buffer_pool_get (); 119 mutt_buffer_printf (buf, "%s/new", Postponed); 120 if (access (mutt_b2s (buf), F_OK) == 0 && 121 stat (mutt_b2s (buf), &st) == -1) 122 { 123 PostCount = 0; 124 LastModify = 0; 125 mutt_buffer_pool_release (&buf); 126 return 0; 127 } 128 mutt_buffer_pool_release (&buf); 129 } 130 131 if (LastModify < st.st_mtime) 132 { 133 LastModify = st.st_mtime; 134 135 if (access (Postponed, R_OK | F_OK) != 0) 136 return (PostCount = 0); 137 if (mx_open_mailbox (Postponed, MUTT_NOSORT | MUTT_QUIET, &ctx) == NULL) 138 PostCount = 0; 139 else 140 PostCount = ctx.msgcount; 141 mx_fastclose_mailbox (&ctx); 142 } 143 144 return (PostCount); 145} 146 147void mutt_update_num_postponed (void) 148{ 149 UpdateNumPostponed = 1; 150} 151 152static void post_entry (char *s, size_t slen, MUTTMENU *menu, int entry) 153{ 154 CONTEXT *ctx = (CONTEXT *) menu->data; 155 156 _mutt_make_string (s, slen, NONULL (HdrFmt), ctx, ctx->hdrs[entry], 157 MUTT_FORMAT_ARROWCURSOR); 158} 159 160static HEADER *select_msg (void) 161{ 162 MUTTMENU *menu; 163 int i, done=0, r=-1; 164 char helpstr[LONG_STRING]; 165 short orig_sort; 166 167 menu = mutt_new_menu (MENU_POST); 168 menu->make_entry = post_entry; 169 menu->max = PostContext->msgcount; 170 menu->title = _("Postponed Messages"); 171 menu->data = PostContext; 172 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_POST, PostponeHelp); 173 mutt_push_current_menu (menu); 174 175 /* The postponed mailbox is setup to have sorting disabled, but the global 176 * Sort variable may indicate something different. Sorting has to be 177 * disabled while the postpone menu is being displayed. */ 178 orig_sort = Sort; 179 Sort = SORT_ORDER; 180 181 while (!done) 182 { 183 switch (i = mutt_menuLoop (menu)) 184 { 185 case OP_DELETE: 186 case OP_UNDELETE: 187 /* should deleted draft messages be saved in the trash folder? */ 188 mutt_set_flag (PostContext, PostContext->hdrs[menu->current], MUTT_DELETE, (i == OP_DELETE) ? 1 : 0); 189 PostCount = PostContext->msgcount - PostContext->deleted; 190 if (option (OPTRESOLVE) && menu->current < menu->max - 1) 191 { 192 menu->oldcurrent = menu->current; 193 menu->current++; 194 if (menu->current >= menu->top + menu->pagelen) 195 { 196 menu->top = menu->current; 197 menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; 198 } 199 else 200 menu->redraw |= REDRAW_MOTION_RESYNCH; 201 } 202 else 203 menu->redraw |= REDRAW_CURRENT; 204 break; 205 206 case OP_GENERIC_SELECT_ENTRY: 207 r = menu->current; 208 done = 1; 209 break; 210 211 case OP_EXIT: 212 done = 1; 213 break; 214 } 215 } 216 217 Sort = orig_sort; 218 mutt_pop_current_menu (menu); 219 mutt_menuDestroy (&menu); 220 return (r > -1 ? PostContext->hdrs[r] : NULL); 221} 222 223/* args: 224 * ctx Context info, used when recalling a message to which 225 * we reply. 226 * hdr envelope/attachment info for recalled message 227 * cur if message was a reply, `cur' is set to the message which 228 * `hdr' is in reply to 229 * fcc fcc for the recalled message 230 * 231 * return vals: 232 * -1 error/no messages 233 * 0 normal exit 234 * SENDREPLY recalled message is a reply 235 */ 236int mutt_get_postponed (CONTEXT *ctx, HEADER *hdr, HEADER **cur, BUFFER *fcc) 237{ 238 HEADER *h; 239 int code = SENDPOSTPONED; 240 LIST *tmp; 241 LIST *last = NULL; 242 LIST *next; 243 const char *p; 244 int opt_delete; 245 int close_rc; 246 247 if (!Postponed) 248 return (-1); 249 250 if ((PostContext = mx_open_mailbox (Postponed, MUTT_NOSORT, NULL)) == NULL) 251 { 252 PostCount = 0; 253 mutt_error _("No postponed messages."); 254 return (-1); 255 } 256 257 /* TODO: 258 * mx_open_mailbox() for IMAP leaves IMAP_REOPEN_ALLOW set. For the 259 * index this is papered-over because it calls mx_check_mailbox() 260 * every event loop (which resets that flag). 261 * 262 * For a stable-branch fix, I'm doing the same here, to prevent 263 * context changes from occuring behind the scenes and causing 264 * segvs, but probably the flag needs to be reset after downloading 265 * headers in imap_open_mailbox(). 266 */ 267 mx_check_mailbox (PostContext, NULL); 268 269 if (! PostContext->msgcount) 270 { 271 PostCount = 0; 272 mx_fastclose_mailbox (PostContext); 273 FREE (&PostContext); 274 mutt_error _("No postponed messages."); 275 return (-1); 276 } 277 278 if (PostContext->msgcount == 1) 279 { 280 /* only one message, so just use that one. */ 281 h = PostContext->hdrs[0]; 282 } 283 else if ((h = select_msg ()) == NULL) 284 { 285 /* messages might have been marked for deletion. 286 * try once more on reopen before giving up. */ 287 close_rc = mx_close_mailbox (PostContext, NULL); 288 if (close_rc > 0) 289 close_rc = mx_close_mailbox (PostContext, NULL); 290 if (close_rc != 0) 291 mx_fastclose_mailbox (PostContext); 292 FREE (&PostContext); 293 return (-1); 294 } 295 296 if (mutt_prepare_template (NULL, PostContext, hdr, h, 0) < 0) 297 { 298 mx_fastclose_mailbox (PostContext); 299 FREE (&PostContext); 300 return (-1); 301 } 302 303 /* finished with this message, so delete it. */ 304 mutt_set_flag (PostContext, h, MUTT_DELETE, 1); 305 mutt_set_flag (PostContext, h, MUTT_PURGE, 1); 306 307 /* update the count for the status display */ 308 PostCount = PostContext->msgcount - PostContext->deleted; 309 310 /* avoid the "purge deleted messages" prompt */ 311 opt_delete = quadoption (OPT_DELETE); 312 set_quadoption (OPT_DELETE, MUTT_YES); 313 close_rc = mx_close_mailbox (PostContext, NULL); 314 if (close_rc > 0) 315 close_rc = mx_close_mailbox (PostContext, NULL); 316 if (close_rc != 0) 317 mx_fastclose_mailbox (PostContext); 318 set_quadoption (OPT_DELETE, opt_delete); 319 320 FREE (&PostContext); 321 322 for (tmp = hdr->env->userhdrs; tmp; ) 323 { 324 if (ascii_strncasecmp ("X-Mutt-References:", tmp->data, 18) == 0) 325 { 326 if (ctx) 327 { 328 /* if a mailbox is currently open, look to see if the original message 329 the user attempted to reply to is in this mailbox */ 330 p = skip_email_wsp(tmp->data + 18); 331 if (!ctx->id_hash) 332 ctx->id_hash = mutt_make_id_hash (ctx); 333 *cur = hash_find (ctx->id_hash, p); 334 } 335 336 /* Remove the X-Mutt-References: header field. */ 337 next = tmp->next; 338 if (last) 339 last->next = tmp->next; 340 else 341 hdr->env->userhdrs = tmp->next; 342 tmp->next = NULL; 343 mutt_free_list (&tmp); 344 tmp = next; 345 if (*cur) 346 code |= SENDREPLY; 347 } 348 else if (ascii_strncasecmp ("X-Mutt-Fcc:", tmp->data, 11) == 0) 349 { 350 p = skip_email_wsp(tmp->data + 11); 351 mutt_buffer_strcpy (fcc, p); 352 mutt_buffer_pretty_mailbox (fcc); 353 354 /* remove the X-Mutt-Fcc: header field */ 355 next = tmp->next; 356 if (last) 357 last->next = tmp->next; 358 else 359 hdr->env->userhdrs = tmp->next; 360 tmp->next = NULL; 361 mutt_free_list (&tmp); 362 tmp = next; 363 /* note that x-mutt-fcc was present. we do this because we want to add a 364 * default fcc if the header was missing, but preserve the request of the 365 * user to not make a copy if the header field is present, but empty. 366 * see http://dev.mutt.org/trac/ticket/3653 367 */ 368 code |= SENDPOSTPONEDFCC; 369 } 370 else if ((WithCrypto & APPLICATION_PGP) 371 && (mutt_strncmp ("Pgp:", tmp->data, 4) == 0 /* this is generated 372 * by old mutt versions 373 */ 374 || mutt_strncmp ("X-Mutt-PGP:", tmp->data, 11) == 0)) 375 { 376 hdr->security = mutt_parse_crypt_hdr (strchr (tmp->data, ':') + 1, 1, 377 APPLICATION_PGP); 378 hdr->security |= APPLICATION_PGP; 379 380 /* remove the pgp field */ 381 next = tmp->next; 382 if (last) 383 last->next = tmp->next; 384 else 385 hdr->env->userhdrs = tmp->next; 386 tmp->next = NULL; 387 mutt_free_list (&tmp); 388 tmp = next; 389 } 390 else if ((WithCrypto & APPLICATION_SMIME) 391 && mutt_strncmp ("X-Mutt-SMIME:", tmp->data, 13) == 0) 392 { 393 hdr->security = mutt_parse_crypt_hdr (strchr (tmp->data, ':') + 1, 1, 394 APPLICATION_SMIME); 395 hdr->security |= APPLICATION_SMIME; 396 397 /* remove the smime field */ 398 next = tmp->next; 399 if (last) 400 last->next = tmp->next; 401 else 402 hdr->env->userhdrs = tmp->next; 403 tmp->next = NULL; 404 mutt_free_list (&tmp); 405 tmp = next; 406 } 407 408#ifdef MIXMASTER 409 else if (mutt_strncmp ("X-Mutt-Mix:", tmp->data, 11) == 0) 410 { 411 char *t; 412 mutt_free_list (&hdr->chain); 413 414 t = strtok (tmp->data + 11, " \t\n"); 415 while (t) 416 { 417 hdr->chain = mutt_add_list (hdr->chain, t); 418 t = strtok (NULL, " \t\n"); 419 } 420 421 next = tmp->next; 422 if (last) 423 last->next = tmp->next; 424 else 425 hdr->env->userhdrs = tmp->next; 426 tmp->next = NULL; 427 mutt_free_list (&tmp); 428 tmp = next; 429 } 430#endif 431 432 else 433 { 434 last = tmp; 435 tmp = tmp->next; 436 } 437 } 438 439 if (option (OPTCRYPTOPPORTUNISTICENCRYPT)) 440 crypt_opportunistic_encrypt (hdr); 441 442 return (code); 443} 444 445 446 447int mutt_parse_crypt_hdr (const char *p, int set_empty_signas, int crypt_app) 448{ 449 char smime_cryptalg[LONG_STRING] = "\0"; 450 char sign_as[LONG_STRING] = "\0", *q; 451 int flags = 0; 452 453 if (!WithCrypto) 454 return 0; 455 456 p = skip_email_wsp(p); 457 for (; *p; p++) 458 { 459 460 switch (*p) 461 { 462 case 'e': 463 case 'E': 464 flags |= ENCRYPT; 465 break; 466 467 case 'o': 468 case 'O': 469 flags |= OPPENCRYPT; 470 break; 471 472 case 'a': 473 case 'A': 474#ifdef USE_AUTOCRYPT 475 flags |= AUTOCRYPT; 476#endif 477 break; 478 479 case 'z': 480 case 'Z': 481#ifdef USE_AUTOCRYPT 482 flags |= AUTOCRYPT_OVERRIDE; 483#endif 484 break; 485 486 case 's': 487 case 'S': 488 flags |= SIGN; 489 q = sign_as; 490 491 if (*(p+1) == '<') 492 { 493 for (p += 2; 494 *p && *p != '>' && q < sign_as + sizeof (sign_as) - 1; 495 *q++ = *p++) 496 ; 497 498 if (*p!='>') 499 { 500 mutt_error _("Illegal crypto header"); 501 return 0; 502 } 503 } 504 505 *q = '\0'; 506 break; 507 508 /* This used to be the micalg parameter. 509 * 510 * It's no longer needed, so we just skip the parameter in order 511 * to be able to recall old messages. 512 */ 513 case 'm': 514 case 'M': 515 if (*(p+1) == '<') 516 { 517 for (p += 2; *p && *p != '>'; p++) 518 ; 519 if (*p != '>') 520 { 521 mutt_error _("Illegal crypto header"); 522 return 0; 523 } 524 } 525 526 break; 527 528 529 case 'c': 530 case 'C': 531 q = smime_cryptalg; 532 533 if (*(p+1) == '<') 534 { 535 for (p += 2; *p && *p != '>' && q < smime_cryptalg + sizeof(smime_cryptalg) - 1; 536 *q++ = *p++) 537 ; 538 539 if (*p != '>') 540 { 541 mutt_error _("Illegal S/MIME header"); 542 return 0; 543 } 544 } 545 546 *q = '\0'; 547 break; 548 549 case 'i': 550 case 'I': 551 flags |= INLINE; 552 break; 553 554 default: 555 mutt_error _("Illegal crypto header"); 556 return 0; 557 } 558 559 } 560 561 /* the cryptalg field must not be empty */ 562 if ((WithCrypto & APPLICATION_SMIME) && *smime_cryptalg) 563 mutt_str_replace (&SmimeCryptAlg, smime_cryptalg); 564 565 /* Set {Smime,Pgp}SignAs, if desired. */ 566 567 if ((WithCrypto & APPLICATION_PGP) && (crypt_app == APPLICATION_PGP) 568 && (flags & SIGN) 569 && (set_empty_signas || *sign_as)) 570 mutt_str_replace (&PgpSignAs, sign_as); 571 572 if ((WithCrypto & APPLICATION_SMIME) && (crypt_app == APPLICATION_SMIME) 573 && (flags & SIGN) 574 && (set_empty_signas || *sign_as)) 575 mutt_str_replace (&SmimeSignAs, sign_as); 576 577 return flags; 578} 579 580 581/* args: 582 * fp If not NULL, file containing the template 583 * ctx If fp is NULL, the context containing the header with the template 584 * newhdr The template is read into this HEADER 585 * hdr The message to recall/resend 586 * resend Set if resending (as opposed to recalling a postponed msg). 587 * Resent messages enable header weeding, and also 588 * discard any existing Message-ID and Mail-Followup-To. 589 */ 590int mutt_prepare_template (FILE *fp, CONTEXT *ctx, HEADER *newhdr, HEADER *hdr, 591 short resend) 592{ 593 MESSAGE *msg = NULL; 594 BUFFER *file = NULL; 595 BODY *b; 596 FILE *bfp; 597 int rv = -1; 598 STATE s; 599 int sec_type; 600 ENVELOPE *protected_headers = NULL; 601 602 memset (&s, 0, sizeof (s)); 603 604 if (!fp && (msg = mx_open_message (ctx, hdr->msgno)) == NULL) 605 return (-1); 606 607 if (!fp) fp = msg->fp; 608 609 bfp = fp; 610 611 /* parse the message header and MIME structure */ 612 613 fseeko (fp, hdr->offset, 0); 614 newhdr->offset = hdr->offset; 615 /* enable header weeding for resent messages */ 616 newhdr->env = mutt_read_rfc822_header (fp, newhdr, 1, resend); 617 newhdr->content->length = hdr->content->length; 618 mutt_parse_part (fp, newhdr->content); 619 620 /* If resending a message, don't keep message_id or mail_followup_to. 621 * Otherwise, we are resuming a postponed message, and want to keep those 622 * headers if they exist. 623 */ 624 if (resend) 625 { 626 FREE (&newhdr->env->message_id); 627 FREE (&newhdr->env->mail_followup_to); 628 } 629 630 /* decrypt pgp/mime encoded messages */ 631 632 if ((WithCrypto & APPLICATION_PGP) && 633 (sec_type = mutt_is_multipart_encrypted (newhdr->content))) 634 { 635 newhdr->security |= sec_type; 636 if (!crypt_valid_passphrase (sec_type)) 637 goto bail; 638 639 mutt_message _("Decrypting message..."); 640 if ((crypt_pgp_decrypt_mime (fp, &bfp, newhdr->content, &b) == -1) 641 || b == NULL) 642 { 643 mutt_error _("Decryption failed."); 644 goto bail; 645 } 646 647 mutt_free_body (&newhdr->content); 648 newhdr->content = b; 649 650 if (b->mime_headers) 651 { 652 protected_headers = b->mime_headers; 653 b->mime_headers = NULL; 654 } 655 656 mutt_clear_error (); 657 } 658 659 /* 660 * remove a potential multipart/signed layer - useful when 661 * resending messages 662 */ 663 664 if (WithCrypto && mutt_is_multipart_signed (newhdr->content)) 665 { 666 newhdr->security |= SIGN; 667 if ((WithCrypto & APPLICATION_PGP) 668 && ascii_strcasecmp (mutt_get_parameter ("protocol", newhdr->content->parameter), "application/pgp-signature") == 0) 669 newhdr->security |= APPLICATION_PGP; 670 else if ((WithCrypto & APPLICATION_SMIME)) 671 newhdr->security |= APPLICATION_SMIME; 672 673 /* destroy the signature */ 674 mutt_free_body (&newhdr->content->parts->next); 675 newhdr->content = mutt_remove_multipart (newhdr->content); 676 677 if (newhdr->content->mime_headers) 678 { 679 mutt_free_envelope (&protected_headers); 680 protected_headers = newhdr->content->mime_headers; 681 newhdr->content->mime_headers = NULL; 682 } 683 } 684 685 686 /* 687 * We don't need no primary multipart. 688 * Note: We _do_ preserve messages! 689 */ 690 if (newhdr->content->type == TYPEMULTIPART) 691 newhdr->content = mutt_remove_multipart_mixed (newhdr->content); 692 693 /* Note: this just uses the *first* alternative and strips the rest. 694 * It might be better to scan for text/plain. On the other hand, 695 * mutt's alternative generation filter in theory allows composing 696 * text/html and generating the text/plain from that. This way will 697 * preserve the alternative originally composed by the user. 698 */ 699 newhdr->content = mutt_remove_multipart_alternative (newhdr->content); 700 701 s.fpin = bfp; 702 703 file = mutt_buffer_pool_get (); 704 705 /* create temporary files for all attachments */ 706 for (b = newhdr->content; b; b = b->next) 707 { 708 709 /* what follows is roughly a receive-mode variant of 710 * mutt_get_tmp_attachment () from muttlib.c 711 */ 712 713 mutt_buffer_clear (file); 714 if (b->filename) 715 { 716 mutt_buffer_strcpy (file, b->filename); 717 b->d_filename = safe_strdup (b->filename); 718 } 719 else 720 { 721 /* avoid Content-Disposition: header with temporary filename */ 722 b->use_disp = 0; 723 } 724 725 /* set up state flags */ 726 727 s.flags = 0; 728 729 if (b->type == TYPETEXT) 730 { 731 if (!ascii_strcasecmp ("yes", mutt_get_parameter ("x-mutt-noconv", b->parameter))) 732 b->noconv = 1; 733 else 734 { 735 s.flags |= MUTT_CHARCONV; 736 b->noconv = 0; 737 } 738 739 mutt_delete_parameter ("x-mutt-noconv", &b->parameter); 740 } 741 742 mutt_adv_mktemp (file); 743 if ((s.fpout = safe_fopen (mutt_b2s (file), "w")) == NULL) 744 goto bail; 745 746 747 if ((WithCrypto & APPLICATION_PGP) && 748 ((sec_type = mutt_is_application_pgp (b)) & (ENCRYPT|SIGN))) 749 { 750 if (sec_type & ENCRYPT) 751 { 752 if (!crypt_valid_passphrase (APPLICATION_PGP)) 753 goto bail; 754 mutt_message _("Decrypting message..."); 755 } 756 757 if (mutt_body_handler (b, &s) < 0) 758 { 759 mutt_error _("Decryption failed."); 760 goto bail; 761 } 762 763 newhdr->security |= sec_type; 764 765 b->type = TYPETEXT; 766 mutt_str_replace (&b->subtype, "plain"); 767 mutt_delete_parameter ("x-action", &b->parameter); 768 } 769 else if ((WithCrypto & APPLICATION_SMIME) && 770 ((sec_type = mutt_is_application_smime (b)) & (ENCRYPT|SIGN))) 771 { 772 if (sec_type & ENCRYPT) 773 { 774 if (!crypt_valid_passphrase (APPLICATION_SMIME)) 775 goto bail; 776 crypt_smime_getkeys (newhdr->env); 777 mutt_message _("Decrypting message..."); 778 } 779 780 if (mutt_body_handler (b, &s) < 0) 781 { 782 mutt_error _("Decryption failed."); 783 goto bail; 784 } 785 786 if (b == newhdr->content && !protected_headers) 787 { 788 protected_headers = b->mime_headers; 789 b->mime_headers = NULL; 790 } 791 792 newhdr->security |= sec_type; 793 b->type = TYPETEXT; 794 mutt_str_replace (&b->subtype, "plain"); 795 } 796 else 797 mutt_decode_attachment (b, &s); 798 799 if (safe_fclose (&s.fpout) != 0) 800 goto bail; 801 802 mutt_str_replace (&b->filename, mutt_b2s (file)); 803 b->unlink = 1; 804 805 mutt_stamp_attachment (b); 806 807 mutt_free_body (&b->parts); 808 if (b->hdr) b->hdr->content = NULL; /* avoid dangling pointer */ 809 } 810 811 if (option (OPTCRYPTPROTHDRSREAD) && 812 protected_headers && 813 protected_headers->subject && 814 mutt_strcmp (newhdr->env->subject, protected_headers->subject)) 815 { 816 mutt_str_replace (&newhdr->env->subject, protected_headers->subject); 817 } 818 mutt_free_envelope (&protected_headers); 819 820 /* Fix encryption flags. */ 821 822 /* No inline if multipart. */ 823 if (WithCrypto && (newhdr->security & INLINE) && newhdr->content->next) 824 newhdr->security &= ~INLINE; 825 826 /* Do we even support multiple mechanisms? */ 827 newhdr->security &= WithCrypto | ~(APPLICATION_PGP|APPLICATION_SMIME); 828 829 /* Theoretically, both could be set. Take the one the user wants to set by default. */ 830 if ((newhdr->security & APPLICATION_PGP) && (newhdr->security & APPLICATION_SMIME)) 831 { 832 if (option (OPTSMIMEISDEFAULT)) 833 newhdr->security &= ~APPLICATION_PGP; 834 else 835 newhdr->security &= ~APPLICATION_SMIME; 836 } 837 838 mutt_rfc3676_space_unstuff (newhdr); 839 840 rv = 0; 841 842bail: 843 844 /* that's it. */ 845 mutt_buffer_pool_release (&file); 846 if (bfp != fp) safe_fclose (&bfp); 847 if (msg) mx_close_message (ctx, &msg); 848 849 if (rv == -1) 850 { 851 mutt_free_envelope (&newhdr->env); 852 mutt_free_body (&newhdr->content); 853 } 854 855 return rv; 856}