NeoMutt  2020-04-24
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 "mutt/lib.h"
41 #include "address/lib.h"
42 #include "config/lib.h"
43 #include "email/lib.h"
44 #include "core/lib.h"
45 #include "conn/lib.h"
46 #include "gui/lib.h"
47 #include "mutt.h"
48 #include "compose.h"
49 #include "alias.h"
50 #include "browser.h"
51 #include "commands.h"
52 #include "context.h"
53 #include "edit.h"
54 #include "format_flags.h"
55 #include "globals.h"
56 #include "hook.h"
57 #include "index.h"
58 #include "init.h"
59 #include "keymap.h"
60 #include "mutt_attach.h"
61 #include "mutt_header.h"
62 #include "mutt_logging.h"
63 #include "mutt_menu.h"
64 #include "muttlib.h"
65 #include "mx.h"
66 #include "opcodes.h"
67 #include "options.h"
68 #include "protos.h"
69 #include "recvattach.h"
70 #include "rfc3676.h"
71 #include "sendlib.h"
72 #include "sort.h"
73 #include "ncrypt/lib.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 
96 /* These Config Variables are only used in compose.c */
98 char *C_Ispell;
99 unsigned char C_Postpone;
100 
101 static const char *There_are_no_attachments = N_("There are no attachments");
102 
103 static void compose_status_line(char *buf, size_t buflen, size_t col, int cols,
104  struct Menu *menu, const char *p);
105 
110 {
111  struct Email *email;
112  struct Buffer *fcc;
113 
114  struct ListHead to_list;
115  struct ListHead cc_list;
116  struct ListHead bcc_list;
117 
118  short to_rows;
119  short cc_rows;
120  short bcc_rows;
121  short sec_rows;
122 
123 #ifdef USE_AUTOCRYPT
126 #endif
131 };
132 
133 #define CHECK_COUNT \
134  if (actx->idxlen == 0) \
135  { \
136  mutt_error(_(There_are_no_attachments)); \
137  break; \
138  }
139 
140 #define CUR_ATTACH actx->idx[actx->v2r[menu->current]]
141 
148 {
156 #ifdef MIXMASTER
158 #endif
161 #ifdef USE_AUTOCRYPT
163 #endif
164 #ifdef USE_NNTP
168 #endif
170 };
171 
174 
175 static const char *const Prompts[] = {
176  /* L10N: Compose menu field. May not want to translate. */
177  N_("From: "),
178  /* L10N: Compose menu field. May not want to translate. */
179  N_("To: "),
180  /* L10N: Compose menu field. May not want to translate. */
181  N_("Cc: "),
182  /* L10N: Compose menu field. May not want to translate. */
183  N_("Bcc: "),
184  /* L10N: Compose menu field. May not want to translate. */
185  N_("Subject: "),
186  /* L10N: Compose menu field. May not want to translate. */
187  N_("Reply-To: "),
188  /* L10N: Compose menu field. May not want to translate. */
189  N_("Fcc: "),
190 #ifdef MIXMASTER
191  /* L10N: "Mix" refers to the MixMaster chain for anonymous email */
192  N_("Mix: "),
193 #endif
194  /* L10N: Compose menu field. Holds "Encrypt", "Sign" related information */
195  N_("Security: "),
196  /* L10N: This string is used by the compose menu.
197  Since it is hidden by default, it does not increase the indentation of
198  other compose menu fields. However, if possible, it should not be longer
199  than the other compose menu fields. Since it shares the row with "Encrypt
200  with:", it should not be longer than 15-20 character cells. */
201  N_("Sign as: "),
202 #ifdef USE_AUTOCRYPT
203  // L10N: The compose menu autocrypt line
204  N_("Autocrypt: "),
205 #endif
206 #ifdef USE_NNTP
207  /* L10N: Compose menu field. May not want to translate. */
208  N_("Newsgroups: "),
209  /* L10N: Compose menu field. May not want to translate. */
210  N_("Followup-To: "),
211  /* L10N: Compose menu field. May not want to translate. */
212  N_("X-Comment-To: "),
213 #endif
214 };
215 
216 static const struct Mapping ComposeHelp[] = {
217  { N_("Send"), OP_COMPOSE_SEND_MESSAGE },
218  { N_("Abort"), OP_EXIT },
219  /* L10N: compose menu help line entry */
220  { N_("To"), OP_COMPOSE_EDIT_TO },
221  /* L10N: compose menu help line entry */
222  { N_("CC"), OP_COMPOSE_EDIT_CC },
223  /* L10N: compose menu help line entry */
224  { N_("Subj"), OP_COMPOSE_EDIT_SUBJECT },
225  { N_("Attach file"), OP_COMPOSE_ATTACH_FILE },
226  { N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION },
227  { N_("Help"), OP_HELP },
228  { NULL, 0 },
229 };
230 
231 #ifdef USE_NNTP
232 static struct Mapping ComposeNewsHelp[] = {
233  { N_("Send"), OP_COMPOSE_SEND_MESSAGE },
234  { N_("Abort"), OP_EXIT },
235  { N_("Newsgroups"), OP_COMPOSE_EDIT_NEWSGROUPS },
236  { N_("Subj"), OP_COMPOSE_EDIT_SUBJECT },
237  { N_("Attach file"), OP_COMPOSE_ATTACH_FILE },
238  { N_("Descrip"), OP_COMPOSE_EDIT_DESCRIPTION },
239  { N_("Help"), OP_HELP },
240  { NULL, 0 },
241 };
242 #endif
243 
244 #ifdef USE_AUTOCRYPT
245 static const char *AutocryptRecUiFlags[] = {
246  /* L10N: Autocrypt recommendation flag: off.
247  * This is displayed when Autocrypt is turned off. */
248  N_("Off"),
249  /* L10N: Autocrypt recommendation flag: no.
250  * This is displayed when Autocrypt cannot encrypt to the recipients. */
251  N_("No"),
252  /* L10N: Autocrypt recommendation flag: discouraged.
253  * This is displayed when Autocrypt believes encryption should not be used.
254  * This might occur if one of the recipient Autocrypt Keys has not been
255  * used recently, or if the only key available is a Gossip Header key. */
256  N_("Discouraged"),
257  /* L10N: Autocrypt recommendation flag: available.
258  * This is displayed when Autocrypt believes encryption is possible, but
259  * leaves enabling it up to the sender. Probably because "prefer encrypt"
260  * is not set in both the sender and recipient keys. */
261  N_("Available"),
262  /* L10N: Autocrypt recommendation flag: yes.
263  * This is displayed when Autocrypt would normally enable encryption
264  * automatically. */
265  N_("Yes"),
266 };
267 #endif
268 
275 static void calc_header_width_padding(int idx, const char *header, bool calc_max)
276 {
277  int width;
278 
279  HeaderPadding[idx] = mutt_str_strlen(header);
280  width = mutt_strwidth(header);
281  if (calc_max && (MaxHeaderWidth < width))
282  MaxHeaderWidth = width;
283  HeaderPadding[idx] -= width;
284 }
285 
294 static void init_header_padding(void)
295 {
296  static bool done = false;
297 
298  if (done)
299  return;
300  done = true;
301 
302  for (int i = 0; i < HDR_ATTACH_TITLE; i++)
303  {
304  if (i == HDR_CRYPTINFO)
305  continue;
306  calc_header_width_padding(i, _(Prompts[i]), true);
307  }
308 
309  /* Don't include "Sign as: " in the MaxHeaderWidth calculation. It
310  * doesn't show up by default, and so can make the indentation of
311  * the other fields look funny. */
313 
314  for (int i = 0; i < HDR_ATTACH_TITLE; i++)
315  {
317  if (HeaderPadding[i] < 0)
318  HeaderPadding[i] = 0;
319  }
320 }
321 
325 static void snd_make_entry(char *buf, size_t buflen, struct Menu *menu, int line)
326 {
327  struct AttachCtx *actx = menu->data;
328 
329  mutt_expando_format(buf, buflen, 0, menu->win_index->state.cols,
331  (unsigned long) (actx->idx[actx->v2r[line]]),
333 }
334 
335 #ifdef USE_AUTOCRYPT
336 
340 static void autocrypt_compose_menu(struct Email *e)
341 {
342  /* L10N: The compose menu autocrypt prompt.
343  (e)ncrypt enables encryption via autocrypt.
344  (c)lear sets cleartext.
345  (a)utomatic defers to the recommendation. */
346  const char *prompt = _("Autocrypt: (e)ncrypt, (c)lear, (a)utomatic?");
347 
349 
350  /* L10N: The letter corresponding to the compose menu autocrypt prompt
351  (e)ncrypt, (c)lear, (a)utomatic */
352  const char *letters = _("eca");
353 
354  int choice = mutt_multi_choice(prompt, letters);
355  switch (choice)
356  {
357  case 1:
360  break;
361  case 2:
362  e->security &= ~SEC_AUTOCRYPT;
364  break;
365  case 3:
368  e->security |= SEC_OPPENCRYPT;
369  break;
370  }
371 }
372 #endif
373 
381 static void draw_floating(struct MuttWindow *win, int row, int col, const char *text)
382 {
384  mutt_window_mvprintw(win, row, col, "%s", text);
386 }
387 
394 static void draw_header(struct MuttWindow *win, int row, enum HeaderField field)
395 {
397  mutt_window_mvprintw(win, row, 0, "%*s", HeaderPadding[field], _(Prompts[field]));
399 }
400 
411 static int calc_address(struct AddressList *al, struct ListHead *slist, short *srows, short cols)
412 {
413  mutt_list_free(slist);
414  mutt_addrlist_write_list(al, slist);
415 
416  int rows = 1;
417  int addr_len;
418  int width_left = cols;
419  struct ListNode *next = NULL;
420  struct ListNode *np = NULL;
421  STAILQ_FOREACH(np, slist, entries)
422  {
423  next = STAILQ_NEXT(np, entries);
424  addr_len = mutt_strwidth(np->data);
425  if (next)
426  addr_len += 2; // ", "
427 
428  try_again:
429  if (addr_len >= width_left)
430  {
431  if (width_left == cols)
432  break;
433 
434  rows++;
435  width_left = cols;
436  goto try_again;
437  }
438 
439  if (addr_len < width_left)
440  width_left -= addr_len;
441  }
442 
443  *srows = MIN(rows, MAX_ADDR_ROWS);
444  return *srows;
445 }
446 
453 static int calc_security(struct Email *e, short *rows)
454 {
456  *rows = 0; // Neither PGP nor SMIME are built into NeoMutt
457  else if ((e->security & (SEC_ENCRYPT | SEC_SIGN)) != 0)
458  *rows = 2; // 'Security:' and 'Sign as:'
459  else
460  *rows = 1; // Just 'Security:'
461 
462 #ifdef USE_AUTOCRYPT
463  if (C_Autocrypt)
464  *rows += 1;
465 #endif
466 
467  return *rows;
468 }
469 
475 static int calc_envelope(struct ComposeRedrawData *rd)
476 {
477  int rows = 4; // 'From:', 'Subject:', 'Reply-To:', 'Fcc:'
478 #ifdef MIXMASTER
479  rows++;
480 #endif
481 
482  struct Email *e = rd->email;
483  struct Envelope *env = e->env;
484  const int cols = rd->win_envelope->state.cols - MaxHeaderWidth;
485 
486 #ifdef USE_NNTP
487  if (OptNewsSend)
488  {
489  rows += 2; // 'Newsgroups:' and 'Followup-To:'
490  if (C_XCommentTo)
491  rows++;
492  }
493  else
494 #endif
495  {
496  rows += calc_address(&env->to, &rd->to_list, &rd->to_rows, cols);
497  rows += calc_address(&env->cc, &rd->cc_list, &rd->cc_rows, cols);
498  rows += calc_address(&env->bcc, &rd->bcc_list, &rd->bcc_rows, cols);
499  }
500  rows += calc_security(e, &rd->sec_rows);
501 
502  return rows;
503 }
504 
510 static int redraw_crypt_lines(struct ComposeRedrawData *rd, int row)
511 {
512  struct Email *e = rd->email;
513 
514  draw_header(rd->win_envelope, row++, HDR_CRYPT);
515 
517  return 0;
518 
519  // We'll probably need two lines for 'Security:' and 'Sign as:'
520  int used = 2;
521  if ((e->security & (SEC_ENCRYPT | SEC_SIGN)) == (SEC_ENCRYPT | SEC_SIGN))
522  {
524  mutt_window_addstr(_("Sign, Encrypt"));
525  }
526  else if (e->security & SEC_ENCRYPT)
527  {
529  mutt_window_addstr(_("Encrypt"));
530  }
531  else if (e->security & SEC_SIGN)
532  {
534  mutt_window_addstr(_("Sign"));
535  }
536  else
537  {
538  /* L10N: This refers to the encryption of the email, e.g. "Security: None" */
540  mutt_window_addstr(_("None"));
541  used = 1; // 'Sign as:' won't be needed
542  }
544 
545  if ((e->security & (SEC_ENCRYPT | SEC_SIGN)))
546  {
547  if (((WithCrypto & APPLICATION_PGP) != 0) && (e->security & APPLICATION_PGP))
548  {
549  if ((e->security & SEC_INLINE))
550  mutt_window_addstr(_(" (inline PGP)"));
551  else
552  mutt_window_addstr(_(" (PGP/MIME)"));
553  }
554  else if (((WithCrypto & APPLICATION_SMIME) != 0) && (e->security & APPLICATION_SMIME))
555  mutt_window_addstr(_(" (S/MIME)"));
556  }
557 
559  mutt_window_addstr(_(" (OppEnc mode)"));
560 
562 
563  if (((WithCrypto & APPLICATION_PGP) != 0) &&
564  (e->security & APPLICATION_PGP) && (e->security & SEC_SIGN))
565  {
567  mutt_window_printf("%s", C_PgpSignAs ? C_PgpSignAs : _("<default>"));
568  }
569 
570  if (((WithCrypto & APPLICATION_SMIME) != 0) &&
571  (e->security & APPLICATION_SMIME) && (e->security & SEC_SIGN))
572  {
574  mutt_window_printf("%s", C_SmimeSignAs ? C_SmimeSignAs : _("<default>"));
575  }
576 
577  if (((WithCrypto & APPLICATION_SMIME) != 0) && (e->security & APPLICATION_SMIME) &&
579  {
580  draw_floating(rd->win_envelope, row - 1, 40, _("Encrypt with: "));
582  }
583 
584 #ifdef USE_AUTOCRYPT
585  if (C_Autocrypt)
586  {
588  if (e->security & SEC_AUTOCRYPT)
589  {
591  mutt_window_addstr(_("Encrypt"));
592  }
593  else
594  {
596  mutt_window_addstr(_("Off"));
597  }
598 
599  /* L10N: The autocrypt compose menu Recommendation field.
600  Displays the output of the recommendation engine
601  (Off, No, Discouraged, Available, Yes) */
602  draw_floating(rd->win_envelope, row, 40, _("Recommendation: "));
604 
605  used++;
606  }
607 #endif
608  return used;
609 }
610 
615 static void update_crypt_info(struct ComposeRedrawData *rd)
616 {
617  struct Email *e = rd->email;
618 
621 
622 #ifdef USE_AUTOCRYPT
623  if (C_Autocrypt)
624  {
626 
627  /* Anything that enables SEC_ENCRYPT or SEC_SIGN, or turns on SMIME
628  * overrides autocrypt, be it oppenc or the user having turned on
629  * those flags manually. */
632  else
633  {
634  if (!(e->security & SEC_AUTOCRYPT_OVERRIDE))
635  {
636  if (rd->autocrypt_rec == AUTOCRYPT_REC_YES)
637  {
640  }
641  else
642  e->security &= ~SEC_AUTOCRYPT;
643  }
644  }
645  }
646 #endif
647 }
648 
649 #ifdef MIXMASTER
650 
656 static void redraw_mix_line(struct ListHead *chain, struct ComposeRedrawData *rd, int row)
657 {
658  char *t = NULL;
659 
660  draw_header(rd->win_envelope, row, HDR_MIX);
661 
662  if (STAILQ_EMPTY(chain))
663  {
664  mutt_window_addstr(_("<no chain defined>"));
666  return;
667  }
668 
669  int c = 12;
670  struct ListNode *np = NULL;
671  STAILQ_FOREACH(np, chain, entries)
672  {
673  t = np->data;
674  if (t && (t[0] == '0') && (t[1] == '\0'))
675  t = "<random>";
676 
677  if (c + mutt_str_strlen(t) + 2 >= rd->win_envelope->state.cols)
678  break;
679 
681  if (STAILQ_NEXT(np, entries))
682  mutt_window_addstr(", ");
683 
684  c += mutt_str_strlen(t) + 2;
685  }
686 }
687 #endif
688 
695 static int check_attachments(struct AttachCtx *actx)
696 {
697  int rc = -1;
698  struct stat st;
699  struct Buffer *pretty = NULL, *msg = NULL;
700 
701  for (int i = 0; i < actx->idxlen; i++)
702  {
703  if (actx->idx[i]->content->type == TYPE_MULTIPART)
704  continue;
705  if (stat(actx->idx[i]->content->filename, &st) != 0)
706  {
707  if (!pretty)
708  pretty = mutt_buffer_pool_get();
709  mutt_buffer_strcpy(pretty, actx->idx[i]->content->filename);
711  /* L10N: This message is displayed in the compose menu when an attachment
712  doesn't stat. %d is the attachment number and %s is the attachment
713  filename. The filename is located last to avoid a long path hiding
714  the error message. */
715  mutt_error(_("Attachment #%d no longer exists: %s"), i + 1, mutt_b2s(pretty));
716  goto cleanup;
717  }
718 
719  if (actx->idx[i]->content->stamp < st.st_mtime)
720  {
721  if (!pretty)
722  pretty = mutt_buffer_pool_get();
723  mutt_buffer_strcpy(pretty, actx->idx[i]->content->filename);
725 
726  if (!msg)
727  msg = mutt_buffer_pool_get();
728  /* L10N: This message is displayed in the compose menu when an attachment
729  is modified behind the scenes. %d is the attachment number and %s is
730  the attachment filename. The filename is located last to avoid a long
731  path hiding the prompt question. */
732  mutt_buffer_printf(msg, _("Attachment #%d modified. Update encoding for %s?"),
733  i + 1, mutt_b2s(pretty));
734 
735  enum QuadOption ans = mutt_yesorno(mutt_b2s(msg), MUTT_YES);
736  if (ans == MUTT_YES)
737  mutt_update_encoding(actx->idx[i]->content);
738  else if (ans == MUTT_ABORT)
739  goto cleanup;
740  }
741  }
742 
743  rc = 0;
744 
745 cleanup:
746  mutt_buffer_pool_release(&pretty);
748  return rc;
749 }
750 
759 static int draw_envelope_addr(int field, struct AddressList *al,
760  struct MuttWindow *win, int row, size_t max_lines)
761 {
762  draw_header(win, row, field);
763 
764  struct ListHead list = STAILQ_HEAD_INITIALIZER(list);
765  int count = mutt_addrlist_write_list(al, &list);
766 
767  int lines_used = 1;
768  int width_left = win->state.cols - MaxHeaderWidth;
769  char more[32];
770  int more_len = 0;
771 
772  char *sep = NULL;
773  struct ListNode *next = NULL;
774  struct ListNode *np = NULL;
775  STAILQ_FOREACH(np, &list, entries)
776  {
777  next = STAILQ_NEXT(np, entries);
778  int addr_len = mutt_strwidth(np->data);
779  if (next)
780  {
781  sep = ", ";
782  addr_len += 2;
783  }
784  else
785  {
786  sep = "";
787  }
788 
789  count--;
790  try_again:
791  more_len = snprintf(more, sizeof(more),
792  ngettext("(+%d more)", "(+%d more)", count), count);
793  mutt_debug(LL_DEBUG3, "text: '%s' len: %d\n", more, more_len);
794 
795  int reserve = ((count > 0) && (lines_used == max_lines)) ? more_len : 0;
796  mutt_debug(LL_DEBUG3, "processing: %s (al:%ld, wl:%d, r:%d, lu:%ld)\n",
797  np->data, addr_len, width_left, reserve, lines_used);
798  if (addr_len >= (width_left - reserve))
799  {
800  mutt_debug(LL_DEBUG3, "not enough space\n");
801  if (lines_used == max_lines)
802  {
803  mutt_debug(LL_DEBUG3, "no more lines\n");
804  mutt_debug(LL_DEBUG3, "truncating: %s\n", np->data);
805  mutt_paddstr(width_left, np->data);
806  break;
807  }
808 
809  if (width_left == (win->state.cols - MaxHeaderWidth))
810  {
811  mutt_debug(LL_DEBUG3, "couldn't print: %s\n", np->data);
812  mutt_paddstr(width_left, np->data);
813  break;
814  }
815 
816  mutt_debug(LL_DEBUG3, "start a new line\n");
818  row++;
819  lines_used++;
820  width_left = win->state.cols - MaxHeaderWidth;
821  mutt_window_move(win, row, MaxHeaderWidth);
822  goto try_again;
823  }
824 
825  if (addr_len < width_left)
826  {
827  mutt_debug(LL_DEBUG3, "space for: %s\n", np->data);
829  mutt_window_addstr(sep);
830  width_left -= addr_len;
831  }
832  mutt_debug(LL_DEBUG3, "%ld addresses remaining\n", count);
833  mutt_debug(LL_DEBUG3, "%ld lines remaining\n", max_lines - lines_used);
834  }
835  mutt_list_free(&list);
836 
837  if (count > 0)
838  {
839  mutt_window_move(win, row, win->state.cols - more_len);
841  mutt_window_addstr(more);
843  mutt_debug(LL_DEBUG3, "%ld more (len %d)\n", count, more_len);
844  }
845  else
846  {
848  }
849 
850  for (int i = lines_used; i < max_lines; i++)
851  {
852  mutt_window_move(win, row + i, 0);
854  }
855 
856  mutt_debug(LL_DEBUG3, "used %d lines\n", lines_used);
857  return lines_used;
858 }
859 
864 static void draw_envelope(struct ComposeRedrawData *rd)
865 {
866  struct Email *e = rd->email;
867  const char *fcc = mutt_b2s(rd->fcc);
868  const int cols = rd->win_envelope->state.cols - MaxHeaderWidth;
869 
870  int row = draw_envelope_addr(HDR_FROM, &e->env->from, rd->win_envelope, 0, 1);
871 
872 #ifdef USE_NNTP
873  if (OptNewsSend)
874  {
876  mutt_paddstr(cols, NONULL(e->env->newsgroups));
877 
879  mutt_paddstr(cols, NONULL(e->env->followup_to));
880 
881  if (C_XCommentTo)
882  {
884  mutt_paddstr(cols, NONULL(e->env->x_comment_to));
885  }
886  }
887  else
888 #endif
889  {
890  row += draw_envelope_addr(HDR_TO, &e->env->to, rd->win_envelope, row, rd->to_rows);
891  row += draw_envelope_addr(HDR_CC, &e->env->cc, rd->win_envelope, row, rd->cc_rows);
892  row += draw_envelope_addr(HDR_BCC, &e->env->bcc, rd->win_envelope, row, rd->bcc_rows);
893  }
894 
895  draw_header(rd->win_envelope, row++, HDR_SUBJECT);
896  mutt_paddstr(cols, NONULL(e->env->subject));
897 
898  row += draw_envelope_addr(HDR_REPLYTO, &e->env->reply_to, rd->win_envelope, row, 1);
899 
900  draw_header(rd->win_envelope, row++, HDR_FCC);
901  mutt_paddstr(cols, fcc);
902 
903  if (WithCrypto)
904  row += redraw_crypt_lines(rd, row);
905 
906 #ifdef MIXMASTER
907  redraw_mix_line(&e->chain, rd, row++);
908 #endif
909 
911  mutt_window_mvaddstr(rd->win_abar, 0, 0, _("-- Attachments"));
914 }
915 
921 static void edit_address_list(int field, struct AddressList *al)
922 {
923  char buf[8192] = { 0 }; /* needs to be large for alias expansion */
924 
926  mutt_addrlist_write(al, buf, sizeof(buf), false);
927  if (mutt_get_field(_(Prompts[field]), buf, sizeof(buf), MUTT_ALIAS) == 0)
928  {
930  mutt_addrlist_parse2(al, buf);
932  }
933 
934  char *err = NULL;
935  if (mutt_addrlist_to_intl(al, &err) != 0)
936  {
937  mutt_error(_("Bad IDN: '%s'"), err);
938  mutt_refresh();
939  FREE(&err);
940  }
941 }
942 
950 static int delete_attachment(struct AttachCtx *actx, int x)
951 {
952  struct AttachPtr **idx = actx->idx;
953  int rindex = actx->v2r[x];
954 
955  if ((rindex == 0) && (actx->idxlen == 1))
956  {
957  mutt_error(_("You may not delete the only attachment"));
958  idx[rindex]->content->tagged = false;
959  return -1;
960  }
961 
962  for (int y = 0; y < actx->idxlen; y++)
963  {
964  if (idx[y]->content->next == idx[rindex]->content)
965  {
966  idx[y]->content->next = idx[rindex]->content->next;
967  break;
968  }
969  }
970 
971  idx[rindex]->content->next = NULL;
972  /* mutt_make_message_attach() creates body->parts, shared by
973  * body->email->content. If we NULL out that, it creates a memory
974  * leak because mutt_free_body() frees body->parts, not
975  * body->email->content.
976  *
977  * Other mutt_send_message() message constructors are careful to free
978  * any body->parts, removing depth:
979  * - mutt_prepare_template() used by postponed, resent, and draft files
980  * - mutt_copy_body() used by the recvattach menu and $forward_attachments.
981  *
982  * I believe it is safe to completely remove the "content->parts =
983  * NULL" statement. But for safety, am doing so only for the case
984  * it must be avoided: message attachments.
985  */
986  if (!idx[rindex]->content->email)
987  idx[rindex]->content->parts = NULL;
988  mutt_body_free(&(idx[rindex]->content));
989  FREE(&idx[rindex]->tree);
990  FREE(&idx[rindex]);
991  for (; rindex < actx->idxlen - 1; rindex++)
992  idx[rindex] = idx[rindex + 1];
993  idx[actx->idxlen - 1] = NULL;
994  actx->idxlen--;
995 
996  return 0;
997 }
998 
1006 static void mutt_gen_compose_attach_list(struct AttachCtx *actx, struct Body *m,
1007  int parent_type, int level)
1008 {
1009  for (; m; m = m->next)
1010  {
1011  if ((m->type == TYPE_MULTIPART) && m->parts &&
1013  {
1014  mutt_gen_compose_attach_list(actx, m->parts, m->type, level);
1015  }
1016  else
1017  {
1018  struct AttachPtr *ap = mutt_mem_calloc(1, sizeof(struct AttachPtr));
1019  mutt_actx_add_attach(actx, ap);
1020  ap->content = m;
1021  m->aptr = ap;
1022  ap->parent_type = parent_type;
1023  ap->level = level;
1024 
1025  /* We don't support multipart messages in the compose menu yet */
1026  }
1027  }
1028 }
1029 
1036 static void mutt_update_compose_menu(struct AttachCtx *actx, struct Menu *menu, bool init)
1037 {
1038  if (init)
1039  {
1040  mutt_gen_compose_attach_list(actx, actx->email->content, -1, 0);
1041  mutt_attach_init(actx);
1042  menu->data = actx;
1043  }
1044 
1045  mutt_update_tree(actx);
1046 
1047  menu->max = actx->vcount;
1048  if (menu->max)
1049  {
1050  if (menu->current >= menu->max)
1051  menu->current = menu->max - 1;
1052  }
1053  else
1054  menu->current = 0;
1055 
1056  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
1057 }
1058 
1065 static void update_idx(struct Menu *menu, struct AttachCtx *actx, struct AttachPtr *ap)
1066 {
1067  ap->level = (actx->idxlen > 0) ? actx->idx[actx->idxlen - 1]->level : 0;
1068  if (actx->idxlen)
1069  actx->idx[actx->idxlen - 1]->content->next = ap->content;
1070  ap->content->aptr = ap;
1071  mutt_actx_add_attach(actx, ap);
1072  mutt_update_compose_menu(actx, menu, false);
1073  menu->current = actx->vcount - 1;
1074 }
1075 
1079 static void compose_custom_redraw(struct Menu *menu)
1080 {
1081  struct ComposeRedrawData *rd = menu->redraw_data;
1082  if (!rd)
1083  return;
1084 
1085  if (menu->redraw & REDRAW_FLOW)
1086  {
1087  rd->win_envelope->req_rows = calc_envelope(rd);
1089  }
1090 
1091  if (menu->redraw & REDRAW_FULL)
1092  {
1093  menu_redraw_full(menu);
1094  draw_envelope(rd);
1095  menu->pagelen = menu->win_index->state.rows;
1096  }
1097 
1098  menu_check_recenter(menu);
1099 
1100  if (menu->redraw & REDRAW_STATUS)
1101  {
1102  char buf[1024];
1103  compose_status_line(buf, sizeof(buf), 0, menu->win_ibar->state.cols, menu,
1105  mutt_window_move(menu->win_ibar, 0, 0);
1107  mutt_draw_statusline(menu->win_ibar->state.cols, buf, sizeof(buf));
1109  menu->redraw &= ~REDRAW_STATUS;
1110  }
1111 
1112 #ifdef USE_SIDEBAR
1113  if (menu->redraw & REDRAW_SIDEBAR)
1114  menu_redraw_sidebar(menu);
1115 #endif
1116 
1117  if (menu->redraw & REDRAW_INDEX)
1118  menu_redraw_index(menu);
1119  else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNC))
1120  menu_redraw_motion(menu);
1121  else if (menu->redraw == REDRAW_CURRENT)
1122  menu_redraw_current(menu);
1123 }
1124 
1131 static void compose_attach_swap(struct Body *msg, struct AttachPtr **idx, short first)
1132 {
1133  /* Reorder Body pointers.
1134  * Must traverse msg from top since Body has no previous ptr. */
1135  for (struct Body *part = msg; part; part = part->next)
1136  {
1137  if (part->next == idx[first]->content)
1138  {
1139  idx[first]->content->next = idx[first + 1]->content->next;
1140  idx[first + 1]->content->next = idx[first]->content;
1141  part->next = idx[first + 1]->content;
1142  break;
1143  }
1144  }
1145 
1146  /* Reorder index */
1147  struct AttachPtr *saved = idx[first];
1148  idx[first] = idx[first + 1];
1149  idx[first + 1] = saved;
1150 
1151  /* Swap ptr->num */
1152  int i = idx[first]->num;
1153  idx[first]->num = idx[first + 1]->num;
1154  idx[first + 1]->num = i;
1155 }
1156 
1165 static unsigned long cum_attachs_size(struct Menu *menu)
1166 {
1167  size_t s = 0;
1168  struct AttachCtx *actx = menu->data;
1169  struct AttachPtr **idx = actx->idx;
1170  struct Content *info = NULL;
1171  struct Body *b = NULL;
1172 
1173  for (unsigned short i = 0; i < actx->idxlen; i++)
1174  {
1175  b = idx[i]->content;
1176 
1177  if (!b->content)
1179 
1180  info = b->content;
1181  if (info)
1182  {
1183  switch (b->encoding)
1184  {
1185  case ENC_QUOTED_PRINTABLE:
1186  s += 3 * (info->lobin + info->hibin) + info->ascii + info->crlf;
1187  break;
1188  case ENC_BASE64:
1189  s += (4 * (info->lobin + info->hibin + info->ascii + info->crlf)) / 3;
1190  break;
1191  default:
1192  s += info->lobin + info->hibin + info->ascii + info->crlf;
1193  break;
1194  }
1195  }
1196  }
1197 
1198  return s;
1199 }
1200 
1211 static const char *compose_format_str(char *buf, size_t buflen, size_t col, int cols,
1212  char op, const char *src, const char *prec,
1213  const char *if_str, const char *else_str,
1214  unsigned long data, MuttFormatFlags flags)
1215 {
1216  char fmt[128], tmp[128];
1217  bool optional = (flags & MUTT_FORMAT_OPTIONAL);
1218  struct Menu *menu = (struct Menu *) data;
1219 
1220  *buf = '\0';
1221  switch (op)
1222  {
1223  case 'a': /* total number of attachments */
1224  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
1225  snprintf(buf, buflen, fmt, menu->max);
1226  break;
1227 
1228  case 'h': /* hostname */
1229  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
1230  snprintf(buf, buflen, fmt, NONULL(ShortHostname));
1231  break;
1232 
1233  case 'l': /* approx length of current message in bytes */
1234  snprintf(fmt, sizeof(fmt), "%%%ss", prec);
1235  mutt_str_pretty_size(tmp, sizeof(tmp), menu ? cum_attachs_size(menu) : 0);
1236  snprintf(buf, buflen, fmt, tmp);
1237  break;
1238 
1239  case 'v':
1240  snprintf(buf, buflen, "%s", mutt_make_version());
1241  break;
1242 
1243  case 0:
1244  *buf = '\0';
1245  return src;
1246 
1247  default:
1248  snprintf(buf, buflen, "%%%s%c", prec, op);
1249  break;
1250  }
1251 
1252  if (optional)
1253  compose_status_line(buf, buflen, col, cols, menu, if_str);
1254  else if (flags & MUTT_FORMAT_OPTIONAL)
1255  compose_status_line(buf, buflen, col, cols, menu, else_str);
1256 
1257  return src;
1258 }
1259 
1269 static void compose_status_line(char *buf, size_t buflen, size_t col, int cols,
1270  struct Menu *menu, const char *src)
1271 {
1272  mutt_expando_format(buf, buflen, col, cols, src, compose_format_str,
1273  (unsigned long) menu, MUTT_FORMAT_NO_FLAGS);
1274 }
1275 
1280 {
1281  if (!nc->event_data || !nc->global_data)
1282  return -1;
1283  if (nc->event_type != NT_CONFIG)
1284  return 0;
1285 
1286  struct EventConfig *ec = nc->event_data;
1287  struct MuttWindow *dlg = nc->global_data;
1288 
1289  if (mutt_str_strcmp(ec->name, "status_on_top") != 0)
1290  return 0;
1291 
1292  struct MuttWindow *win_ebar = mutt_window_find(dlg, WT_INDEX_BAR);
1293  if (!win_ebar)
1294  return 0;
1295 
1296  TAILQ_REMOVE(&dlg->children, win_ebar, entries);
1297 
1298  if (C_StatusOnTop)
1299  TAILQ_INSERT_HEAD(&dlg->children, win_ebar, entries);
1300  else
1301  TAILQ_INSERT_TAIL(&dlg->children, win_ebar, entries);
1302 
1303  mutt_window_reflow(dlg);
1304  return 0;
1305 }
1306 
1317 int mutt_compose_menu(struct Email *e, struct Buffer *fcc, struct Email *e_cur, int flags)
1318 {
1319  char helpstr[1024]; // This isn't copied by the help bar
1320  char buf[PATH_MAX];
1321  int rc = -1;
1322  bool loop = true;
1323  bool fcc_set = false; /* has the user edited the Fcc: field ? */
1324  struct ComposeRedrawData redraw = { 0 };
1325 
1326  STAILQ_INIT(&redraw.to_list);
1327  STAILQ_INIT(&redraw.cc_list);
1328  STAILQ_INIT(&redraw.bcc_list);
1329 
1330  struct ComposeRedrawData *rd = &redraw;
1331 #ifdef USE_NNTP
1332  bool news = OptNewsSend; /* is it a news article ? */
1333 #endif
1334 
1336 
1337  struct MuttWindow *dlg =
1340 #ifdef USE_DEBUG_WINDOW
1341  dlg->name = "compose";
1342 #endif
1343  dlg->type = WT_DIALOG;
1344 
1345  struct MuttWindow *envelope =
1348  envelope->type = WT_PAGER;
1349 
1350  struct MuttWindow *abar = mutt_window_new(
1352  abar->type = WT_PAGER_BAR;
1353 
1354  struct MuttWindow *attach =
1357  attach->type = WT_INDEX;
1358 
1359  struct MuttWindow *ebar = mutt_window_new(
1361  ebar->type = WT_INDEX_BAR;
1362 
1363  rd->email = e;
1364  rd->fcc = fcc;
1365  rd->win_envelope = envelope;
1366  rd->win_cbar = ebar;
1367  rd->win_attach = attach;
1368  rd->win_abar = abar;
1369 
1370  if (C_StatusOnTop)
1371  {
1372  mutt_window_add_child(dlg, ebar);
1373  mutt_window_add_child(dlg, envelope);
1374  mutt_window_add_child(dlg, abar);
1375  mutt_window_add_child(dlg, attach);
1376  }
1377  else
1378  {
1379  mutt_window_add_child(dlg, envelope);
1380  mutt_window_add_child(dlg, abar);
1381  mutt_window_add_child(dlg, attach);
1382  mutt_window_add_child(dlg, ebar);
1383  }
1384 
1386  dialog_push(dlg);
1387 
1388  envelope->req_rows = calc_envelope(rd);
1389  mutt_window_reflow(dlg);
1390 
1391  struct Menu *menu = mutt_menu_new(MENU_COMPOSE);
1392 
1393  menu->pagelen = attach->state.rows;
1394  menu->win_index = attach;
1395  menu->win_ibar = ebar;
1396 
1397  menu->make_entry = snd_make_entry;
1398  menu->tag = attach_tag;
1399 #ifdef USE_NNTP
1400  if (news)
1401  menu->help = mutt_compile_help(helpstr, sizeof(helpstr), MENU_COMPOSE, ComposeNewsHelp);
1402  else
1403 #endif
1404  menu->help = mutt_compile_help(helpstr, sizeof(helpstr), MENU_COMPOSE, ComposeHelp);
1406  menu->redraw_data = rd;
1407  mutt_menu_push_current(menu);
1408 
1409  struct AttachCtx *actx = mutt_actx_new();
1410  actx->email = e;
1411  mutt_update_compose_menu(actx, menu, true);
1412 
1413  update_crypt_info(rd);
1414 
1415  /* Since this is rather long lived, we don't use the pool */
1416  struct Buffer fname = mutt_buffer_make(PATH_MAX);
1417 
1418  bool redraw_env = false;
1419  while (loop)
1420  {
1421  if (redraw_env)
1422  {
1423  redraw_env = false;
1424  envelope->req_rows = calc_envelope(rd);
1425  mutt_window_reflow(dlg);
1426  }
1427 
1428 #ifdef USE_NNTP
1429  OptNews = false; /* for any case */
1430 #endif
1431  const int op = mutt_menu_loop(menu);
1432  switch (op)
1433  {
1434  case OP_COMPOSE_EDIT_FROM:
1436  update_crypt_info(rd);
1438  redraw_env = true;
1439  break;
1440 
1441  case OP_COMPOSE_EDIT_TO:
1442  {
1443 #ifdef USE_NNTP
1444  if (news)
1445  break;
1446 #endif
1447  edit_address_list(HDR_TO, &e->env->to);
1448  update_crypt_info(rd);
1450  redraw_env = true;
1451  break;
1452  }
1453 
1454  case OP_COMPOSE_EDIT_BCC:
1455  {
1456 #ifdef USE_NNTP
1457  if (news)
1458  break;
1459 #endif
1461  update_crypt_info(rd);
1463  redraw_env = true;
1464  break;
1465  }
1466 
1467  case OP_COMPOSE_EDIT_CC:
1468  {
1469 #ifdef USE_NNTP
1470  if (news)
1471  break;
1472 #endif
1473  edit_address_list(HDR_CC, &e->env->cc);
1474  update_crypt_info(rd);
1476  redraw_env = true;
1477  break;
1478  }
1479 
1480 #ifdef USE_NNTP
1481  case OP_COMPOSE_EDIT_NEWSGROUPS:
1482  if (!news)
1483  break;
1484  if (e->env->newsgroups)
1485  mutt_str_strfcpy(buf, e->env->newsgroups, sizeof(buf));
1486  else
1487  buf[0] = '\0';
1488  if (mutt_get_field(Prompts[HDR_NEWSGROUPS], buf, sizeof(buf), MUTT_COMP_NO_FLAGS) == 0)
1489  {
1490  mutt_str_replace(&e->env->newsgroups, buf);
1491  redraw_env = true;
1492  }
1493  break;
1494 
1495  case OP_COMPOSE_EDIT_FOLLOWUP_TO:
1496  if (!news)
1497  break;
1498  if (e->env->followup_to)
1499  mutt_str_strfcpy(buf, e->env->followup_to, sizeof(buf));
1500  else
1501  buf[0] = '\0';
1502  if (mutt_get_field(Prompts[HDR_FOLLOWUPTO], buf, sizeof(buf), MUTT_COMP_NO_FLAGS) == 0)
1503  {
1504  mutt_str_replace(&e->env->followup_to, buf);
1505  redraw_env = true;
1506  }
1507  break;
1508 
1509  case OP_COMPOSE_EDIT_X_COMMENT_TO:
1510  if (!(news && C_XCommentTo))
1511  break;
1512  if (e->env->x_comment_to)
1513  mutt_str_strfcpy(buf, e->env->x_comment_to, sizeof(buf));
1514  else
1515  buf[0] = '\0';
1516  if (mutt_get_field(Prompts[HDR_XCOMMENTTO], buf, sizeof(buf), MUTT_COMP_NO_FLAGS) == 0)
1517  {
1518  mutt_str_replace(&e->env->x_comment_to, buf);
1519  redraw_env = true;
1520  }
1521  break;
1522 #endif
1523 
1524  case OP_COMPOSE_EDIT_SUBJECT:
1525  if (e->env->subject)
1526  mutt_str_strfcpy(buf, e->env->subject, sizeof(buf));
1527  else
1528  buf[0] = '\0';
1529  if (mutt_get_field(Prompts[HDR_SUBJECT], buf, sizeof(buf), MUTT_COMP_NO_FLAGS) == 0)
1530  {
1531  mutt_str_replace(&e->env->subject, buf);
1532  redraw_env = true;
1533  }
1535  break;
1536 
1537  case OP_COMPOSE_EDIT_REPLY_TO:
1540  redraw_env = true;
1541  break;
1542 
1543  case OP_COMPOSE_EDIT_FCC:
1544  mutt_buffer_copy(&fname, fcc);
1546  {
1547  mutt_buffer_copy(fcc, &fname);
1549  fcc_set = true;
1550  redraw_env = true;
1551  }
1553  break;
1554 
1555  case OP_COMPOSE_EDIT_MESSAGE:
1556  if (C_Editor && (mutt_str_strcmp("builtin", C_Editor) != 0) && !C_EditHeaders)
1557  {
1562  menu->redraw = REDRAW_FULL;
1564  break;
1565  }
1566  /* fallthrough */
1567 
1568  case OP_COMPOSE_EDIT_HEADERS:
1570  if ((mutt_str_strcmp("builtin", C_Editor) != 0) &&
1571  ((op == OP_COMPOSE_EDIT_HEADERS) || ((op == OP_COMPOSE_EDIT_MESSAGE) && C_EditHeaders)))
1572  {
1573  const char *tag = NULL;
1574  char *err = NULL;
1575  mutt_env_to_local(e->env);
1577  if (mutt_env_to_intl(e->env, &tag, &err))
1578  {
1579  mutt_error(_("Bad IDN in '%s': '%s'"), tag, err);
1580  FREE(&err);
1581  }
1582  update_crypt_info(rd);
1583  redraw_env = true;
1584  }
1585  else
1586  {
1587  /* this is grouped with OP_COMPOSE_EDIT_HEADERS because the
1588  * attachment list could change if the user invokes ~v to edit
1589  * the message with headers, in which we need to execute the
1590  * code below to regenerate the index array */
1591  mutt_builtin_editor(e->content->filename, e, e_cur);
1592  }
1593 
1596 
1597  /* attachments may have been added */
1598  if (actx->idxlen && actx->idx[actx->idxlen - 1]->content->next)
1599  {
1600  mutt_actx_entries_free(actx);
1601  mutt_update_compose_menu(actx, menu, true);
1602  }
1603 
1604  menu->redraw = REDRAW_FULL;
1606  break;
1607 
1608  case OP_COMPOSE_ATTACH_KEY:
1609  {
1610  if (!(WithCrypto & APPLICATION_PGP))
1611  break;
1612  struct AttachPtr *ap = mutt_mem_calloc(1, sizeof(struct AttachPtr));
1614  if (ap->content)
1615  {
1616  update_idx(menu, actx, ap);
1617  menu->redraw |= REDRAW_INDEX;
1618  }
1619  else
1620  FREE(&ap);
1621 
1622  menu->redraw |= REDRAW_STATUS;
1623 
1625  break;
1626  }
1627 
1628  case OP_COMPOSE_MOVE_UP:
1629  if (menu->current == 0)
1630  {
1631  mutt_error(_("Attachment is already at top"));
1632  break;
1633  }
1634  if (menu->current == 1)
1635  {
1636  mutt_error(_("The fundamental part can't be moved"));
1637  break;
1638  }
1639  compose_attach_swap(e->content, actx->idx, menu->current - 1);
1640  menu->redraw = REDRAW_INDEX;
1641  menu->current--;
1642  break;
1643 
1644  case OP_COMPOSE_MOVE_DOWN:
1645  if (menu->current == (actx->idxlen - 1))
1646  {
1647  mutt_error(_("Attachment is already at bottom"));
1648  break;
1649  }
1650  if (menu->current == 0)
1651  {
1652  mutt_error(_("The fundamental part can't be moved"));
1653  break;
1654  }
1655  compose_attach_swap(e->content, actx->idx, menu->current);
1656  menu->redraw = REDRAW_INDEX;
1657  menu->current++;
1658  break;
1659 
1660  case OP_COMPOSE_GROUP_ALTS:
1661  {
1662  if (menu->tagged < 2)
1663  {
1664  mutt_error(
1665  _("Grouping 'alternatives' requires at least 2 tagged messages"));
1666  break;
1667  }
1668 
1669  struct Body *group = mutt_body_new();
1670  group->type = TYPE_MULTIPART;
1671  group->subtype = mutt_str_strdup("alternative");
1672  group->disposition = DISP_INLINE;
1673 
1674  struct Body *alts = NULL;
1675  /* group tagged message into a multipart/alternative */
1676  struct Body *bptr = e->content;
1677  for (int i = 0; bptr;)
1678  {
1679  if (bptr->tagged)
1680  {
1681  bptr->tagged = false;
1682  bptr->disposition = DISP_INLINE;
1683 
1684  /* for first match, set group desc according to match */
1685 #define ALTS_TAG "Alternatives for \"%s\""
1686  if (!group->description)
1687  {
1688  char *p = bptr->description ? bptr->description : bptr->filename;
1689  if (p)
1690  {
1691  group->description =
1692  mutt_mem_calloc(1, strlen(p) + strlen(ALTS_TAG) + 1);
1693  sprintf(group->description, ALTS_TAG, p);
1694  }
1695  }
1696 
1697  // append bptr to the alts list, and remove from the e->content list
1698  if (alts)
1699  {
1700  alts->next = bptr;
1701  bptr = bptr->next;
1702  alts = alts->next;
1703  alts->next = NULL;
1704  }
1705  else
1706  {
1707  group->parts = bptr;
1708  alts = bptr;
1709  bptr = bptr->next;
1710  alts->next = NULL;
1711  }
1712 
1713  for (int j = i; j < actx->idxlen - 1; j++)
1714  {
1715  actx->idx[j] = actx->idx[j + 1];
1716  actx->idx[j + 1] = NULL; /* for debug reason */
1717  }
1718  actx->idxlen--;
1719  }
1720  else
1721  {
1722  bptr = bptr->next;
1723  i++;
1724  }
1725  }
1726 
1727  group->next = NULL;
1729 
1730  /* if no group desc yet, make one up */
1731  if (!group->description)
1732  group->description = mutt_str_strdup("unknown alternative group");
1733 
1734  struct AttachPtr *gptr = mutt_mem_calloc(1, sizeof(struct AttachPtr));
1735  gptr->content = group;
1736  update_idx(menu, actx, gptr);
1737  menu->redraw = REDRAW_INDEX;
1738  break;
1739  }
1740 
1741  case OP_COMPOSE_GROUP_LINGUAL:
1742  {
1743  if (menu->tagged < 2)
1744  {
1745  mutt_error(
1746  _("Grouping 'multilingual' requires at least 2 tagged messages"));
1747  break;
1748  }
1749 
1750  /* traverse to see whether all the parts have Content-Language: set */
1751  int tagged_with_lang_num = 0;
1752  for (struct Body *b = e->content; b; b = b->next)
1753  if (b->tagged && b->language && *b->language)
1754  tagged_with_lang_num++;
1755 
1756  if (menu->tagged != tagged_with_lang_num)
1757  {
1758  if (mutt_yesorno(
1759  _("Not all parts have 'Content-Language' set, continue?"), MUTT_YES) != MUTT_YES)
1760  {
1761  mutt_message(_("Not sending this message"));
1762  break;
1763  }
1764  }
1765 
1766  struct Body *group = mutt_body_new();
1767  group->type = TYPE_MULTIPART;
1768  group->subtype = mutt_str_strdup("multilingual");
1769  group->disposition = DISP_INLINE;
1770 
1771  struct Body *alts = NULL;
1772  /* group tagged message into a multipart/multilingual */
1773  struct Body *bptr = e->content;
1774  for (int i = 0; bptr;)
1775  {
1776  if (bptr->tagged)
1777  {
1778  bptr->tagged = false;
1779  bptr->disposition = DISP_INLINE;
1780 
1781  /* for first match, set group desc according to match */
1782 #define LINGUAL_TAG "Multilingual part for \"%s\""
1783  if (!group->description)
1784  {
1785  char *p = bptr->description ? bptr->description : bptr->filename;
1786  if (p)
1787  {
1788  group->description =
1789  mutt_mem_calloc(1, strlen(p) + strlen(LINGUAL_TAG) + 1);
1790  sprintf(group->description, LINGUAL_TAG, p);
1791  }
1792  }
1793 
1794  // append bptr to the alts list, and remove from the e->content list
1795  if (alts)
1796  {
1797  alts->next = bptr;
1798  bptr = bptr->next;
1799  alts = alts->next;
1800  alts->next = NULL;
1801  }
1802  else
1803  {
1804  group->parts = bptr;
1805  alts = bptr;
1806  bptr = bptr->next;
1807  alts->next = NULL;
1808  }
1809 
1810  for (int j = i; j < actx->idxlen - 1; j++)
1811  {
1812  actx->idx[j] = actx->idx[j + 1];
1813  actx->idx[j + 1] = NULL; /* for debug reason */
1814  }
1815  actx->idxlen--;
1816  }
1817  else
1818  {
1819  bptr = bptr->next;
1820  i++;
1821  }
1822  }
1823 
1824  group->next = NULL;
1826 
1827  /* if no group desc yet, make one up */
1828  if (!group->description)
1829  group->description = mutt_str_strdup("unknown multilingual group");
1830 
1831  struct AttachPtr *gptr = mutt_mem_calloc(1, sizeof(struct AttachPtr));
1832  gptr->content = group;
1833  update_idx(menu, actx, gptr);
1834  menu->redraw = REDRAW_INDEX;
1835  break;
1836  }
1837 
1838  case OP_COMPOSE_ATTACH_FILE:
1839  {
1840  char *prompt = _("Attach file");
1841  int numfiles = 0;
1842  char **files = NULL;
1843 
1844  mutt_buffer_reset(&fname);
1845  if ((mutt_buffer_enter_fname_full(prompt, &fname, false, true, &files,
1846  &numfiles, MUTT_SEL_MULTI) == -1) ||
1847  mutt_buffer_is_empty(&fname))
1848  {
1849  break;
1850  }
1851 
1852  bool error = false;
1853  if (numfiles > 1)
1854  {
1855  mutt_message(ngettext("Attaching selected file...",
1856  "Attaching selected files...", numfiles));
1857  }
1858  for (int i = 0; i < numfiles; i++)
1859  {
1860  char *att = files[i];
1861  struct AttachPtr *ap = mutt_mem_calloc(1, sizeof(struct AttachPtr));
1862  ap->unowned = true;
1863  ap->content = mutt_make_file_attach(att);
1864  if (ap->content)
1865  update_idx(menu, actx, ap);
1866  else
1867  {
1868  error = true;
1869  mutt_error(_("Unable to attach %s"), att);
1870  FREE(&ap);
1871  }
1872  FREE(&files[i]);
1873  }
1874 
1875  FREE(&files);
1876  if (!error)
1877  mutt_clear_error();
1878 
1879  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
1881  break;
1882  }
1883 
1884  case OP_COMPOSE_ATTACH_MESSAGE:
1885 #ifdef USE_NNTP
1886  case OP_COMPOSE_ATTACH_NEWS_MESSAGE:
1887 #endif
1888  {
1889  mutt_buffer_reset(&fname);
1890  char *prompt = _("Open mailbox to attach message from");
1891 
1892 #ifdef USE_NNTP
1893  OptNews = false;
1894  if (Context && (op == OP_COMPOSE_ATTACH_NEWS_MESSAGE))
1895  {
1897  if (!CurrentNewsSrv)
1898  break;
1899 
1900  prompt = _("Open newsgroup to attach message from");
1901  OptNews = true;
1902  }
1903 #endif
1904 
1905  if (Context)
1906  {
1907 #ifdef USE_NNTP
1908  if ((op == OP_COMPOSE_ATTACH_MESSAGE) ^ (Context->mailbox->type == MUTT_NNTP))
1909 #endif
1910  {
1913  }
1914  }
1915 
1916  if ((mutt_buffer_enter_fname(prompt, &fname, true) == -1) ||
1917  mutt_buffer_is_empty(&fname))
1918  {
1919  break;
1920  }
1921 
1922 #ifdef USE_NNTP
1923  if (OptNews)
1925  else
1926 #endif
1927  mutt_buffer_expand_path(&fname);
1928 #ifdef USE_IMAP
1929  if (imap_path_probe(mutt_b2s(&fname), NULL) != MUTT_IMAP)
1930 #endif
1931 #ifdef USE_POP
1932  if (pop_path_probe(mutt_b2s(&fname), NULL) != MUTT_POP)
1933 #endif
1934 #ifdef USE_NNTP
1935  if (!OptNews && (nntp_path_probe(mutt_b2s(&fname), NULL) != MUTT_NNTP))
1936 #endif
1937  if (mx_path_probe(mutt_b2s(&fname)) != MUTT_NOTMUCH)
1938  {
1939  /* check to make sure the file exists and is readable */
1940  if (access(mutt_b2s(&fname), R_OK) == -1)
1941  {
1942  mutt_perror(mutt_b2s(&fname));
1943  break;
1944  }
1945  }
1946 
1947  menu->redraw = REDRAW_FULL;
1948 
1949  struct Mailbox *m = mx_path_resolve(mutt_b2s(&fname));
1950  bool old_readonly = m->readonly;
1951  struct Context *ctx = mx_mbox_open(m, MUTT_READONLY);
1952  if (!ctx)
1953  {
1954  mutt_error(_("Unable to open mailbox %s"), mutt_b2s(&fname));
1956  m = NULL;
1957  break;
1958  }
1959 
1960  if (ctx->mailbox->msg_count == 0)
1961  {
1962  mx_mbox_close(&ctx);
1963  mutt_error(_("No messages in that folder"));
1964  break;
1965  }
1966 
1967  struct Context *ctx_cur = Context; /* remember current folder and sort methods */
1968  int old_sort = C_Sort; /* C_Sort, SortAux could be changed in mutt_index_menu() */
1969  int old_sort_aux = C_SortAux;
1970 
1971  Context = ctx;
1972  OptAttachMsg = true;
1973  mutt_message(_("Tag the messages you want to attach"));
1974  struct MuttWindow *dlg_index = index_pager_init();
1976  dialog_push(dlg_index);
1977  mutt_index_menu(dlg_index);
1978  dialog_pop();
1980  index_pager_shutdown(dlg_index);
1981  mutt_window_free(&dlg_index);
1982  OptAttachMsg = false;
1983 
1984  if (!Context)
1985  {
1986  /* go back to the folder we started from */
1987  Context = ctx_cur;
1988  /* Restore old $sort and $sort_aux */
1989  C_Sort = old_sort;
1990  C_SortAux = old_sort_aux;
1991  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
1992  break;
1993  }
1994 
1995  for (int i = 0; i < Context->mailbox->msg_count; i++)
1996  {
1997  if (!Context->mailbox->emails[i])
1998  break;
2000  continue;
2001 
2002  struct AttachPtr *ap = mutt_mem_calloc(1, sizeof(struct AttachPtr));
2004  Context->mailbox->emails[i], true);
2005  if (ap->content)
2006  update_idx(menu, actx, ap);
2007  else
2008  {
2009  mutt_error(_("Unable to attach"));
2010  FREE(&ap);
2011  }
2012  }
2013  menu->redraw |= REDRAW_FULL;
2014 
2015  Context->mailbox->readonly = old_readonly;
2017  ctx_free(&Context);
2018 
2019  /* go back to the folder we started from */
2020  Context = ctx_cur;
2021  /* Restore old $sort and $sort_aux */
2022  C_Sort = old_sort;
2023  C_SortAux = old_sort_aux;
2025  break;
2026  }
2027 
2028  case OP_DELETE:
2029  CHECK_COUNT;
2030  if (CUR_ATTACH->unowned)
2031  CUR_ATTACH->content->unlink = false;
2032  if (delete_attachment(actx, menu->current) == -1)
2033  break;
2034  mutt_update_compose_menu(actx, menu, false);
2035  if (menu->current == 0)
2036  e->content = actx->idx[0]->content;
2037 
2039  break;
2040 
2041  case OP_COMPOSE_TOGGLE_RECODE:
2042  {
2043  CHECK_COUNT;
2044  if (!mutt_is_text_part(CUR_ATTACH->content))
2045  {
2046  mutt_error(_("Recoding only affects text attachments"));
2047  break;
2048  }
2049  CUR_ATTACH->content->noconv = !CUR_ATTACH->content->noconv;
2050  if (CUR_ATTACH->content->noconv)
2051  mutt_message(_("The current attachment won't be converted"));
2052  else
2053  mutt_message(_("The current attachment will be converted"));
2054  menu->redraw = REDRAW_CURRENT;
2056  break;
2057  }
2058 
2059  case OP_COMPOSE_EDIT_DESCRIPTION:
2060  CHECK_COUNT;
2062  buf, CUR_ATTACH->content->description ? CUR_ATTACH->content->description : "",
2063  sizeof(buf));
2064  /* header names should not be translated */
2065  if (mutt_get_field("Description: ", buf, sizeof(buf), MUTT_COMP_NO_FLAGS) == 0)
2066  {
2067  mutt_str_replace(&CUR_ATTACH->content->description, buf);
2068  menu->redraw = REDRAW_CURRENT;
2069  }
2071  break;
2072 
2073  case OP_COMPOSE_UPDATE_ENCODING:
2074  CHECK_COUNT;
2075  if (menu->tagprefix)
2076  {
2077  struct Body *top = NULL;
2078  for (top = e->content; top; top = top->next)
2079  {
2080  if (top->tagged)
2081  mutt_update_encoding(top);
2082  }
2083  menu->redraw = REDRAW_FULL;
2084  }
2085  else
2086  {
2087  mutt_update_encoding(CUR_ATTACH->content);
2089  }
2091  break;
2092 
2093  case OP_COMPOSE_TOGGLE_DISPOSITION:
2094  /* toggle the content-disposition between inline/attachment */
2095  CUR_ATTACH->content->disposition =
2096  (CUR_ATTACH->content->disposition == DISP_INLINE) ? DISP_ATTACH : DISP_INLINE;
2097  menu->redraw = REDRAW_CURRENT;
2098  break;
2099 
2100  case OP_EDIT_TYPE:
2101  CHECK_COUNT;
2102  {
2103  mutt_edit_content_type(NULL, CUR_ATTACH->content, NULL);
2104 
2105  /* this may have been a change to text/something */
2106  mutt_update_encoding(CUR_ATTACH->content);
2107 
2108  menu->redraw = REDRAW_CURRENT;
2109  }
2111  break;
2112 
2113  case OP_COMPOSE_EDIT_LANGUAGE:
2114  CHECK_COUNT;
2115  buf[0] = '\0'; /* clear buffer first */
2116  if (CUR_ATTACH->content->language)
2117  mutt_str_strfcpy(buf, CUR_ATTACH->content->language, sizeof(buf));
2118  if (mutt_get_field("Content-Language: ", buf, sizeof(buf), MUTT_COMP_NO_FLAGS) == 0)
2119  {
2120  CUR_ATTACH->content->language = mutt_str_strdup(buf);
2122  mutt_clear_error();
2123  }
2124  else
2125  mutt_warning(_("Empty 'Content-Language'"));
2127  break;
2128 
2129  case OP_COMPOSE_EDIT_ENCODING:
2130  CHECK_COUNT;
2131  mutt_str_strfcpy(buf, ENCODING(CUR_ATTACH->content->encoding), sizeof(buf));
2132  if ((mutt_get_field("Content-Transfer-Encoding: ", buf, sizeof(buf),
2133  MUTT_COMP_NO_FLAGS) == 0) &&
2134  (buf[0] != '\0'))
2135  {
2136  int enc = mutt_check_encoding(buf);
2137  if ((enc != ENC_OTHER) && (enc != ENC_UUENCODED))
2138  {
2139  CUR_ATTACH->content->encoding = enc;
2141  mutt_clear_error();
2142  }
2143  else
2144  mutt_error(_("Invalid encoding"));
2145  }
2147  break;
2148 
2149  case OP_COMPOSE_SEND_MESSAGE:
2150  /* Note: We don't invoke send2-hook here, since we want to leave
2151  * users an opportunity to change settings from the ":" prompt. */
2152  if (check_attachments(actx) != 0)
2153  {
2154  menu->redraw = REDRAW_FULL;
2155  break;
2156  }
2157 
2158 #ifdef MIXMASTER
2159  if (!STAILQ_EMPTY(&e->chain) && (mix_check_message(e) != 0))
2160  break;
2161 #endif
2162 
2163  if (!fcc_set && !mutt_buffer_is_empty(fcc))
2164  {
2165  enum QuadOption ans =
2166  query_quadoption(C_Copy, _("Save a copy of this message?"));
2167  if (ans == MUTT_ABORT)
2168  break;
2169  else if (ans == MUTT_NO)
2170  mutt_buffer_reset(fcc);
2171  }
2172 
2173  loop = false;
2174  rc = 0;
2175  break;
2176 
2177  case OP_COMPOSE_EDIT_FILE:
2178  CHECK_COUNT;
2179  mutt_edit_file(NONULL(C_Editor), CUR_ATTACH->content->filename);
2180  mutt_update_encoding(CUR_ATTACH->content);
2183  break;
2184 
2185  case OP_COMPOSE_TOGGLE_UNLINK:
2186  CHECK_COUNT;
2187  CUR_ATTACH->content->unlink = !CUR_ATTACH->content->unlink;
2188 
2189  menu->redraw = REDRAW_INDEX;
2190  /* No send2hook since this doesn't change the message. */
2191  break;
2192 
2193  case OP_COMPOSE_GET_ATTACHMENT:
2194  CHECK_COUNT;
2195  if (menu->tagprefix)
2196  {
2197  for (struct Body *top = e->content; top; top = top->next)
2198  {
2199  if (top->tagged)
2201  }
2202  menu->redraw = REDRAW_FULL;
2203  }
2204  else if (mutt_get_tmp_attachment(CUR_ATTACH->content) == 0)
2205  menu->redraw = REDRAW_CURRENT;
2206 
2207  /* No send2hook since this doesn't change the message. */
2208  break;
2209 
2210  case OP_COMPOSE_RENAME_ATTACHMENT:
2211  {
2212  CHECK_COUNT;
2213  char *src = NULL;
2214  if (CUR_ATTACH->content->d_filename)
2215  src = CUR_ATTACH->content->d_filename;
2216  else
2217  src = CUR_ATTACH->content->filename;
2219  int ret = mutt_buffer_get_field(_("Send attachment with name: "), &fname, MUTT_FILE);
2220  if (ret == 0)
2221  {
2222  /* As opposed to RENAME_FILE, we don't check buf[0] because it's
2223  * valid to set an empty string here, to erase what was set */
2224  mutt_str_replace(&CUR_ATTACH->content->d_filename, mutt_b2s(&fname));
2225  menu->redraw = REDRAW_CURRENT;
2226  }
2227  break;
2228  }
2229 
2230  case OP_COMPOSE_RENAME_FILE:
2231  CHECK_COUNT;
2232  mutt_buffer_strcpy(&fname, CUR_ATTACH->content->filename);
2234  if ((mutt_buffer_get_field(_("Rename to: "), &fname, MUTT_FILE) == 0) &&
2235  !mutt_buffer_is_empty(&fname))
2236  {
2237  struct stat st;
2238  if (stat(CUR_ATTACH->content->filename, &st) == -1)
2239  {
2240  /* L10N: "stat" is a system call. Do "man 2 stat" for more information. */
2241  mutt_error(_("Can't stat %s: %s"), mutt_b2s(&fname), strerror(errno));
2242  break;
2243  }
2244 
2245  mutt_buffer_expand_path(&fname);
2246  if (mutt_file_rename(CUR_ATTACH->content->filename, mutt_b2s(&fname)))
2247  break;
2248 
2249  mutt_str_replace(&CUR_ATTACH->content->filename, mutt_b2s(&fname));
2250  menu->redraw = REDRAW_CURRENT;
2251 
2252  if (CUR_ATTACH->content->stamp >= st.st_mtime)
2254  }
2256  break;
2257 
2258  case OP_COMPOSE_NEW_MIME:
2259  {
2260  mutt_buffer_reset(&fname);
2261  if ((mutt_buffer_get_field(_("New file: "), &fname, MUTT_FILE) != 0) ||
2262  mutt_buffer_is_empty(&fname))
2263  {
2264  continue;
2265  }
2266  mutt_buffer_expand_path(&fname);
2267 
2268  /* Call to lookup_mime_type () ? maybe later */
2269  char type[256] = { 0 };
2270  if ((mutt_get_field("Content-Type: ", type, sizeof(type), MUTT_COMP_NO_FLAGS) != 0) ||
2271  (type[0] == '\0'))
2272  {
2273  continue;
2274  }
2275 
2276  char *p = strchr(type, '/');
2277  if (!p)
2278  {
2279  mutt_error(_("Content-Type is of the form base/sub"));
2280  continue;
2281  }
2282  *p++ = 0;
2283  enum ContentType itype = mutt_check_mime_type(type);
2284  if (itype == TYPE_OTHER)
2285  {
2286  mutt_error(_("Unknown Content-Type %s"), type);
2287  continue;
2288  }
2289  struct AttachPtr *ap = mutt_mem_calloc(1, sizeof(struct AttachPtr));
2290  /* Touch the file */
2291  FILE *fp = mutt_file_fopen(mutt_b2s(&fname), "w");
2292  if (!fp)
2293  {
2294  mutt_error(_("Can't create file %s"), mutt_b2s(&fname));
2295  FREE(&ap);
2296  continue;
2297  }
2298  mutt_file_fclose(&fp);
2299 
2300  ap->content = mutt_make_file_attach(mutt_b2s(&fname));
2301  if (!ap->content)
2302  {
2303  mutt_error(_("What we have here is a failure to make an attachment"));
2304  FREE(&ap);
2305  continue;
2306  }
2307  update_idx(menu, actx, ap);
2308 
2309  CUR_ATTACH->content->type = itype;
2310  mutt_str_replace(&CUR_ATTACH->content->subtype, p);
2311  CUR_ATTACH->content->unlink = true;
2312  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
2313 
2314  if (mutt_compose_attachment(CUR_ATTACH->content))
2315  {
2316  mutt_update_encoding(CUR_ATTACH->content);
2317  menu->redraw = REDRAW_FULL;
2318  }
2320  break;
2321  }
2322 
2323  case OP_COMPOSE_EDIT_MIME:
2324  CHECK_COUNT;
2325  if (mutt_edit_attachment(CUR_ATTACH->content))
2326  {
2327  mutt_update_encoding(CUR_ATTACH->content);
2328  menu->redraw = REDRAW_FULL;
2329  }
2331  break;
2332 
2333  case OP_VIEW_ATTACH:
2334  case OP_DISPLAY_HEADERS:
2335  CHECK_COUNT;
2336  mutt_attach_display_loop(menu, op, NULL, actx, false);
2337  menu->redraw = REDRAW_FULL;
2338  /* no send2hook, since this doesn't modify the message */
2339  break;
2340 
2341  case OP_SAVE:
2342  CHECK_COUNT;
2343  mutt_save_attachment_list(actx, NULL, menu->tagprefix,
2344  CUR_ATTACH->content, NULL, menu);
2345  /* no send2hook, since this doesn't modify the message */
2346  break;
2347 
2348  case OP_PRINT:
2349  CHECK_COUNT;
2350  mutt_print_attachment_list(actx, NULL, menu->tagprefix, CUR_ATTACH->content);
2351  /* no send2hook, since this doesn't modify the message */
2352  break;
2353 
2354  case OP_PIPE:
2355  case OP_FILTER:
2356  CHECK_COUNT;
2357  mutt_pipe_attachment_list(actx, NULL, menu->tagprefix,
2358  CUR_ATTACH->content, (op == OP_FILTER));
2359  if (op == OP_FILTER) /* cte might have changed */
2360  menu->redraw = menu->tagprefix ? REDRAW_FULL : REDRAW_CURRENT;
2361  menu->redraw |= REDRAW_STATUS;
2363  break;
2364 
2365  case OP_EXIT:
2366  {
2367  enum QuadOption ans =
2368  query_quadoption(C_Postpone, _("Save (postpone) draft message?"));
2369  if (ans == MUTT_NO)
2370  {
2371  for (int i = 0; i < actx->idxlen; i++)
2372  if (actx->idx[i]->unowned)
2373  actx->idx[i]->content->unlink = false;
2374 
2375  if (!(flags & MUTT_COMPOSE_NOFREEHEADER))
2376  {
2377  for (int i = 0; i < actx->idxlen; i++)
2378  {
2379  /* avoid freeing other attachments */
2380  actx->idx[i]->content->next = NULL;
2381  /* See the comment in delete_attachment() */
2382  if (!actx->idx[i]->content->email)
2383  actx->idx[i]->content->parts = NULL;
2384  mutt_body_free(&actx->idx[i]->content);
2385  }
2386  }
2387  rc = -1;
2388  loop = false;
2389  break;
2390  }
2391  else if (ans == MUTT_ABORT)
2392  break; /* abort */
2393  }
2394  /* fallthrough */
2395 
2396  case OP_COMPOSE_POSTPONE_MESSAGE:
2397  if (check_attachments(actx) != 0)
2398  {
2399  menu->redraw = REDRAW_FULL;
2400  break;
2401  }
2402 
2403  loop = false;
2404  rc = 1;
2405  break;
2406 
2407  case OP_COMPOSE_ISPELL:
2408  endwin();
2409  snprintf(buf, sizeof(buf), "%s -x %s", NONULL(C_Ispell), e->content->filename);
2410  if (mutt_system(buf) == -1)
2411  mutt_error(_("Error running \"%s\""), buf);
2412  else
2413  {
2415  menu->redraw |= REDRAW_STATUS;
2416  }
2417  break;
2418 
2419  case OP_COMPOSE_WRITE_MESSAGE:
2420  mutt_buffer_reset(&fname);
2421  if (Context)
2422  {
2425  }
2426  if (actx->idxlen)
2427  e->content = actx->idx[0]->content;
2428  if ((mutt_buffer_enter_fname(_("Write message to mailbox"), &fname, true) != -1) &&
2429  !mutt_buffer_is_empty(&fname))
2430  {
2431  mutt_message(_("Writing message to %s ..."), mutt_b2s(&fname));
2432  mutt_buffer_expand_path(&fname);
2433 
2434  if (e->content->next)
2436 
2437  if (mutt_write_fcc(mutt_b2s(&fname), e, NULL, false, NULL, NULL) == 0)
2438  mutt_message(_("Message written"));
2439 
2441  }
2442  break;
2443 
2444  case OP_COMPOSE_PGP_MENU:
2445  if (!(WithCrypto & APPLICATION_PGP))
2446  break;
2447  if (!crypt_has_module_backend(APPLICATION_PGP))
2448  {
2449  mutt_error(_("No PGP backend configured"));
2450  break;
2451  }
2452  if (((WithCrypto & APPLICATION_SMIME) != 0) && (e->security & APPLICATION_SMIME))
2453  {
2454  if (e->security & (SEC_ENCRYPT | SEC_SIGN))
2455  {
2456  if (mutt_yesorno(_("S/MIME already selected. Clear and continue?"), MUTT_YES) != MUTT_YES)
2457  {
2458  mutt_clear_error();
2459  break;
2460  }
2461  e->security &= ~(SEC_ENCRYPT | SEC_SIGN);
2462  }
2463  e->security &= ~APPLICATION_SMIME;
2464  e->security |= APPLICATION_PGP;
2465  update_crypt_info(rd);
2466  }
2467  e->security = crypt_pgp_send_menu(e);
2468  update_crypt_info(rd);
2470  redraw_env = true;
2471  break;
2472 
2473  case OP_FORGET_PASSPHRASE:
2475  break;
2476 
2477  case OP_COMPOSE_SMIME_MENU:
2478  if (!(WithCrypto & APPLICATION_SMIME))
2479  break;
2480  if (!crypt_has_module_backend(APPLICATION_SMIME))
2481  {
2482  mutt_error(_("No S/MIME backend configured"));
2483  break;
2484  }
2485 
2486  if (((WithCrypto & APPLICATION_PGP) != 0) && (e->security & APPLICATION_PGP))
2487  {
2488  if (e->security & (SEC_ENCRYPT | SEC_SIGN))
2489  {
2490  if (mutt_yesorno(_("PGP already selected. Clear and continue?"), MUTT_YES) != MUTT_YES)
2491  {
2492  mutt_clear_error();
2493  break;
2494  }
2495  e->security &= ~(SEC_ENCRYPT | SEC_SIGN);
2496  }
2497  e->security &= ~APPLICATION_PGP;
2499  update_crypt_info(rd);
2500  }
2502  update_crypt_info(rd);
2504  redraw_env = true;
2505  break;
2506 
2507 #ifdef MIXMASTER
2508  case OP_COMPOSE_MIX:
2511  redraw_env = true;
2512  break;
2513 #endif
2514 
2515 #ifdef USE_AUTOCRYPT
2516  case OP_COMPOSE_AUTOCRYPT_MENU:
2517  if (!C_Autocrypt)
2518  break;
2519 
2520  if ((WithCrypto & APPLICATION_SMIME) && (e->security & APPLICATION_SMIME))
2521  {
2522  if (e->security & (SEC_ENCRYPT | SEC_SIGN))
2523  {
2524  if (mutt_yesorno(_("S/MIME already selected. Clear and continue?"), MUTT_YES) != MUTT_YES)
2525  {
2526  mutt_clear_error();
2527  break;
2528  }
2529  e->security &= ~(SEC_ENCRYPT | SEC_SIGN);
2530  }
2531  e->security &= ~APPLICATION_SMIME;
2532  e->security |= APPLICATION_PGP;
2533  update_crypt_info(rd);
2534  }
2536  update_crypt_info(rd);
2538  redraw_env = true;
2539  break;
2540 #endif
2541  }
2542  }
2543 
2544  mutt_buffer_dealloc(&fname);
2545 
2546 #ifdef USE_AUTOCRYPT
2547  /* This is a fail-safe to make sure the bit isn't somehow turned
2548  * on. The user could have disabled the option after setting SEC_AUTOCRYPT,
2549  * or perhaps resuming or replying to an autocrypt message. */
2550  if (!C_Autocrypt)
2551  e->security &= ~SEC_AUTOCRYPT;
2552 #endif
2553 
2554  mutt_menu_pop_current(menu);
2555  mutt_menu_free(&menu);
2556  dialog_pop();
2558  mutt_window_free(&dlg);
2559 
2560  if (actx->idxlen)
2561  e->content = actx->idx[0]->content;
2562  else
2563  e->content = NULL;
2564 
2565  mutt_actx_free(&actx);
2566 
2567  return rc;
2568 }
Mail will be encrypted.
Definition: color.h:67
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
Convenience wrapper for the gui headers.
#define mutt_warning(...)
Definition: logging.h:82
The "current" mailbox.
Definition: context.h:37
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:192
WHERE bool C_StatusOnTop
Config: Display the status bar at the top.
Definition: globals.h:252
AutocryptRec
Recommendation.
Definition: lib.h:152
struct Body * mutt_make_message_attach(struct Mailbox *m, struct Email *e, bool attach_msg)
Create a message attachment.
Definition: sendlib.c:1511
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t *callback, unsigned long data, MuttFormatFlags flags)
Expand expandos (x) in a string.
Definition: muttlib.c:862
static int delete_attachment(struct AttachCtx *actx, int x)
Delete an attachment.
Definition: compose.c:950
Bold text.
Definition: color.h:64
void mutt_stamp_attachment(struct Body *a)
Timestamp an Attachment.
Definition: sendlib.c:1443
Unknown Content-Type.
Definition: mime.h:31
#define REDRAW_FULL
Redraw everything.
Definition: mutt_menu.h:47
void mutt_actx_free(struct AttachCtx **ptr)
Free an Attachment Context.
Definition: attach.c:140
void mix_make_chain(struct MuttWindow *win, struct ListHead *chainhead, int cols)
Create a Mixmaster chain.
Definition: remailer.c:601
bool message_is_tagged(struct Context *ctx, struct Email *e)
Is a message in the index tagged (and within limit)
Definition: context.c:351
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
WHERE bool C_Autocrypt
Config: Enables the Autocrypt feature.
Definition: globals.h:200
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:1151
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
struct Body * mutt_remove_multipart(struct Body *b)
Extract the multipart body if it exists.
Definition: sendlib.c:1772
Representation of the email&#39;s header.
#define WithCrypto
Definition: lib.h:163
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, unsigned long data, MuttFormatFlags flags)
Create the status bar string for compose mode - Implements format_t.
Definition: compose.c:1211
void mutt_expand_aliases(struct AddressList *al)
Expand aliases in a List of Addresses.
Definition: alias.c:303
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
if(!test_colorize_)
Definition: acutest.h:499
GUI editor for an email&#39;s headers.
#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
GUI selectable list of items.
Definition: mutt_menu.h:82
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe()
Definition: imap.c:2524
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
WHERE char * C_AttachFormat
Config: printf-like format string for the attachment menu.
Definition: globals.h:101
&#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:593
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.
const char * mutt_path_basename(const char *f)
Find the last component for a pathname.
Definition: path.c:329
Pager Bar containing status info about the Pager.
Definition: mutt_window.h:77
Dialog (nested Windows) displayed to the user.
Definition: mutt_window.h:70
User aborted the question (with Ctrl-G)
Definition: quad.h:38
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: init.c:1112
GUI manage the main index (list of emails)
#define mutt_message(...)
Definition: logging.h:83
struct MuttWindow * win_abar
Attachments label.
Definition: compose.c:128
Autocrypt should be used.
Definition: lib.h:158
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:1341
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:40
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition: address.c:1329
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:1412
static void draw_floating(struct MuttWindow *win, int row, int col, const char *text)
Draw a floating label.
Definition: compose.c:381
static void compose_custom_redraw(struct Menu *menu)
Redraw the compose menu - Implements Menu::custom_redraw()
Definition: compose.c:1079
void mutt_curses_set_color(enum ColorId color)
Set the current colour for text.
Definition: mutt_curses.c:55
void dialog_pop(void)
Hide a Window from the user.
Definition: mutt_window.c:628
#define REDRAW_FLOW
Used by pager to reflow text.
Definition: mutt_menu.h:49
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:125
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:651
static int const char * fmt
Definition: acutest.h:488
struct MuttWindow * mutt_window_find(struct MuttWindow *root, enum WindowType type)
Find a Window of a given type.
Definition: mutt_window.c:577
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:134
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:150
void dialog_push(struct MuttWindow *dlg)
Display a Window to the user.
Definition: mutt_window.c:605
static const char * There_are_no_attachments
Definition: compose.c:101
#define MUTT_FORMAT_ARROWCURSOR
Reserve space for arrow_cursor.
Definition: format_flags.h:35
struct Body * content
List of MIME parts.
Definition: email.h:90
void crypt_forget_passphrase(void)
Forget a passphrase and display a message.
Definition: crypt.c:104
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
static void edit_address_list(int field, struct AddressList *al)
Let the user edit the address list.
Definition: compose.c:921
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:759
"Cc:" field
Definition: compose.c:151
int mutt_edit_attachment(struct Body *a)
Edit an attachment.
Definition: mutt_attach.c:257
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:275
int MaxHeaderWidth
Definition: compose.c:173
#define _(a)
Definition: message.h:28
static void update_crypt_info(struct ComposeRedrawData *rd)
Update the crypto info.
Definition: compose.c:615
#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:73
struct Context * mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:255
WHERE unsigned char C_Copy
Config: Save outgoing emails to $record.
Definition: globals.h:180
static void redraw_mix_line(struct ListHead *chain, struct ComposeRedrawData *rd, int row)
Redraw the Mixmaster chain.
Definition: compose.c:656
static int calc_envelope(struct ComposeRedrawData *rd)
Calculate how many rows the envelope will need.
Definition: compose.c:475
A division of the screen.
Definition: mutt_window.h:88
#define SEC_AUTOCRYPT
(Autocrypt) Message will be, or was Autocrypt encrypt+signed
Definition: lib.h:134
void mutt_actx_add_attach(struct AttachCtx *actx, struct AttachPtr *attach)
Add an Attachment to an Attachment Context.
Definition: attach.c:40
struct ListHead bcc_list
Definition: compose.c:116
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:692
struct Email * email
Definition: compose.c:111
char * C_Ispell
Config: External command to perform spell-checking.
Definition: compose.c:98
struct Buffer * fcc
Definition: compose.c:112
long hibin
8-bit characters
Definition: content.h:35
#define MUTT_READONLY
Open in read-only mode.
Definition: mx.h:55
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:613
"Subject:" field
Definition: compose.c:153
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:695
"Sign as:" field (encryption/signing info)
Definition: compose.c:160
Flags to control mutt_expando_format()
"From:" field
Definition: compose.c:149
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:1006
static int calc_security(struct Email *e, short *rows)
Calculate how many rows the security info will need.
Definition: compose.c:453
struct NntpAccountData * nntp_select_server(struct Mailbox *m, char *server, bool leave_lock)
Open a connection to an NNTP server.
Definition: newsrc.c:1014
Container for Accounts, Notifications.
Definition: neomutt.h:35
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:688
int mutt_write_fcc(const char *path, struct Email *e, const char *msgid, bool post, const char *fcc, char **finalpath)
Write email to FCC mailbox.
Definition: sendlib.c:3258
Representation of a single alias to an email address.
The "-- Attachments" line.
Definition: compose.c:169
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:123
#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:90
static int calc_address(struct AddressList *al, struct ListHead *slist, short *srows, short cols)
Calculate how many rows an AddressList will need.
Definition: compose.c:411
The body of an email.
Definition: body.h:34
#define REDRAW_SIDEBAR
Redraw the sidebar.
Definition: mutt_menu.h:51
unsigned int disposition
content-disposition
Definition: body.h:67
struct Content * mutt_get_content_info(const char *fname, struct Body *b)
Analyze file to determine MIME encoding to use.
Definition: sendlib.c:1031
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:1243
GUI component for displaying/selecting items from a list.
An Index Window containing a selection list.
Definition: mutt_window.h:74
Hundreds of global variables to back the user variables.
Email Address Handling.
#define SEC_INLINE
Email has an inline signature.
Definition: lib.h:132
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:325
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:74
#define CUR_ATTACH
Definition: compose.c:140
enum NotifyType event_type
Send: Event type, e.g. NT_ACCOUNT.
Definition: observer.h:41
Some miscellaneous functions.
int mutt_dlg_index_observer(struct NotifyCallback *nc)
Listen for config changes affecting the Index/Pager - Implements observer_t.
Definition: index.c:4130
size_t dsize
Length of data.
Definition: buffer.h:37
void * redraw_data
Definition: mutt_menu.h:154
void mutt_print_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top)
Print a list of Attachments.
Definition: recvattach.c:1032
int mutt_get_tmp_attachment(struct Body *a)
Get a temporary copy of an attachment.
Definition: mutt_attach.c:68
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: curs_lib.c:377
WHERE bool C_EditHeaders
Config: Let the user edit the email headers whilst editing an email.
Definition: globals.h:212
struct Mailbox * mailbox
Definition: context.h:51
Parse and execute user-defined hooks.
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
SecurityFlags mutt_is_multipart_encrypted(struct Body *b)
Does the message have encrypted parts?
Definition: crypt.c:459
Many unsorted constants and some structs.
API for mailboxes.
enum AutocryptRec autocrypt_rec
Definition: compose.c:124
void mutt_generate_boundary(struct ParameterList *pl)
Create a unique boundary id for a MIME part.
Definition: sendlib.c:636
struct Body * mutt_make_file_attach(const char *path)
Create a file attachment.
Definition: sendlib.c:1650
#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:318
struct AddressList from
Email&#39;s &#39;From&#39; list.
Definition: envelope.h:57
struct MuttWindow * win_ibar
Definition: mutt_menu.h:95
int crypt_smime_send_menu(struct Email *e)
Wrapper for CryptModuleSpecs::send_menu()
Definition: cryptglue.c:533
bool readonly
Don&#39;t allow changes to the mailbox.
Definition: mailbox.h:119
void mutt_update_encoding(struct Body *a)
Update the encoding type.
Definition: sendlib.c:1480
struct MuttWindow * win_envelope
Envelope: From, To, etc.
Definition: compose.c:127
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:864
int parent_type
Type of parent attachment, e.g. TYPE_MULTIPART.
Definition: attach.h:38
struct Envelope * env
Envelope information.
Definition: email.h:89
Convenience wrapper for the core headers.
#define ENCODING(x)
Definition: mime.h:85
void mx_fastclose_mailbox(struct Mailbox *m)
free up memory associated with the Mailbox
Definition: mx.c:414
struct AddressList cc
Email&#39;s &#39;Cc&#39; list.
Definition: envelope.h:59
"X-Comment-To:" field
Definition: compose.c:167
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
static void compose_attach_swap(struct Body *msg, struct AttachPtr **idx, short first)
Swap two adjacent entries in the attachment list.
Definition: compose.c:1131
void mutt_rfc3676_space_unstuff(struct Email *e)
Remove RFC3676 space stuffing.
Definition: rfc3676.c:481
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:58
bool tagged
This attachment is tagged.
Definition: body.h:70
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
unsigned int encoding
content-transfer-encoding
Definition: body.h:66
Base-64 encoded text.
Definition: mime.h:52
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:1269
#define REDRAW_MOTION
Redraw after moving the menu list.
Definition: mutt_menu.h:43
void * global_data
Data from notify_observer_add()
Definition: observer.h:44
Usenet network mailbox type; talk to an NNTP server.
bool unowned
Don&#39;t unlink on detach.
Definition: attach.h:42
struct Body * mutt_make_multipart(struct Body *b)
Create a multipart email.
Definition: sendlib.c:1746
Window has a fixed size.
Definition: mutt_window.h:44
Plain text.
Definition: color.h:77
const char * help
Quickref for the current menu.
Definition: mutt_menu.h:85
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:745
#define REDRAW_MOTION_RESYNC
Redraw any changing the menu selection.
Definition: mutt_menu.h:44
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:58
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:138
WHERE char * C_NewsServer
Config: (nntp) Url of the news server.
Definition: globals.h:130
int mutt_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question.
Definition: curs_lib.c:910
void mutt_refresh(void)
Force a refresh of the screen.
Definition: curs_lib.c:106
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:99
WHERE short C_SortAux
Config: Secondary sort method for the index.
Definition: sort.h:59
WHERE struct Context * Context
Definition: globals.h:42
int attach_tag(struct Menu *menu, int sel, int act)
Tag an attachment - Implements Menu::tag()
Definition: recvattach.c:450
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:93
WHERE bool C_CryptOpportunisticEncrypt
Config: Enable encryption when the recipient&#39;s key is available.
Definition: globals.h:268
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:137
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:46
int mutt_check_encoding(const char *c)
Check the encoding type.
Definition: parse.c:427
#define ALTS_TAG
struct MuttWindow * mutt_window_new(enum MuttWindowOrientation orient, enum MuttWindowSize size, int rows, int cols)
Create a new Window.
Definition: mutt_window.c:56
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:1336
#define PATH_MAX
Definition: mutt.h:44
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:39
#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:57
void mutt_attach_init(struct AttachCtx *actx)
Create a new Attachment context.
Definition: recvattach.c:1336
bool tagprefix
Definition: mutt_menu.h:92
long ascii
Number of ascii chars.
Definition: content.h:39
Manage where the email is piped to external commands.
#define SEC_AUTOCRYPT_OVERRIDE
(Autocrypt) Indicates manual set/unset of encryption
Definition: lib.h:135
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:2836
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1750
char * data
Pointer to data.
Definition: buffer.h:35
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:54
WHERE char * C_SmimeEncryptWith
Config: Algorithm for encryption.
Definition: globals.h:166
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:776
int crypt_pgp_send_menu(struct Email *e)
Wrapper for CryptModuleSpecs::send_menu()
Definition: cryptglue.c:387
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
static void mutt_update_compose_menu(struct AttachCtx *actx, struct Menu *menu, bool init)
Redraw the compose window.
Definition: compose.c:1036
void(* custom_redraw)(struct Menu *menu)
Redraw the menu.
Definition: mutt_menu.h:152
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:138
char * description
content-description
Definition: body.h:40
int pagelen
Number of entries per screen.
Definition: mutt_menu.h:91
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:437
"Security:" field (encryption/signing info)
Definition: compose.c:159
static const char *const Prompts[]
Definition: compose.c:175
struct Notify * notify
Notifications handler.
Definition: neomutt.h:37
#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:1231
int mutt_index_menu(struct MuttWindow *dlg)
Display a list of emails.
Definition: index.c:1115
#define MUTT_SEL_MULTI
Multi-selection is enabled.
Definition: browser.h:46
void mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:455
int mutt_window_mvaddstr(struct MuttWindow *win, int row, int col, const char *str)
Move the cursor and write a fixed string to a Window.
Definition: mutt_window.c:292
#define SEC_OPPENCRYPT
Opportunistic encrypt mode.
Definition: lib.h:133
int tagged
Number of tagged entries.
Definition: mutt_menu.h:110
bool notify_observer_add(struct Notify *notify, observer_t callback, void *global_data)
Add an observer to an object.
Definition: notify.c:153
#define MUTT_FILE
Do file completion.
Definition: mutt.h:58
int mutt_window_move(struct MuttWindow *win, int row, int col)
Move the cursor in a Window.
Definition: mutt_window.c:278
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
unsigned int type
content-type primary type
Definition: body.h:65
void * event_data
Data from notify_send()
Definition: observer.h:43
int max
Number of entries in the menu.
Definition: mutt_menu.h:88
void mutt_window_add_child(struct MuttWindow *parent, struct MuttWindow *child)
Add a child to Window.
Definition: mutt_window.c:473
&#39;Notmuch&#39; (virtual) Mailbox type
Definition: mailbox.h:54
int autocrypt_rec_override
Definition: compose.c:125
"Bcc:" field
Definition: compose.c:152
"Followup-To:" field
Definition: compose.c:166
struct MuttWindow * mutt_window_dialog(struct MuttWindow *win)
Find the parent Dialog of a Window.
Definition: mutt_window.c:560
#define mutt_buffer_get_field(field, buf, complete)
Definition: curs_lib.h:84
size_t mutt_addrlist_write_list(const struct AddressList *al, struct ListHead *list)
Write Addresses to a List.
Definition: address.c:1195
#define LINGUAL_TAG
MuttRedrawFlags redraw
When to redraw the screen.
Definition: mutt_menu.h:89
Config has changed.
Definition: notify_type.h:34
#define SEC_SIGN
Email is signed.
Definition: lib.h:126
#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:172
static void update_idx(struct Menu *menu, struct AttachCtx *actx, struct AttachPtr *ap)
Add a new attchment to the message.
Definition: compose.c:1065
struct Connection * conn
Definition: lib.h:104
Window containing paged free-form text.
Definition: mutt_window.h:76
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:877
Mail will be encrypted and signed.
Definition: color.h:66
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:458
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:168
void crypt_opportunistic_encrypt(struct Email *e)
Can all recipients be determined.
Definition: crypt.c:1036
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1318
struct Body * crypt_pgp_make_key_attachment(void)
Wrapper for CryptModuleSpecs::pgp_make_key_attachment()
Definition: cryptglue.c:308
void mutt_actx_entries_free(struct AttachCtx *actx)
Free entries in an Attachment Context.
Definition: attach.c:103
GUI basic built-in text editor.
long lobin
Unprintable 7-bit chars (eg., control chars)
Definition: content.h:36
char * data
String.
Definition: list.h:35
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:552
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:327
int mutt_addrlist_to_intl(struct AddressList *al, char **err)
Convert an Address list to Punycode.
Definition: address.c:1247
Routines for managing attachments.
"Mix:" field (Mixmaster chain)
Definition: compose.c:157
static unsigned long cum_attachs_size(struct Menu *menu)
Cumulative Attachments Size.
Definition: compose.c:1165
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
#define REDRAW_INDEX
Redraw the index.
Definition: mutt_menu.h:42
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
WHERE bool C_XCommentTo
Config: (nntp) Add &#39;X-Comment-To&#39; header that contains article author.
Definition: globals.h:283
short * v2r
Mapping from virtual to real attachment.
Definition: attach.h:58
struct Body * content
Attachment.
Definition: attach.h:36
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, unsigned long data, MuttFormatFlags flags)
Format a string for the attachment menu - Implements format_t.
Definition: recvattach.c:208
void mutt_window_reflow(struct MuttWindow *win)
Resize a Window and its children.
Definition: mutt_window.c:345
time_t stamp
Time stamp of last encoding update.
Definition: body.h:61
int HeaderPadding[HDR_ATTACH_TITLE]
Definition: compose.c:172
unsigned char C_Postpone
Config: Save messages to the C_Postponed folder.
Definition: compose.c:99
#define mutt_error(...)
Definition: logging.h:84
int mutt_builtin_editor(const char *path, struct Email *e_new, struct Email *e_cur)
Show the user the built-in editor.
Definition: edit.c:401
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:463
char * followup_to
List of &#39;followup-to&#39; fields.
Definition: envelope.h:77
Quoted-printable text.
Definition: mime.h:51
static void autocrypt_compose_menu(struct Email *e)
Autocrypt compose settings.
Definition: compose.c:340
void mutt_update_tree(struct AttachCtx *actx)
Refresh the list of attachments.
Definition: recvattach.c:142
Connection Library.
WHERE char * C_Editor
Config: External command to use as an email editor.
Definition: globals.h:111
struct MuttWindow * win_cbar
Compose bar.
Definition: compose.c:130
HeaderField
Ordered list of headers for the compose screen.
Definition: compose.c:147
long crlf
\r and \n characters
Definition: content.h:38
struct ListHead cc_list
Definition: compose.c:115
static void draw_header(struct MuttWindow *win, int row, enum HeaderField field)
Draw an aligned label.
Definition: compose.c:394
WHERE char * ShortHostname
Short version of the hostname.
Definition: globals.h:50
#define FREE(x)
Definition: memory.h:40
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1655
void * data
Extra data for the current menu.
Definition: mutt_menu.h:86
#define MUTT_COMPOSE_NOFREEHEADER
Definition: compose.h:35
Mapping between user-readable string and a constant.
Definition: mapping.h:29
#define STAILQ_EMPTY(head)
Definition: queue.h:345
struct MuttWindow * win_attach
List of Attachments.
Definition: compose.c:129
struct MuttWindowList children
Children Windows.
Definition: mutt_window.h:101
#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:4115
struct NntpAccountData * CurrentNewsSrv
Current NNTP news server.
Definition: nntp.c:84
short req_rows
Number of rows required.
Definition: mutt_window.h:90
#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:185
int mutt_window_mvprintw(struct MuttWindow *win, int row, int col, const char *fmt,...)
Move the cursor and write a formatted string to a Window.
Definition: mutt_window.c:311
"Newsgroups:" field
Definition: compose.c:165
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:294
int mutt_window_addstr(const char *str)
Write a string to a Window.
Definition: mutt_window.c:430
struct MuttWindow * index_pager_init(void)
Allocate the Windows for the Index/Pager.
Definition: index.c:4022
struct ListHead to_list
Definition: compose.c:114
void ctx_free(struct Context **ptr)
Free a Context.
Definition: context.c:48
Handling of global boolean variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Miscellaneous functions for sending an email.
void mutt_message_hook(struct Mailbox *m, struct Email *e, HookFlags type)
Perform a message hook.
Definition: hook.c:576
char * C_ComposeFormat
Config: printf-like format string for the Compose panel&#39;s status bar.
Definition: compose.c:97
static int redraw_crypt_lines(struct ComposeRedrawData *rd, int row)
Update the encryption info in the compose window.
Definition: compose.c:510
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
bool mutt_edit_content_type(struct Email *e, struct Body *b, FILE *fp)
Edit the content type of an attachment.
Definition: commands.c:1249
int const char int line
Definition: acutest.h:617
int current
Current entry.
Definition: mutt_menu.h:87
WHERE bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: options.h:47
"Fcc:" (save folder) field
Definition: compose.c:155
#define CHECK_COUNT
Definition: compose.c:133
WHERE char * C_PgpSignAs
Config: Use this alternative key for signing messages.
Definition: globals.h:161
struct MuttWindow * win_index
Definition: mutt_menu.h:94
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:588
char * mutt_compile_help(char *buf, size_t buflen, enum MenuType menu, const struct Mapping *items)
Create the text for the help menu.
Definition: help.c:116
WHERE char * C_SmimeSignAs
Config: Use this alternative key for signing messages.
Definition: globals.h:165
A List node for strings.
Definition: list.h:33
void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *acct)
Make fully qualified url from newsgroup name.
Definition: newsrc.c:563
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
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:75
enum WindowType type
Window type, e.g. WT_SIDEBAR.
Definition: mutt_window.h:103
static const char * AutocryptRecUiFlags[]
Definition: compose.c:245
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:119
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:353
WHERE bool OptNews
(pseudo) used to change reader mode
Definition: options.h:46
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:1144
Keep track when the compose screen needs redrawing.
Definition: compose.c:109
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:641
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:87
Log at debug level 3.
Definition: logging.h:42
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
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:928
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:1317
#define REDRAW_CURRENT
Redraw the current line of the menu.
Definition: mutt_menu.h:45
Support of Mixmaster anonymous remailer.
int mutt_compose_attachment(struct Body *a)
Create an attachment.
Definition: mutt_attach.c:118
int mix_check_message(struct Email *e)
Safety-check the message before passing it to mixmaster.
Definition: remailer.c:854
static int mutt_dlg_compose_observer(struct NotifyCallback *nc)
Listen for config changes affecting the Compose menu - Implements observer_t.
Definition: compose.c:1279
The header of an Email.
Definition: envelope.h:54
short vcount
The number of virtual attachments.
Definition: attach.h:59
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1549
ContentType
Content-Type.
Definition: mime.h:29
"Reply-To:" field
Definition: compose.c:154