mutt stable branch with some hacks
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}