mutt stable branch with some hacks
at jcs 1782 lines 50 kB view raw
1/* 2 * Copyright (C) 1996-2000,2002,2007,2010,2012 Michael R. Elkins <me@mutt.org> 3 * Copyright (C) 2004 g10 Code GmbH 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 "version.h" 25#include "mutt.h" 26#include "mutt_curses.h" 27#include "mutt_idna.h" 28#include "mutt_menu.h" 29#include "mutt_crypt.h" 30#include "rfc1524.h" 31#include "mime.h" 32#include "attach.h" 33#include "mapping.h" 34#include "mailbox.h" 35#include "sort.h" 36#include "charset.h" 37#include "rfc3676.h" 38 39#ifdef MIXMASTER 40#include "remailer.h" 41#endif 42 43#ifdef USE_AUTOCRYPT 44#include "autocrypt/autocrypt.h" 45#endif 46 47#include <errno.h> 48#include <string.h> 49#include <sys/stat.h> 50#include <sys/wait.h> 51#include <unistd.h> 52#include <stdlib.h> 53 54static const char* There_are_no_attachments = N_("There are no attachments."); 55 56#define CHECK_COUNT if (actx->idxlen == 0) { mutt_error _(There_are_no_attachments); break; } 57 58#define CURATTACH actx->idx[actx->v2r[menu->current]] 59 60 61enum 62{ 63 HDR_FROM = 0, 64 HDR_TO, 65 HDR_CC, 66 HDR_BCC, 67 HDR_SUBJECT, 68 HDR_REPLYTO, 69 HDR_FCC, 70 71#ifdef MIXMASTER 72 HDR_MIX, 73#endif 74 75 HDR_CRYPT, 76 HDR_CRYPTINFO, 77#ifdef USE_AUTOCRYPT 78 HDR_AUTOCRYPT, 79#endif 80 81 HDR_ATTACH_TITLE, /* the "-- Attachments" line */ 82 HDR_ATTACH /* where to start printing the attachments */ 83}; 84 85int HeaderPadding[HDR_ATTACH_TITLE] = {0}; 86int MaxHeaderWidth = 0; 87 88#define HDR_XOFFSET MaxHeaderWidth 89#define W (MuttIndexWindow->cols - MaxHeaderWidth) 90 91static const char * const Prompts[] = 92{ 93 /* L10N: Compose menu field. May not want to translate. */ 94 N_("From: "), 95 /* L10N: Compose menu field. May not want to translate. */ 96 N_("To: "), 97 /* L10N: Compose menu field. May not want to translate. */ 98 N_("Cc: "), 99 /* L10N: Compose menu field. May not want to translate. */ 100 N_("Bcc: "), 101 /* L10N: Compose menu field. May not want to translate. */ 102 N_("Subject: "), 103 /* L10N: Compose menu field. May not want to translate. */ 104 N_("Reply-To: "), 105 /* L10N: Compose menu field. May not want to translate. */ 106 N_("Fcc: "), 107#ifdef MIXMASTER 108 /* L10N: "Mix" refers to the MixMaster chain for anonymous email */ 109 N_("Mix: "), 110#endif 111 /* L10N: Compose menu field. Holds "Encrypt", "Sign" related information */ 112 N_("Security: "), 113 /* L10N: 114 * This string is used by the compose menu. 115 * Since it is hidden by default, it does not increase the 116 * indentation of other compose menu fields. However, if possible, 117 * it should not be longer than the other compose menu fields. 118 * 119 * Since it shares the row with "Encrypt with:", it should not be longer 120 * than 15-20 character cells. 121 */ 122 N_("Sign as: "), 123#ifdef USE_AUTOCRYPT 124 /* L10N: 125 The compose menu autocrypt line 126 */ 127 N_("Autocrypt: ") 128#endif 129}; 130 131static const struct mapping_t ComposeHelp[] = { 132 { N_("Send"), OP_COMPOSE_SEND_MESSAGE }, 133 { N_("Abort"), OP_EXIT }, 134 /* L10N: compose menu help line entry */ 135 { N_("To"), OP_COMPOSE_EDIT_TO }, 136 /* L10N: compose menu help line entry */ 137 { N_("CC"), OP_COMPOSE_EDIT_CC }, 138 /* L10N: compose menu help line entry */ 139 { N_("Subj"), OP_COMPOSE_EDIT_SUBJECT }, 140 { N_("Attach file"), OP_COMPOSE_ATTACH_FILE }, 141 { N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION }, 142 { N_("Help"), OP_HELP }, 143 { NULL, 0 } 144}; 145 146#ifdef USE_AUTOCRYPT 147static const char *AutocryptRecUiFlags[] = { 148 /* L10N: Autocrypt recommendation flag: off. 149 * This is displayed when Autocrypt is turned off. */ 150 N_("Off"), 151 /* L10N: Autocrypt recommendation flag: no. 152 * This is displayed when Autocrypt cannot encrypt to the recipients. */ 153 N_("No"), 154 /* L10N: Autocrypt recommendation flag: discouraged. 155 * This is displayed when Autocrypt believes encryption should not be used. 156 * This might occur if one of the recipient Autocrypt Keys has not been 157 * used recently, or if the only key available is a Gossip Header key. */ 158 N_("Discouraged"), 159 /* L10N: Autocrypt recommendation flag: available. 160 * This is displayed when Autocrypt believes encryption is possible, but 161 * leaves enabling it up to the sender. Probably because "prefer encrypt" 162 * is not set in both the sender and recipient keys. */ 163 N_("Available"), 164 /* L10N: Autocrypt recommendation flag: yes. 165 * This is displayed when Autocrypt would normally enable encryption 166 * automatically. */ 167 N_("Yes"), 168}; 169#endif 170 171typedef struct 172{ 173 HEADER *msg; 174 BUFFER *fcc; 175#ifdef USE_AUTOCRYPT 176 autocrypt_rec_t autocrypt_rec; 177 int autocrypt_rec_override; 178#endif 179} compose_redraw_data_t; 180 181static void calc_header_width_padding (int idx, const char *header, int calc_max) 182{ 183 int width; 184 185 HeaderPadding[idx] = mutt_strlen (header); 186 width = mutt_strwidth (header); 187 if (calc_max && MaxHeaderWidth < width) 188 MaxHeaderWidth = width; 189 HeaderPadding[idx] -= width; 190} 191 192 193/* The padding needed for each header is strlen() + max_width - strwidth(). 194 * 195 * calc_header_width_padding sets each entry in HeaderPadding to 196 * strlen - width. Then, afterwards, we go through and add max_width 197 * to each entry. 198 */ 199static void init_header_padding (void) 200{ 201 static short done = 0; 202 int i; 203 204 if (done) 205 return; 206 done = 1; 207 208 for (i = 0; i < HDR_ATTACH_TITLE; i++) 209 { 210 if (i == HDR_CRYPTINFO) 211 continue; 212 calc_header_width_padding (i, _(Prompts[i]), 1); 213 } 214 215 /* Don't include "Sign as: " in the MaxHeaderWidth calculation. It 216 * doesn't show up by default, and so can make the indentation of 217 * the other fields look funny. */ 218 calc_header_width_padding (HDR_CRYPTINFO, _(Prompts[HDR_CRYPTINFO]), 0); 219 220 for (i = 0; i < HDR_ATTACH_TITLE; i++) 221 { 222 HeaderPadding[i] += MaxHeaderWidth; 223 if (HeaderPadding[i] < 0) 224 HeaderPadding[i] = 0; 225 } 226} 227 228static void snd_entry (char *b, size_t blen, MUTTMENU *menu, int num) 229{ 230 ATTACH_CONTEXT *actx = (ATTACH_CONTEXT *)menu->data; 231 232 mutt_FormatString (b, blen, 0, MuttIndexWindow->cols, NONULL (AttachFormat), mutt_attach_fmt, 233 (unsigned long)(actx->idx[actx->v2r[num]]), 234 MUTT_FORMAT_STAT_FILE | MUTT_FORMAT_ARROWCURSOR); 235} 236 237#ifdef USE_AUTOCRYPT 238static void autocrypt_compose_menu (HEADER *msg) 239{ 240 char *prompt, *letters; 241 int choice; 242 243 msg->security |= APPLICATION_PGP; 244 245 /* L10N: 246 The compose menu autocrypt prompt. 247 (e)ncrypt enables encryption via autocrypt. 248 (c)lear sets cleartext. 249 (a)utomatic defers to the recommendation. 250 */ 251 prompt = _("Autocrypt: (e)ncrypt, (c)lear, (a)utomatic? "); 252 253 /* L10N: 254 The letter corresponding to the compose menu autocrypt prompt 255 (e)ncrypt, (c)lear, (a)utomatic 256 */ 257 letters = _("eca"); 258 259 choice = mutt_multi_choice (prompt, letters); 260 switch (choice) 261 { 262 case 1: 263 msg->security |= (AUTOCRYPT | AUTOCRYPT_OVERRIDE); 264 msg->security &= ~(ENCRYPT | SIGN | OPPENCRYPT | INLINE); 265 break; 266 case 2: 267 msg->security &= ~AUTOCRYPT; 268 msg->security |= AUTOCRYPT_OVERRIDE; 269 break; 270 case 3: 271 msg->security &= ~AUTOCRYPT_OVERRIDE; 272 if (option (OPTCRYPTOPPORTUNISTICENCRYPT)) 273 msg->security |= OPPENCRYPT; 274 break; 275 } 276} 277#endif 278 279static void redraw_crypt_lines (compose_redraw_data_t *rd) 280{ 281 HEADER *msg = rd->msg; 282 283 SETCOLOR (MT_COLOR_COMPOSE_HEADER); 284 mutt_window_mvprintw (MuttIndexWindow, HDR_CRYPT, 0, 285 "%*s", HeaderPadding[HDR_CRYPT], _(Prompts[HDR_CRYPT])); 286 NORMAL_COLOR; 287 288 if ((WithCrypto & (APPLICATION_PGP | APPLICATION_SMIME)) == 0) 289 { 290 addstr(_("Not supported")); 291 return; 292 } 293 294 if ((msg->security & (ENCRYPT | SIGN)) == (ENCRYPT | SIGN)) 295 { 296 SETCOLOR (MT_COLOR_COMPOSE_SECURITY_BOTH); 297 addstr (_("Sign, Encrypt")); 298 } 299 else if (msg->security & ENCRYPT) 300 { 301 SETCOLOR (MT_COLOR_COMPOSE_SECURITY_ENCRYPT); 302 addstr (_("Encrypt")); 303 } 304 else if (msg->security & SIGN) 305 { 306 SETCOLOR (MT_COLOR_COMPOSE_SECURITY_SIGN); 307 addstr (_("Sign")); 308 } 309 else 310 { 311 SETCOLOR (MT_COLOR_COMPOSE_SECURITY_NONE); 312 addstr (_("None")); 313 } 314 NORMAL_COLOR; 315 316 if ((msg->security & (ENCRYPT | SIGN))) 317 { 318 if ((WithCrypto & APPLICATION_PGP) && (msg->security & APPLICATION_PGP)) 319 { 320 if ((msg->security & INLINE)) 321 addstr (_(" (inline PGP)")); 322 else 323 addstr (_(" (PGP/MIME)")); 324 } 325 else if ((WithCrypto & APPLICATION_SMIME) && 326 (msg->security & APPLICATION_SMIME)) 327 addstr (_(" (S/MIME)")); 328 } 329 330 if (option (OPTCRYPTOPPORTUNISTICENCRYPT) && (msg->security & OPPENCRYPT)) 331 addstr (_(" (OppEnc mode)")); 332 333 mutt_window_clrtoeol (MuttIndexWindow); 334 mutt_window_move (MuttIndexWindow, HDR_CRYPTINFO, 0); 335 mutt_window_clrtoeol (MuttIndexWindow); 336 337 if ((WithCrypto & APPLICATION_PGP) 338 && (msg->security & APPLICATION_PGP) && (msg->security & SIGN)) 339 { 340 SETCOLOR (MT_COLOR_COMPOSE_HEADER); 341 printw ("%*s", HeaderPadding[HDR_CRYPTINFO], _(Prompts[HDR_CRYPTINFO])); 342 NORMAL_COLOR; 343 printw ("%s", PgpSignAs ? PgpSignAs : _("<default>")); 344 } 345 346 if ((WithCrypto & APPLICATION_SMIME) 347 && (msg->security & APPLICATION_SMIME) && (msg->security & SIGN)) 348 { 349 SETCOLOR (MT_COLOR_COMPOSE_HEADER); 350 printw ("%*s", HeaderPadding[HDR_CRYPTINFO], _(Prompts[HDR_CRYPTINFO])); 351 NORMAL_COLOR; 352 printw ("%s", SmimeSignAs ? SmimeSignAs : _("<default>")); 353 } 354 355 if ((WithCrypto & APPLICATION_SMIME) 356 && (msg->security & APPLICATION_SMIME) 357 && (msg->security & ENCRYPT) 358 && SmimeCryptAlg) 359 { 360 SETCOLOR (MT_COLOR_COMPOSE_HEADER); 361 mutt_window_mvprintw (MuttIndexWindow, HDR_CRYPTINFO, 40, "%s", _("Encrypt with: ")); 362 NORMAL_COLOR; 363 printw ("%s", NONULL(SmimeCryptAlg)); 364 } 365 366#ifdef USE_AUTOCRYPT 367 mutt_window_move (MuttIndexWindow, HDR_AUTOCRYPT, 0); 368 mutt_window_clrtoeol (MuttIndexWindow); 369 if (option (OPTAUTOCRYPT)) 370 { 371 SETCOLOR (MT_COLOR_COMPOSE_HEADER); 372 printw ("%*s", HeaderPadding[HDR_AUTOCRYPT], _(Prompts[HDR_AUTOCRYPT])); 373 NORMAL_COLOR; 374 if (msg->security & AUTOCRYPT) 375 { 376 SETCOLOR (MT_COLOR_COMPOSE_SECURITY_ENCRYPT); 377 addstr (_("Encrypt")); 378 } 379 else 380 { 381 SETCOLOR (MT_COLOR_COMPOSE_SECURITY_NONE); 382 addstr (_("Off")); 383 } 384 385 SETCOLOR (MT_COLOR_COMPOSE_HEADER); 386 mutt_window_mvprintw (MuttIndexWindow, HDR_AUTOCRYPT, 40, "%s", 387 /* L10N: 388 The autocrypt compose menu Recommendation field. 389 Displays the output of the recommendation engine 390 (Off, No, Discouraged, Available, Yes) 391 */ 392 _("Recommendation: ")); 393 NORMAL_COLOR; 394 printw ("%s", _(AutocryptRecUiFlags[rd->autocrypt_rec])); 395 } 396#endif 397} 398 399static void update_crypt_info (compose_redraw_data_t *rd) 400{ 401 HEADER *msg = rd->msg; 402 403 if (option (OPTCRYPTOPPORTUNISTICENCRYPT)) 404 crypt_opportunistic_encrypt (msg); 405 406#ifdef USE_AUTOCRYPT 407 if (option (OPTAUTOCRYPT)) 408 { 409 rd->autocrypt_rec = mutt_autocrypt_ui_recommendation (msg, NULL); 410 411 /* Anything that enables ENCRYPT or SIGN, or turns on SMIME 412 * overrides autocrypt, be it oppenc or the user having turned on 413 * those flags manually. */ 414 if (msg->security & (ENCRYPT | SIGN | APPLICATION_SMIME)) 415 msg->security &= ~(AUTOCRYPT | AUTOCRYPT_OVERRIDE); 416 else 417 { 418 if (!(msg->security & AUTOCRYPT_OVERRIDE)) 419 { 420 if (rd->autocrypt_rec == AUTOCRYPT_REC_YES) 421 { 422 msg->security |= (AUTOCRYPT | APPLICATION_PGP); 423 msg->security &= ~(INLINE | APPLICATION_SMIME); 424 } 425 else 426 msg->security &= ~AUTOCRYPT; 427 } 428 } 429 } 430#endif 431 432 redraw_crypt_lines (rd); 433} 434 435 436#ifdef MIXMASTER 437 438static void redraw_mix_line (LIST *chain) 439{ 440 int c; 441 char *t; 442 443 SETCOLOR (MT_COLOR_COMPOSE_HEADER); 444 mutt_window_mvprintw (MuttIndexWindow, HDR_MIX, 0, 445 "%*s", HeaderPadding[HDR_MIX], _(Prompts[HDR_MIX])); 446 NORMAL_COLOR; 447 448 if (!chain) 449 { 450 addstr ("<no chain defined>"); 451 mutt_window_clrtoeol (MuttIndexWindow); 452 return; 453 } 454 455 for (c = 12; chain; chain = chain->next) 456 { 457 t = chain->data; 458 if (t && t[0] == '0' && t[1] == '\0') 459 t = "<random>"; 460 461 if (c + mutt_strlen (t) + 2 >= MuttIndexWindow->cols) 462 break; 463 464 addstr (NONULL(t)); 465 if (chain->next) 466 addstr (", "); 467 468 c += mutt_strlen (t) + 2; 469 } 470} 471#endif /* MIXMASTER */ 472 473static int 474check_attachments(ATTACH_CONTEXT *actx) 475{ 476 int i, r, rc = -1; 477 struct stat st; 478 BUFFER *pretty = NULL, *msg = NULL; 479 480 for (i = 0; i < actx->idxlen; i++) 481 { 482 if (stat(actx->idx[i]->content->filename, &st) != 0) 483 { 484 if (!pretty) 485 pretty = mutt_buffer_pool_get (); 486 mutt_buffer_strcpy (pretty, actx->idx[i]->content->filename); 487 mutt_buffer_pretty_mailbox (pretty); 488 /* L10N: 489 This message is displayed in the compose menu when an attachment 490 doesn't stat. %d is the attachment number and %s is the 491 attachment filename. 492 The filename is located last to avoid a long path hiding the 493 error message. 494 */ 495 mutt_error (_("Attachment #%d no longer exists: %s"), 496 i+1, mutt_b2s (pretty)); 497 goto cleanup; 498 } 499 500 if (actx->idx[i]->content->stamp < st.st_mtime) 501 { 502 if (!pretty) 503 pretty = mutt_buffer_pool_get (); 504 mutt_buffer_strcpy (pretty, actx->idx[i]->content->filename); 505 mutt_buffer_pretty_mailbox (pretty); 506 507 if (!msg) 508 msg = mutt_buffer_pool_get (); 509 /* L10N: 510 This message is displayed in the compose menu when an attachment 511 is modified behind the scenes. %d is the attachment number 512 and %s is the attachment filename. 513 The filename is located last to avoid a long path hiding the 514 prompt question. 515 */ 516 mutt_buffer_printf (msg, _("Attachment #%d modified. Update encoding for %s?"), 517 i+1, mutt_b2s (pretty)); 518 519 if ((r = mutt_yesorno (mutt_b2s (msg), MUTT_YES)) == MUTT_YES) 520 mutt_update_encoding (actx->idx[i]->content); 521 else if (r == -1) 522 goto cleanup; 523 } 524 } 525 526 rc = 0; 527 528cleanup: 529 mutt_buffer_pool_release (&pretty); 530 mutt_buffer_pool_release (&msg); 531 return rc; 532} 533 534static void draw_envelope_addr (int line, ADDRESS *addr) 535{ 536 char buf[LONG_STRING]; 537 538 buf[0] = 0; 539 rfc822_write_address (buf, sizeof (buf), addr, 1); 540 SETCOLOR (MT_COLOR_COMPOSE_HEADER); 541 mutt_window_mvprintw (MuttIndexWindow, line, 0, 542 "%*s", HeaderPadding[line], _(Prompts[line])); 543 NORMAL_COLOR; 544 mutt_paddstr (W, buf); 545} 546 547static void draw_envelope (compose_redraw_data_t *rd) 548{ 549 HEADER *msg = rd->msg; 550 const char *fcc = mutt_b2s (rd->fcc); 551 552 draw_envelope_addr (HDR_FROM, msg->env->from); 553 draw_envelope_addr (HDR_TO, msg->env->to); 554 draw_envelope_addr (HDR_CC, msg->env->cc); 555 draw_envelope_addr (HDR_BCC, msg->env->bcc); 556 557 SETCOLOR (MT_COLOR_COMPOSE_HEADER); 558 mutt_window_mvprintw (MuttIndexWindow, HDR_SUBJECT, 0, 559 "%*s", HeaderPadding[HDR_SUBJECT], _(Prompts[HDR_SUBJECT])); 560 NORMAL_COLOR; 561 mutt_paddstr (W, NONULL (msg->env->subject)); 562 563 draw_envelope_addr (HDR_REPLYTO, msg->env->reply_to); 564 565 SETCOLOR (MT_COLOR_COMPOSE_HEADER); 566 mutt_window_mvprintw (MuttIndexWindow, HDR_FCC, 0, 567 "%*s", HeaderPadding[HDR_FCC], _(Prompts[HDR_FCC])); 568 NORMAL_COLOR; 569 mutt_paddstr (W, fcc); 570 571 if (WithCrypto) 572 redraw_crypt_lines (rd); 573 574#ifdef MIXMASTER 575 redraw_mix_line (msg->chain); 576#endif 577 578 SETCOLOR (MT_COLOR_STATUS); 579 mutt_window_mvaddstr (MuttIndexWindow, HDR_ATTACH_TITLE, 0, _("-- Attachments")); 580 mutt_window_clrtoeol (MuttIndexWindow); 581 582 NORMAL_COLOR; 583} 584 585static void edit_address_list (int line, ADDRESS **addr) 586{ 587 char buf[HUGE_STRING] = ""; /* needs to be large for alias expansion */ 588 char *err = NULL; 589 590 mutt_addrlist_to_local (*addr); 591 rfc822_write_address (buf, sizeof (buf), *addr, 0); 592 if (mutt_get_field (_(Prompts[line]), buf, sizeof (buf), MUTT_ALIAS) == 0) 593 { 594 rfc822_free_address (addr); 595 *addr = mutt_parse_adrlist (*addr, buf); 596 *addr = mutt_expand_aliases (*addr); 597 } 598 599 if (mutt_addrlist_to_intl (*addr, &err) != 0) 600 { 601 mutt_error (_("Warning: '%s' is a bad IDN."), err); 602 mutt_refresh(); 603 FREE (&err); 604 } 605 606 /* redraw the expanded list so the user can see the result */ 607 buf[0] = 0; 608 rfc822_write_address (buf, sizeof (buf), *addr, 1); 609 mutt_window_move (MuttIndexWindow, line, HDR_XOFFSET); 610 mutt_paddstr (W, buf); 611} 612 613static int delete_attachment (ATTACH_CONTEXT *actx, int x) 614{ 615 ATTACHPTR **idx = actx->idx; 616 int rindex = actx->v2r[x]; 617 int y; 618 619 if (rindex == 0 && actx->idxlen == 1) 620 { 621 mutt_error _("You may not delete the only attachment."); 622 idx[rindex]->content->tagged = 0; 623 return (-1); 624 } 625 626 for (y = 0; y < actx->idxlen; y++) 627 { 628 if (idx[y]->content->next == idx[rindex]->content) 629 { 630 idx[y]->content->next = idx[rindex]->content->next; 631 break; 632 } 633 } 634 635 idx[rindex]->content->next = NULL; 636 /* mutt_make_message_attach() creates body->parts, shared by 637 * body->hdr->content. If we NULL out that, it creates a memory 638 * leak because mutt_free_body() frees body->parts, not 639 * body->hdr->content. 640 * 641 * Other ci_send_message() message constructors are careful to free 642 * any body->parts, removing depth: 643 * - mutt_prepare_template() used by postponed, resent, and draft files 644 * - mutt_copy_body() used by the recvattach menu and $forward_attachments. 645 * 646 * I believe it is safe to completely remove the "content->parts = 647 * NULL" statement. But for safety, am doing so only for the case 648 * it must be avoided: message attachments. 649 */ 650 if (!idx[rindex]->content->hdr) 651 idx[rindex]->content->parts = NULL; 652 mutt_free_body (&(idx[rindex]->content)); 653 FREE (&idx[rindex]->tree); 654 FREE (&idx[rindex]); 655 for (; rindex < actx->idxlen - 1; rindex++) 656 idx[rindex] = idx[rindex+1]; 657 idx[actx->idxlen - 1] = NULL; 658 actx->idxlen--; 659 660 return (0); 661} 662 663static void mutt_gen_compose_attach_list (ATTACH_CONTEXT *actx, 664 BODY *m, 665 int parent_type, 666 int level) 667{ 668 ATTACHPTR *new; 669 670 for (; m; m = m->next) 671 { 672 if (m->type == TYPEMULTIPART && m->parts 673 && (!(WithCrypto & APPLICATION_PGP) || !mutt_is_multipart_encrypted(m)) 674 ) 675 { 676 mutt_gen_compose_attach_list (actx, m->parts, m->type, level); 677 } 678 else 679 { 680 new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); 681 mutt_actx_add_attach (actx, new); 682 new->content = m; 683 m->aptr = new; 684 new->parent_type = parent_type; 685 new->level = level; 686 687 /* We don't support multipart messages in the compose menu yet */ 688 } 689 } 690} 691 692static void mutt_update_compose_menu (ATTACH_CONTEXT *actx, MUTTMENU *menu, int init) 693{ 694 if (init) 695 { 696 mutt_gen_compose_attach_list (actx, actx->hdr->content, -1, 0); 697 mutt_attach_init (actx); 698 menu->data = actx; 699 } 700 701 mutt_update_tree (actx); 702 703 menu->max = actx->vcount; 704 if (menu->max) 705 { 706 if (menu->current >= menu->max) 707 menu->current = menu->max - 1; 708 } 709 else 710 menu->current = 0; 711 712 menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; 713} 714 715static void update_idx (MUTTMENU *menu, ATTACH_CONTEXT *actx, ATTACHPTR *new) 716{ 717 new->level = (actx->idxlen > 0) ? actx->idx[actx->idxlen-1]->level : 0; 718 if (actx->idxlen) 719 actx->idx[actx->idxlen - 1]->content->next = new->content; 720 new->content->aptr = new; 721 mutt_actx_add_attach (actx, new); 722 mutt_update_compose_menu (actx, menu, 0); 723 menu->current = actx->vcount - 1; 724} 725 726 727/* 728 * cum_attachs_size: Cumulative Attachments Size 729 * 730 * Returns the total number of bytes used by the attachments in the 731 * attachment list _after_ content-transfer-encodings have been 732 * applied. 733 * 734 */ 735 736static unsigned long cum_attachs_size (MUTTMENU *menu) 737{ 738 size_t s; 739 unsigned short i; 740 ATTACH_CONTEXT *actx = menu->data; 741 ATTACHPTR **idx = actx->idx; 742 CONTENT *info; 743 BODY *b; 744 745 for (i = 0, s = 0; i < actx->idxlen; i++) 746 { 747 b = idx[i]->content; 748 749 if (!b->content) 750 b->content = mutt_get_content_info (b->filename, b); 751 752 if ((info = b->content)) 753 { 754 switch (b->encoding) 755 { 756 case ENCQUOTEDPRINTABLE: 757 s += 3 * (info->lobin + info->hibin) + info->ascii + info->crlf; 758 break; 759 case ENCBASE64: 760 s += (4 * (info->lobin + info->hibin + info->ascii + info->crlf)) / 3; 761 break; 762 default: 763 s += info->lobin + info->hibin + info->ascii + info->crlf; 764 break; 765 } 766 } 767 } 768 769 return s; 770} 771 772/* prototype for use below */ 773static void compose_status_line (char *buf, size_t buflen, size_t col, int cols, MUTTMENU *menu, 774 const char *p); 775 776/* 777 * compose_format_str() 778 * 779 * %a = total number of attachments 780 * %h = hostname [option] 781 * %l = approx. length of current message (in bytes) 782 * %v = Mutt version 783 * 784 * This function is similar to status_format_str(). Look at that function for 785 * help when modifying this function. 786 */ 787 788static const char * 789compose_format_str (char *buf, size_t buflen, size_t col, int cols, char op, const char *src, 790 const char *prefix, const char *ifstring, 791 const char *elsestring, 792 unsigned long data, format_flag flags) 793{ 794 char fmt[SHORT_STRING], tmp[SHORT_STRING]; 795 int optional = (flags & MUTT_FORMAT_OPTIONAL); 796 MUTTMENU *menu = (MUTTMENU *) data; 797 798 *buf = 0; 799 switch (op) 800 { 801 case 'a': /* total number of attachments */ 802 snprintf (fmt, sizeof (fmt), "%%%sd", prefix); 803 snprintf (buf, buflen, fmt, menu->max); 804 break; 805 806 case 'h': /* hostname */ 807 snprintf (fmt, sizeof (fmt), "%%%ss", prefix); 808 snprintf (buf, buflen, fmt, NONULL(Hostname)); 809 break; 810 811 case 'l': /* approx length of current message in bytes */ 812 snprintf (fmt, sizeof (fmt), "%%%ss", prefix); 813 mutt_pretty_size (tmp, sizeof (tmp), menu ? cum_attachs_size(menu) : 0); 814 snprintf (buf, buflen, fmt, tmp); 815 break; 816 817 case 'v': 818 snprintf (fmt, sizeof (fmt), "Mutt %%s"); 819 snprintf (buf, buflen, fmt, MUTT_VERSION); 820 break; 821 822 case 0: 823 *buf = 0; 824 return (src); 825 826 default: 827 snprintf (buf, buflen, "%%%s%c", prefix, op); 828 break; 829 } 830 831 if (optional) 832 compose_status_line (buf, buflen, col, cols, menu, ifstring); 833 else if (flags & MUTT_FORMAT_OPTIONAL) 834 compose_status_line (buf, buflen, col, cols, menu, elsestring); 835 836 return (src); 837} 838 839static void compose_status_line (char *buf, size_t buflen, size_t col, int cols, 840 MUTTMENU *menu, const char *p) 841{ 842 mutt_FormatString (buf, buflen, col, cols, p, compose_format_str, 843 (unsigned long) menu, 0); 844} 845 846static void compose_menu_redraw (MUTTMENU *menu) 847{ 848 char buf[LONG_STRING]; 849 compose_redraw_data_t *rd = menu->redraw_data; 850 851 if (!rd) 852 return; 853 854 if (menu->redraw & REDRAW_FULL) 855 { 856 menu_redraw_full (menu); 857 858 draw_envelope (rd); 859 menu->offset = HDR_ATTACH; 860 menu->pagelen = MuttIndexWindow->rows - HDR_ATTACH; 861 } 862 863 menu_check_recenter (menu); 864 865 if (menu->redraw & REDRAW_STATUS) 866 { 867 compose_status_line (buf, sizeof (buf), 0, MuttStatusWindow->cols, menu, NONULL(ComposeFormat)); 868 mutt_window_move (MuttStatusWindow, 0, 0); 869 SETCOLOR (MT_COLOR_STATUS); 870 mutt_paddstr (MuttStatusWindow->cols, buf); 871 NORMAL_COLOR; 872 menu->redraw &= ~REDRAW_STATUS; 873 } 874 875#ifdef USE_SIDEBAR 876 if (menu->redraw & REDRAW_SIDEBAR) 877 menu_redraw_sidebar (menu); 878#endif 879 880 if (menu->redraw & REDRAW_INDEX) 881 menu_redraw_index (menu); 882 else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH)) 883 menu_redraw_motion (menu); 884 else if (menu->redraw == REDRAW_CURRENT) 885 menu_redraw_current (menu); 886} 887 888 889/* return values: 890 * 891 * 1 message should be postponed 892 * 0 normal exit 893 * -1 abort message 894 */ 895int mutt_compose_menu (HEADER *msg, /* structure for new message */ 896 BUFFER *fcc, /* where to save a copy of the message */ 897 HEADER *cur, /* current message */ 898 int flags) 899{ 900 char helpstr[LONG_STRING]; 901 char buf[LONG_STRING]; 902 BUFFER *fname = NULL; 903 MUTTMENU *menu; 904 ATTACH_CONTEXT *actx; 905 ATTACHPTR *new; 906 int i, close = 0; 907 int r = -1; /* return value */ 908 int op = 0; 909 int loop = 1; 910 int fccSet = 0; /* has the user edited the Fcc: field ? */ 911 CONTEXT *ctx = NULL, *this = NULL; 912 /* Sort, SortAux could be changed in mutt_index_menu() */ 913 int oldSort, oldSortAux; 914 struct stat st; 915 compose_redraw_data_t rd = {0}; 916 917 init_header_padding (); 918 919 rd.msg = msg; 920 rd.fcc = fcc; 921 922 menu = mutt_new_menu (MENU_COMPOSE); 923 menu->offset = HDR_ATTACH; 924 menu->make_entry = snd_entry; 925 menu->tag = mutt_tag_attach; 926 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeHelp); 927 menu->custom_menu_redraw = compose_menu_redraw; 928 menu->redraw_data = &rd; 929 mutt_push_current_menu (menu); 930 931 actx = safe_calloc (sizeof(ATTACH_CONTEXT), 1); 932 actx->hdr = msg; 933 mutt_update_compose_menu (actx, menu, 1); 934 935 update_crypt_info (&rd); 936 937 /* Since this is rather long lived, we don't use the pool */ 938 fname = mutt_buffer_new (); 939 mutt_buffer_increase_size (fname, LONG_STRING); 940 941 while (loop) 942 { 943 switch (op = mutt_menuLoop (menu)) 944 { 945 case OP_COMPOSE_EDIT_FROM: 946 edit_address_list (HDR_FROM, &msg->env->from); 947 update_crypt_info (&rd); 948 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 949 break; 950 case OP_COMPOSE_EDIT_TO: 951 edit_address_list (HDR_TO, &msg->env->to); 952 update_crypt_info (&rd); 953 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 954 break; 955 case OP_COMPOSE_EDIT_BCC: 956 edit_address_list (HDR_BCC, &msg->env->bcc); 957 update_crypt_info (&rd); 958 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 959 break; 960 case OP_COMPOSE_EDIT_CC: 961 edit_address_list (HDR_CC, &msg->env->cc); 962 update_crypt_info (&rd); 963 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 964 break; 965 case OP_COMPOSE_EDIT_SUBJECT: 966 if (msg->env->subject) 967 strfcpy (buf, msg->env->subject, sizeof (buf)); 968 else 969 buf[0] = 0; 970 if (mutt_get_field (_("Subject: "), buf, sizeof (buf), 0) == 0) 971 { 972 mutt_str_replace (&msg->env->subject, buf); 973 mutt_window_move (MuttIndexWindow, HDR_SUBJECT, HDR_XOFFSET); 974 if (msg->env->subject) 975 mutt_paddstr (W, msg->env->subject); 976 else 977 mutt_window_clrtoeol(MuttIndexWindow); 978 } 979 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 980 break; 981 case OP_COMPOSE_EDIT_REPLY_TO: 982 edit_address_list (HDR_REPLYTO, &msg->env->reply_to); 983 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 984 break; 985 case OP_COMPOSE_EDIT_FCC: 986 mutt_buffer_strcpy (fname, mutt_b2s (fcc)); 987 if (mutt_buffer_get_field (_("Fcc: "), fname, MUTT_FILE | MUTT_CLEAR) == 0) 988 { 989 mutt_buffer_strcpy (fcc, mutt_b2s (fname)); 990 mutt_buffer_pretty_mailbox (fcc); 991 mutt_window_move (MuttIndexWindow, HDR_FCC, HDR_XOFFSET); 992 mutt_paddstr (W, mutt_b2s (fcc)); 993 fccSet = 1; 994 } 995 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 996 break; 997 case OP_COMPOSE_EDIT_MESSAGE: 998 if (Editor && (mutt_strcmp ("builtin", Editor) != 0) && !option (OPTEDITHDRS)) 999 { 1000 mutt_rfc3676_space_unstuff (msg); 1001 mutt_edit_file (Editor, msg->content->filename); 1002 mutt_rfc3676_space_stuff (msg); 1003 mutt_update_encoding (msg->content); 1004 menu->redraw = REDRAW_FULL; 1005 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1006 break; 1007 } 1008 /* fall through */ 1009 case OP_COMPOSE_EDIT_HEADERS: 1010 mutt_rfc3676_space_unstuff (msg); 1011 1012 if (mutt_strcmp ("builtin", Editor) != 0 && 1013 (op == OP_COMPOSE_EDIT_HEADERS || 1014 (op == OP_COMPOSE_EDIT_MESSAGE && option (OPTEDITHDRS)))) 1015 { 1016 char *tag = NULL, *err = NULL; 1017 mutt_env_to_local (msg->env); 1018 mutt_edit_headers (NONULL (Editor), msg->content->filename, msg, 1019 fcc); 1020 if (mutt_env_to_intl (msg->env, &tag, &err)) 1021 { 1022 mutt_error (_("Bad IDN in \"%s\": '%s'"), tag, err); 1023 FREE (&err); 1024 } 1025 update_crypt_info (&rd); 1026 } 1027 else 1028 { 1029 /* this is grouped with OP_COMPOSE_EDIT_HEADERS because the 1030 attachment list could change if the user invokes ~v to edit 1031 the message with headers, in which we need to execute the 1032 code below to regenerate the index array */ 1033 mutt_builtin_editor (msg->content->filename, msg, cur); 1034 } 1035 1036 mutt_rfc3676_space_stuff (msg); 1037 mutt_update_encoding (msg->content); 1038 1039 /* attachments may have been added */ 1040 if (actx->idxlen && actx->idx[actx->idxlen - 1]->content->next) 1041 { 1042 mutt_actx_free_entries (actx); 1043 mutt_update_compose_menu (actx, menu, 1); 1044 } 1045 1046 menu->redraw = REDRAW_FULL; 1047 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1048 break; 1049 1050 1051 1052 case OP_COMPOSE_ATTACH_KEY: 1053 if (!(WithCrypto & APPLICATION_PGP)) 1054 break; 1055 1056 new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); 1057 if ((new->content = crypt_pgp_make_key_attachment()) != NULL) 1058 { 1059 update_idx (menu, actx, new); 1060 menu->redraw |= REDRAW_INDEX; 1061 } 1062 else 1063 FREE (&new); 1064 1065 menu->redraw |= REDRAW_STATUS; 1066 1067 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1068 break; 1069 1070 1071 case OP_COMPOSE_ATTACH_FILE: 1072 { 1073 char *prompt, **files; 1074 int error, numfiles; 1075 1076 mutt_buffer_clear (fname); 1077 prompt = _("Attach file"); 1078 numfiles = 0; 1079 files = NULL; 1080 1081 if ((_mutt_buffer_enter_fname (prompt, fname, 0, 1, &files, &numfiles) == -1) || 1082 !mutt_buffer_len (fname)) 1083 break; 1084 1085 error = 0; 1086 if (numfiles > 1) 1087 mutt_message _("Attaching selected files..."); 1088 for (i = 0; i < numfiles; i++) 1089 { 1090 char *att = files[i]; 1091 new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); 1092 new->unowned = 1; 1093 new->content = mutt_make_file_attach (att); 1094 if (new->content != NULL) 1095 update_idx (menu, actx, new); 1096 else 1097 { 1098 error = 1; 1099 mutt_error (_("Unable to attach %s!"), att); 1100 FREE (&new); 1101 } 1102 FREE (&files[i]); 1103 } 1104 1105 FREE (&files); 1106 if (!error) mutt_clear_error (); 1107 1108 menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; 1109 } 1110 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1111 break; 1112 1113 case OP_COMPOSE_ATTACH_MESSAGE: 1114 { 1115 char *prompt; 1116 HEADER *h; 1117 1118 mutt_buffer_clear (fname); 1119 prompt = _("Open mailbox to attach message from"); 1120 1121 if (Context) 1122 { 1123 mutt_buffer_strcpy (fname, NONULL (Context->path)); 1124 mutt_buffer_pretty_mailbox (fname); 1125 } 1126 1127 if ((mutt_buffer_enter_fname (prompt, fname, 1) == -1) || 1128 !mutt_buffer_len (fname)) 1129 break; 1130 1131 mutt_buffer_expand_path (fname); 1132#ifdef USE_IMAP 1133 if (!mx_is_imap (mutt_b2s (fname))) 1134#endif 1135#ifdef USE_POP 1136 if (!mx_is_pop (mutt_b2s (fname))) 1137#endif 1138 /* check to make sure the file exists and is readable */ 1139 if (access (mutt_b2s (fname), R_OK) == -1) 1140 { 1141 mutt_perror (mutt_b2s (fname)); 1142 break; 1143 } 1144 1145 menu->redraw = REDRAW_FULL; 1146 1147 ctx = mx_open_mailbox (mutt_b2s (fname), MUTT_READONLY, NULL); 1148 if (ctx == NULL) 1149 { 1150 mutt_error (_("Unable to open mailbox %s"), mutt_b2s (fname)); 1151 break; 1152 } 1153 1154 if (!ctx->msgcount) 1155 { 1156 mx_close_mailbox (ctx, NULL); 1157 FREE (&ctx); 1158 mutt_error _("No messages in that folder."); 1159 break; 1160 } 1161 1162 this = Context; /* remember current folder and sort methods*/ 1163 oldSort = Sort; oldSortAux = SortAux; 1164 1165 Context = ctx; 1166 set_option(OPTATTACHMSG); 1167 mutt_message _("Tag the messages you want to attach!"); 1168 close = mutt_index_menu (); 1169 unset_option(OPTATTACHMSG); 1170 1171 if (!Context) 1172 { 1173 /* go back to the folder we started from */ 1174 Context = this; 1175 /* Restore old $sort and $sort_aux */ 1176 Sort = oldSort; 1177 SortAux = oldSortAux; 1178 menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; 1179 break; 1180 } 1181 1182 for (i = 0; i < Context->msgcount; i++) 1183 { 1184 h = Context->hdrs[i]; 1185 if (h->tagged) 1186 { 1187 new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); 1188 new->content = mutt_make_message_attach (Context, h, 1); 1189 if (new->content != NULL) 1190 update_idx (menu, actx, new); 1191 else 1192 { 1193 mutt_error _("Unable to attach!"); 1194 FREE (&new); 1195 } 1196 } 1197 } 1198 menu->redraw |= REDRAW_FULL; 1199 1200 if (close == OP_QUIT) 1201 mx_close_mailbox (Context, NULL); 1202 else 1203 mx_fastclose_mailbox (Context); 1204 FREE (&Context); 1205 1206 /* go back to the folder we started from */ 1207 Context = this; 1208 /* Restore old $sort and $sort_aux */ 1209 Sort = oldSort; 1210 SortAux = oldSortAux; 1211 } 1212 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1213 break; 1214 1215 case OP_DELETE: 1216 CHECK_COUNT; 1217 if (CURATTACH->unowned) 1218 CURATTACH->content->unlink = 0; 1219 if (delete_attachment (actx, menu->current) == -1) 1220 break; 1221 mutt_update_compose_menu (actx, menu, 0); 1222 if (menu->current == 0) 1223 msg->content = actx->idx[0]->content; 1224 1225 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1226 break; 1227 1228 case OP_COMPOSE_TOGGLE_RECODE: 1229 { 1230 CHECK_COUNT; 1231 if (!mutt_is_text_part (CURATTACH->content)) 1232 { 1233 mutt_error (_("Recoding only affects text attachments.")); 1234 break; 1235 } 1236 CURATTACH->content->noconv = !CURATTACH->content->noconv; 1237 if (CURATTACH->content->noconv) 1238 mutt_message (_("The current attachment won't be converted.")); 1239 else 1240 mutt_message (_("The current attachment will be converted.")); 1241 menu->redraw = REDRAW_CURRENT; 1242 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1243 break; 1244 } 1245 1246 case OP_COMPOSE_EDIT_DESCRIPTION: 1247 CHECK_COUNT; 1248 strfcpy (buf, 1249 CURATTACH->content->description ? 1250 CURATTACH->content->description : "", 1251 sizeof (buf)); 1252 /* header names should not be translated */ 1253 if (mutt_get_field ("Description: ", buf, sizeof (buf), 0) == 0) 1254 { 1255 mutt_str_replace (&CURATTACH->content->description, buf); 1256 menu->redraw = REDRAW_CURRENT; 1257 } 1258 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1259 break; 1260 1261 case OP_COMPOSE_UPDATE_ENCODING: 1262 CHECK_COUNT; 1263 if (menu->tagprefix) 1264 { 1265 BODY *top; 1266 for (top = msg->content; top; top = top->next) 1267 { 1268 if (top->tagged) 1269 mutt_update_encoding (top); 1270 } 1271 menu->redraw = REDRAW_FULL; 1272 } 1273 else 1274 { 1275 mutt_update_encoding(CURATTACH->content); 1276 menu->redraw = REDRAW_CURRENT | REDRAW_STATUS; 1277 } 1278 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1279 break; 1280 1281 case OP_COMPOSE_TOGGLE_DISPOSITION: 1282 /* toggle the content-disposition between inline/attachment */ 1283 CURATTACH->content->disposition = (CURATTACH->content->disposition == DISPINLINE) ? DISPATTACH : DISPINLINE; 1284 menu->redraw = REDRAW_CURRENT; 1285 break; 1286 1287 case OP_EDIT_TYPE: 1288 CHECK_COUNT; 1289 { 1290 mutt_edit_content_type (NULL, CURATTACH->content, NULL); 1291 1292 /* this may have been a change to text/something */ 1293 mutt_update_encoding (CURATTACH->content); 1294 1295 menu->redraw = REDRAW_CURRENT; 1296 } 1297 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1298 break; 1299 1300 case OP_COMPOSE_EDIT_ENCODING: 1301 CHECK_COUNT; 1302 strfcpy (buf, ENCODING (CURATTACH->content->encoding), 1303 sizeof (buf)); 1304 if (mutt_get_field ("Content-Transfer-Encoding: ", buf, 1305 sizeof (buf), 0) == 0 && buf[0]) 1306 { 1307 if ((i = mutt_check_encoding (buf)) != ENCOTHER && i != ENCUUENCODED) 1308 { 1309 CURATTACH->content->encoding = i; 1310 menu->redraw = REDRAW_CURRENT | REDRAW_STATUS; 1311 mutt_clear_error(); 1312 } 1313 else 1314 mutt_error _("Invalid encoding."); 1315 } 1316 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1317 break; 1318 1319 case OP_COMPOSE_SEND_MESSAGE: 1320 1321 /* Note: We don't invoke send2-hook here, since we want to leave 1322 * users an opportunity to change settings from the ":" prompt. 1323 */ 1324 1325 if (check_attachments(actx) != 0) 1326 { 1327 menu->redraw = REDRAW_FULL; 1328 break; 1329 } 1330 1331 1332#ifdef MIXMASTER 1333 if (msg->chain && mix_check_message (msg) != 0) 1334 break; 1335#endif 1336 1337 if (!fccSet && mutt_buffer_len (fcc)) 1338 { 1339 if ((i = query_quadoption (OPT_COPY, 1340 _("Save a copy of this message?"))) == -1) 1341 break; 1342 else if (i == MUTT_NO) 1343 mutt_buffer_clear (fcc); 1344 } 1345 1346 loop = 0; 1347 r = 0; 1348 break; 1349 1350 case OP_COMPOSE_EDIT_FILE: 1351 CHECK_COUNT; 1352 mutt_edit_file (NONULL(Editor), CURATTACH->content->filename); 1353 mutt_update_encoding (CURATTACH->content); 1354 menu->redraw = REDRAW_CURRENT | REDRAW_STATUS; 1355 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1356 break; 1357 1358 case OP_COMPOSE_TOGGLE_UNLINK: 1359 CHECK_COUNT; 1360 CURATTACH->content->unlink = !CURATTACH->content->unlink; 1361 menu->redraw = REDRAW_INDEX; 1362 /* No send2hook since this doesn't change the message. */ 1363 break; 1364 1365 case OP_COMPOSE_GET_ATTACHMENT: 1366 CHECK_COUNT; 1367 if (menu->tagprefix) 1368 { 1369 BODY *top; 1370 for (top = msg->content; top; top = top->next) 1371 { 1372 if (top->tagged) 1373 mutt_get_tmp_attachment(top); 1374 } 1375 menu->redraw = REDRAW_FULL; 1376 } 1377 else if (mutt_get_tmp_attachment(CURATTACH->content) == 0) 1378 menu->redraw = REDRAW_CURRENT; 1379 1380 /* No send2hook since this doesn't change the message. */ 1381 break; 1382 1383 case OP_COMPOSE_RENAME_ATTACHMENT: 1384 { 1385 char *src; 1386 int ret; 1387 1388 CHECK_COUNT; 1389 if (CURATTACH->content->d_filename) 1390 src = CURATTACH->content->d_filename; 1391 else 1392 src = CURATTACH->content->filename; 1393 mutt_buffer_strcpy (fname, mutt_basename (NONULL (src))); 1394 ret = mutt_buffer_get_field (_("Send attachment with name: "), 1395 fname, MUTT_FILE); 1396 if (ret == 0) 1397 { 1398 /* 1399 * As opposed to RENAME_FILE, we don't check fname[0] because it's 1400 * valid to set an empty string here, to erase what was set 1401 */ 1402 mutt_str_replace (&CURATTACH->content->d_filename, mutt_b2s (fname)); 1403 menu->redraw = REDRAW_CURRENT; 1404 } 1405 } 1406 break; 1407 1408 case OP_COMPOSE_RENAME_FILE: 1409 CHECK_COUNT; 1410 mutt_buffer_strcpy (fname, CURATTACH->content->filename); 1411 mutt_buffer_pretty_mailbox (fname); 1412 1413 if ((mutt_buffer_get_field (_("Rename to: "), fname, MUTT_FILE) == 0) && 1414 mutt_buffer_len (fname)) 1415 { 1416 if (stat(CURATTACH->content->filename, &st) == -1) 1417 { 1418 /* L10N: 1419 "stat" is a system call. Do "man 2 stat" for more information. */ 1420 mutt_error (_("Can't stat %s: %s"), mutt_b2s (fname), strerror (errno)); 1421 break; 1422 } 1423 1424 mutt_buffer_expand_path (fname); 1425 if (mutt_rename_file (CURATTACH->content->filename, mutt_b2s (fname))) 1426 break; 1427 1428 mutt_str_replace (&CURATTACH->content->filename, mutt_b2s (fname)); 1429 menu->redraw = REDRAW_CURRENT; 1430 1431 if (CURATTACH->content->stamp >= st.st_mtime) 1432 mutt_stamp_attachment(CURATTACH->content); 1433 } 1434 1435 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1436 break; 1437 1438 case OP_COMPOSE_NEW_MIME: 1439 { 1440 char type[STRING]; 1441 char *p; 1442 int itype; 1443 FILE *fp; 1444 1445 mutt_window_clearline (MuttMessageWindow, 0); 1446 mutt_buffer_clear (fname); 1447 if ((mutt_buffer_get_field (_("New file: "), fname, MUTT_FILE) != 0) || 1448 !mutt_buffer_len (fname)) 1449 continue; 1450 mutt_buffer_expand_path (fname); 1451 1452 /* Call to lookup_mime_type () ? maybe later */ 1453 type[0] = 0; 1454 if (mutt_get_field ("Content-Type: ", type, sizeof (type), 0) != 0 1455 || !type[0]) 1456 continue; 1457 1458 if (!(p = strchr (type, '/'))) 1459 { 1460 mutt_error _("Content-Type is of the form base/sub"); 1461 continue; 1462 } 1463 *p++ = 0; 1464 if ((itype = mutt_check_mime_type (type)) == TYPEOTHER) 1465 { 1466 mutt_error (_("Unknown Content-Type %s"), type); 1467 continue; 1468 } 1469 1470 new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR)); 1471 /* Touch the file */ 1472 if (!(fp = safe_fopen (mutt_b2s (fname), "w"))) 1473 { 1474 mutt_error (_("Can't create file %s"), mutt_b2s (fname)); 1475 FREE (&new); 1476 continue; 1477 } 1478 safe_fclose (&fp); 1479 1480 if ((new->content = mutt_make_file_attach (mutt_b2s (fname))) == NULL) 1481 { 1482 mutt_error _("What we have here is a failure to make an attachment"); 1483 FREE (&new); 1484 continue; 1485 } 1486 update_idx (menu, actx, new); 1487 1488 CURATTACH->content->type = itype; 1489 mutt_str_replace (&CURATTACH->content->subtype, p); 1490 CURATTACH->content->unlink = 1; 1491 menu->redraw |= REDRAW_INDEX | REDRAW_STATUS; 1492 1493 if (mutt_compose_attachment (CURATTACH->content)) 1494 { 1495 mutt_update_encoding (CURATTACH->content); 1496 menu->redraw = REDRAW_FULL; 1497 } 1498 } 1499 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1500 break; 1501 1502 case OP_COMPOSE_EDIT_MIME: 1503 CHECK_COUNT; 1504 if (mutt_edit_attachment (CURATTACH->content)) 1505 { 1506 mutt_update_encoding (CURATTACH->content); 1507 menu->redraw = REDRAW_FULL; 1508 } 1509 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1510 break; 1511 1512 case OP_COMPOSE_VIEW_ALT: 1513 case OP_COMPOSE_VIEW_ALT_TEXT: 1514 case OP_COMPOSE_VIEW_ALT_MAILCAP: 1515 { 1516 BODY *alternative; 1517 1518 if (!SendMultipartAltFilter) 1519 { 1520 mutt_error _("$send_multipart_alternative_filter is not set"); 1521 break; 1522 } 1523 alternative = mutt_run_send_alternative_filter (msg->content); 1524 if (!alternative) 1525 break; 1526 switch (op) 1527 { 1528 case OP_COMPOSE_VIEW_ALT_TEXT: 1529 op = MUTT_AS_TEXT; 1530 break; 1531 case OP_COMPOSE_VIEW_ALT_MAILCAP: 1532 op = MUTT_MAILCAP; 1533 break; 1534 default: 1535 op = MUTT_REGULAR; 1536 break; 1537 } 1538 mutt_view_attachment (NULL, alternative, op, NULL, actx); 1539 mutt_free_body (&alternative); 1540 break; 1541 } 1542 1543 case OP_VIEW_ATTACH: 1544 case OP_DISPLAY_HEADERS: 1545 CHECK_COUNT; 1546 mutt_attach_display_loop (menu, op, NULL, actx, 0); 1547 menu->redraw = REDRAW_FULL; 1548 /* no send2hook, since this doesn't modify the message */ 1549 break; 1550 1551 case OP_SAVE: 1552 CHECK_COUNT; 1553 mutt_save_attachment_list (actx, NULL, menu->tagprefix, CURATTACH->content, NULL, menu); 1554 /* no send2hook, since this doesn't modify the message */ 1555 break; 1556 1557 case OP_PRINT: 1558 CHECK_COUNT; 1559 mutt_print_attachment_list (actx, NULL, menu->tagprefix, CURATTACH->content); 1560 /* no send2hook, since this doesn't modify the message */ 1561 break; 1562 1563 case OP_PIPE: 1564 case OP_FILTER: 1565 CHECK_COUNT; 1566 mutt_pipe_attachment_list (actx, NULL, menu->tagprefix, CURATTACH->content, op == OP_FILTER); 1567 if (op == OP_FILTER) /* cte might have changed */ 1568 menu->redraw = menu->tagprefix ? REDRAW_FULL : REDRAW_CURRENT; 1569 menu->redraw |= REDRAW_STATUS; 1570 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1571 break; 1572 1573 case OP_EXIT: 1574 if ((i = query_quadoption (OPT_POSTPONE, _("Postpone this message?"))) == MUTT_NO) 1575 { 1576 for (i = 0; i < actx->idxlen; i++) 1577 if (actx->idx[i]->unowned) 1578 actx->idx[i]->content->unlink = 0; 1579 1580 if (!(flags & MUTT_COMPOSE_NOFREEHEADER)) 1581 { 1582 for (i = 0; i < actx->idxlen; i++) 1583 { 1584 /* avoid freeing other attachments */ 1585 actx->idx[i]->content->next = NULL; 1586 /* See the comment in delete_attachment() */ 1587 if (!actx->idx[i]->content->hdr) 1588 actx->idx[i]->content->parts = NULL; 1589 mutt_free_body (&actx->idx[i]->content); 1590 } 1591 } 1592 r = -1; 1593 loop = 0; 1594 break; 1595 } 1596 else if (i == -1) 1597 break; /* abort */ 1598 1599 /* fall through */ 1600 1601 case OP_COMPOSE_POSTPONE_MESSAGE: 1602 1603 if (check_attachments(actx) != 0) 1604 { 1605 menu->redraw = REDRAW_FULL; 1606 break; 1607 } 1608 1609 loop = 0; 1610 r = 1; 1611 break; 1612 1613 case OP_COMPOSE_ISPELL: 1614 endwin (); 1615 snprintf (buf, sizeof (buf), "%s -x %s", NONULL(Ispell), msg->content->filename); 1616 if (mutt_system (buf) == -1) 1617 mutt_error (_("Error running \"%s\"!"), buf); 1618 else 1619 { 1620 mutt_update_encoding (msg->content); 1621 menu->redraw |= REDRAW_STATUS; 1622 } 1623 break; 1624 1625 case OP_COMPOSE_WRITE_MESSAGE: 1626 1627 mutt_buffer_clear (fname); 1628 if (Context) 1629 { 1630 mutt_buffer_strcpy (fname, NONULL (Context->path)); 1631 mutt_buffer_pretty_mailbox (fname); 1632 } 1633 if (actx->idxlen) 1634 msg->content = actx->idx[0]->content; 1635 if ((mutt_buffer_enter_fname (_("Write message to mailbox"), fname, 1636 1) != -1) && 1637 mutt_buffer_len (fname)) 1638 { 1639 mutt_message (_("Writing message to %s ..."), mutt_b2s (fname)); 1640 mutt_buffer_expand_path (fname); 1641 1642 if (msg->content->next) 1643 msg->content = mutt_make_multipart_mixed (msg->content); 1644 1645 if (mutt_write_fcc (mutt_b2s (fname), msg, NULL, 0, NULL) == 0) 1646 mutt_message _("Message written."); 1647 1648 msg->content = mutt_remove_multipart_mixed (msg->content); 1649 } 1650 break; 1651 1652 1653 1654 case OP_COMPOSE_PGP_MENU: 1655 if (!(WithCrypto & APPLICATION_PGP)) 1656 break; 1657 if (!crypt_has_module_backend (APPLICATION_PGP)) 1658 { 1659 mutt_error _("No PGP backend configured"); 1660 break; 1661 } 1662 if ((WithCrypto & APPLICATION_SMIME) 1663 && (msg->security & APPLICATION_SMIME)) 1664 { 1665 if (msg->security & (ENCRYPT | SIGN)) 1666 { 1667 if (mutt_yesorno (_("S/MIME already selected. Clear & continue ? "), 1668 MUTT_YES) != MUTT_YES) 1669 { 1670 mutt_clear_error (); 1671 break; 1672 } 1673 msg->security &= ~(ENCRYPT | SIGN); 1674 } 1675 msg->security &= ~APPLICATION_SMIME; 1676 msg->security |= APPLICATION_PGP; 1677 update_crypt_info (&rd); 1678 } 1679 msg->security = crypt_pgp_send_menu (msg); 1680 update_crypt_info (&rd); 1681 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1682 break; 1683 1684 1685 case OP_FORGET_PASSPHRASE: 1686 crypt_forget_passphrase (); 1687 break; 1688 1689 1690 case OP_COMPOSE_SMIME_MENU: 1691 if (!(WithCrypto & APPLICATION_SMIME)) 1692 break; 1693 if (!crypt_has_module_backend (APPLICATION_SMIME)) 1694 { 1695 mutt_error _("No S/MIME backend configured"); 1696 break; 1697 } 1698 1699 if ((WithCrypto & APPLICATION_PGP) 1700 && (msg->security & APPLICATION_PGP)) 1701 { 1702 if (msg->security & (ENCRYPT | SIGN)) 1703 { 1704 if (mutt_yesorno (_("PGP already selected. Clear & continue ? "), 1705 MUTT_YES) != MUTT_YES) 1706 { 1707 mutt_clear_error (); 1708 break; 1709 } 1710 msg->security &= ~(ENCRYPT | SIGN); 1711 } 1712 msg->security &= ~APPLICATION_PGP; 1713 msg->security |= APPLICATION_SMIME; 1714 update_crypt_info (&rd); 1715 } 1716 msg->security = crypt_smime_send_menu(msg); 1717 update_crypt_info (&rd); 1718 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1719 break; 1720 1721 1722#ifdef MIXMASTER 1723 case OP_COMPOSE_MIX: 1724 1725 mix_make_chain (&msg->chain); 1726 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1727 break; 1728#endif 1729 1730#ifdef USE_AUTOCRYPT 1731 case OP_COMPOSE_AUTOCRYPT_MENU: 1732 if (!option (OPTAUTOCRYPT)) 1733 break; 1734 1735 if ((WithCrypto & APPLICATION_SMIME) 1736 && (msg->security & APPLICATION_SMIME)) 1737 { 1738 if (msg->security & (ENCRYPT | SIGN)) 1739 { 1740 if (mutt_yesorno (_("S/MIME already selected. Clear & continue ? "), 1741 MUTT_YES) != MUTT_YES) 1742 { 1743 mutt_clear_error (); 1744 break; 1745 } 1746 msg->security &= ~(ENCRYPT | SIGN); 1747 } 1748 msg->security &= ~APPLICATION_SMIME; 1749 msg->security |= APPLICATION_PGP; 1750 update_crypt_info (&rd); 1751 } 1752 autocrypt_compose_menu (msg); 1753 update_crypt_info (&rd); 1754 mutt_message_hook (NULL, msg, MUTT_SEND2HOOK); 1755 break; 1756#endif 1757 } 1758 } 1759 1760 mutt_buffer_free (&fname); 1761 1762#ifdef USE_AUTOCRYPT 1763 /* This is a fail-safe to make sure the bit isn't somehow turned 1764 * on. The user could have disabled the option after setting AUTOCRYPT, 1765 * or perhaps resuming or replying to an autocrypt message. 1766 */ 1767 if (!option (OPTAUTOCRYPT)) 1768 msg->security &= ~AUTOCRYPT; 1769#endif 1770 1771 mutt_pop_current_menu (menu); 1772 mutt_menuDestroy (&menu); 1773 1774 if (actx->idxlen) 1775 msg->content = actx->idx[0]->content; 1776 else 1777 msg->content = NULL; 1778 1779 mutt_free_attach_context (&actx); 1780 1781 return (r); 1782}