NeoMutt  2020-09-25
Teaching an old dog new tricks
DOXYGEN
compose.c
Go to the documentation of this file.
1 
32 #include "config.h"
33 #include <errno.h>
34 #include <limits.h>
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
40 #include "private.h"
41 #include "mutt/lib.h"
42 #include "address/lib.h"
43 #include "config/lib.h"
44 #include "email/lib.h"
45 #include "core/lib.h"
46 #include "alias/lib.h"
47 #include "conn/lib.h"
48 #include "gui/lib.h"
49 #include "mutt.h"
50 #include "lib.h"
51 #include "ncrypt/lib.h"
52 #include "send/lib.h"
53 #include "browser.h"
54 #include "commands.h"
55 #include "context.h"
56 #include "format_flags.h"
57 #include "hook.h"
58 #include "index.h"
59 #include "init.h"
60 #include "keymap.h"
61 #include "mutt_attach.h"
62 #include "mutt_globals.h"
63 #include "mutt_header.h"
64 #include "mutt_logging.h"
65 #include "mutt_menu.h"
66 #include "muttlib.h"
67 #include "mx.h"
68 #include "opcodes.h"
69 #include "options.h"
70 #include "protos.h"
71 #include "recvattach.h"
72 #include "rfc3676.h"
73 #include "sort.h"
74 #ifdef ENABLE_NLS
75 #include <libintl.h>
76 #endif
77 #ifdef MIXMASTER
78 #include "remailer.h"
79 #endif
80 #ifdef USE_NNTP
81 #include "nntp/lib.h"
82 #endif
83 #ifdef USE_POP
84 #include "pop/lib.h"
85 #endif
86 #ifdef USE_IMAP
87 #include "imap/lib.h"
88 #endif
89 #ifdef USE_AUTOCRYPT
90 #include "autocrypt/lib.h"
91 #endif
92 
94 #define MAX_ADDR_ROWS 5
95 
97 #define MAX_USER_HDR_ROWS 5
98 
99 static const char *There_are_no_attachments = N_("There are no attachments");
100 
101 static void compose_status_line(char *buf, size_t buflen, size_t col, int cols,
102  struct Menu *menu, const char *p);
103 
108 {
109  struct Email *email;
110  struct Buffer *fcc;
111 
112  struct ListHead to_list;
113  struct ListHead cc_list;
114  struct ListHead bcc_list;
115 
116  short to_rows;
117  short cc_rows;
118  short bcc_rows;
119  short sec_rows;
120 
121 #ifdef USE_AUTOCRYPT
124 #endif
129 };
130 
131 #define CHECK_COUNT \
132  if (actx->idxlen == 0) \
133  { \
134  mutt_error(_(There_are_no_attachments)); \
135  break; \
136  }
137 
138 #define CUR_ATTACH actx->idx[actx->v2r[menu->current]]
139 
146 {
154 #ifdef MIXMASTER
156 #endif
159 #ifdef USE_AUTOCRYPT
161 #endif
162 #ifdef USE_NNTP
166 #endif
169 };
170 
171 static int HeaderPadding[HDR_ATTACH_TITLE] = { 0 };
172 static int MaxHeaderWidth = 0;
173 
174 static const char *const Prompts[] = {
175  /* L10N: Compose menu field. May not want to translate. */
176  N_("From: "),
177  /* L10N: Compose menu field. May not want to translate. */
178  N_("To: "),
179  /* L10N: Compose menu field. May not want to translate. */
180  N_("Cc: "),
181  /* L10N: Compose menu field. May not want to translate. */
182  N_("Bcc: "),
183  /* L10N: Compose menu field. May not want to translate. */
184  N_("Subject: "),
185  /* L10N: Compose menu field. May not want to translate. */
186  N_("Reply-To: "),
187  /* L10N: Compose menu field. May not want to translate. */
188  N_("Fcc: "),
189 #ifdef MIXMASTER
190  /* L10N: "Mix" refers to the MixMaster chain for anonymous email */
191  N_("Mix: "),
192 #endif
193  /* L10N: Compose menu field. Holds "Encrypt", "Sign" related information */
194  N_("Security: "),
195  /* L10N: This string is used by the compose menu.
196  Since it is hidden by default, it does not increase the indentation of
197  other compose menu fields. However, if possible, it should not be longer
198  than the other compose menu fields. Since it shares the row with "Encrypt
199  with:", it should not be longer than 15-20 character cells. */
200  N_("Sign as: "),
201 #ifdef USE_AUTOCRYPT
202  // L10N: The compose menu autocrypt line
203  N_("Autocrypt: "),
204 #endif
205 #ifdef USE_NNTP
206  /* L10N: Compose menu field. May not want to translate. */
207  N_("Newsgroups: "),
208  /* L10N: Compose menu field. May not want to translate. */
209  N_("Followup-To: "),
210  /* L10N: Compose menu field. May not want to translate. */
211  N_("X-Comment-To: "),
212 #endif
213  N_("Headers: "),
214 };
215 
217 static const struct Mapping ComposeHelp[] = {
218  // clang-format off
219  { N_("Send"), OP_COMPOSE_SEND_MESSAGE },
220  { N_("Abort"), OP_EXIT },
221  /* L10N: compose menu help line entry */
222  { N_("To"), OP_COMPOSE_EDIT_TO },
223  /* L10N: compose menu help line entry */
224  { N_("CC"), OP_COMPOSE_EDIT_CC },
225  /* L10N: compose menu help line entry */
226  { N_("Subj"), OP_COMPOSE_EDIT_SUBJECT },
227  { N_("Attach file"), OP_COMPOSE_ATTACH_FILE },
228  { N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION },
229  { N_("Help"), OP_HELP },
230  { NULL, 0 },
231  // clang-format on
232 };
233 
234 #ifdef USE_NNTP
235 static const struct Mapping ComposeNewsHelp[] = {
237  // clang-format off
238  { N_("Send"), OP_COMPOSE_SEND_MESSAGE },
239  { N_("Abort"), OP_EXIT },
240  { N_("Newsgroups"), OP_COMPOSE_EDIT_NEWSGROUPS },
241  { N_("Subj"), OP_COMPOSE_EDIT_SUBJECT },
242  { N_("Attach file"), OP_COMPOSE_ATTACH_FILE },
243  { N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION },
244  { N_("Help"), OP_HELP },
245  { NULL, 0 },
246  // clang-format on
247 };
248 #endif
249 
250 #ifdef USE_AUTOCRYPT
251 static const char *AutocryptRecUiFlags[] = {
252  /* L10N: Autocrypt recommendation flag: off.
253  * This is displayed when Autocrypt is turned off. */
254  N_("Off"),
255  /* L10N: Autocrypt recommendation flag: no.
256  * This is displayed when Autocrypt cannot encrypt to the recipients. */
257  N_("No"),
258  /* L10N: Autocrypt recommendation flag: discouraged.
259  * This is displayed when Autocrypt believes encryption should not be used.
260  * This might occur if one of the recipient Autocrypt Keys has not been
261  * used recently, or if the only key available is a Gossip Header key. */
262  N_("Discouraged"),
263  /* L10N: Autocrypt recommendation flag: available.
264  * This is displayed when Autocrypt believes encryption is possible, but
265  * leaves enabling it up to the sender. Probably because "prefer encrypt"
266  * is not set in both the sender and recipient keys. */
267  N_("Available"),
268  /* L10N: Autocrypt recommendation flag: yes.
269  * This is displayed when Autocrypt would normally enable encryption
270  * automatically. */
271  N_("Yes"),
272 };
273 #endif
274 
281 static void calc_header_width_padding(int idx, const char *header, bool calc_max)
282 {
283  int width;
284 
285  HeaderPadding[idx] = mutt_str_len(header);
286  width = mutt_strwidth(header);
287  if (calc_max && (MaxHeaderWidth < width))
288  MaxHeaderWidth = width;
289  HeaderPadding[idx] -= width;
290 }
291 
300 static void init_header_padding(void)
301 {
302  static bool done = false;
303 
304  if (done)
305  return;
306  done = true;
307 
308  for (int i = 0; i < HDR_ATTACH_TITLE; i++)
309  {
310  if (i == HDR_CRYPTINFO)
311  continue;
312  calc_header_width_padding(i, _(Prompts[i]), true);
313  }
314 
315  /* Don't include "Sign as: " in the MaxHeaderWidth calculation. It
316  * doesn't show up by default, and so can make the indentation of
317  * the other fields look funny. */
319 
320  for (int i = 0; i < HDR_ATTACH_TITLE; i++)
321  {
323  if (HeaderPadding[i] < 0)
324  HeaderPadding[i] = 0;
325  }
326 }
327 
331 static void snd_make_entry(char *buf, size_t buflen, struct Menu *menu, int line)
332 {
333  struct AttachCtx *actx = menu->mdata;
334 
335  mutt_expando_format(buf, buflen, 0, menu->win_index->state.cols, NONULL(C_AttachFormat),
336  attach_format_str, (intptr_t)(actx->idx[actx->v2r[line]]),
338 }
339 
340 #ifdef USE_AUTOCRYPT
341 
345 static void autocrypt_compose_menu(struct Email *e)
346 {
347  /* L10N: The compose menu autocrypt prompt.
348  (e)ncrypt enables encryption via autocrypt.
349  (c)lear sets cleartext.
350  (a)utomatic defers to the recommendation. */
351  const char *prompt = _("Autocrypt: (e)ncrypt, (c)lear, (a)utomatic?");
352 
354 
355  /* L10N: The letter corresponding to the compose menu autocrypt prompt
356  (e)ncrypt, (c)lear, (a)utomatic */
357  const char *letters = _("eca");
358 
359  int choice = mutt_multi_choice(prompt, letters);
360  switch (choice)
361  {
362  case 1:
365  break;
366  case 2:
367  e->security &= ~SEC_AUTOCRYPT;
369  break;
370  case 3:
373  e->security |= SEC_OPPENCRYPT;
374  break;
375  }
376 }
377 #endif
378 
386 static void draw_floating(struct MuttWindow *win, int col, int row, const char *text)
387 {
389  mutt_window_mvprintw(win, col, row, "%s", text);
391 }
392 
399 static void draw_header(struct MuttWindow *win, int row, enum HeaderField field)
400 {
402  mutt_window_mvprintw(win, 0, row, "%*s", HeaderPadding[field], _(Prompts[field]));
404 }
405 
415 static void draw_header_content(struct MuttWindow *win, int row,
416  enum HeaderField field, const char *content)
417 {
418  mutt_window_move(win, HeaderPadding[field], row);
419  mutt_paddstr(win->state.cols - HeaderPadding[field], content);
420 }
421 
432 static int calc_address(struct AddressList *al, struct ListHead *slist,
433  short cols, short *srows)
434 {
435  mutt_list_free(slist);
436  mutt_addrlist_write_list(al, slist);
437 
438  int rows = 1;
439  int addr_len;
440  int width_left = cols;
441  struct ListNode *next = NULL;
442  struct ListNode *np = NULL;
443  STAILQ_FOREACH(np, slist, entries)
444  {
445  next = STAILQ_NEXT(np, entries);
446  addr_len = mutt_strwidth(np->data);
447  if (next)
448  addr_len += 2; // ", "
449 
450  try_again:
451  if (addr_len >= width_left)
452  {
453  if (width_left == cols)
454  break;
455 
456  rows++;
457  width_left = cols;
458  goto try_again;
459  }
460 
461  if (addr_len < width_left)
462  width_left -= addr_len;
463  }
464 
465  *srows = MIN(rows, MAX_ADDR_ROWS);
466  return *srows;
467 }
468 
475 static int calc_security(struct Email *e, short *rows)
476 {
478  *rows = 0; // Neither PGP nor SMIME are built into NeoMutt
479  else if ((e->security & (SEC_ENCRYPT | SEC_SIGN)) != 0)
480  *rows = 2; // 'Security:' and 'Sign as:'
481  else
482  *rows = 1; // Just 'Security:'
483 
484 #ifdef USE_AUTOCRYPT
485  if (C_Autocrypt)
486  *rows += 1;
487 #endif
488 
489  return *rows;
490 }
491 
497 static int calc_user_hdrs(const struct ListHead *hdrs)
498 {
499  int rows = 0; /* Don't print at all if no custom headers*/
500  struct ListNode *np = NULL;
501  STAILQ_FOREACH(np, hdrs, entries)
502  {
503  if (rows == MAX_USER_HDR_ROWS)
504  break;
505  rows++;
506  }
507  return rows;
508 }
509 
515 static int calc_envelope(struct ComposeRedrawData *rd)
516 {
517  int rows = 4; // 'From:', 'Subject:', 'Reply-To:', 'Fcc:'
518 #ifdef MIXMASTER
519  rows++;
520 #endif
521 
522  struct Email *e = rd->email;
523  struct Envelope *env = e->env;
524  const int cols = rd->win_envelope->state.cols - MaxHeaderWidth;
525 
526 #ifdef USE_NNTP
527  if (OptNewsSend)
528  {
529  rows += 2; // 'Newsgroups:' and 'Followup-To:'
530  if (C_XCommentTo)
531  rows++;
532  }
533  else
534 #endif
535  {
536  rows += calc_address(&env->to, &rd->to_list, cols, &rd->to_rows);
537  rows += calc_address(&env->cc, &rd->cc_list, cols, &rd->cc_rows);
538  rows += calc_address(&env->bcc, &rd->bcc_list, cols, &rd->bcc_rows);
539  }
540  rows += calc_security(e, &rd->sec_rows);
542  rows += calc_user_hdrs(&env->userhdrs);
543 
544  return rows;
545 }
546 
552 static int redraw_crypt_lines(struct ComposeRedrawData *rd, int row)
553 {
554  struct Email *e = rd->email;
555 
556  draw_header(rd->win_envelope, row++, HDR_CRYPT);
557 
559  return 0;
560 
561  // We'll probably need two lines for 'Security:' and 'Sign as:'
562  int used = 2;
563  if ((e->security & (SEC_ENCRYPT | SEC_SIGN)) == (SEC_ENCRYPT | SEC_SIGN))
564  {
566  mutt_window_addstr(_("Sign, Encrypt"));
567  }
568  else if (e->security & SEC_ENCRYPT)
569  {
571  mutt_window_addstr(_("Encrypt"));
572  }
573  else if (e->security & SEC_SIGN)
574  {
576  mutt_window_addstr(_("Sign"));
577  }
578  else
579  {
580  /* L10N: This refers to the encryption of the email, e.g. "Security: None" */
582  mutt_window_addstr(_("None"));
583  used = 1; // 'Sign as:' won't be needed
584  }
586 
587  if ((e->security & (SEC_ENCRYPT | SEC_SIGN)))
588  {
589  if (((WithCrypto & APPLICATION_PGP) != 0) && (e->security & APPLICATION_PGP))
590  {
591  if ((e->security & SEC_INLINE))
592  mutt_window_addstr(_(" (inline PGP)"));
593  else
594  mutt_window_addstr(_(" (PGP/MIME)"));
595  }
596  else if (((WithCrypto & APPLICATION_SMIME) != 0) && (e->security & APPLICATION_SMIME))
597  mutt_window_addstr(_(" (S/MIME)"));
598  }
599 
601  mutt_window_addstr(_(" (OppEnc mode)"));
602 
604 
605  if (((WithCrypto & APPLICATION_PGP) != 0) &&
606  (e->security & APPLICATION_PGP) && (e->security & SEC_SIGN))
607  {
609  mutt_window_printf("%s", C_PgpSignAs ? C_PgpSignAs : _("<default>"));
610  }
611 
612  if (((WithCrypto & APPLICATION_SMIME) != 0) &&
613  (e->security & APPLICATION_SMIME) && (e->security & SEC_SIGN))
614  {
616  mutt_window_printf("%s", C_SmimeSignAs ? C_SmimeSignAs : _("<default>"));
617  }
618 
619  if (((WithCrypto & APPLICATION_SMIME) != 0) && (e->security & APPLICATION_SMIME) &&
621  {
622  draw_floating(rd->win_envelope, 40, row - 1, _("Encrypt with: "));
624  }
625 
626 #ifdef USE_AUTOCRYPT
627  if (C_Autocrypt)
628  {
630  if (e->security & SEC_AUTOCRYPT)
631  {
633  mutt_window_addstr(_("Encrypt"));
634  }
635  else
636  {
638  mutt_window_addstr(_("Off"));
639  }
640 
641  /* L10N: The autocrypt compose menu Recommendation field.
642  Displays the output of the recommendation engine
643  (Off, No, Discouraged, Available, Yes) */
644  draw_floating(rd->win_envelope, 40, row, _("Recommendation: "));
646 
647  used++;
648  }
649 #endif
650  return used;
651 }
652 
657 static void update_crypt_info(struct ComposeRedrawData *rd)
658 {
659  struct Email *e = rd->email;
660 
663 
664 #ifdef USE_AUTOCRYPT
665  if (C_Autocrypt)
666  {
668 
669  /* Anything that enables SEC_ENCRYPT or SEC_SIGN, or turns on SMIME
670  * overrides autocrypt, be it oppenc or the user having turned on
671  * those flags manually. */
674  else
675  {
676  if (!(e->security & SEC_AUTOCRYPT_OVERRIDE))
677  {
678  if (rd->autocrypt_rec == AUTOCRYPT_REC_YES)
679  {
682  }
683  else
684  e->security &= ~SEC_AUTOCRYPT;
685  }
686  }
687  }
688 #endif
689 }
690 
691 #ifdef MIXMASTER
692 
698 static void redraw_mix_line(struct ListHead *chain, struct ComposeRedrawData *rd, int row)
699 {
700  char *t = NULL;
701 
702  draw_header(rd->win_envelope, row, HDR_MIX);
703 
704  if (STAILQ_EMPTY(chain))
705  {
706  mutt_window_addstr(_("<no chain defined>"));
708  return;
709  }
710 
711  int c = 12;
712  struct ListNode *np = NULL;
713  STAILQ_FOREACH(np, chain, entries)
714  {
715  t = np->data;
716  if (t && (t[0] == '0') && (t[1] == '\0'))
717  t = "<random>";
718 
719  if (c + mutt_str_len(t) + 2 >= rd->win_envelope->state.cols)
720  break;
721 
723  if (STAILQ_NEXT(np, entries))
724  mutt_window_addstr(", ");
725 
726  c += mutt_str_len(t) + 2;
727  }
728 }
729 #endif
730 
737 static int check_attachments(struct AttachCtx *actx)
738 {
739  int rc = -1;
740  struct stat st;
741  struct Buffer *pretty = NULL, *msg = NULL;
742 
743  for (int i = 0; i < actx->idxlen; i++)
744  {
745  if (actx->idx[i]->body->type == TYPE_MULTIPART)
746  continue;
747  if (stat(actx->idx[i]->body->filename, &st) != 0)
748  {
749  if (!pretty)
750  pretty = mutt_buffer_pool_get();
751  mutt_buffer_strcpy(pretty, actx->idx[i]->body->filename);
753  /* L10N: This message is displayed in the compose menu when an attachment
754  doesn't stat. %d is the attachment number and %s is the attachment
755  filename. The filename is located last to avoid a long path hiding
756  the error message. */
757  mutt_error(_("Attachment #%d no longer exists: %s"), i + 1, mutt_b2s(pretty));
758  goto cleanup;
759  }
760 
761  if (actx->idx[i]->body->stamp < st.st_mtime)
762  {
763  if (!pretty)
764  pretty = mutt_buffer_pool_get();
765  mutt_buffer_strcpy(pretty, actx->idx[i]->body->filename);
767 
768  if (!msg)
769  msg = mutt_buffer_pool_get();
770  /* L10N: This message is displayed in the compose menu when an attachment
771  is modified behind the scenes. %d is the attachment number and %s is
772  the attachment filename. The filename is located last to avoid a long
773  path hiding the prompt question. */
774  mutt_buffer_printf(msg, _("Attachment #%d modified. Update encoding for %s?"),
775  i + 1, mutt_b2s(pretty));
776 
777  enum QuadOption ans = mutt_yesorno(mutt_b2s(msg), MUTT_YES);
778  if (ans == MUTT_YES)
779  mutt_update_encoding(actx->idx[i]->body, NeoMutt->sub);
780  else if (ans == MUTT_ABORT)
781  goto cleanup;
782  }
783  }
784 
785  rc = 0;
786 
787 cleanup:
788  mutt_buffer_pool_release(&pretty);
790  return rc;
791 }
792 
801 static int draw_envelope_addr(int field, struct AddressList *al,
802  struct MuttWindow *win, int row, size_t max_lines)
803 {
804  draw_header(win, row, field);
805 
806  struct ListHead list = STAILQ_HEAD_INITIALIZER(list);
807  int count = mutt_addrlist_write_list(al, &list);
808 
809  int lines_used = 1;
810  int width_left = win->state.cols - MaxHeaderWidth;
811  char more[32];
812  int more_len = 0;
813 
814  char *sep = NULL;
815  struct ListNode *next = NULL;
816  struct ListNode *np = NULL;
817  STAILQ_FOREACH(np, &list, entries)
818  {
819  next = STAILQ_NEXT(np, entries);
820  int addr_len = mutt_strwidth(np->data);
821  if (next)
822  {
823  sep = ", ";
824  addr_len += 2;
825  }
826  else
827  {
828  sep = "";
829  }
830 
831  count--;
832  try_again:
833  more_len = snprintf(more, sizeof(more),
834  ngettext("(+%d more)", "(+%d more)", count), count);
835  mutt_debug(LL_DEBUG3, "text: '%s' len: %d\n", more, more_len);
836 
837  int reserve = ((count > 0) && (lines_used == max_lines)) ? more_len : 0;
838  mutt_debug(LL_DEBUG3, "processing: %s (al:%ld, wl:%d, r:%d, lu:%ld)\n",
839  np->data, addr_len, width_left, reserve, lines_used);
840  if (addr_len >= (width_left - reserve))
841  {
842  mutt_debug(LL_DEBUG3, "not enough space\n");
843  if (lines_used == max_lines)
844  {
845  mutt_debug(LL_DEBUG3, "no more lines\n");
846  mutt_debug(LL_DEBUG3, "truncating: %s\n", np->data);
847  mutt_paddstr(width_left, np->data);
848  break;
849  }
850 
851  if (width_left == (win->state.cols - MaxHeaderWidth))
852  {
853  mutt_debug(LL_DEBUG3, "couldn't print: %s\n", np->data);
854  mutt_paddstr(width_left, np->data);
855  break;
856  }
857 
858  mutt_debug(LL_DEBUG3, "start a new line\n");
860  row++;
861  lines_used++;
862  width_left = win->state.cols - MaxHeaderWidth;
863  mutt_window_move(win, MaxHeaderWidth, row);
864  goto try_again;
865  }
866 
867  if (addr_len < width_left)
868  {
869  mutt_debug(LL_DEBUG3, "space for: %s\n", np->data);
871  mutt_window_addstr(sep);
872  width_left -= addr_len;
873  }
874  mutt_debug(LL_DEBUG3, "%ld addresses remaining\n", count);
875  mutt_debug(LL_DEBUG3, "%ld lines remaining\n", max_lines - lines_used);
876  }
877  mutt_list_free(&list);
878 
879  if (count > 0)
880  {
881  mutt_window_move(win, win->state.cols - more_len, row);
883  mutt_window_addstr(more);
885  mutt_debug(LL_DEBUG3, "%ld more (len %d)\n", count, more_len);
886  }
887  else
888  {
890  }
891 
892  for (int i = lines_used; i < max_lines; i++)
893  {
894  mutt_window_move(win, 0, row + i);
896  }
897 
898  mutt_debug(LL_DEBUG3, "used %d lines\n", lines_used);
899  return lines_used;
900 }
901 
907 static int draw_envelope_user_hdrs(const struct ComposeRedrawData *rd, int row)
908 {
909  const char *overflow_text = "...";
910  int rows_used = 0;
911 
912  struct ListNode *first = STAILQ_FIRST(&rd->email->env->userhdrs);
913  if (!first)
914  return rows_used;
915 
916  /* Draw first entry on same line as prompt */
921  first->data);
922  rows_used++;
923 
924  /* Draw any following entries on their own line */
925  struct ListNode *np = STAILQ_NEXT(first, entries);
926  if (!np)
927  return rows_used;
928 
929  STAILQ_FOREACH_FROM(np, &rd->email->env->userhdrs, entries)
930  {
931  if ((rows_used == (MAX_USER_HDR_ROWS - 1)) && STAILQ_NEXT(np, entries))
932  {
933  draw_header_content(rd->win_envelope, row + rows_used, HDR_CUSTOM_HEADERS, overflow_text);
934  rows_used++;
935  break;
936  }
937  draw_header_content(rd->win_envelope, row + rows_used, HDR_CUSTOM_HEADERS, np->data);
938  rows_used++;
939  }
940  return rows_used;
941 }
942 
947 static void draw_envelope(struct ComposeRedrawData *rd)
948 {
949  struct Email *e = rd->email;
950  const char *fcc = mutt_b2s(rd->fcc);
951  const int cols = rd->win_envelope->state.cols - MaxHeaderWidth;
952 
953  int row = draw_envelope_addr(HDR_FROM, &e->env->from, rd->win_envelope, 0, 1);
954 
955 #ifdef USE_NNTP
956  if (OptNewsSend)
957  {
959  mutt_paddstr(cols, NONULL(e->env->newsgroups));
960 
962  mutt_paddstr(cols, NONULL(e->env->followup_to));
963 
964  if (C_XCommentTo)
965  {
967  mutt_paddstr(cols, NONULL(e->env->x_comment_to));
968  }
969  }
970  else
971 #endif
972  {
973  row += draw_envelope_addr(HDR_TO, &e->env->to, rd->win_envelope, row, rd->to_rows);
974  row += draw_envelope_addr(HDR_CC, &e->env->cc, rd->win_envelope, row, rd->cc_rows);
975  row += draw_envelope_addr(HDR_BCC, &e->env->bcc, rd->win_envelope, row, rd->bcc_rows);
976  }
977 
978  draw_header(rd->win_envelope, row++, HDR_SUBJECT);
979  mutt_paddstr(cols, NONULL(e->env->subject));
980 
981  row += draw_envelope_addr(HDR_REPLYTO, &e->env->reply_to, rd->win_envelope, row, 1);
982 
983  draw_header(rd->win_envelope, row++, HDR_FCC);
984  mutt_paddstr(cols, fcc);
985 
986  if (WithCrypto)
987  row += redraw_crypt_lines(rd, row);
988 
989 #ifdef MIXMASTER
990  redraw_mix_line(&e->chain, rd, row++);
991 #endif
993  row += draw_envelope_user_hdrs(rd, row);
994 
996  mutt_window_mvaddstr(rd->win_abar, 0, 0, _("-- Attachments"));
999 }
1000 
1007 static bool edit_address_list(int field, struct AddressList *al)
1008 {
1009  char buf[8192] = { 0 }; /* needs to be large for alias expansion */
1010  char old_list[8192] = { 0 };
1011 
1013  mutt_addrlist_write(al, buf, sizeof(buf), false);
1014  mutt_str_copy(old_list, buf, sizeof(buf));
1015  if (mutt_get_field(_(Prompts[field]), buf, sizeof(buf), MUTT_ALIAS) == 0)
1016  {
1017  mutt_addrlist_clear(al);
1018  mutt_addrlist_parse2(al, buf);
1019  mutt_expand_aliases(al);
1020  }
1021 
1022  char *err = NULL;
1023  if (mutt_addrlist_to_intl(al, &err) != 0)
1024  {
1025  mutt_error(_("Bad IDN: '%s'"), err);
1026  mutt_refresh();
1027  FREE(&err);
1028  }
1029 
1030  return !mutt_str_equal(buf, old_list);
1031 }
1032 
1040 static int delete_attachment(struct AttachCtx *actx, int x)
1041 {
1042  struct AttachPtr **idx = actx->idx;
1043  int rindex = actx->v2r[x];
1044 
1045  if ((rindex == 0) && (actx->idxlen == 1))
1046  {
1047  mutt_error(_("You may not delete the only attachment"));
1048  idx[rindex]->body->tagged = false;
1049  return -1;
1050  }
1051 
1052  for (int y = 0; y < actx->idxlen; y++)
1053  {
1054  if (idx[y]->body->next == idx[rindex]->body)
1055  {
1056  idx[y]->body->next = idx[rindex]->body->next;
1057  break;
1058  }
1059  }
1060 
1061  idx[rindex]->body->next = NULL;
1062  /* mutt_make_message_attach() creates body->parts, shared by
1063  * body->email->body. If we NULL out that, it creates a memory
1064  * leak because mutt_free_body() frees body->parts, not
1065  * body->email->body.
1066  *
1067  * Other mutt_send_message() message constructors are careful to free
1068  * any body->parts, removing depth:
1069  * - mutt_prepare_template() used by postponed, resent, and draft files
1070  * - mutt_copy_body() used by the recvattach menu and $forward_attachments.
1071  *
1072  * I believe it is safe to completely remove the "body->parts =
1073  * NULL" statement. But for safety, am doing so only for the case
1074  * it must be avoided: message attachments.
1075  */
1076  if (!idx[rindex]->body->email)
1077  idx[rindex]->body->parts = NULL;
1078  mutt_body_free(&(idx[rindex]->body));
1079  FREE(&idx[rindex]->tree);
1080  FREE(&idx[rindex]);
1081  for (; rindex < actx->idxlen - 1; rindex++)
1082  idx[rindex] = idx[rindex + 1];
1083  idx[actx->idxlen - 1] = NULL;
1084  actx->idxlen--;
1085 
1086  return 0;
1087 }
1088 
1096 static void mutt_gen_compose_attach_list(struct AttachCtx *actx, struct Body *m,
1097  int parent_type, int level)
1098 {
1099  for (; m; m = m->next)
1100  {
1101  if ((m->type == TYPE_MULTIPART) && m->parts &&
1103  {
1104  mutt_gen_compose_attach_list(actx, m->parts, m->type, level);
1105  }
1106  else
1107  {
1108  struct AttachPtr *ap = mutt_mem_calloc(1, sizeof(struct AttachPtr));
1109  mutt_actx_add_attach(actx, ap);
1110  ap->body = m;
1111  m->aptr = ap;
1112  ap->parent_type = parent_type;
1113  ap->level = level;
1114 
1115  /* We don't support multipart messages in the compose menu yet */
1116  }
1117  }
1118 }
1119 
1126 static void mutt_update_compose_menu(struct AttachCtx *actx, struct Menu *menu, bool init)
1127 {
1128  if (init)
1129  {
1130  mutt_gen_compose_attach_list(actx, actx->email->body, -1, 0);
1131  mutt_attach_init(actx);
1132  menu->mdata = actx;
1133  }
1134 
1135  mutt_update_tree(actx);
1136 
1137  menu->max = actx->vcount;
1138  if (menu->max)
1139  {
1140  if (menu->current >= menu->max)
1141  menu->current = menu->max - 1;
1142  }
1143  else
1144  menu->current = 0;
1145 
1146  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
1147 }
1148 
1155 static void update_idx(struct Menu *menu, struct AttachCtx *actx, struct AttachPtr *ap)
1156 {
1157  ap->level = (actx->idxlen > 0) ? actx->idx[actx->idxlen - 1]->level : 0;
1158  if (actx->idxlen)
1159  actx->idx[actx->idxlen - 1]->body->next = ap->body;
1160  ap->body->aptr = ap;
1161  mutt_actx_add_attach(actx, ap);
1162  mutt_update_compose_menu(actx, menu, false);
1163  menu->current = actx->vcount - 1;
1164 }
1165 
1169 static void compose_custom_redraw(struct Menu *menu)
1170 {
1171  struct ComposeRedrawData *rd = menu->redraw_data;
1172  if (!rd)
1173  return;
1174 
1175  if (menu->redraw & REDRAW_FLOW)
1176  {
1177  rd->win_envelope->req_rows = calc_envelope(rd);
1179  }
1180 
1181  if (menu->redraw & REDRAW_FULL)
1182  {
1183  menu_redraw_full(menu);
1184  draw_envelope(rd);
1185  menu->pagelen = menu->win_index->state.rows;
1186  }
1187 
1188  menu_check_recenter(menu);
1189 
1190  if (menu->redraw & REDRAW_STATUS)
1191  {
1192  char buf[1024];
1193  compose_status_line(buf, sizeof(buf), 0, menu->win_ibar->state.cols, menu,
1195  mutt_window_move(menu->win_ibar, 0, 0);
1197  mutt_draw_statusline(menu->win_ibar->state.cols, buf, sizeof(buf));
1199  menu->redraw &= ~REDRAW_STATUS;
1200  }
1201 
1202  if (menu->redraw & REDRAW_INDEX)
1203  menu_redraw_index(menu);
1204  else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNC))
1205  menu_redraw_motion(menu);
1206  else if (menu->redraw == REDRAW_CURRENT)
1207  menu_redraw_current(menu);
1208  menu->redraw = REDRAW_NO_FLAGS;
1209 }
1210 
1217 static void compose_attach_swap(struct Body *msg, struct AttachPtr **idx, short first)
1218 {
1219  /* Reorder Body pointers.
1220  * Must traverse msg from top since Body has no previous ptr. */
1221  for (struct Body *part = msg; part; part = part->next)
1222  {
1223  if (part->next == idx[first]->body)
1224  {
1225  idx[first]->body->next = idx[first + 1]->body->next;
1226  idx[first + 1]->body->next = idx[first]->body;
1227  part->next = idx[first + 1]->body;
1228  break;
1229  }
1230  }
1231 
1232  /* Reorder index */
1233  struct AttachPtr *saved = idx[first];
1234  idx[first] = idx[first + 1];
1235  idx[first + 1] = saved;
1236 
1237  /* Swap ptr->num */
1238  int i = idx[first]->num;
1239  idx[first]->num = idx[first + 1]->num;
1240  idx[first + 1]->num = i;
1241 }
1242 
1251 static unsigned long cum_attachs_size(struct Menu *menu)
1252 {
1253  size_t s = 0;
1254  struct AttachCtx *actx = menu->mdata;
1255  struct AttachPtr **idx = actx->idx;
1256  struct Content *info = NULL;
1257  struct Body *b = NULL;
1258 
1259  for (unsigned short i = 0; i < actx->idxlen; i++)
1260  {
1261  b = idx[i]->body;
1262 
1263  if (!b->content)
1265 
1266  info = b->content;
1267  if (info)
1268  {
1269  switch (b->encoding)
1270  {
1271  case ENC_QUOTED_PRINTABLE:
1272  s += 3 * (info->lobin + info->hibin) + info->ascii + info->crlf;
1273  break;
1274  case ENC_BASE64:
1275  s += (4 * (info->lobin + info->hibin + info->ascii + info->crlf)) / 3;
1276  break;
1277  default:
1278  s += info->lobin + info->hibin + info->ascii + info->crlf;
1279  break;
1280  }
1281  }
1282  }
1283 
1284  return s;
1285 }
1286 
1297 static const char *compose_format_str(char *buf, size_t buflen, size_t col, int cols,
1298  char op, const char *src, const char *prec,
1299  const char *if_str, const char *else_str,
1300  intptr_t data, MuttFormatFlags flags)
1301 {
1302  char fmt[128], tmp[128];
1303  bool optional = (flags & MUTT_FORMAT_OPTIONAL);
1304  struct Menu *menu = (struct Menu *) data;
1305 
1306  *buf = '\0';
1307  switch (op)
1308  {
1309  case 'a': /* total number of attachments */
1310  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
1311  snprintf(buf, buflen, fmt, menu->max);
1312  break;
1313 
1314  case 'h': /* hostname */
1315  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
1316  snprintf(buf, buflen, fmt, NONULL(ShortHostname));
1317  break;
1318 
1319  case 'l': /* approx length of current message in bytes */
1320  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
1321  mutt_str_pretty_size(tmp, sizeof(tmp), menu ? cum_attachs_size(menu) : 0);
1322  snprintf(buf, buflen, fmt, tmp);
1323  break;
1324 
1325  case 'v':
1326  snprintf(buf, buflen, "%s", mutt_make_version());
1327  break;
1328 
1329  case 0:
1330  *buf = '\0';
1331  return src;
1332 
1333  default:
1334  snprintf(buf, buflen, "%%%s%c", prec, op);
1335  break;
1336  }
1337 
1338  if (optional)
1339  compose_status_line(buf, buflen, col, cols, menu, if_str);
1340  else if (flags & MUTT_FORMAT_OPTIONAL)
1341  compose_status_line(buf, buflen, col, cols, menu, else_str);
1342 
1343  return src;
1344 }
1345 
1355 static void compose_status_line(char *buf, size_t buflen, size_t col, int cols,
1356  struct Menu *menu, const char *src)
1357 {
1358  mutt_expando_format(buf, buflen, col, cols, src, compose_format_str,
1359  (intptr_t) menu, MUTT_FORMAT_NO_FLAGS);
1360 }
1361 
1366 {
1367  if (!nc->event_data || !nc->global_data)
1368  return -1;
1369  if (nc->event_type != NT_CONFIG)
1370  return 0;
1371 
1372  struct EventConfig *ec = nc->event_data;
1373  struct MuttWindow *dlg = nc->global_data;
1374 
1375  if (!mutt_str_equal(ec->name, "status_on_top"))
1376  return 0;
1377 
1378  struct MuttWindow *win_ebar = mutt_window_find(dlg, WT_INDEX_BAR);
1379  if (!win_ebar)
1380  return 0;
1381 
1382  TAILQ_REMOVE(&dlg->children, win_ebar, entries);
1383 
1384  if (C_StatusOnTop)
1385  TAILQ_INSERT_HEAD(&dlg->children, win_ebar, entries);
1386  else
1387  TAILQ_INSERT_TAIL(&dlg->children, win_ebar, entries);
1388 
1389  mutt_window_reflow(dlg);
1390  return 0;
1391 }
1392 
1403 int mutt_compose_menu(struct Email *e, struct Buffer *fcc, struct Email *e_cur, int flags)
1404 {
1405  char buf[PATH_MAX];
1406  int rc = -1;
1407  bool loop = true;
1408  bool fcc_set = false; /* has the user edited the Fcc: field ? */
1409  struct ComposeRedrawData redraw = { 0 };
1410 
1411  STAILQ_INIT(&redraw.to_list);
1412  STAILQ_INIT(&redraw.cc_list);
1413  STAILQ_INIT(&redraw.bcc_list);
1414 
1415  struct ComposeRedrawData *rd = &redraw;
1416 #ifdef USE_NNTP
1417  bool news = OptNewsSend; /* is it a news article ? */
1418 #endif
1419 
1421 
1422  struct MuttWindow *dlg =
1425 
1426  struct MuttWindow *envelope =
1429 
1430  struct MuttWindow *abar =
1433 
1434  struct MuttWindow *attach =
1437 
1438  struct MuttWindow *ebar =
1441 
1442  rd->email = e;
1443  rd->fcc = fcc;
1444  rd->win_envelope = envelope;
1445  rd->win_cbar = ebar;
1446  rd->win_attach = attach;
1447  rd->win_abar = abar;
1448 
1449  if (C_StatusOnTop)
1450  {
1451  mutt_window_add_child(dlg, ebar);
1452  mutt_window_add_child(dlg, envelope);
1453  mutt_window_add_child(dlg, abar);
1454  mutt_window_add_child(dlg, attach);
1455  }
1456  else
1457  {
1458  mutt_window_add_child(dlg, envelope);
1459  mutt_window_add_child(dlg, abar);
1460  mutt_window_add_child(dlg, attach);
1461  mutt_window_add_child(dlg, ebar);
1462  }
1463 
1465  dialog_push(dlg);
1466 
1467 #ifdef USE_NNTP
1468  if (news)
1469  dlg->help_data = ComposeNewsHelp;
1470  else
1471 #endif
1472  dlg->help_data = ComposeHelp;
1473  dlg->help_menu = MENU_COMPOSE;
1474 
1475  envelope->req_rows = calc_envelope(rd);
1476  mutt_window_reflow(dlg);
1477 
1478  struct Menu *menu = mutt_menu_new(MENU_COMPOSE);
1479 
1480  menu->pagelen = attach->state.rows;
1481  menu->win_index = attach;
1482  menu->win_ibar = ebar;
1483 
1484  menu->make_entry = snd_make_entry;
1485  menu->tag = attach_tag;
1487  menu->redraw_data = rd;
1488  mutt_menu_push_current(menu);
1489 
1490  struct AttachCtx *actx = mutt_actx_new();
1491  actx->email = e;
1492  mutt_update_compose_menu(actx, menu, true);
1493 
1494  update_crypt_info(rd);
1495 
1496  /* Since this is rather long lived, we don't use the pool */
1497  struct Buffer fname = mutt_buffer_make(PATH_MAX);
1498 
1499  bool redraw_env = false;
1500  while (loop)
1501  {
1502  if (redraw_env)
1503  {
1504  redraw_env = false;
1505  envelope->req_rows = calc_envelope(rd);
1506  mutt_window_reflow(dlg);
1507  }
1508 
1509 #ifdef USE_NNTP
1510  OptNews = false; /* for any case */
1511 #endif
1512  const int op = mutt_menu_loop(menu);
1513  if (op >= 0)
1514  mutt_debug(LL_DEBUG1, "Got op %s (%d)\n", OpStrings[op][0], op);
1515  switch (op)
1516  {
1517  case OP_COMPOSE_EDIT_FROM:
1518  if (edit_address_list(HDR_FROM, &e->env->from))
1519  {
1520  update_crypt_info(rd);
1522  redraw_env = true;
1523  }
1524  break;
1525 
1526  case OP_COMPOSE_EDIT_TO:
1527  {
1528 #ifdef USE_NNTP
1529  if (news)
1530  break;
1531 #endif
1532  if (edit_address_list(HDR_TO, &e->env->to))
1533  {
1534  update_crypt_info(rd);
1536  redraw_env = true;
1537  }
1538  break;
1539  }
1540 
1541  case OP_COMPOSE_EDIT_BCC:
1542  {
1543 #ifdef USE_NNTP
1544  if (news)
1545  break;
1546 #endif
1547  if (edit_address_list(HDR_BCC, &e->env->bcc))
1548  {
1549  update_crypt_info(rd);
1551  redraw_env = true;
1552  }
1553  break;
1554  }
1555 
1556  case OP_COMPOSE_EDIT_CC:
1557  {
1558 #ifdef USE_NNTP
1559  if (news)
1560  break;
1561 #endif
1562  if (edit_address_list(HDR_CC, &e->env->cc))
1563  {
1564  update_crypt_info(rd);
1566  redraw_env = true;
1567  }
1568  break;
1569  }
1570 
1571 #ifdef USE_NNTP
1572  case OP_COMPOSE_EDIT_NEWSGROUPS:
1573  if (!news)
1574  break;
1575  mutt_str_copy(buf, e->env->newsgroups, sizeof(buf));
1576  if (mutt_get_field(Prompts[HDR_NEWSGROUPS], buf, sizeof(buf), MUTT_COMP_NO_FLAGS) == 0)
1577  {
1578  mutt_str_replace(&e->env->newsgroups, buf);
1579  redraw_env = true;
1580  }
1581  break;
1582 
1583  case OP_COMPOSE_EDIT_FOLLOWUP_TO:
1584  if (!news)
1585  break;
1586  mutt_str_copy(buf, e->env->followup_to, sizeof(buf));
1587  if (mutt_get_field(Prompts[HDR_FOLLOWUPTO], buf, sizeof(buf), MUTT_COMP_NO_FLAGS) == 0)
1588  {
1589  mutt_str_replace(&e->env->followup_to, buf);
1590  redraw_env = true;
1591  }
1592  break;
1593 
1594  case OP_COMPOSE_EDIT_X_COMMENT_TO:
1595  if (!(news && C_XCommentTo))
1596  break;
1597  mutt_str_copy(buf, e->env->x_comment_to, sizeof(buf));
1598  if (mutt_get_field(Prompts[HDR_XCOMMENTTO], buf, sizeof(buf), MUTT_COMP_NO_FLAGS) == 0)
1599  {
1600  mutt_str_replace(&e->env->x_comment_to, buf);
1601  redraw_env = true;
1602  }
1603  break;
1604 #endif
1605 
1606  case OP_COMPOSE_EDIT_SUBJECT:
1607  mutt_str_copy(buf, e->env->subject, sizeof(buf));
1608  if (mutt_get_field(Prompts[HDR_SUBJECT], buf, sizeof(buf), MUTT_COMP_NO_FLAGS) == 0)
1609  {
1610  if (!mutt_str_equal(e->env->subject, buf))
1611  {
1612  mutt_str_replace(&e->env->subject, buf);
1613  redraw_env = true;
1615  }
1616  }
1617  break;
1618 
1619  case OP_COMPOSE_EDIT_REPLY_TO:
1621  {
1623  redraw_env = true;
1624  }
1625  break;
1626 
1627  case OP_COMPOSE_EDIT_FCC:
1628  mutt_buffer_copy(&fname, fcc);
1630  {
1631  if (!mutt_str_equal(fcc->data, fname.data))
1632  {
1633  mutt_buffer_copy(fcc, &fname);
1635  fcc_set = true;
1636  redraw_env = true;
1638  }
1639  }
1640  break;
1641 
1642  case OP_COMPOSE_EDIT_MESSAGE:
1643  if (!C_EditHeaders)
1644  {
1649  menu->redraw = REDRAW_FULL;
1650  /* Unconditional hook since editor was invoked */
1652  break;
1653  }
1654  /* fallthrough */
1655 
1656  case OP_COMPOSE_EDIT_HEADERS:
1658  const char *tag = NULL;
1659  char *err = NULL;
1660  mutt_env_to_local(e->env);
1662  if (mutt_env_to_intl(e->env, &tag, &err))
1663  {
1664  mutt_error(_("Bad IDN in '%s': '%s'"), tag, err);
1665  FREE(&err);
1666  }
1667  update_crypt_info(rd);
1668  redraw_env = true;
1669 
1672 
1673  /* attachments may have been added */
1674  if (actx->idxlen && actx->idx[actx->idxlen - 1]->body->next)
1675  {
1676  mutt_actx_entries_free(actx);
1677  mutt_update_compose_menu(actx, menu, true);
1678  }
1679 
1680  menu->redraw = REDRAW_FULL;
1681  /* Unconditional hook since editor was invoked */
1683  break;
1684 
1685  case OP_COMPOSE_ATTACH_KEY:
1686  {
1687  if (!(WithCrypto & APPLICATION_PGP))
1688  break;
1689  struct AttachPtr *ap = mutt_mem_calloc(1, sizeof(struct AttachPtr));
1691  if (ap->body)
1692  {
1693  update_idx(menu, actx, ap);
1694  menu->redraw |= REDRAW_INDEX;
1696  }
1697  else
1698  FREE(&ap);
1699 
1700  menu->redraw |= REDRAW_STATUS;
1701  break;
1702  }
1703 
1704  case OP_COMPOSE_MOVE_UP:
1705  if (menu->current == 0)
1706  {
1707  mutt_error(_("Attachment is already at top"));
1708  break;
1709  }
1710  if (menu->current == 1)
1711  {
1712  mutt_error(_("The fundamental part can't be moved"));
1713  break;
1714  }
1715  compose_attach_swap(e->body, actx->idx, menu->current - 1);
1716  menu->redraw |= REDRAW_INDEX;
1717  menu->current--;
1718  break;
1719 
1720  case OP_COMPOSE_MOVE_DOWN:
1721  if (menu->current == (actx->idxlen - 1))
1722  {
1723  mutt_error(_("Attachment is already at bottom"));
1724  break;
1725  }
1726  if (menu->current == 0)
1727  {
1728  mutt_error(_("The fundamental part can't be moved"));
1729  break;
1730  }
1731  compose_attach_swap(e->body, actx->idx, menu->current);
1732  menu->redraw |= REDRAW_INDEX;
1733  menu->current++;
1734  break;
1735 
1736  case OP_COMPOSE_GROUP_ALTS:
1737  {
1738  if (menu->tagged < 2)
1739  {
1740  mutt_error(
1741  _("Grouping 'alternatives' requires at least 2 tagged messages"));
1742  break;
1743  }
1744 
1745  struct Body *group = mutt_body_new();
1746  group->type = TYPE_MULTIPART;
1747  group->subtype = mutt_str_dup("alternative");
1748  group->disposition = DISP_INLINE;
1749 
1750  struct Body *alts = NULL;
1751  /* group tagged message into a multipart/alternative */
1752  struct Body *bptr = e->body;
1753  for (int i = 0; bptr;)
1754  {
1755  if (bptr->tagged)
1756  {
1757  bptr->tagged = false;
1758  bptr->disposition = DISP_INLINE;
1759 
1760  /* for first match, set group desc according to match */
1761 #define ALTS_TAG "Alternatives for \"%s\""
1762  if (!group->description)
1763  {
1764  char *p = bptr->description ? bptr->description : bptr->filename;
1765  if (p)
1766  {
1767  group->description =
1768  mutt_mem_calloc(1, strlen(p) + strlen(ALTS_TAG) + 1);
1769  sprintf(group->description, ALTS_TAG, p);
1770  }
1771  }
1772 
1773  // append bptr to the alts list, and remove from the e->body list
1774  if (alts)
1775  {
1776  alts->next = bptr;
1777  bptr = bptr->next;
1778  alts = alts->next;
1779  alts->next = NULL;
1780  }
1781  else
1782  {
1783  group->parts = bptr;
1784  alts = bptr;
1785  bptr = bptr->next;
1786  alts->next = NULL;
1787  }
1788 
1789  for (int j = i; j < actx->idxlen - 1; j++)
1790  {
1791  actx->idx[j] = actx->idx[j + 1];
1792  actx->idx[j + 1] = NULL; /* for debug reason */
1793  }
1794  actx->idxlen--;
1795  }
1796  else
1797  {
1798  bptr = bptr->next;
1799  i++;
1800  }
1801  }
1802 
1803  group->next = NULL;
1805 
1806  /* if no group desc yet, make one up */
1807  if (!group->description)
1808  group->description = mutt_str_dup("unknown alternative group");
1809 
1810  struct AttachPtr *gptr = mutt_mem_calloc(1, sizeof(struct AttachPtr));
1811  gptr->body = group;
1812  update_idx(menu, actx, gptr);
1813  menu->redraw |= REDRAW_INDEX;
1814  break;
1815  }
1816 
1817  case OP_COMPOSE_GROUP_LINGUAL:
1818  {
1819  if (menu->tagged < 2)
1820  {
1821  mutt_error(
1822  _("Grouping 'multilingual' requires at least 2 tagged messages"));
1823  break;
1824  }
1825 
1826  /* traverse to see whether all the parts have Content-Language: set */
1827  int tagged_with_lang_num = 0;
1828  for (struct Body *b = e->body; b; b = b->next)
1829  if (b->tagged && b->language && *b->language)
1830  tagged_with_lang_num++;
1831 
1832  if (menu->tagged != tagged_with_lang_num)
1833  {
1834  if (mutt_yesorno(
1835  _("Not all parts have 'Content-Language' set, continue?"), MUTT_YES) != MUTT_YES)
1836  {
1837  mutt_message(_("Not sending this message"));
1838  break;
1839  }
1840  }
1841 
1842  struct Body *group = mutt_body_new();
1843  group->type = TYPE_MULTIPART;
1844  group->subtype = mutt_str_dup("multilingual");
1845  group->disposition = DISP_INLINE;
1846 
1847  struct Body *alts = NULL;
1848  /* group tagged message into a multipart/multilingual */
1849  struct Body *bptr = e->body;
1850  for (int i = 0; bptr;)
1851  {
1852  if (bptr->tagged)
1853  {
1854  bptr->tagged = false;
1855  bptr->disposition = DISP_INLINE;
1856 
1857  /* for first match, set group desc according to match */
1858 #define LINGUAL_TAG "Multilingual part for \"%s\""
1859  if (!group->description)
1860  {
1861  char *p = bptr->description ? bptr->description : bptr->filename;
1862  if (p)
1863  {
1864  group->description =
1865  mutt_mem_calloc(1, strlen(p) + strlen(LINGUAL_TAG) + 1);
1866  sprintf(group->description, LINGUAL_TAG, p);
1867  }
1868  }
1869 
1870  // append bptr to the alts list, and remove from the e->body list
1871  if (alts)
1872  {
1873  alts->next = bptr;
1874  bptr = bptr->next;
1875  alts = alts->next;
1876  alts->next = NULL;
1877  }
1878  else
1879  {
1880  group->parts = bptr;
1881  alts = bptr;
1882  bptr = bptr->next;
1883  alts->next = NULL;
1884  }
1885 
1886  for (int j = i; j < actx->idxlen - 1; j++)
1887  {
1888  actx->idx[j] = actx->idx[j + 1];
1889  actx->idx[j + 1] = NULL; /* for debug reason */
1890  }
1891  actx->idxlen--;
1892  }
1893  else
1894  {
1895  bptr = bptr->next;
1896  i++;
1897  }
1898  }
1899 
1900  group->next = NULL;
1902 
1903  /* if no group desc yet, make one up */
1904  if (!group->description)
1905  group->description = mutt_str_dup("unknown multilingual group");
1906 
1907  struct AttachPtr *gptr = mutt_mem_calloc(1, sizeof(struct AttachPtr));
1908  gptr->body = group;
1909  update_idx(menu, actx, gptr);
1910  menu->redraw |= REDRAW_INDEX;
1911  break;
1912  }
1913 
1914  case OP_COMPOSE_ATTACH_FILE:
1915  {
1916  char *prompt = _("Attach file");
1917  int numfiles = 0;
1918  char **files = NULL;
1919 
1920  mutt_buffer_reset(&fname);
1921  if ((mutt_buffer_enter_fname_full(prompt, &fname, false, true, &files,
1922  &numfiles, MUTT_SEL_MULTI) == -1) ||
1923  mutt_buffer_is_empty(&fname))
1924  {
1925  for (int i = 0; i < numfiles; i++)
1926  FREE(&files[i]);
1927 
1928  FREE(&files);
1929  break;
1930  }
1931 
1932  bool error = false;
1933  bool added_attachment = false;
1934  if (numfiles > 1)
1935  {
1936  mutt_message(ngettext("Attaching selected file...",
1937  "Attaching selected files...", numfiles));
1938  }
1939  for (int i = 0; i < numfiles; i++)
1940  {
1941  char *att = files[i];
1942  struct AttachPtr *ap = mutt_mem_calloc(1, sizeof(struct AttachPtr));
1943  ap->unowned = true;
1944  ap->body = mutt_make_file_attach(att, NeoMutt->sub);
1945  if (ap->body)
1946  {
1947  added_attachment = true;
1948  update_idx(menu, actx, ap);
1949  }
1950  else
1951  {
1952  error = true;
1953  mutt_error(_("Unable to attach %s"), att);
1954  FREE(&ap);
1955  }
1956  FREE(&files[i]);
1957  }
1958 
1959  FREE(&files);
1960  if (!error)
1961  mutt_clear_error();
1962 
1963  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
1964  if (added_attachment)
1966  break;
1967  }
1968 
1969  case OP_COMPOSE_ATTACH_MESSAGE:
1970 #ifdef USE_NNTP
1971  case OP_COMPOSE_ATTACH_NEWS_MESSAGE:
1972 #endif
1973  {
1974  mutt_buffer_reset(&fname);
1975  char *prompt = _("Open mailbox to attach message from");
1976 
1977 #ifdef USE_NNTP
1978  OptNews = false;
1979  if (Context && (op == OP_COMPOSE_ATTACH_NEWS_MESSAGE))
1980  {
1982  if (!CurrentNewsSrv)
1983  break;
1984 
1985  prompt = _("Open newsgroup to attach message from");
1986  OptNews = true;
1987  }
1988 #endif
1989 
1990  if (Context)
1991  {
1992 #ifdef USE_NNTP
1993  if ((op == OP_COMPOSE_ATTACH_MESSAGE) ^ (Context->mailbox->type == MUTT_NNTP))
1994 #endif
1995  {
1998  }
1999  }
2000 
2001  if ((mutt_buffer_enter_fname(prompt, &fname, true) == -1) ||
2002  mutt_buffer_is_empty(&fname))
2003  {
2004  break;
2005  }
2006 
2007 #ifdef USE_NNTP
2008  if (OptNews)
2010  else
2011 #endif
2012  mutt_buffer_expand_path(&fname);
2013 #ifdef USE_IMAP
2014  if (imap_path_probe(mutt_b2s(&fname), NULL) != MUTT_IMAP)
2015 #endif
2016 #ifdef USE_POP
2017  if (pop_path_probe(mutt_b2s(&fname), NULL) != MUTT_POP)
2018 #endif
2019 #ifdef USE_NNTP
2020  if (!OptNews && (nntp_path_probe(mutt_b2s(&fname), NULL) != MUTT_NNTP))
2021 #endif
2022  if (mx_path_probe(mutt_b2s(&fname)) != MUTT_NOTMUCH)
2023  {
2024  /* check to make sure the file exists and is readable */
2025  if (access(mutt_b2s(&fname), R_OK) == -1)
2026  {
2027  mutt_perror(mutt_b2s(&fname));
2028  break;
2029  }
2030  }
2031 
2032  menu->redraw = REDRAW_FULL;
2033 
2034  struct Mailbox *m = mx_path_resolve(mutt_b2s(&fname));
2035  bool old_readonly = m->readonly;
2036  struct Context *ctx = mx_mbox_open(m, MUTT_READONLY);
2037  if (!ctx)
2038  {
2039  mutt_error(_("Unable to open mailbox %s"), mutt_b2s(&fname));
2041  m = NULL;
2042  break;
2043  }
2044 
2045  if (ctx->mailbox->msg_count == 0)
2046  {
2047  mx_mbox_close(&ctx);
2048  mutt_error(_("No messages in that folder"));
2049  break;
2050  }
2051 
2052  struct Context *ctx_cur = Context; /* remember current folder and sort methods */
2053  int old_sort = C_Sort; /* C_Sort, SortAux could be changed in mutt_index_menu() */
2054  int old_sort_aux = C_SortAux;
2055 
2056  Context = ctx;
2057  OptAttachMsg = true;
2058  mutt_message(_("Tag the messages you want to attach"));
2059  struct MuttWindow *dlgindex = index_pager_init();
2060  dialog_push(dlgindex);
2061  mutt_index_menu(dlgindex);
2062  dialog_pop();
2063  index_pager_shutdown(dlgindex);
2064  mutt_window_free(&dlgindex);
2065  OptAttachMsg = false;
2066 
2067  if (!Context)
2068  {
2069  /* go back to the folder we started from */
2070  Context = ctx_cur;
2071  /* Restore old $sort and $sort_aux */
2072  C_Sort = old_sort;
2073  C_SortAux = old_sort_aux;
2074  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
2075  break;
2076  }
2077 
2078  bool added_attachment = false;
2079  for (int i = 0; i < Context->mailbox->msg_count; i++)
2080  {
2081  if (!Context->mailbox->emails[i])
2082  break;
2084  continue;
2085 
2086  struct AttachPtr *ap = mutt_mem_calloc(1, sizeof(struct AttachPtr));
2088  Context->mailbox, Context->mailbox->emails[i], true, NeoMutt->sub);
2089  if (ap->body)
2090  {
2091  added_attachment = true;
2092  update_idx(menu, actx, ap);
2093  }
2094  else
2095  {
2096  mutt_error(_("Unable to attach"));
2097  FREE(&ap);
2098  }
2099  }
2100  menu->redraw |= REDRAW_FULL;
2101 
2102  Context->mailbox->readonly = old_readonly;
2104  ctx_free(&Context);
2105 
2106  /* go back to the folder we started from */
2107  Context = ctx_cur;
2108  /* Restore old $sort and $sort_aux */
2109  C_Sort = old_sort;
2110  C_SortAux = old_sort_aux;
2111  if (added_attachment)
2113  break;
2114  }
2115 
2116  case OP_DELETE:
2117  CHECK_COUNT;
2118  if (CUR_ATTACH->unowned)
2119  CUR_ATTACH->body->unlink = false;
2120  if (delete_attachment(actx, menu->current) == -1)
2121  break;
2122  mutt_update_compose_menu(actx, menu, false);
2123  if (menu->current == 0)
2124  e->body = actx->idx[0]->body;
2125 
2127  break;
2128 
2129  case OP_COMPOSE_TOGGLE_RECODE:
2130  {
2131  CHECK_COUNT;
2132  if (!mutt_is_text_part(CUR_ATTACH->body))
2133  {
2134  mutt_error(_("Recoding only affects text attachments"));
2135  break;
2136  }
2137  CUR_ATTACH->body->noconv = !CUR_ATTACH->body->noconv;
2138  if (CUR_ATTACH->body->noconv)
2139  mutt_message(_("The current attachment won't be converted"));
2140  else
2141  mutt_message(_("The current attachment will be converted"));
2142  menu->redraw |= REDRAW_CURRENT;
2144  break;
2145  }
2146 
2147  case OP_COMPOSE_EDIT_DESCRIPTION:
2148  CHECK_COUNT;
2149  mutt_str_copy(buf, CUR_ATTACH->body->description, sizeof(buf));
2150  /* header names should not be translated */
2151  if (mutt_get_field("Description: ", buf, sizeof(buf), MUTT_COMP_NO_FLAGS) == 0)
2152  {
2153  if (!mutt_str_equal(CUR_ATTACH->body->description, buf))
2154  {
2155  mutt_str_replace(&CUR_ATTACH->body->description, buf);
2156  menu->redraw |= REDRAW_CURRENT;
2158  }
2159  }
2160  break;
2161 
2162  case OP_COMPOSE_UPDATE_ENCODING:
2163  {
2164  CHECK_COUNT;
2165  bool encoding_updated = false;
2166  if (menu->tagprefix)
2167  {
2168  struct Body *top = NULL;
2169  for (top = e->body; top; top = top->next)
2170  {
2171  if (top->tagged)
2172  {
2173  encoding_updated = true;
2175  }
2176  }
2177  menu->redraw = REDRAW_FULL;
2178  }
2179  else
2180  {
2182  encoding_updated = true;
2183  menu->redraw |= REDRAW_CURRENT | REDRAW_STATUS;
2184  }
2185  if (encoding_updated)
2187  break;
2188  }
2189 
2190  case OP_COMPOSE_TOGGLE_DISPOSITION:
2191  /* toggle the content-disposition between inline/attachment */
2192  CUR_ATTACH->body->disposition =
2193  (CUR_ATTACH->body->disposition == DISP_INLINE) ? DISP_ATTACH : DISP_INLINE;
2194  menu->redraw |= REDRAW_CURRENT;
2195  break;
2196 
2197  case OP_EDIT_TYPE:
2198  CHECK_COUNT;
2199  {
2200  if (mutt_edit_content_type(NULL, CUR_ATTACH->body, NULL))
2201  {
2202  /* this may have been a change to text/something */
2204  menu->redraw |= REDRAW_CURRENT;
2206  }
2207  }
2208  break;
2209 
2210  case OP_COMPOSE_EDIT_LANGUAGE:
2211  CHECK_COUNT;
2212  mutt_str_copy(buf, CUR_ATTACH->body->language, sizeof(buf));
2213  if (mutt_get_field("Content-Language: ", buf, sizeof(buf), MUTT_COMP_NO_FLAGS) == 0)
2214  {
2215  if (!mutt_str_equal(CUR_ATTACH->body->language, buf))
2216  {
2217  CUR_ATTACH->body->language = mutt_str_dup(buf);
2218  menu->redraw |= REDRAW_CURRENT | REDRAW_STATUS;
2220  }
2221  mutt_clear_error();
2222  }
2223  else
2224  mutt_warning(_("Empty 'Content-Language'"));
2225  break;
2226 
2227  case OP_COMPOSE_EDIT_ENCODING:
2228  CHECK_COUNT;
2229  mutt_str_copy(buf, ENCODING(CUR_ATTACH->body->encoding), sizeof(buf));
2230  if ((mutt_get_field("Content-Transfer-Encoding: ", buf, sizeof(buf),
2231  MUTT_COMP_NO_FLAGS) == 0) &&
2232  (buf[0] != '\0'))
2233  {
2234  int enc = mutt_check_encoding(buf);
2235  if ((enc != ENC_OTHER) && (enc != ENC_UUENCODED))
2236  {
2237  if (enc != CUR_ATTACH->body->encoding)
2238  {
2239  CUR_ATTACH->body->encoding = enc;
2240  menu->redraw |= REDRAW_CURRENT | REDRAW_STATUS;
2241  mutt_clear_error();
2243  }
2244  }
2245  else
2246  mutt_error(_("Invalid encoding"));
2247  }
2248  break;
2249 
2250  case OP_COMPOSE_SEND_MESSAGE:
2251  /* Note: We don't invoke send2-hook here, since we want to leave
2252  * users an opportunity to change settings from the ":" prompt. */
2253  if (check_attachments(actx) != 0)
2254  {
2255  menu->redraw = REDRAW_FULL;
2256  break;
2257  }
2258 
2259 #ifdef MIXMASTER
2260  if (!STAILQ_EMPTY(&e->chain) && (mix_check_message(e) != 0))
2261  break;
2262 #endif
2263 
2264  if (!fcc_set && !mutt_buffer_is_empty(fcc))
2265  {
2266  enum QuadOption ans =
2267  query_quadoption(C_Copy, _("Save a copy of this message?"));
2268  if (ans == MUTT_ABORT)
2269  break;
2270  else if (ans == MUTT_NO)
2271  mutt_buffer_reset(fcc);
2272  }
2273 
2274  loop = false;
2275  rc = 0;
2276  break;
2277 
2278  case OP_COMPOSE_EDIT_FILE:
2279  CHECK_COUNT;
2280  mutt_edit_file(NONULL(C_Editor), CUR_ATTACH->body->filename);
2282  menu->redraw |= REDRAW_CURRENT | REDRAW_STATUS;
2283  /* Unconditional hook since editor was invoked */
2285  break;
2286 
2287  case OP_COMPOSE_TOGGLE_UNLINK:
2288  CHECK_COUNT;
2289  CUR_ATTACH->body->unlink = !CUR_ATTACH->body->unlink;
2290 
2291  menu->redraw |= REDRAW_INDEX;
2292  /* No send2hook since this doesn't change the message. */
2293  break;
2294 
2295  case OP_COMPOSE_GET_ATTACHMENT:
2296  CHECK_COUNT;
2297  if (menu->tagprefix)
2298  {
2299  for (struct Body *top = e->body; top; top = top->next)
2300  {
2301  if (top->tagged)
2303  }
2304  menu->redraw |= REDRAW_FULL;
2305  }
2306  else if (mutt_get_tmp_attachment(CUR_ATTACH->body) == 0)
2307  menu->redraw |= REDRAW_CURRENT;
2308 
2309  /* No send2hook since this doesn't change the message. */
2310  break;
2311 
2312  case OP_COMPOSE_RENAME_ATTACHMENT:
2313  {
2314  CHECK_COUNT;
2315  char *src = NULL;
2316  if (CUR_ATTACH->body->d_filename)
2317  src = CUR_ATTACH->body->d_filename;
2318  else
2319  src = CUR_ATTACH->body->filename;
2321  int ret = mutt_buffer_get_field(_("Send attachment with name: "), &fname, MUTT_FILE);
2322  if (ret == 0)
2323  {
2324  /* As opposed to RENAME_FILE, we don't check buf[0] because it's
2325  * valid to set an empty string here, to erase what was set */
2326  mutt_str_replace(&CUR_ATTACH->body->d_filename, mutt_b2s(&fname));
2327  menu->redraw |= REDRAW_CURRENT;
2328  }
2329  break;
2330  }
2331 
2332  case OP_COMPOSE_RENAME_FILE:
2333  CHECK_COUNT;
2334  mutt_buffer_strcpy(&fname, CUR_ATTACH->body->filename);
2336  if ((mutt_buffer_get_field(_("Rename to: "), &fname, MUTT_FILE) == 0) &&
2337  !mutt_buffer_is_empty(&fname))
2338  {
2339  struct stat st;
2340  if (stat(CUR_ATTACH->body->filename, &st) == -1)
2341  {
2342  /* L10N: "stat" is a system call. Do "man 2 stat" for more information. */
2343  mutt_error(_("Can't stat %s: %s"), mutt_b2s(&fname), strerror(errno));
2344  break;
2345  }
2346 
2347  mutt_buffer_expand_path(&fname);
2348  if (mutt_file_rename(CUR_ATTACH->body->filename, mutt_b2s(&fname)))
2349  break;
2350 
2351  mutt_str_replace(&CUR_ATTACH->body->filename, mutt_b2s(&fname));
2352  menu->redraw |= REDRAW_CURRENT;
2353 
2354  if (CUR_ATTACH->body->stamp >= st.st_mtime)
2357  }
2358  break;
2359 
2360  case OP_COMPOSE_NEW_MIME:
2361  {
2362  mutt_buffer_reset(&fname);
2363  if ((mutt_buffer_get_field(_("New file: "), &fname, MUTT_FILE) != 0) ||
2364  mutt_buffer_is_empty(&fname))
2365  {
2366  continue;
2367  }
2368  mutt_buffer_expand_path(&fname);
2369 
2370  /* Call to lookup_mime_type () ? maybe later */
2371  char type[256] = { 0 };
2372  if ((mutt_get_field("Content-Type: ", type, sizeof(type), MUTT_COMP_NO_FLAGS) != 0) ||
2373  (type[0] == '\0'))
2374  {
2375  continue;
2376  }
2377 
2378  char *p = strchr(type, '/');
2379  if (!p)
2380  {
2381  mutt_error(_("Content-Type is of the form base/sub"));
2382  continue;
2383  }
2384  *p++ = 0;
2385  enum ContentType itype = mutt_check_mime_type(type);
2386  if (itype == TYPE_OTHER)
2387  {
2388  mutt_error(_("Unknown Content-Type %s"), type);
2389  continue;
2390  }
2391  struct AttachPtr *ap = mutt_mem_calloc(1, sizeof(struct AttachPtr));
2392  /* Touch the file */
2393  FILE *fp = mutt_file_fopen(mutt_b2s(&fname), "w");
2394  if (!fp)
2395  {
2396  mutt_error(_("Can't create file %s"), mutt_b2s(&fname));
2397  FREE(&ap);
2398  continue;
2399  }
2400  mutt_file_fclose(&fp);
2401 
2402  ap->body = mutt_make_file_attach(mutt_b2s(&fname), NeoMutt->sub);
2403  if (!ap->body)
2404  {
2405  mutt_error(_("What we have here is a failure to make an attachment"));
2406  FREE(&ap);
2407  continue;
2408  }
2409  update_idx(menu, actx, ap);
2410 
2411  CUR_ATTACH->body->type = itype;
2412  mutt_str_replace(&CUR_ATTACH->body->subtype, p);
2413  CUR_ATTACH->body->unlink = true;
2414  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
2415 
2417  {
2419  menu->redraw = REDRAW_FULL;
2420  }
2422  break;
2423  }
2424 
2425  case OP_COMPOSE_EDIT_MIME:
2426  CHECK_COUNT;
2427  if (mutt_edit_attachment(CUR_ATTACH->body))
2428  {
2430  menu->redraw = REDRAW_FULL;
2432  }
2433  break;
2434 
2435  case OP_VIEW_ATTACH:
2436  case OP_DISPLAY_HEADERS:
2437  CHECK_COUNT;
2438  mutt_attach_display_loop(menu, op, NULL, actx, false);
2439  menu->redraw = REDRAW_FULL;
2440  /* no send2hook, since this doesn't modify the message */
2441  break;
2442 
2443  case OP_SAVE:
2444  CHECK_COUNT;
2445  mutt_save_attachment_list(actx, NULL, menu->tagprefix, CUR_ATTACH->body, NULL, menu);
2446  /* no send2hook, since this doesn't modify the message */
2447  break;
2448 
2449  case OP_PRINT:
2450  CHECK_COUNT;
2451  mutt_print_attachment_list(actx, NULL, menu->tagprefix, CUR_ATTACH->body);
2452  /* no send2hook, since this doesn't modify the message */
2453  break;
2454 
2455  case OP_PIPE:
2456  case OP_FILTER:
2457  CHECK_COUNT;
2458  mutt_pipe_attachment_list(actx, NULL, menu->tagprefix, CUR_ATTACH->body,
2459  (op == OP_FILTER));
2460  if (op == OP_FILTER) /* cte might have changed */
2461  menu->redraw |= menu->tagprefix ? REDRAW_FULL : REDRAW_CURRENT;
2462  menu->redraw |= REDRAW_STATUS;
2464  break;
2465 
2466  case OP_EXIT:
2467  {
2468  enum QuadOption ans =
2469  query_quadoption(C_Postpone, _("Save (postpone) draft message?"));
2470  if (ans == MUTT_NO)
2471  {
2472  for (int i = 0; i < actx->idxlen; i++)
2473  if (actx->idx[i]->unowned)
2474  actx->idx[i]->body->unlink = false;
2475 
2476  if (!(flags & MUTT_COMPOSE_NOFREEHEADER))
2477  {
2478  for (int i = 0; i < actx->idxlen; i++)
2479  {
2480  /* avoid freeing other attachments */
2481  actx->idx[i]->body->next = NULL;
2482  /* See the comment in delete_attachment() */
2483  if (!actx->idx[i]->body->email)
2484  actx->idx[i]->body->parts = NULL;
2485  mutt_body_free(&actx->idx[i]->body);
2486  }
2487  }
2488  rc = -1;
2489  loop = false;
2490  break;
2491  }
2492  else if (ans == MUTT_ABORT)
2493  break; /* abort */
2494  }
2495  /* fallthrough */
2496 
2497  case OP_COMPOSE_POSTPONE_MESSAGE:
2498  if (check_attachments(actx) != 0)
2499  {
2500  menu->redraw = REDRAW_FULL;
2501  break;
2502  }
2503 
2504  loop = false;
2505  rc = 1;
2506  break;
2507 
2508  case OP_COMPOSE_ISPELL:
2509  endwin();
2510  snprintf(buf, sizeof(buf), "%s -x %s", NONULL(C_Ispell), e->body->filename);
2511  if (mutt_system(buf) == -1)
2512  mutt_error(_("Error running \"%s\""), buf);
2513  else
2514  {
2516  menu->redraw |= REDRAW_STATUS;
2517  }
2518  break;
2519 
2520  case OP_COMPOSE_WRITE_MESSAGE:
2521  mutt_buffer_reset(&fname);
2522  if (Context)
2523  {
2526  }
2527  if (actx->idxlen)
2528  e->body = actx->idx[0]->body;
2529  if ((mutt_buffer_enter_fname(_("Write message to mailbox"), &fname, true) != -1) &&
2530  !mutt_buffer_is_empty(&fname))
2531  {
2532  mutt_message(_("Writing message to %s ..."), mutt_b2s(&fname));
2533  mutt_buffer_expand_path(&fname);
2534 
2535  if (e->body->next)
2536  e->body = mutt_make_multipart(e->body);
2537 
2538  if (mutt_write_fcc(mutt_b2s(&fname), e, NULL, false, NULL, NULL,
2539  NeoMutt->sub) == 0)
2540  mutt_message(_("Message written"));
2541 
2542  e->body = mutt_remove_multipart(e->body);
2543  }
2544  break;
2545 
2546  case OP_COMPOSE_PGP_MENU:
2547  {
2548  const SecurityFlags old_flags = e->security;
2549  if (!(WithCrypto & APPLICATION_PGP))
2550  break;
2551  if (!crypt_has_module_backend(APPLICATION_PGP))
2552  {
2553  mutt_error(_("No PGP backend configured"));
2554  break;
2555  }
2556  if (((WithCrypto & APPLICATION_SMIME) != 0) && (e->security & APPLICATION_SMIME))
2557  {
2558  if (e->security & (SEC_ENCRYPT | SEC_SIGN))
2559  {
2560  if (mutt_yesorno(_("S/MIME already selected. Clear and continue?"), MUTT_YES) != MUTT_YES)
2561  {
2562  mutt_clear_error();
2563  break;
2564  }
2565  e->security &= ~(SEC_ENCRYPT | SEC_SIGN);
2566  }
2567  e->security &= ~APPLICATION_SMIME;
2568  e->security |= APPLICATION_PGP;
2569  update_crypt_info(rd);
2570  }
2571  e->security = crypt_pgp_send_menu(e);
2572  update_crypt_info(rd);
2573  if (old_flags != e->security)
2574  {
2576  redraw_env = true;
2577  }
2578  break;
2579  }
2580 
2581  case OP_FORGET_PASSPHRASE:
2583  break;
2584 
2585  case OP_COMPOSE_SMIME_MENU:
2586  {
2587  const SecurityFlags old_flags = e->security;
2588  if (!(WithCrypto & APPLICATION_SMIME))
2589  break;
2590  if (!crypt_has_module_backend(APPLICATION_SMIME))
2591  {
2592  mutt_error(_("No S/MIME backend configured"));
2593  break;
2594  }
2595 
2596  if (((WithCrypto & APPLICATION_PGP) != 0) && (e->security & APPLICATION_PGP))
2597  {
2598  if (e->security & (SEC_ENCRYPT | SEC_SIGN))
2599  {
2600  if (mutt_yesorno(_("PGP already selected. Clear and continue?"), MUTT_YES) != MUTT_YES)
2601  {
2602  mutt_clear_error();
2603  break;
2604  }
2605  e->security &= ~(SEC_ENCRYPT | SEC_SIGN);
2606  }
2607  e->security &= ~APPLICATION_PGP;
2609  update_crypt_info(rd);
2610  }
2612  update_crypt_info(rd);
2613  if (old_flags != e->security)
2614  {
2616  redraw_env = true;
2617  }
2618  break;
2619  }
2620 
2621 #ifdef MIXMASTER
2622  case OP_COMPOSE_MIX:
2624  rd->win_envelope->state.cols);
2626  redraw_env = true;
2627  break;
2628 #endif
2629 
2630 #ifdef USE_AUTOCRYPT
2631  case OP_COMPOSE_AUTOCRYPT_MENU:
2632  {
2633  const SecurityFlags old_flags = e->security;
2634  if (!C_Autocrypt)
2635  break;
2636 
2637  if ((WithCrypto & APPLICATION_SMIME) && (e->security & APPLICATION_SMIME))
2638  {
2639  if (e->security & (SEC_ENCRYPT | SEC_SIGN))
2640  {
2641  if (mutt_yesorno(_("S/MIME already selected. Clear and continue?"), MUTT_YES) != MUTT_YES)
2642  {
2643  mutt_clear_error();
2644  break;
2645  }
2646  e->security &= ~(SEC_ENCRYPT | SEC_SIGN);
2647  }
2648  e->security &= ~APPLICATION_SMIME;
2649  e->security |= APPLICATION_PGP;
2650  update_crypt_info(rd);
2651  }
2653  update_crypt_info(rd);
2654  if (old_flags != e->security)
2655  {
2657  redraw_env = true;
2658  }
2659  break;
2660  }
2661 #endif
2662  }
2663  }
2664 
2665  mutt_buffer_dealloc(&fname);
2666 
2667 #ifdef USE_AUTOCRYPT
2668  /* This is a fail-safe to make sure the bit isn't somehow turned
2669  * on. The user could have disabled the option after setting SEC_AUTOCRYPT,
2670  * or perhaps resuming or replying to an autocrypt message. */
2671  if (!C_Autocrypt)
2672  e->security &= ~SEC_AUTOCRYPT;
2673 #endif
2674 
2675  mutt_menu_pop_current(menu);
2676  mutt_menu_free(&menu);
2677  dialog_pop();
2679  mutt_window_free(&dlg);
2680 
2681  if (actx->idxlen)
2682  e->body = actx->idx[0]->body;
2683  else
2684  e->body = NULL;
2685 
2686  mutt_actx_free(&actx);
2687 
2688  mutt_list_free(&redraw.to_list);
2689  mutt_list_free(&redraw.cc_list);
2690  mutt_list_free(&redraw.bcc_list);
2691  return rc;
2692 }
Mail will be encrypted.
Definition: color.h:67
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
Convenience wrapper for the gui headers.
struct Content * mutt_get_content_info(const char *fname, struct Body *b, struct ConfigSubset *sub)
Analyze file to determine MIME encoding to use.
Definition: sendlib.c:466
bool C_EditHeaders
Config: Let the user edit the email headers whilst editing an email.
Definition: config.c:41
#define mutt_warning(...)
Definition: logging.h:82
The "current" mailbox.
Definition: context.h:38
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:201
AutocryptRec
Recommendation.
Definition: lib.h:154
static int delete_attachment(struct AttachCtx *actx, int x)
Delete an attachment.
Definition: compose.c:1040
Bold text.
Definition: color.h:64
void mutt_stamp_attachment(struct Body *a)
Timestamp an Attachment.
Definition: sendlib.c:891
Unknown Content-Type.
Definition: mime.h:31
static int calc_user_hdrs(const struct ListHead *hdrs)
Calculate how many rows are needed for user-defined headers.
Definition: compose.c:497
#define REDRAW_FULL
Redraw everything.
Definition: mutt_menu.h:45
void mutt_actx_free(struct AttachCtx **ptr)
Free an Attachment Context.
Definition: attach.c:140
bool message_is_tagged(struct Context *ctx, struct Email *e)
Is a message in the index tagged (and within limit)
Definition: context.c:332
char * filename
when sending a message, this is the file to which this structure refers
Definition: body.h:46
Manage keymappings.
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
#define NONULL(x)
Definition: string2.h:37
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
An email to which things will be attached.
Definition: attach.h:34
int mutt_attach_display_loop(struct Menu *menu, int op, struct Email *e, struct AttachCtx *actx, bool recv)
Event loop for the Attachment menu.
Definition: recvattach.c:1281
int msg_count
Total number of messages.
Definition: mailbox.h:91
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
Representation of the email&#39;s header.
#define WithCrypto
Definition: lib.h:123
void mutt_expand_aliases(struct AddressList *al)
Expand aliases in a List of Addresses.
Definition: alias.c:294
IMAP network mailbox.
The envelope/body of an email.
Definition: email.h:37
#define MUTT_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:62
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define MIN(a, b)
Definition: memory.h:31
#define mutt_perror(...)
Definition: logging.h:85
#define MUTT_ALIAS
Do alias "completion" by calling up the alias-menu.
Definition: mutt.h:57
struct Content * content
Detailed info about the content of the attachment.
Definition: body.h:51
void mutt_update_encoding(struct Body *a, struct ConfigSubset *sub)
Update the encoding type.
Definition: sendlib.c:903
GUI selectable list of items.
Definition: mutt_menu.h:52
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe()
Definition: imap.c:2385
Config/command parsing.
Data passed to a notification function.
Definition: observer.h:39
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:36
struct Body * body
List of MIME parts.
Definition: email.h:91
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:52
int mx_mbox_close(struct Context **ptr)
Save changes and close mailbox.
Definition: mx.c:630
Structs that make up an email.
size_t mutt_buffer_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer&#39;s contents to another Buffer.
Definition: buffer.c:445
The "currently-open" mailbox.
struct AddressList reply_to
Email&#39;s &#39;reply-to&#39;.
Definition: envelope.h:62
Autocrypt end-to-end encryption.
Convenience wrapper for the send headers.
const char * mutt_path_basename(const char *f)
Find the last component for a pathname.
Definition: path.c:329
GUI manage the main index (list of emails)
#define mutt_message(...)
Definition: logging.h:83
struct MuttWindow * win_abar
Attachments label.
Definition: compose.c:126
int help_menu
Menu for key bindings, e.g. MENU_PAGER.
Definition: mutt_window.h:134
Autocrypt should be used.
Definition: lib.h:160
Window uses all available vertical space.
Definition: mutt_window.h:35
struct Email * email
Used by recvattach for updating.
Definition: attach.h:51
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition: file.c:1347
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition: address.c:1386
struct AddressList bcc
Email&#39;s &#39;Bcc&#39; list.
Definition: envelope.h:60
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1468
#define STAILQ_FOREACH_FROM(var, head, field)
Definition: queue.h:354
bool C_Autocrypt
Config: Enables the Autocrypt feature.
Definition: config.c:37
static void compose_custom_redraw(struct Menu *menu)
Redraw the compose menu - Implements Menu::custom_redraw()
Definition: compose.c:1169
void mutt_curses_set_color(enum ColorId color)
Set the current colour for text.
Definition: mutt_curses.c:57
#define REDRAW_FLOW
Used by pager to reflow text.
Definition: mutt_menu.h:47
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:85
#define MAX_USER_HDR_ROWS
Maximum number of rows to use for the Headers: field.
Definition: compose.c:97
void mutt_save_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top, struct Email *e, struct Menu *menu)
Save a list of attachments.
Definition: recvattach.c:722
struct MuttWindow * mutt_window_find(struct MuttWindow *root, enum WindowType type)
Find a Window of a given type.
Definition: mutt_window.c:688
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:254
int mutt_write_fcc(const char *path, struct Email *e, const char *msgid, bool post, const char *fcc, char **finalpath, struct ConfigSubset *sub)
Write email to FCC mailbox.
Definition: sendlib.c:1507
NeoMutt Logging.
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
"To:" field
Definition: compose.c:148
static const char * There_are_no_attachments
Definition: compose.c:99
#define MUTT_FORMAT_ARROWCURSOR
Reserve space for arrow_cursor.
Definition: format_flags.h:35
void crypt_forget_passphrase(void)
Forget a passphrase and display a message.
Definition: crypt.c:92
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
A config-change event.
Definition: subset.h:70
static int draw_envelope_addr(int field, struct AddressList *al, struct MuttWindow *win, int row, size_t max_lines)
Write addresses to the compose window.
Definition: compose.c:801
"Cc:" field
Definition: compose.c:149
int mutt_edit_attachment(struct Body *a)
Edit an attachment.
Definition: mutt_attach.c:258
String manipulation buffer.
Definition: buffer.h:33
static void calc_header_width_padding(int idx, const char *header, bool calc_max)
Calculate the width needed for the compose labels.
Definition: compose.c:281
static int MaxHeaderWidth
Definition: compose.c:172
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
struct ListHead userhdrs
user defined headers
Definition: envelope.h:83
static void draw_header_content(struct MuttWindow *win, int row, enum HeaderField field, const char *content)
Draw content on a separate line aligned to header prompt.
Definition: compose.c:415
#define _(a)
Definition: message.h:28
static void update_crypt_info(struct ComposeRedrawData *rd)
Update the crypto info.
Definition: compose.c:657
#define MAX_ADDR_ROWS
Maximum number of rows to use for the To:, Cc:, Bcc: fields.
Definition: compose.c:94
struct Body * next
next attachment in the list
Definition: body.h:53
short idxlen
Number of attachmentes.
Definition: attach.h:55
Compose an email.
Definition: keymap.h:76
struct Context * mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:303
static void redraw_mix_line(struct ListHead *chain, struct ComposeRedrawData *rd, int row)
Redraw the Mixmaster chain.
Definition: compose.c:698
static int calc_envelope(struct ComposeRedrawData *rd)
Calculate how many rows the envelope will need.
Definition: compose.c:515
A division of the screen.
Definition: mutt_window.h:115
#define SEC_AUTOCRYPT
(Autocrypt) Message will be, or was Autocrypt encrypt+signed
Definition: lib.h:94
char * C_NewsServer
Config: (nntp) Url of the news server.
Definition: config.c:41
void mutt_actx_add_attach(struct AttachCtx *actx, struct AttachPtr *attach)
Add an Attachment to an Attachment Context.
Definition: attach.c:40
uint16_t SecurityFlags
Flags, e.g. SEC_ENCRYPT.
Definition: lib.h:83
struct ListHead bcc_list
Definition: compose.c:114
struct Email * email
Definition: compose.c:109
struct Buffer * fcc
Definition: compose.c:110
WHERE struct Context * Context
Definition: mutt_globals.h:42
long hibin
8-bit characters
Definition: content.h:35
#define MUTT_READONLY
Open in read-only mode.
Definition: mx.h:54
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
int mutt_addrlist_parse2(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:616
"Subject:" field
Definition: compose.c:151
int mutt_window_mvaddstr(struct MuttWindow *win, int col, int row, const char *str)
Move the cursor and write a fixed string to a Window.
Definition: mutt_window.c:406
int mutt_env_to_intl(struct Envelope *env, const char **tag, char **err)
Convert an Envelope&#39;s Address fields to Punycode format.
Definition: envelope.c:309
#define MUTT_FORMAT_STAT_FILE
Used by attach_format_str.
Definition: format_flags.h:34
static int check_attachments(struct AttachCtx *actx)
Check if any attachments have changed or been deleted.
Definition: compose.c:737
"Sign as:" field (encryption/signing info)
Definition: compose.c:158
Flags to control mutt_expando_format()
"From:" field
Definition: compose.c:147
All user-callable functions.
static void mutt_gen_compose_attach_list(struct AttachCtx *actx, struct Body *m, int parent_type, int level)
Generate the attachment list for the compose screen.
Definition: compose.c:1096
static void draw_floating(struct MuttWindow *win, int col, int row, const char *text)
Draw a floating label.
Definition: compose.c:386
static int calc_security(struct Email *e, short *rows)
Calculate how many rows the security info will need.
Definition: compose.c:475
struct NntpAccountData * nntp_select_server(struct Mailbox *m, char *server, bool leave_lock)
Open a connection to an NNTP server.
Definition: newsrc.c:1008
Container for Accounts, Notifications.
Definition: neomutt.h:36
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:598
"Autocrypt:" and "Recommendation:" fields
Definition: compose.c:160
The "-- Attachments" line.
Definition: compose.c:168
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
#define mutt_get_field(field, buf, buflen, complete)
Definition: curs_lib.h:91
The body of an email.
Definition: body.h:34
void dlg_select_mixmaster_chain(struct MuttWindow *win, struct ListHead *chainhead, int cols)
Create a Mixmaster chain.
Definition: remailer.c:573
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
unsigned int disposition
content-disposition, ContentDisposition
Definition: body.h:67
Convenience wrapper for the config headers.
void mutt_paddstr(int n, const char *s)
Display a string on screen, padded if necessary.
Definition: curs_lib.c:1266
int mutt_window_move(struct MuttWindow *win, int col, int row)
Move the cursor in a Window.
Definition: mutt_window.c:392
GUI component for displaying/selecting items from a list.
An Index Window containing a selection list.
Definition: mutt_window.h:94
Email Address Handling.
#define SEC_INLINE
Email has an inline signature.
Definition: lib.h:92
static void snd_make_entry(char *buf, size_t buflen, struct Menu *menu, int line)
Format a menu item for the attachment list - Implements Menu::make_entry()
Definition: compose.c:331
char * tree
Tree characters to display.
Definition: attach.h:39
Assorted sorting methods.
void mutt_window_free(struct MuttWindow **ptr)
Free a Window and its children.
Definition: mutt_window.c:186
#define CUR_ATTACH
Definition: compose.c:138
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
enum NotifyType event_type
Send: Event type, e.g. NT_ACCOUNT.
Definition: observer.h:42
Some miscellaneous functions.
size_t dsize
Length of data.
Definition: buffer.h:37
void * redraw_data
Definition: mutt_menu.h:123
void mutt_print_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top)
Print a list of Attachments.
Definition: recvattach.c:1162
unsigned char C_Postpone
Config: Save messages to the $postponed folder.
Definition: config.c:43
int mutt_get_tmp_attachment(struct Body *a)
Get a temporary copy of an attachment.
Definition: mutt_attach.c:69
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: curs_lib.c:379
struct Mailbox * mailbox
Definition: context.h:50
Parse and execute user-defined hooks.
SecurityFlags mutt_is_multipart_encrypted(struct Body *b)
Does the message have encrypted parts?
Definition: crypt.c:447
#define MUTT_COMPOSE_NOFREEHEADER
Definition: lib.h:41
bool C_CryptOpportunisticEncrypt
Config: Enable encryption when the recipient&#39;s key is available.
Definition: config.c:56
Many unsorted constants and some structs.
API for mailboxes.
enum AutocryptRec autocrypt_rec
Definition: compose.c:122
#define STAILQ_INIT(head)
Definition: queue.h:369
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:56
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition: parse.c:324
struct AddressList from
Email&#39;s &#39;From&#39; list.
Definition: envelope.h:57
struct MuttWindow * win_ibar
Definition: mutt_menu.h:64
int crypt_smime_send_menu(struct Email *e)
Wrapper for CryptModuleSpecs::send_menu()
Definition: cryptglue.c:528
bool readonly
Don&#39;t allow changes to the mailbox.
Definition: mailbox.h:119
struct MuttWindow * win_envelope
Envelope: From, To, etc.
Definition: compose.c:125
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
static void draw_envelope(struct ComposeRedrawData *rd)
Write the email headers to the compose window.
Definition: compose.c:947
int parent_type
Type of parent attachment, e.g. TYPE_MULTIPART.
Definition: attach.h:38
struct Envelope * env
Envelope information.
Definition: email.h:90
Convenience wrapper for the core headers.
char * C_PgpSignAs
Config: Use this alternative key for signing messages.
Definition: config.c:67
struct Body * mutt_make_file_attach(const char *path, struct ConfigSubset *sub)
Create a file attachment.
Definition: sendlib.c:1082
void mutt_generate_boundary(struct ParameterList *pl)
Create a unique boundary id for a MIME part.
Definition: multipart.c:86
#define ENCODING(x)
Definition: mime.h:92
void mx_fastclose_mailbox(struct Mailbox *m)
free up memory associated with the Mailbox
Definition: mx.c:445
struct AddressList cc
Email&#39;s &#39;Cc&#39; list.
Definition: envelope.h:59
"X-Comment-To:" field
Definition: compose.c:165
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
Content is attached.
Definition: mime.h:63
void dialog_push(struct MuttWindow *dlg)
Display a Window to the user.
Definition: dialog.c:66
static void compose_attach_swap(struct Body *msg, struct AttachPtr **idx, short first)
Swap two adjacent entries in the attachment list.
Definition: compose.c:1217
void mutt_rfc3676_space_unstuff(struct Email *e)
Remove RFC3676 space stuffing.
Definition: rfc3676.c:492
struct AttachCtx * mutt_actx_new(void)
Create a new Attachment Context.
Definition: attach.c:131
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:60
bool tagged
This attachment is tagged.
Definition: body.h:70
Email Aliases.
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition: body.h:66
User aborted the question (with Ctrl-G)
Definition: quad.h:38
const char * OpStrings[][2]
Definition: opcodes.c:28
Window with a custom drawing function.
Definition: mutt_window.h:92
Base-64 encoded text.
Definition: mime.h:52
bool notify_observer_add(struct Notify *notify, enum NotifyType type, observer_t callback, void *global_data)
Add an observer to an object.
Definition: notify.c:169
static void compose_status_line(char *buf, size_t buflen, size_t col, int cols, struct Menu *menu, const char *p)
Compose the string for the status bar.
Definition: compose.c:1355
#define REDRAW_MOTION
Redraw after moving the menu list.
Definition: mutt_menu.h:41
void * global_data
Data from notify_observer_add()
Definition: observer.h:45
Usenet network mailbox type; talk to an NNTP server.
struct Body * mutt_make_multipart(struct Body *b)
Create a multipart email.
Definition: multipart.c:100
bool unowned
Don&#39;t unlink on detach.
Definition: attach.h:42
Window has a fixed size.
Definition: mutt_window.h:44
Plain text.
Definition: color.h:77
char * subtype
content-type subtype
Definition: body.h:37
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:834
int mutt_buffer_enter_fname_full(const char *prompt, struct Buffer *fname, bool mailbox, bool multiple, char ***files, int *numfiles, SelectFileFlags flags)
Ask the user to select a file.
Definition: curs_lib.c:768
#define REDRAW_MOTION_RESYNC
Redraw any changing the menu selection.
Definition: mutt_menu.h:42
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:57
Info about an attachment.
Definition: content.h:33
#define mutt_b2s(buf)
Definition: buffer.h:41
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:98
static const struct Mapping ComposeHelp[]
Help Bar for the Compose dialog.
Definition: compose.c:217
int mutt_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question.
Definition: curs_lib.c:933
void mutt_refresh(void)
Force a refresh of the screen.
Definition: curs_lib.c:108
Prototypes for many functions.
char * x_comment_to
List of &#39;X-comment-to&#39; fields.
Definition: envelope.h:78
struct ListHead chain
Mixmaster chain.
Definition: email.h:102
WHERE short C_SortAux
Config: Secondary sort method for the index.
Definition: sort.h:61
struct Body * mutt_remove_multipart(struct Body *b)
Extract the multipart body if it exists.
Definition: multipart.c:126
int attach_tag(struct Menu *menu, int sel, int act)
Tag an attachment - Implements Menu::tag()
Definition: recvattach.c:458
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:120
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:97
void * mdata
Extra data for the current menu.
Definition: mutt_menu.h:55
WHERE bool OptAttachMsg
(pseudo) used by attach-message
Definition: options.h:31
&#39;POP3&#39; Mailbox type
Definition: mailbox.h:55
#define REDRAW_STATUS
Redraw the status bar.
Definition: mutt_menu.h:44
int mutt_check_encoding(const char *c)
Check the encoding type.
Definition: parse.c:397
#define ALTS_TAG
WHERE char * C_Editor
Config: External command to use as an email editor.
Definition: mutt_globals.h:90
char * C_SmimeEncryptWith
Config: Algorithm for encryption.
Definition: config.c:71
Status bar (takes a pattern)
Definition: color.h:94
A mailbox.
Definition: mailbox.h:81
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1359
#define PATH_MAX
Definition: mutt.h:44
#define MUTT_WIN_SIZE_UNLIMITED
Use as much space as possible.
Definition: mutt_window.h:49
Header labels, e.g. From:
Definition: color.h:65
int num
Attachment index number.
Definition: attach.h:41
short rows
Number of rows, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:58
void mutt_attach_init(struct AttachCtx *actx)
Create a new Attachment context.
Definition: recvattach.c:1465
bool tagprefix
Definition: mutt_menu.h:61
long ascii
Number of ascii chars.
Definition: content.h:39
struct Body * mutt_make_message_attach(struct Mailbox *m, struct Email *e, bool attach_msg, struct ConfigSubset *sub)
Create a message attachment.
Definition: sendlib.c:935
Manage where the email is piped to external commands.
#define SEC_AUTOCRYPT_OVERRIDE
(Autocrypt) Indicates manual set/unset of encryption
Definition: lib.h:95
struct AttachPtr * aptr
Menu information, used in recvattach.c.
Definition: body.h:57
enum MailboxType nntp_path_probe(const char *path, const struct stat *st)
Is this an NNTP Mailbox? - Implements MxOps::path_probe()
Definition: nntp.c:2828
WHERE char * ShortHostname
Short version of the hostname.
Definition: mutt_globals.h:50
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1659
const struct Mapping * help_data
Data for the Help Bar.
Definition: mutt_window.h:135
char * data
Pointer to data.
Definition: buffer.h:35
static bool edit_address_list(int field, struct AddressList *al)
Let the user edit the address list.
Definition: compose.c:1007
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:54
int crypt_pgp_send_menu(struct Email *e)
Wrapper for CryptModuleSpecs::send_menu()
Definition: cryptglue.c:382
POP network mailbox.
RFC3676 Format Flowed routines.
FILE * fp
Used in the recvattach menu.
Definition: attach.h:37
GUI present the user with a selectable list.
API for encryption/signing of emails.
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:802
Mail will be signed.
Definition: color.h:69
GUI editor for an email&#39;s headers.
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
static void mutt_update_compose_menu(struct AttachCtx *actx, struct Menu *menu, bool init)
Redraw the compose window.
Definition: compose.c:1126
void(* custom_redraw)(struct Menu *menu)
Redraw the menu.
Definition: mutt_menu.h:121
Handling of email attachments.
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
int(* tag)(struct Menu *menu, int sel, int act)
Tag some menu items.
Definition: mutt_menu.h:107
char * description
content-description
Definition: body.h:40
int pagelen
Number of entries per screen.
Definition: mutt_menu.h:60
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:433
"Security:" field (encryption/signing info)
Definition: compose.c:157
char * C_Ispell
Config: External command to perform spell-checking.
Definition: config.c:42
static const char *const Prompts[]
Definition: compose.c:174
struct Notify * notify
Notifications handler.
Definition: neomutt.h:38
#define STAILQ_NEXT(elm, field)
Definition: queue.h:397
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib...
Definition: email.h:39
enum MailboxType pop_path_probe(const char *path, const struct stat *st)
Is this a POP Mailbox? - Implements MxOps::path_probe()
Definition: pop.c:1222
int mutt_index_menu(struct MuttWindow *dlg)
Display a list of emails.
Definition: index.c:1139
#define MUTT_SEL_MULTI
Multi-selection is enabled.
Definition: browser.h:42
#define SEC_OPPENCRYPT
Opportunistic encrypt mode.
Definition: lib.h:93
int tagged
Number of tagged entries.
Definition: mutt_menu.h:78
#define MUTT_FILE
Do file completion.
Definition: mutt.h:58
static int draw_envelope_user_hdrs(const struct ComposeRedrawData *rd, int row)
Write user-defined headers to the compose window.
Definition: compose.c:907
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
char * C_SmimeSignAs
Config: Use this alternative key for signing messages.
Definition: config.c:70
"Headers:" field
Definition: compose.c:167
unsigned int type
content-type primary type, ContentType
Definition: body.h:65
void * event_data
Data from notify_send()
Definition: observer.h:44
int max
Number of entries in the menu.
Definition: mutt_menu.h:57
void mutt_window_add_child(struct MuttWindow *parent, struct MuttWindow *child)
Add a child to Window.
Definition: mutt_window.c:575
&#39;Notmuch&#39; (virtual) Mailbox type
Definition: mailbox.h:54
int autocrypt_rec_override
Definition: compose.c:123
bool C_ComposeShowUserHeaders
Config: Whether to display user-defined headers.
Definition: config.c:39
"Bcc:" field
Definition: compose.c:150
"Followup-To:" field
Definition: compose.c:164
#define mutt_buffer_get_field(field, buf, complete)
Definition: curs_lib.h:85
size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
Write Addresses to a List.
Definition: address.c:1201
#define LINGUAL_TAG
MuttRedrawFlags redraw
When to redraw the screen.
Definition: mutt_menu.h:58
Config has changed, NotifyConfig, EventConfig.
Definition: notify_type.h:37
#define SEC_SIGN
Email is signed.
Definition: lib.h:86
#define TAILQ_INSERT_HEAD(head, elm, field)
Definition: queue.h:789
bool crypt_has_module_backend(SecurityFlags type)
Is there a crypto backend for a given type?
Definition: cryptglue.c:167
static const char * compose_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, intptr_t data, MuttFormatFlags flags)
Create the status bar string for compose mode - Implements format_t.
Definition: compose.c:1297
static void update_idx(struct Menu *menu, struct AttachCtx *actx, struct AttachPtr *ap)
Add a new attchment to the message.
Definition: compose.c:1155
struct Connection * conn
Definition: lib.h:102
void mutt_pipe_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top, bool filter)
Pipe a list of attachments to a command.
Definition: recvattach.c:1007
Mail will be encrypted and signed.
Definition: color.h:66
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: curs_lib.c:517
void mutt_env_to_local(struct Envelope *env)
Convert an Envelope&#39;s Address fields to local format.
Definition: envelope.c:271
int mutt_window_printf(const char *fmt,...)
Write a formatted string to a Window.
Definition: mutt_window.c:560
void mutt_edit_headers(const char *editor, const char *body, struct Email *e, struct Buffer *fcc)
Let the user edit the message header and body.
Definition: mutt_header.c:167
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:631
void crypt_opportunistic_encrypt(struct Email *e)
Can all recipients be determined.
Definition: crypt.c:1021
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1353
struct Body * crypt_pgp_make_key_attachment(void)
Wrapper for CryptModuleSpecs::pgp_make_key_attachment()
Definition: cryptglue.c:303
void mutt_actx_entries_free(struct AttachCtx *actx)
Free entries in an Attachment Context.
Definition: attach.c:103
long lobin
Unprintable 7-bit chars (eg., control chars)
Definition: content.h:36
char * data
String.
Definition: list.h:36
char * subject
Email&#39;s subject.
Definition: envelope.h:66
Type: &#39;multipart/*&#39;.
Definition: mime.h:37
enum AutocryptRec mutt_autocrypt_ui_recommendation(struct Email *e, char **keylist)
Get the recommended action for an Email.
Definition: autocrypt.c:551
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:323
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1304
Routines for managing attachments.
Log at debug level 1.
Definition: logging.h:40
"Mix:" field (Mixmaster chain)
Definition: compose.c:155
static unsigned long cum_attachs_size(struct Menu *menu)
Cumulative Attachments Size.
Definition: compose.c:1251
char * C_ComposeFormat
Config: printf-like format string for the Compose panel&#39;s status bar.
Definition: config.c:38
char * newsgroups
List of newsgroups.
Definition: envelope.h:75
#define MUTT_SEND2_HOOK
send2-hook: when changing fields in the compose menu
Definition: hook.h:56
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:716
#define REDRAW_INDEX
Redraw the index.
Definition: mutt_menu.h:40
Compose Dialog, mutt_compose_menu()
Definition: mutt_window.h:79
short * v2r
Mapping from virtual to real attachment.
Definition: attach.h:58
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
static int calc_address(struct AddressList *al, struct ListHead *slist, short cols, short *srows)
Calculate how many rows an AddressList will need.
Definition: compose.c:432
void mutt_window_reflow(struct MuttWindow *win)
Resize a Window and its children.
Definition: mutt_window.c:443
time_t stamp
Time stamp of last encoding update.
Definition: body.h:61
static int HeaderPadding[HDR_ATTACH_TITLE]
Definition: compose.c:171
WHERE bool C_StatusOnTop
Config: Display the status bar at the top.
Definition: mutt_globals.h:163
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t callback, intptr_t data, MuttFormatFlags flags)
Expand expandos (x) in a string.
Definition: muttlib.c:772
#define mutt_error(...)
Definition: logging.h:84
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition: body.h:69
void mutt_rfc3676_space_stuff(struct Email *e)
Perform RFC3676 space stuffing on an Email.
Definition: rfc3676.c:479
char * followup_to
List of &#39;followup-to&#39; fields.
Definition: envelope.h:77
void dialog_pop(void)
Hide a Window from the user.
Definition: dialog.c:98
Quoted-printable text.
Definition: mime.h:51
static void autocrypt_compose_menu(struct Email *e)
Autocrypt compose settings.
Definition: compose.c:345
void mutt_update_tree(struct AttachCtx *actx)
Refresh the list of attachments.
Definition: recvattach.c:151
Connection Library.
struct MuttWindow * win_cbar
Compose bar.
Definition: compose.c:128
HeaderField
Ordered list of headers for the compose screen.
Definition: compose.c:145
long crlf
\r and \n characters
Definition: content.h:38
struct ListHead cc_list
Definition: compose.c:113
static void draw_header(struct MuttWindow *win, int row, enum HeaderField field)
Draw an aligned label.
Definition: compose.c:399
#define FREE(x)
Definition: memory.h:40
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1696
const char * attach_format_str(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, intptr_t data, MuttFormatFlags flags)
Format a string for the attachment menu - Implements format_t.
Definition: recvattach.c:217
Mapping between user-readable string and a constant.
Definition: mapping.h:31
bool C_XCommentTo
Config: (nntp) Add &#39;X-Comment-To&#39; header that contains article author.
Definition: config.c:55
WHERE char * C_AttachFormat
Config: printf-like format string for the attachment menu.
Definition: mutt_globals.h:87
#define STAILQ_EMPTY(head)
Definition: queue.h:345
struct MuttWindow * win_attach
List of Attachments.
Definition: compose.c:127
struct MuttWindowList children
Children Windows.
Definition: mutt_window.h:129
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:321
void index_pager_shutdown(struct MuttWindow *dlg)
Clear up any non-Window parts.
Definition: index.c:4106
struct NntpAccountData * CurrentNewsSrv
Current NNTP news server.
Definition: nntp.c:76
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:39
short req_rows
Number of rows required.
Definition: mutt_window.h:118
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
struct AddressList to
Email&#39;s &#39;To&#39; list.
Definition: envelope.h:58
bool notify_observer_remove(struct Notify *notify, observer_t callback, void *global_data)
Remove an observer from an object.
Definition: notify.c:207
int mutt_window_mvprintw(struct MuttWindow *win, int col, int row, const char *fmt,...)
Move the cursor and write a formatted string to a Window.
Definition: mutt_window.c:425
"Newsgroups:" field
Definition: compose.c:163
Mail will not be encrypted or signed.
Definition: color.h:68
static void init_header_padding(void)
Calculate how much padding the compose table will need.
Definition: compose.c:300
int mutt_window_addstr(const char *str)
Write a string to a Window.
Definition: mutt_window.c:532
struct MuttWindow * index_pager_init(void)
Allocate the Windows for the Index/Pager.
Definition: index.c:4089
struct ListHead to_list
Definition: compose.c:112
void ctx_free(struct Context **ptr)
Free a Context.
Definition: context.c:50
Hundreds of global variables to back the user variables.
Handling of global boolean variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
void mutt_message_hook(struct Mailbox *m, struct Email *e, HookFlags type)
Perform a message hook.
Definition: hook.c:573
static int redraw_crypt_lines(struct ComposeRedrawData *rd, int row)
Update the encryption info in the compose window.
Definition: compose.c:552
struct MuttWindow * mutt_window_new(enum WindowType type, enum MuttWindowOrientation orient, enum MuttWindowSize size, int cols, int rows)
Create a new Window.
Definition: mutt_window.c:166
bool mutt_edit_content_type(struct Email *e, struct Body *b, FILE *fp)
Edit the content type of an attachment.
Definition: commands.c:1272
int current
Current entry.
Definition: mutt_menu.h:56
WHERE bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: options.h:46
"Fcc:" (save folder) field
Definition: compose.c:153
#define CHECK_COUNT
Definition: compose.c:131
struct MuttWindow * win_index
Definition: mutt_menu.h:63
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:588
A List node for strings.
Definition: list.h:34
void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *acct)
Make fully qualified url from newsgroup name.
Definition: newsrc.c:557
Window wants as much space as possible.
Definition: mutt_window.h:45
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
#define REDRAW_NO_FLAGS
No flags are set.
Definition: mutt_menu.h:39
Content is inline.
Definition: mime.h:62
struct ParameterList parameter
parameters of the content-type
Definition: body.h:39
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:53
#define N_(a)
Definition: message.h:32
struct Email * email
header information for message/rfc822
Definition: body.h:55
Index Bar containing status info about the Index.
Definition: mutt_window.h:95
static const char * AutocryptRecUiFlags[]
Definition: compose.c:251
#define STAILQ_FIRST(head)
Definition: queue.h:347
struct MuttWindow * dialog_find(struct MuttWindow *win)
Find the parent Dialog of a Window.
Definition: dialog.c:46
A set of attachments.
Definition: attach.h:49
const char * name
Name of config item that changed.
Definition: subset.h:73
int level
Nesting depth of attachment.
Definition: attach.h:40
Encoding unknown.
Definition: mime.h:48
struct AttachPtr ** idx
Array of attachments.
Definition: attach.h:54
void(* make_entry)(char *buf, size_t buflen, struct Menu *menu, int line)
Format a item for a menu.
Definition: mutt_menu.h:88
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:355
WHERE bool OptNews
(pseudo) used to change reader mode
Definition: options.h:45
size_t mutt_addrlist_write(const struct AddressList *al, char *buf, size_t buflen, bool display)
Write an Address to a buffer.
Definition: address.c:1150
Keep track when the compose screen needs redrawing.
Definition: compose.c:107
int mutt_system(const char *cmd)
Run an external command.
Definition: system.c:51
#define mutt_buffer_enter_fname(prompt, fname, mailbox)
Definition: curs_lib.h:88
Log at debug level 3.
Definition: logging.h:42
UUEncoded text.
Definition: mime.h:54
void mutt_draw_statusline(int cols, const char *buf, size_t buflen)
Draw a highlighted status bar.
Definition: index.c:960
int mutt_compose_menu(struct Email *e, struct Buffer *fcc, struct Email *e_cur, int flags)
Allow the user to edit the message envelope.
Definition: compose.c:1403
#define REDRAW_CURRENT
Redraw the current line of the menu.
Definition: mutt_menu.h:43
Support of Mixmaster anonymous remailer.
int mutt_compose_attachment(struct Body *a)
Create an attachment.
Definition: mutt_attach.c:119
int mix_check_message(struct Email *e)
Safety-check the message before passing it to mixmaster.
Definition: remailer.c:793
static int mutt_dlg_compose_observer(struct NotifyCallback *nc)
Listen for config changes affecting the Compose menu - Implements observer_t.
Definition: compose.c:1365
The header of an Email.
Definition: envelope.h:54
short vcount
The number of virtual attachments.
Definition: attach.h:59
static const struct Mapping ComposeNewsHelp[]
Help Bar for the News Compose dialog.
Definition: compose.c:236
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1460
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:40
ContentType
Content-Type.
Definition: mime.h:29
"Reply-To:" field
Definition: compose.c:152
struct Body * body
Attachment.
Definition: attach.h:36
unsigned char C_Copy
Config: Save outgoing emails to $record.
Definition: config.c:40