NeoMutt  2025-12-11-911-gd8d604
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
functions.c
Go to the documentation of this file.
1
24
30
31#include "config.h"
32#include <errno.h>
33#include <stdbool.h>
34#include <string.h>
35#include <sys/stat.h>
36#include <unistd.h>
37#include "private.h"
38#include "mutt/lib.h"
39#include "config/lib.h"
40#include "email/lib.h"
41#include "core/lib.h"
42#include "conn/lib.h"
43#include "gui/lib.h"
44#include "mutt.h"
45#include "functions.h"
46#include "lib.h"
47#include "attach/lib.h"
48#include "browser/lib.h"
49#include "editor/lib.h"
50#include "history/lib.h"
51#include "hooks/lib.h"
52#include "imap/lib.h"
53#include "index/lib.h"
54#include "key/lib.h"
55#include "menu/lib.h"
56#include "ncrypt/lib.h"
57#include "nntp/lib.h"
58#include "pop/lib.h"
59#include "question/lib.h"
60#include "send/lib.h"
61#include "attach_data.h"
62#include "external.h"
63#include "globals.h"
64#include "module_data.h"
65#include "mutt_logging.h"
66#include "muttlib.h"
67#include "mx.h"
68#include "nntp/adata.h" // IWYU pragma: keep
69#include "shared_data.h"
70#ifdef ENABLE_NLS
71#include <libintl.h>
72#endif
73
74// clang-format off
78static const struct MenuFuncOp OpCompose[] = { /* map: compose */
79 { "attach-file", OP_ATTACH_ATTACH_FILE },
80 { "attach-key", OP_ATTACH_ATTACH_KEY },
81 { "attach-message", OP_ATTACH_ATTACH_MESSAGE },
82 { "attach-news-message", OP_ATTACH_ATTACH_NEWS_MESSAGE },
83#ifdef USE_AUTOCRYPT
84 { "autocrypt-menu", OP_COMPOSE_AUTOCRYPT_MENU },
85#endif
86 { "copy-file", OP_ATTACH_SAVE },
87 { "detach-file", OP_ATTACH_DETACH },
88 { "display-toggle-weed", OP_DISPLAY_HEADERS },
89 { "edit-bcc", OP_ENVELOPE_EDIT_BCC },
90 { "edit-cc", OP_ENVELOPE_EDIT_CC },
91 { "edit-content-id", OP_ATTACH_EDIT_CONTENT_ID },
92 { "edit-description", OP_ATTACH_EDIT_DESCRIPTION },
93 { "edit-encoding", OP_ATTACH_EDIT_ENCODING },
94 { "edit-fcc", OP_ENVELOPE_EDIT_FCC },
95 { "edit-file", OP_COMPOSE_EDIT_FILE },
96 { "edit-followup-to", OP_ENVELOPE_EDIT_FOLLOWUP_TO },
97 { "edit-from", OP_ENVELOPE_EDIT_FROM },
98 { "edit-headers", OP_ENVELOPE_EDIT_HEADERS },
99 { "edit-language", OP_ATTACH_EDIT_LANGUAGE },
100 { "edit-message", OP_COMPOSE_EDIT_MESSAGE },
101 { "edit-mime", OP_ATTACH_EDIT_MIME },
102 { "edit-newsgroups", OP_ENVELOPE_EDIT_NEWSGROUPS },
103 { "edit-reply-to", OP_ENVELOPE_EDIT_REPLY_TO },
104 { "edit-subject", OP_ENVELOPE_EDIT_SUBJECT },
105 { "edit-to", OP_ENVELOPE_EDIT_TO },
106 { "edit-type", OP_ATTACH_EDIT_TYPE },
107 { "edit-x-comment-to", OP_ENVELOPE_EDIT_X_COMMENT_TO },
108 { "exit", OP_EXIT },
109 { "filter-entry", OP_ATTACH_FILTER },
110 { "forget-passphrase", OP_FORGET_PASSPHRASE },
111 { "get-attachment", OP_ATTACH_GET_ATTACHMENT },
112 { "group-alternatives", OP_ATTACH_GROUP_ALTS },
113 { "group-multilingual", OP_ATTACH_GROUP_LINGUAL },
114 { "group-related", OP_ATTACH_GROUP_RELATED },
115 { "ispell", OP_COMPOSE_ISPELL },
116 { "move-down", OP_ATTACH_MOVE_DOWN },
117 { "move-up", OP_ATTACH_MOVE_UP },
118 { "new-mime", OP_ATTACH_NEW_MIME },
119 { "pgp-menu", OP_COMPOSE_PGP_MENU },
120 { "pipe-entry", OP_PIPE },
121 { "pipe-message", OP_PIPE },
122 { "postpone-message", OP_COMPOSE_POSTPONE_MESSAGE },
123 { "preview-page-down", OP_PREVIEW_PAGE_DOWN },
124 { "preview-page-up", OP_PREVIEW_PAGE_UP },
125 { "print-entry", OP_ATTACH_PRINT },
126 { "rename-attachment", OP_ATTACH_RENAME_ATTACHMENT },
127 { "rename-file", OP_COMPOSE_RENAME_FILE },
128 { "send-message", OP_COMPOSE_SEND_MESSAGE },
129 { "smime-menu", OP_COMPOSE_SMIME_MENU },
130 { "toggle-disposition", OP_ATTACH_TOGGLE_DISPOSITION },
131 { "toggle-recode", OP_ATTACH_TOGGLE_RECODE },
132 { "toggle-unlink", OP_ATTACH_TOGGLE_UNLINK },
133 { "ungroup-attachment", OP_ATTACH_UNGROUP },
134 { "update-encoding", OP_ATTACH_UPDATE_ENCODING },
135 { "view-attach", OP_ATTACH_VIEW },
136 { "view-mailcap", OP_ATTACH_VIEW_MAILCAP },
137 { "view-pager", OP_ATTACH_VIEW_PAGER },
138 { "view-text", OP_ATTACH_VIEW_TEXT },
139 { "write-fcc", OP_COMPOSE_WRITE_MESSAGE },
140 { NULL, 0 },
141};
142
146static const struct MenuOpSeq ComposeDefaultBindings[] = { /* map: compose */
147 { OP_ATTACH_ATTACH_FILE, "a" },
148 { OP_ATTACH_ATTACH_KEY, "\033k" }, // <Alt-k>
149 { OP_ATTACH_ATTACH_MESSAGE, "A" },
150 { OP_ATTACH_DETACH, "D" },
151 { OP_ATTACH_EDIT_CONTENT_ID, "\033i" }, // <Alt-i>
152 { OP_ATTACH_EDIT_DESCRIPTION, "d" },
153 { OP_ATTACH_EDIT_ENCODING, "\005" }, // <Ctrl-E>
154 { OP_ATTACH_EDIT_LANGUAGE, "\014" }, // <Ctrl-L>
155 { OP_ATTACH_EDIT_MIME, "m" },
156 { OP_ATTACH_EDIT_TYPE, "\024" }, // <Ctrl-T>
157 { OP_ATTACH_FILTER, "F" },
158 { OP_ATTACH_GET_ATTACHMENT, "G" },
159 { OP_ATTACH_GROUP_ALTS, "&" },
160 { OP_ATTACH_GROUP_LINGUAL, "^" },
161 { OP_ATTACH_GROUP_RELATED, "%" },
162 { OP_ATTACH_MOVE_DOWN, "+" },
163 { OP_ATTACH_MOVE_UP, "-" },
164 { OP_ATTACH_NEW_MIME, "n" },
165 { OP_EXIT, "q" },
166 { OP_PIPE, "|" },
167 { OP_ATTACH_PRINT, "l" },
168 { OP_ATTACH_RENAME_ATTACHMENT, "\017" }, // <Ctrl-O>
169 { OP_ATTACH_SAVE, "C" },
170 { OP_ATTACH_TOGGLE_DISPOSITION, "\004" }, // <Ctrl-D>
171 { OP_ATTACH_TOGGLE_UNLINK, "u" },
172 { OP_ATTACH_UNGROUP, "#" },
173 { OP_ATTACH_UPDATE_ENCODING, "U" },
174 { OP_ATTACH_VIEW, "<keypadenter>" },
175 { OP_ATTACH_VIEW, "\n" }, // <Enter>
176 { OP_ATTACH_VIEW, "\r" }, // <Return>
177#ifdef USE_AUTOCRYPT
178 { OP_COMPOSE_AUTOCRYPT_MENU, "o" },
179#endif
180 { OP_COMPOSE_EDIT_FILE, "\033e" }, // <Alt-e>
181 { OP_COMPOSE_EDIT_MESSAGE, "e" },
182 { OP_COMPOSE_ISPELL, "i" },
183 { OP_COMPOSE_PGP_MENU, "p" },
184 { OP_COMPOSE_POSTPONE_MESSAGE, "P" },
185 { OP_COMPOSE_RENAME_FILE, "R" },
186 { OP_COMPOSE_SEND_MESSAGE, "y" },
187 { OP_COMPOSE_SMIME_MENU, "S" },
188 { OP_COMPOSE_WRITE_MESSAGE, "w" },
189 { OP_DISPLAY_HEADERS, "h" },
190 { OP_ENVELOPE_EDIT_BCC, "b" },
191 { OP_ENVELOPE_EDIT_CC, "c" },
192 { OP_ENVELOPE_EDIT_FCC, "f" },
193 { OP_ENVELOPE_EDIT_FROM, "\033f" }, // <Alt-f>
194 { OP_ENVELOPE_EDIT_HEADERS, "E" },
195 { OP_ENVELOPE_EDIT_REPLY_TO, "r" },
196 { OP_ENVELOPE_EDIT_SUBJECT, "s" },
197 { OP_ENVELOPE_EDIT_TO, "t" },
198 { OP_PREVIEW_PAGE_DOWN, "<pagedown>" },
199 { OP_PREVIEW_PAGE_UP, "<pageup>" },
200 { OP_FORGET_PASSPHRASE, "\006" }, // <Ctrl-F>
201 { OP_TAG, "T" },
202 { 0, NULL },
203};
204// clang-format on
205
209void compose_init_keys(struct NeoMutt *n, struct SubMenu *sm_generic)
210{
212 ASSERT(mod_data);
213
214 struct MenuDefinition *md = NULL;
215 struct SubMenu *sm = NULL;
216
218 md = km_register_menu(MENU_COMPOSE, "compose");
219 km_menu_add_submenu(md, sm);
220 km_menu_add_submenu(md, sm_generic);
222
223 mod_data->md_compose = md;
224}
225
231static bool check_count(struct AttachCtx *actx)
232{
233 if (actx->idxlen == 0)
234 {
235 mutt_error(_("There are no attachments"));
236 return false;
237 }
238
239 return true;
240}
241
248static char *gen_cid(void)
249{
250 char rndid[MUTT_RANDTAG_LEN + 1];
251
252 mutt_rand_base32(rndid, sizeof(rndid) - 1);
253 rndid[MUTT_RANDTAG_LEN] = 0;
254
255 return mutt_str_dup(rndid);
256}
257
264static bool check_cid(const char *cid)
265{
266 static const char *check = "^[-\\.0-9@A-Z_a-z]+$";
267
268 struct Regex *check_cid_regex = mutt_regex_new(check, 0, NULL);
269
270 const bool valid = mutt_regex_match(check_cid_regex, cid);
271
272 mutt_regex_free(&check_cid_regex);
273
274 return valid;
275}
276
284static bool content_id_exists(struct AttachCtx *actx, const struct Body *skip, const char *cid)
285{
286 if (!actx || !cid)
287 return false;
288
289 for (int i = 0; i < actx->idxlen; i++)
290 {
291 struct Body *b = actx->idx[i]->body;
292 if ((b == skip) || !b->content_id)
293 continue;
294
295 if (mutt_str_equal(b->content_id, cid))
296 return true;
297 }
298
299 return false;
300}
301
309static int check_attachments(struct AttachCtx *actx, struct ConfigSubset *sub)
310{
311 int rc = -1;
312 struct stat st = { 0 };
313 struct Buffer *pretty = NULL, *msg = NULL;
314
315 for (int i = 0; i < actx->idxlen; i++)
316 {
317 if (actx->idx[i]->body->type == TYPE_MULTIPART)
318 continue;
319 if (stat(actx->idx[i]->body->filename, &st) != 0)
320 {
321 if (!pretty)
322 pretty = buf_pool_get();
323 buf_strcpy(pretty, actx->idx[i]->body->filename);
324 pretty_mailbox(pretty);
325 /* L10N: This message is displayed in the compose menu when an attachment
326 doesn't stat. %d is the attachment number and %s is the attachment
327 filename. The filename is located last to avoid a long path hiding
328 the error message. */
329 mutt_error(_("Attachment #%d no longer exists: %s"), i + 1, buf_string(pretty));
330 goto cleanup;
331 }
332
333 if (actx->idx[i]->body->stamp < st.st_mtime)
334 {
335 if (!pretty)
336 pretty = buf_pool_get();
337 buf_strcpy(pretty, actx->idx[i]->body->filename);
338 pretty_mailbox(pretty);
339
340 if (!msg)
341 msg = buf_pool_get();
342 /* L10N: This message is displayed in the compose menu when an attachment
343 is modified behind the scenes. %d is the attachment number and %s is
344 the attachment filename. The filename is located last to avoid a long
345 path hiding the prompt question. */
346 buf_printf(msg, _("Attachment #%d modified. Update encoding for %s?"),
347 i + 1, buf_string(pretty));
348
350 if (ans == MUTT_YES)
351 mutt_update_encoding(actx->idx[i]->body, sub);
352 else if (ans == MUTT_ABORT)
353 goto cleanup;
354 }
355 }
356
357 rc = 0;
358
359cleanup:
360 buf_pool_release(&pretty);
361 buf_pool_release(&msg);
362 return rc;
363}
364
373static int delete_attachment(struct AttachCtx *actx, int aidx, struct ConfigSubset *sub)
374{
375 if (!actx || (aidx < 0) || (aidx >= actx->idxlen))
376 return -1;
377
378 struct AttachPtr **idx = actx->idx;
379 struct Body *b_previous = NULL;
380 struct Body *b_parent = NULL;
381
382 if (aidx == 0)
383 {
384 struct Body *b = actx->idx[0]->body;
385 if (!b->next) // There's only one attachment left
386 {
387 mutt_error(_("You may not delete the only attachment"));
388 return -1;
389 }
390
391 if (cs_subset_bool(sub, "compose_confirm_detach_first"))
392 {
393 /* L10N: Prompt when trying to hit <detach-file> on the first entry in
394 the compose menu. This entry is most likely the message they just
395 typed. Hitting yes will remove the entry and unlink the file, so
396 it's worth confirming they really meant to do it. */
397 enum QuadOption ans = query_yesorno_help(_("Really delete the main message?"), MUTT_NO,
398 sub, "compose_confirm_detach_first");
399 if (ans == MUTT_NO)
400 {
401 idx[aidx]->body->tagged = false;
402 return -1;
403 }
404 }
405 }
406
407 if (idx[aidx]->level > 0)
408 {
409 if (attach_body_parent(idx[0]->body, NULL, idx[aidx]->body, &b_parent))
410 {
411 if (attach_body_count(b_parent->parts, false) < 3)
412 {
413 mutt_error(_("Can't leave group with only one attachment"));
414 return -1;
415 }
416 }
417 }
418
419 // reorder body pointers
420 if (aidx > 0)
421 {
422 if (attach_body_previous(idx[0]->body, idx[aidx]->body, &b_previous))
423 b_previous->next = idx[aidx]->body->next;
424 else if (attach_body_parent(idx[0]->body, NULL, idx[aidx]->body, &b_parent))
425 b_parent->parts = idx[aidx]->body->next;
426 }
427
428 // free memory
429 int part_count = 1;
430 if (aidx < (actx->idxlen - 1))
431 {
432 if ((idx[aidx]->body->type == TYPE_MULTIPART) &&
433 (idx[aidx + 1]->level > idx[aidx]->level))
434 {
435 part_count += attach_body_count(idx[aidx]->body->parts, true);
436 }
437 }
438 idx[aidx]->body->next = NULL;
439 mutt_body_free(&(idx[aidx]->body));
440 for (int i = 0; i < part_count; i++)
441 {
442 FREE(&idx[aidx + i]->tree);
443 FREE(&idx[aidx + i]);
444 }
445
446 // reorder attachment list
447 for (int i = aidx; i < (actx->idxlen - part_count); i++)
448 idx[i] = idx[i + part_count];
449 for (int i = 0; i < part_count; i++)
450 idx[actx->idxlen - i - 1] = NULL;
451 actx->idxlen -= part_count;
452
453 return 0;
454}
455
462static void update_idx(struct Menu *menu, struct AttachCtx *actx, struct AttachPtr *ap)
463{
464 ap->level = 0;
465 for (int i = actx->idxlen; i > 0; i--)
466 {
467 if (ap->level == actx->idx[i - 1]->level)
468 {
469 actx->idx[i - 1]->body->next = ap->body;
470 break;
471 }
472 }
473
474 ap->body->aptr = ap;
475 mutt_actx_add_attach(actx, ap);
476 update_menu(actx, menu, false);
477 menu_set_index(menu, actx->vcount - 1);
478}
479
487static void compose_attach_swap(struct Email *e, struct AttachCtx *actx, int first, int second)
488{
489 struct AttachPtr **idx = actx->idx;
490
491 // check that attachments really are adjacent
492 if (idx[first]->body->next != idx[second]->body)
493 return;
494
495 // reorder Body pointers
496 if (first == 0)
497 {
498 // first attachment is the fundamental part
499 idx[first]->body->next = idx[second]->body->next;
500 idx[second]->body->next = idx[first]->body;
501 e->body = idx[second]->body;
502 }
503 else
504 {
505 // find previous attachment
506 struct Body *b_previous = NULL;
507 struct Body *b_parent = NULL;
508 if (attach_body_previous(e->body, idx[first]->body, &b_previous))
509 {
510 idx[first]->body->next = idx[second]->body->next;
511 idx[second]->body->next = idx[first]->body;
512 b_previous->next = idx[second]->body;
513 }
514 else if (attach_body_parent(e->body, NULL, idx[first]->body, &b_parent))
515 {
516 idx[first]->body->next = idx[second]->body->next;
517 idx[second]->body->next = idx[first]->body;
518 b_parent->parts = idx[second]->body;
519 }
520 }
521
522 // reorder attachment list
523 struct AttachPtr *saved = idx[second];
524 for (int i = second; i > first; i--)
525 idx[i] = idx[i - 1];
526 idx[first] = saved;
527
528 // if moved attachment is a group then move subparts too
529 if ((idx[first]->body->type == TYPE_MULTIPART) && (second < actx->idxlen - 1))
530 {
531 int i = second + 1;
532 while (idx[i]->level > idx[first]->level)
533 {
534 saved = idx[i];
535 int destidx = i - second + first;
536 for (int j = i; j > destidx; j--)
537 idx[j] = idx[j - 1];
538 idx[destidx] = saved;
539 i++;
540 if (i >= actx->idxlen)
541 break;
542 }
543 }
544}
545
553static int body_index(struct AttachCtx *actx, const struct Body *b)
554{
555 if (!actx || !b)
556 return -1;
557
558 for (int i = 0; i < actx->idxlen; i++)
559 {
560 if (actx->idx[i]->body == b)
561 return i;
562 }
563
564 return -1;
565}
566
574static int prev_sibling(struct AttachCtx *actx, int index)
575{
576 if (!actx || (index <= 0) || (index >= actx->idxlen))
577 return -1;
578
579 const int level = actx->idx[index]->level;
580 int previdx = index - 1;
581 while ((previdx > 0) && (actx->idx[previdx]->level > level))
582 previdx--;
583
584 return (actx->idx[previdx]->level == level) ? previdx : -1;
585}
586
594static int next_sibling(struct AttachCtx *actx, int index)
595{
596 if (!actx || (index < 0) || (index >= actx->idxlen))
597 return -1;
598
599 const int level = actx->idx[index]->level;
600 int nextidx = index + 1;
601 while ((nextidx < actx->idxlen) && (actx->idx[nextidx]->level > level))
602 nextidx++;
603
604 if ((nextidx >= actx->idxlen) || (actx->idx[nextidx]->level != level))
605 return -1;
606
607 return nextidx;
608}
609
616static bool body_array_contains(struct BodyArray *ba, const struct Body *b)
617{
618 struct Body **bp = NULL;
619 ARRAY_FOREACH(bp, ba)
620 {
621 if (*bp == b)
622 return true;
623 }
624
625 return false;
626}
627
634static bool selected_attachments_are_siblings(struct Email *e, struct BodyArray *ba)
635{
636 if (!e || !ba)
637 return false;
638
639 int level = -1;
640 struct Body *parent = NULL;
641 struct Body **bp = NULL;
642 ARRAY_FOREACH(bp, ba)
643 {
644 struct Body *this_parent = NULL;
645 struct Body *body = *bp;
646 if (body->aptr && (body->aptr->level > 0))
647 {
648 if (!attach_body_parent(e->body, NULL, body, &this_parent))
649 return false;
650 }
651
652 if (level == -1)
653 {
654 level = body->aptr ? body->aptr->level : 0;
655 parent = this_parent;
656 continue;
657 }
658
659 if ((level != (body->aptr ? body->aptr->level : 0)) || (parent != this_parent))
660 return false;
661 }
662
663 return true;
664}
665
672{
673 return (count > 1) ? MENU_REDRAW_FULL : MENU_REDRAW_CURRENT;
674}
675
683static int move_attachment(struct ComposeSharedData *shared, bool up, int count)
684{
685 struct AttachCtx *actx = shared->adata->actx;
686 if (!check_count(actx))
687 return FR_ERROR;
688
689 struct Menu *menu = shared->adata->menu;
690 struct AttachPtr *cur_att = current_attachment(actx, menu);
691 if (!cur_att)
692 return FR_ERROR;
693
694 struct Body *cur_body = cur_att->body;
695 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
696 ba_add_selection(&ba, actx, menu, menu->tag_prefix, count);
697 if (ARRAY_EMPTY(&ba))
698 goto done;
699
700 if ((ARRAY_SIZE(&ba) > 1) && !selected_attachments_are_siblings(shared->email, &ba))
701 {
702 mutt_error(_("Selected attachments must be siblings"));
703 ARRAY_FREE(&ba);
704 return FR_ERROR;
705 }
706
707 bool moved = false;
708 if (up)
709 {
710 struct Body **bp = NULL;
711 ARRAY_FOREACH(bp, &ba)
712 {
713 const int index = body_index(actx, *bp);
714 const int previdx = prev_sibling(actx, index);
715 if (previdx < 0)
716 continue;
717 if (body_array_contains(&ba, actx->idx[previdx]->body))
718 continue;
719
720 compose_attach_swap(shared->email, actx, previdx, index);
721 moved = true;
722 }
723 }
724 else
725 {
726 for (int i = ARRAY_SIZE(&ba) - 1; i >= 0; i--)
727 {
728 struct Body **bp = ARRAY_GET(&ba, i);
729 const int index = body_index(actx, *bp);
730 const int nextidx = next_sibling(actx, index);
731 if (nextidx < 0)
732 continue;
733 if (body_array_contains(&ba, actx->idx[nextidx]->body))
734 continue;
735
736 compose_attach_swap(shared->email, actx, index, nextidx);
737 moved = true;
738 }
739 }
740
741 if (!moved)
742 {
743 mutt_error(up ? _("Attachment is already at top") : _("Attachment is already at bottom"));
744 goto done;
745 }
746
747 mutt_update_tree(actx);
750
751 const int index = body_index(actx, cur_body);
752 if (index >= 0)
753 menu_set_index(menu, index);
754
755 ARRAY_FREE(&ba);
756 return FR_SUCCESS;
757
758done:
759 ARRAY_FREE(&ba);
760 return FR_ERROR;
761}
762
771static int group_attachments(struct ComposeSharedData *shared, char *subtype,
772 struct BodyArray *ba)
773{
774 struct AttachCtx *actx = shared->adata->actx;
775 int group_level = -1;
776 struct Body *bptr_parent = NULL;
777
778 // Attachments to be grouped must have the same parent
779 for (int i = 0; i < actx->idxlen; i++)
780 {
781 if (body_array_contains(ba, actx->idx[i]->body))
782 {
783 if (group_level == -1)
784 {
785 group_level = actx->idx[i]->level;
786 }
787 else
788 {
789 if (group_level != actx->idx[i]->level)
790 {
791 mutt_error(_("Attachments to be grouped must have the same parent"));
792 return FR_ERROR;
793 }
794 }
795 // if not at top level check if all tagged attachments have same parent
796 if (group_level > 0)
797 {
798 if (bptr_parent)
799 {
800 struct Body *bptr_test = NULL;
801 if (!attach_body_parent(actx->idx[0]->body, NULL, actx->idx[i]->body, &bptr_test))
802 mutt_debug(LL_DEBUG5, "can't find parent\n");
803 if (bptr_test != bptr_parent)
804 {
805 mutt_error(_("Attachments to be grouped must have the same parent"));
806 return FR_ERROR;
807 }
808 }
809 else
810 {
811 if (!attach_body_parent(actx->idx[0]->body, NULL, actx->idx[i]->body, &bptr_parent))
812 mutt_debug(LL_DEBUG5, "can't find parent\n");
813 }
814 }
815 }
816 }
817
818 // Can't group all attachments unless at top level
819 if (bptr_parent)
820 {
821 if (ARRAY_SIZE(ba) == attach_body_count(bptr_parent->parts, false))
822 {
823 mutt_error(_("Can't leave group with only one attachment"));
824 return FR_ERROR;
825 }
826 }
827
828 struct Body *group = mutt_body_new();
829 group->type = TYPE_MULTIPART;
830 group->subtype = mutt_str_dup(subtype);
831 group->encoding = ENC_7BIT;
832
833 struct Body *bptr_first = NULL; // first selected attachment
834 struct Body *bptr = NULL; // current selected attachment
835 struct Body *group_parent = NULL; // parent of group
836 struct Body *group_previous = NULL; // previous body to group
837 struct Body *group_part = NULL; // current attachment in group
838 int group_idx = 0; // index in attachment list where group will be inserted
839 int group_last_idx = 0; // index of last part of previous found group
840 int group_parent_type = TYPE_OTHER;
841
842 for (int i = 0; i < actx->idxlen; i++)
843 {
844 bptr = actx->idx[i]->body;
845 if (body_array_contains(ba, bptr))
846 {
847 // set group properties based on first selected attachment
848 if (!bptr_first)
849 {
850 group->disposition = bptr->disposition;
851 if (bptr->language && !mutt_str_equal(subtype, "multilingual"))
852 group->language = mutt_str_dup(bptr->language);
853 group_parent_type = bptr->aptr->parent_type;
854 bptr_first = bptr;
855 if (i > 0)
856 {
857 if (!attach_body_previous(shared->email->body, bptr, &group_previous))
858 {
859 mutt_debug(LL_DEBUG5, "couldn't find previous\n");
860 }
861 if (!attach_body_parent(shared->email->body, NULL, bptr, &group_parent))
862 {
863 mutt_debug(LL_DEBUG5, "couldn't find parent\n");
864 }
865 }
866 }
867
868 if (bptr->tagged)
869 {
870 shared->adata->menu->num_tagged--;
871 bptr->tagged = false;
872 }
873 bptr->aptr->level++;
875
876 // append bptr to the group parts list and remove from email body list
877 struct Body *bptr_previous = NULL;
878 if (attach_body_previous(shared->email->body, bptr, &bptr_previous))
879 bptr_previous->next = bptr->next;
880 else if (attach_body_parent(shared->email->body, NULL, bptr, &bptr_parent))
881 bptr_parent->parts = bptr->next;
882 else
883 shared->email->body = bptr->next;
884
885 if (group_part)
886 {
887 // add bptr to group parts list
888 group_part->next = bptr;
889 group_part = group_part->next;
890 group_part->next = NULL;
891
892 // reorder attachments and set levels
893 int bptr_attachments = attach_body_count(bptr, true);
894 for (int j = i + 1; j < (i + bptr_attachments); j++)
895 actx->idx[j]->level++;
896 if (i > (group_last_idx + 1))
897 {
898 for (int j = 0; j < bptr_attachments; j++)
899 {
900 struct AttachPtr *saved = actx->idx[i + bptr_attachments - 1];
901 for (int k = i + bptr_attachments - 1; k > (group_last_idx + 1); k--)
902 actx->idx[k] = actx->idx[k - 1];
903 actx->idx[group_last_idx + 1] = saved;
904 }
905 }
906 i += bptr_attachments - 1;
907 group_last_idx += bptr_attachments;
908 }
909 else
910 {
911 group_idx = i;
912 group->parts = bptr;
913 group_part = bptr;
914 group_part->next = NULL;
915 int bptr_attachments = attach_body_count(bptr, true);
916 for (int j = i + 1; j < (i + bptr_attachments); j++)
917 actx->idx[j]->level++;
918 i += bptr_attachments - 1;
919 group_last_idx = i;
920 }
921 }
922 }
923
924 if (!bptr_first)
925 {
926 mutt_body_free(&group);
927 return FR_ERROR;
928 }
929
930 // set group->next
931 int next_aidx = group_idx + attach_body_count(group->parts, true);
932 if (group_parent)
933 {
934 // find next attachment with the same parent as the group
935 struct Body *b = NULL;
936 struct Body *b_parent = NULL;
937 while (next_aidx < actx->idxlen)
938 {
939 b = actx->idx[next_aidx]->body;
940 b_parent = NULL;
941 if (attach_body_parent(shared->email->body, NULL, b, &b_parent))
942 {
943 if (group_parent == b_parent)
944 {
945 group->next = b;
946 break;
947 }
948 }
949 next_aidx++;
950 }
951 }
952 else if (next_aidx < actx->idxlen)
953 {
954 // group is at top level
955 group->next = actx->idx[next_aidx]->body;
956 }
957
958 // set previous or parent for group
959 if (group_previous)
960 group_previous->next = group;
961 else if (group_parent)
962 group_parent->parts = group;
963
965
966 struct AttachPtr *group_ap = mutt_aptr_new();
967 group_ap->body = group;
968 group_ap->body->aptr = group_ap;
969 group_ap->level = group_level;
970 group_ap->parent_type = group_parent_type;
971
972 // insert group into attachment list
973 mutt_actx_ins_attach(actx, group_ap, group_idx);
974
975 // update email body and last attachment pointers
976 shared->email->body = actx->idx[0]->body;
977 actx->idx[actx->idxlen - 1]->body->next = NULL;
978
979 update_menu(actx, shared->adata->menu, false);
980 shared->adata->menu->current = group_idx;
982
983 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
984 return FR_SUCCESS;
985}
986
987// -----------------------------------------------------------------------------
988
992static int op_attach_attach_file(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
993{
994 struct ComposeSharedData *shared = fdata->shared;
995 char *prompt = _("Attach file");
996 int numfiles = 0;
997 char **files = NULL;
998
999 struct Buffer *fname = buf_pool_get();
1000 if ((mw_enter_fname(prompt, fname, false, NULL, true, &files, &numfiles,
1001 MUTT_SEL_MULTI) == -1) ||
1002 buf_is_empty(fname))
1003 {
1004 for (int i = 0; i < numfiles; i++)
1005 FREE(&files[i]);
1006
1007 FREE(&files);
1008 buf_pool_release(&fname);
1009 return FR_NO_ACTION;
1010 }
1011
1012 bool error = false;
1013 bool added_attachment = false;
1014 if (numfiles > 1)
1015 {
1016 mutt_message(ngettext("Attaching selected file...",
1017 "Attaching selected files...", numfiles));
1018 }
1019 for (int i = 0; i < numfiles; i++)
1020 {
1021 char *att = files[i];
1022 if (!att)
1023 continue;
1024
1025 struct AttachPtr *ap = mutt_aptr_new();
1026 ap->unowned = true;
1027 ap->body = mutt_make_file_attach(att, shared->sub);
1028 if (ap->body)
1029 {
1030 added_attachment = true;
1031 update_idx(shared->adata->menu, shared->adata->actx, ap);
1032 }
1033 else
1034 {
1035 error = true;
1036 mutt_error(_("Unable to attach %s"), att);
1037 mutt_aptr_free(&ap);
1038 }
1039 FREE(&files[i]);
1040 }
1041
1042 FREE(&files);
1043 buf_pool_release(&fname);
1044
1045 if (!error)
1047
1050 if (added_attachment)
1051 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1052 return added_attachment ? FR_SUCCESS : (error ? FR_ERROR : FR_NO_ACTION);
1053}
1054
1058static int op_attach_attach_key(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1059{
1060 struct ComposeSharedData *shared = fdata->shared;
1061 if (!(WithCrypto & APPLICATION_PGP))
1062 return FR_NOT_IMPL;
1063 struct AttachPtr *ap = mutt_aptr_new();
1065 if (ap->body)
1066 {
1067 update_idx(shared->adata->menu, shared->adata->actx, ap);
1069 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1070 }
1071 else
1072 {
1073 mutt_aptr_free(&ap);
1074 }
1075
1077 return FR_SUCCESS;
1078}
1079
1088 const struct KeyEvent *event)
1089{
1091 struct ComposeSharedData *shared = fdata->shared;
1092 char *prompt = _("Open mailbox to attach message from");
1093
1094 OptNews = false;
1095 const int op = event->op;
1096 if (shared->mailbox && (op == OP_ATTACH_ATTACH_NEWS_MESSAGE))
1097 {
1098 const char *const c_news_server = cs_subset_string(shared->sub, "news_server");
1099 mod_data->current_news_srv = nntp_select_server(shared->mailbox, c_news_server, false);
1100 if (!mod_data->current_news_srv)
1101 return FR_NO_ACTION;
1102
1103 prompt = _("Open newsgroup to attach message from");
1104 OptNews = true;
1105 }
1106
1107 struct Buffer *fname = buf_pool_get();
1108 if (shared->mailbox)
1109 {
1110 if ((op == OP_ATTACH_ATTACH_MESSAGE) ^ (shared->mailbox->type == MUTT_NNTP))
1111 {
1112 buf_strcpy(fname, mailbox_path(shared->mailbox));
1113 pretty_mailbox(fname);
1114 }
1115 }
1116
1117 if ((mw_enter_fname(prompt, fname, true, shared->mailbox, false, NULL, NULL,
1118 MUTT_SEL_NONE) == -1) ||
1119 buf_is_empty(fname))
1120 {
1121 buf_pool_release(&fname);
1122 return FR_NO_ACTION;
1123 }
1124
1125 if (OptNews)
1126 nntp_expand_path(fname->data, fname->dsize,
1127 &mod_data->current_news_srv->conn->account);
1128 else
1129 expand_path(fname, false);
1130
1131 if (imap_path_probe(buf_string(fname), NULL) != MUTT_IMAP)
1132 {
1133 if (pop_path_probe(buf_string(fname), NULL) != MUTT_POP)
1134 {
1135 if (!OptNews && (nntp_path_probe(buf_string(fname), NULL) != MUTT_NNTP))
1136 {
1137 if (mx_path_probe(buf_string(fname)) != MUTT_NOTMUCH)
1138 {
1139 /* check to make sure the file exists and is readable */
1140 if (access(buf_string(fname), R_OK) == -1)
1141 {
1142 mutt_perror("%s", buf_string(fname));
1143 buf_pool_release(&fname);
1144 return FR_ERROR;
1145 }
1146 }
1147 }
1148 }
1149 }
1150
1152
1153 struct Mailbox *m_attach = mx_path_resolve(buf_string(fname));
1154 const bool old_readonly = m_attach->readonly;
1155 if (!mx_mbox_open(m_attach, MUTT_READONLY))
1156 {
1157 mutt_error(_("Unable to open mailbox %s"), buf_string(fname));
1158 mx_fastclose_mailbox(m_attach, false);
1159 m_attach = NULL;
1160 buf_pool_release(&fname);
1161 return FR_ERROR;
1162 }
1163 buf_pool_release(&fname);
1164
1165 if (m_attach->msg_count == 0)
1166 {
1167 mx_mbox_close(m_attach);
1168 mutt_error(_("No messages in that folder"));
1169 return FR_ERROR;
1170 }
1171
1172 /* `$sort`, `$sort_aux`, `$use_threads` could be changed in dlg_index() */
1173 const enum EmailSortType old_sort = cs_subset_sort(shared->sub, "sort");
1174 const enum EmailSortType old_sort_aux = cs_subset_sort(shared->sub, "sort_aux");
1175 const unsigned char old_use_threads = cs_subset_enum(shared->sub, "use_threads");
1176
1177 mutt_message(_("Tag the messages you want to attach"));
1178 struct MuttWindow *dlg = index_pager_init();
1179 struct IndexSharedData *index_shared = dlg->wdata;
1180 index_shared->attach_msg = true;
1181 dialog_push(dlg);
1182 struct Mailbox *m_attach_new = dlg_index(dlg, m_attach);
1183 dialog_pop();
1184 mutt_window_free(&dlg);
1185
1186 if (!shared->mailbox)
1187 {
1188 /* Restore old $sort variables */
1189 cs_subset_str_native_set(shared->sub, "sort", old_sort, NULL);
1190 cs_subset_str_native_set(shared->sub, "sort_aux", old_sort_aux, NULL);
1191 cs_subset_str_native_set(shared->sub, "use_threads", old_use_threads, NULL);
1194 return FR_SUCCESS;
1195 }
1196
1197 bool added_attachment = false;
1198 for (int i = 0; i < m_attach_new->msg_count; i++)
1199 {
1200 if (!m_attach_new->emails[i])
1201 break;
1202 if (!message_is_tagged(m_attach_new->emails[i]))
1203 continue;
1204
1205 struct AttachPtr *ap = mutt_aptr_new();
1206 ap->body = mutt_make_message_attach(m_attach_new, m_attach_new->emails[i],
1207 true, shared->sub);
1208 if (ap->body)
1209 {
1210 added_attachment = true;
1211 update_idx(shared->adata->menu, shared->adata->actx, ap);
1212 }
1213 else
1214 {
1215 mutt_error(_("Unable to attach"));
1216 mutt_aptr_free(&ap);
1217 }
1218 }
1220
1221 if (m_attach_new == m_attach)
1222 {
1223 m_attach->readonly = old_readonly;
1224 }
1225 mx_fastclose_mailbox(m_attach_new, false);
1226
1227 /* Restore old $sort variables */
1228 cs_subset_str_native_set(shared->sub, "sort", old_sort, NULL);
1229 cs_subset_str_native_set(shared->sub, "sort_aux", old_sort_aux, NULL);
1230 cs_subset_str_native_set(shared->sub, "use_threads", old_use_threads, NULL);
1232 if (added_attachment)
1233 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1234 return added_attachment ? FR_SUCCESS : FR_ERROR;
1235}
1236
1240static int op_attach_detach(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1241{
1242 struct ComposeSharedData *shared = fdata->shared;
1243 struct AttachCtx *actx = shared->adata->actx;
1244 if (!check_count(actx))
1245 return FR_ERROR;
1246
1247 struct Menu *menu = shared->adata->menu;
1248 int rc = FR_NO_ACTION;
1249 struct AttachPtrArray aa = ARRAY_HEAD_INITIALIZER;
1250 aa_add_selection(&aa, actx, menu, menu->tag_prefix, event->count);
1251
1252 if (ARRAY_SIZE(&aa) > 1)
1253 {
1254 for (int i = ARRAY_SIZE(&aa) - 1; i >= 0; i--)
1255 {
1256 struct AttachPtr **app = ARRAY_GET(&aa, i);
1257 if (!app || !*app)
1258 continue;
1259 struct AttachPtr *ap = *app;
1260
1261 if (ap->unowned)
1262 ap->body->unlink = false;
1263
1264 const int index = body_index(actx, ap->body);
1265 if ((index >= 0) && (delete_attachment(actx, index, fdata->n->sub) == 0))
1266 rc = FR_SUCCESS;
1267 }
1268
1269 if (rc != FR_SUCCESS)
1270 {
1271 menu->num_tagged = 0;
1272 for (int i = 0; i < actx->idxlen; i++)
1273 {
1274 if (actx->idx[i]->body->tagged)
1275 menu->num_tagged++;
1276 }
1277 update_menu(actx, menu, false);
1278 ARRAY_FREE(&aa);
1279 return FR_ERROR;
1280 }
1281 }
1282 else
1283 {
1284 struct AttachPtr *cur_att = current_attachment(actx, menu);
1285 if (cur_att->unowned)
1286 cur_att->body->unlink = false;
1287
1288 const int index = menu_get_index(menu);
1289 if (delete_attachment(actx, index, fdata->n->sub) == -1)
1290 {
1291 menu->num_tagged = 0;
1292 for (int i = 0; i < actx->idxlen; i++)
1293 {
1294 if (actx->idx[i]->body->tagged)
1295 menu->num_tagged++;
1296 }
1297 update_menu(actx, menu, false);
1298 ARRAY_FREE(&aa);
1299 return FR_ERROR;
1300 }
1301
1302 rc = FR_SUCCESS;
1303 }
1304
1305 menu->num_tagged = 0;
1306 for (int i = 0; i < actx->idxlen; i++)
1307 {
1308 if (actx->idx[i]->body->tagged)
1309 menu->num_tagged++;
1310 }
1311
1312 update_menu(actx, menu, false);
1314
1315 const int index = menu_get_index(menu);
1316 if (index == 0)
1317 shared->email->body = actx->idx[0]->body;
1318
1319 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1320 ARRAY_FREE(&aa);
1321 return rc;
1322}
1323
1328 const struct KeyEvent *event)
1329{
1330 struct ComposeSharedData *shared = fdata->shared;
1331 struct AttachCtx *actx = shared->adata->actx;
1332 if (!check_count(actx))
1333 return FR_ERROR;
1334
1335 int rc = FR_NO_ACTION;
1336 struct Menu *menu = shared->adata->menu;
1337 struct Buffer *buf = buf_pool_get();
1338 struct Buffer *cid = buf_pool_get();
1339 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1340 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
1341
1342 struct AttachPtr *cur_att = current_attachment(actx, menu);
1343
1344 char *id = cur_att->body->content_id;
1345 if (id)
1346 {
1347 buf_strcpy(buf, id);
1348 }
1349 else
1350 {
1351 id = gen_cid();
1352 buf_strcpy(buf, id);
1353 FREE(&id);
1354 }
1355
1356 if (mw_get_field("Content-ID: ", buf, MUTT_COMP_NONE, HC_OTHER, NULL, NULL) == 0)
1357 {
1358 if (check_cid(buf_string(buf)))
1359 {
1360 bool changed = false;
1361 struct Body **bp = NULL;
1362 ARRAY_FOREACH(bp, &ba)
1363 {
1364 const bool multi = (ARRAY_SIZE(&ba) > 1);
1365 int suffix = multi ? (ARRAY_FOREACH_IDX_bp + 1) : 0;
1366
1367 do
1368 {
1369 if (suffix == 0)
1370 buf_strcpy(cid, buf_string(buf));
1371 else
1372 buf_printf(cid, "%s-%d", buf_string(buf), suffix);
1373
1374 suffix++;
1375 } while (content_id_exists(actx, *bp, buf_string(cid)));
1376
1377 if (!mutt_str_equal((*bp)->content_id, buf_string(cid)))
1378 {
1379 mutt_str_replace(&(*bp)->content_id, buf_string(cid));
1380 changed = true;
1381 }
1382 }
1383
1384 if (changed)
1385 {
1388 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1389 rc = FR_SUCCESS;
1390 }
1391 }
1392 else
1393 {
1394 mutt_error(_("Content-ID can only contain the characters: -.0-9@A-Z_a-z"));
1395 rc = FR_ERROR;
1396 }
1397 }
1398
1399 ARRAY_FREE(&ba);
1400 buf_pool_release(&cid);
1401 buf_pool_release(&buf);
1402
1403 if (rc != FR_ERROR)
1405
1406 return rc;
1407}
1408
1413 const struct KeyEvent *event)
1414{
1415 struct ComposeSharedData *shared = fdata->shared;
1416 struct AttachCtx *actx = shared->adata->actx;
1417 if (!check_count(actx))
1418 return FR_ERROR;
1419
1420 int rc = FR_NO_ACTION;
1421 struct Menu *menu = shared->adata->menu;
1422 struct Buffer *buf = buf_pool_get();
1423 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1424
1425 struct AttachPtr *cur_att = current_attachment(actx, menu);
1426 buf_strcpy(buf, cur_att->body->description);
1427
1428 /* header names should not be translated */
1429 if (mw_get_field("Description: ", buf, MUTT_COMP_NONE, HC_OTHER, NULL, NULL) == 0)
1430 {
1431 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
1432
1433 bool changed = false;
1434 struct Body **bp = NULL;
1435 ARRAY_FOREACH(bp, &ba)
1436 {
1437 if (!mutt_str_equal((*bp)->description, buf_string(buf)))
1438 {
1439 mutt_str_replace(&(*bp)->description, buf_string(buf));
1440 changed = true;
1441 }
1442 }
1443
1444 if (changed)
1445 {
1447 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1448 rc = FR_SUCCESS;
1449 }
1450 }
1451
1452 ARRAY_FREE(&ba);
1453 buf_pool_release(&buf);
1454 return rc;
1455}
1456
1461 const struct KeyEvent *event)
1462{
1463 struct ComposeSharedData *shared = fdata->shared;
1464 struct AttachCtx *actx = shared->adata->actx;
1465 if (!check_count(actx))
1466 return FR_ERROR;
1467
1468 int rc = FR_NO_ACTION;
1469 struct Menu *menu = shared->adata->menu;
1470 struct Buffer *buf = buf_pool_get();
1471 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1472
1473 struct AttachPtr *cur_att = current_attachment(actx, menu);
1474 buf_strcpy(buf, ENCODING(cur_att->body->encoding));
1475
1476 if ((mw_get_field("Content-Transfer-Encoding: ", buf, MUTT_COMP_NONE,
1477 HC_OTHER, NULL, NULL) == 0) &&
1478 !buf_is_empty(buf))
1479 {
1480 int enc = mutt_check_encoding(buf_string(buf));
1481 if ((enc != ENC_OTHER) && (enc != ENC_UUENCODED))
1482 {
1483 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
1484
1485 bool changed = false;
1486 struct Body **bp = NULL;
1487 ARRAY_FOREACH(bp, &ba)
1488 {
1489 if ((*bp)->encoding != enc)
1490 {
1491 (*bp)->encoding = enc;
1492 changed = true;
1493 }
1494 }
1495
1496 if (changed)
1497 {
1501 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1502 rc = FR_SUCCESS;
1503 }
1504 }
1505 else
1506 {
1507 mutt_error(_("Invalid encoding"));
1508 rc = FR_ERROR;
1509 }
1510 }
1511
1512 ARRAY_FREE(&ba);
1513 buf_pool_release(&buf);
1514 return rc;
1515}
1516
1521 const struct KeyEvent *event)
1522{
1523 struct ComposeSharedData *shared = fdata->shared;
1524 struct AttachCtx *actx = shared->adata->actx;
1525 if (!check_count(actx))
1526 return FR_ERROR;
1527
1528 int rc = FR_NO_ACTION;
1529 struct Menu *menu = shared->adata->menu;
1530 struct Buffer *buf = buf_pool_get();
1531 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1532 struct AttachPtr *cur_att = current_attachment(actx, menu);
1533
1534 buf_strcpy(buf, cur_att->body->language);
1535 if (mw_get_field("Content-Language: ", buf, MUTT_COMP_NONE, HC_OTHER, NULL, NULL) == 0)
1536 {
1537 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
1538
1539 bool changed = false;
1540 struct Body **bp = NULL;
1541 ARRAY_FOREACH(bp, &ba)
1542 {
1543 if (!mutt_str_equal((*bp)->language, buf_string(buf)))
1544 {
1545 mutt_str_replace(&(*bp)->language, buf_string(buf));
1546 changed = true;
1547 }
1548 }
1549
1550 if (changed)
1551 {
1554 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1555 rc = FR_SUCCESS;
1556 }
1558 }
1559 else
1560 {
1561 mutt_warning(_("Empty 'Content-Language'"));
1562 rc = FR_ERROR;
1563 }
1564
1565 ARRAY_FREE(&ba);
1566 buf_pool_release(&buf);
1567 return rc;
1568}
1569
1573static int op_attach_edit_mime(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1574{
1575 struct ComposeSharedData *shared = fdata->shared;
1576 struct AttachCtx *actx = shared->adata->actx;
1577 if (!check_count(actx))
1578 return FR_ERROR;
1579
1580 int rc = FR_NO_ACTION;
1581 struct Menu *menu = shared->adata->menu;
1582 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1583 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
1584 if (ARRAY_EMPTY(&ba))
1585 goto done;
1586
1587 struct Body **bp = NULL;
1588 ARRAY_FOREACH(bp, &ba)
1589 {
1590 if (!mutt_edit_attachment(*bp))
1591 continue;
1592
1593 mutt_update_encoding(*bp, shared->sub);
1594 rc = FR_SUCCESS;
1595 }
1596
1597 if (rc != FR_SUCCESS)
1598 goto done;
1599
1601 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1602
1603done:
1604 ARRAY_FREE(&ba);
1605 return rc;
1606}
1607
1611static int op_attach_edit_type(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1612{
1613 struct ComposeSharedData *shared = fdata->shared;
1614 if (!check_count(shared->adata->actx))
1615 return FR_ERROR;
1616
1617 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
1618 shared->adata->menu);
1619 if (!mutt_edit_content_type(NULL, cur_att->body, NULL))
1620 return FR_NO_ACTION;
1621
1622 /* this may have been a change to text/something */
1623 mutt_update_encoding(cur_att->body, shared->sub);
1625 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1626 return FR_SUCCESS;
1627}
1628
1636static int op_attach_filter(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1637{
1638 struct ComposeSharedData *shared = fdata->shared;
1639 struct AttachCtx *actx = shared->adata->actx;
1640 if (!check_count(actx))
1641 return FR_ERROR;
1642
1643 struct Menu *menu = shared->adata->menu;
1644 struct AttachPtrArray aa = ARRAY_HEAD_INITIALIZER;
1645 aa_add_selection(&aa, actx, menu, menu->tag_prefix, event->count);
1646 struct AttachPtr **app = NULL;
1647 ARRAY_FOREACH(app, &aa)
1648 {
1649 if ((*app)->body->type == TYPE_MULTIPART)
1650 {
1651 ARRAY_FREE(&aa);
1652 mutt_error(_("Can't filter multipart attachments"));
1653 return FR_ERROR;
1654 }
1655 }
1656
1657 const int op = event->op;
1658 mutt_pipe_attachment_list(&aa, (op == OP_ATTACH_FILTER));
1659 if (op == OP_ATTACH_FILTER) /* cte might have changed */
1660 {
1662 }
1663 ARRAY_FREE(&aa);
1665 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1666 return FR_SUCCESS;
1667}
1668
1673 const struct KeyEvent *event)
1674{
1675 struct ComposeSharedData *shared = fdata->shared;
1676 struct AttachCtx *actx = shared->adata->actx;
1677 if (!check_count(actx))
1678 return FR_ERROR;
1679
1680 int rc = FR_ERROR;
1681 struct Menu *menu = shared->adata->menu;
1682 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1683 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
1684 if (ARRAY_EMPTY(&ba))
1685 goto done;
1686
1687 struct Body **bp = NULL;
1688 bool got_attachment = false;
1689 ARRAY_FOREACH(bp, &ba)
1690 {
1691 if ((*bp)->type == TYPE_MULTIPART)
1692 {
1693 mutt_warning(_("Can't get multipart attachments"));
1694 continue;
1695 }
1697 got_attachment = true;
1698 }
1699
1700 if (got_attachment)
1701 {
1703 rc = FR_SUCCESS;
1704 }
1705
1706done:
1707 ARRAY_FREE(&ba);
1708 /* No send2hook since this doesn't change the message. */
1709 return rc;
1710}
1711
1715static int op_attach_group_alts(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1716{
1717 struct ComposeSharedData *shared = fdata->shared;
1718 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1719 ba_add_selection(&ba, shared->adata->actx, shared->adata->menu,
1720 shared->adata->menu->tag_prefix, event->count);
1721 if (ARRAY_SIZE(&ba) < 2)
1722 {
1723 ARRAY_FREE(&ba);
1724 mutt_error(_("Grouping 'alternatives' requires at least 2 selected attachments"));
1725 return FR_ERROR;
1726 }
1727
1728 const int rc = group_attachments(shared, "alternative", &ba);
1729 ARRAY_FREE(&ba);
1730 return rc;
1731}
1732
1737 const struct KeyEvent *event)
1738{
1739 struct ComposeSharedData *shared = fdata->shared;
1740 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1741 ba_add_selection(&ba, shared->adata->actx, shared->adata->menu,
1742 shared->adata->menu->tag_prefix, event->count);
1743 if (ARRAY_SIZE(&ba) < 2)
1744 {
1745 ARRAY_FREE(&ba);
1746 mutt_error(_("Grouping 'multilingual' requires at least 2 selected attachments"));
1747 return FR_ERROR;
1748 }
1749
1750 int tagged_with_lang_num = 0;
1751 struct Body **bp = NULL;
1752 ARRAY_FOREACH(bp, &ba)
1753 {
1754 if ((*bp)->language && *(*bp)->language)
1755 tagged_with_lang_num++;
1756 }
1757
1758 if (ARRAY_SIZE(&ba) != tagged_with_lang_num)
1759 {
1760 if (query_yesorno(_("Not all parts have 'Content-Language' set, continue?"),
1761 MUTT_YES) != MUTT_YES)
1762 {
1763 ARRAY_FREE(&ba);
1764 mutt_message(_("Not sending this message"));
1765 return FR_ERROR;
1766 }
1767 }
1768
1769 const int rc = group_attachments(shared, "multilingual", &ba);
1770 ARRAY_FREE(&ba);
1771 return rc;
1772}
1773
1778 const struct KeyEvent *event)
1779{
1780 struct ComposeSharedData *shared = fdata->shared;
1781 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1782 ba_add_selection(&ba, shared->adata->actx, shared->adata->menu,
1783 shared->adata->menu->tag_prefix, event->count);
1784 if (ARRAY_SIZE(&ba) < 2)
1785 {
1786 ARRAY_FREE(&ba);
1787 mutt_error(_("Grouping 'related' requires at least 2 selected attachments"));
1788 return FR_ERROR;
1789 }
1790
1791 // ensure Content-ID is set for selected attachments
1792 struct Body **bp = NULL;
1793 ARRAY_FOREACH(bp, &ba)
1794 {
1795 if ((*bp)->type == TYPE_MULTIPART)
1796 continue;
1797
1798 if (!(*bp)->content_id)
1799 {
1800 (*bp)->content_id = gen_cid();
1801 }
1802 }
1803
1804 const int rc = group_attachments(shared, "related", &ba);
1805 ARRAY_FREE(&ba);
1806 return rc;
1807}
1808
1812static int op_attach_move_down(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1813{
1814 struct ComposeSharedData *shared = fdata->shared;
1815 if (shared->adata->menu->tag_prefix || (event->count > 1))
1816 return move_attachment(shared, false, event->count);
1817
1818 int index = menu_get_index(shared->adata->menu);
1819
1820 struct AttachCtx *actx = shared->adata->actx;
1821
1822 if (index < 0)
1823 return FR_ERROR;
1824
1825 if (index == (actx->idxlen - 1))
1826 {
1827 mutt_error(_("Attachment is already at bottom"));
1828 return FR_ERROR;
1829 }
1830 if ((actx->idx[index]->parent_type == TYPE_MULTIPART) &&
1831 !actx->idx[index]->body->next)
1832 {
1833 mutt_error(_("Attachment can't be moved out of group"));
1834 return FR_ERROR;
1835 }
1836
1837 // find next attachment at current level
1838 int nextidx = index + 1;
1839 while ((nextidx < actx->idxlen) &&
1840 (actx->idx[nextidx]->level > actx->idx[index]->level))
1841 {
1842 nextidx++;
1843 }
1844 if (nextidx == actx->idxlen)
1845 {
1846 mutt_error(_("Attachment is already at bottom"));
1847 return FR_ERROR;
1848 }
1849
1850 // find final position
1851 int finalidx = index + 1;
1852 if (nextidx < actx->idxlen - 1)
1853 {
1854 if ((actx->idx[nextidx]->body->type == TYPE_MULTIPART) &&
1855 (actx->idx[nextidx + 1]->level > actx->idx[nextidx]->level))
1856 {
1857 finalidx += attach_body_count(actx->idx[nextidx]->body->parts, true);
1858 }
1859 }
1860
1861 compose_attach_swap(shared->email, shared->adata->actx, index, nextidx);
1862 mutt_update_tree(shared->adata->actx);
1865 menu_set_index(shared->adata->menu, finalidx);
1866 return FR_SUCCESS;
1867}
1868
1872static int op_attach_move_up(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1873{
1874 struct ComposeSharedData *shared = fdata->shared;
1875 if (shared->adata->menu->tag_prefix || (event->count > 1))
1876 return move_attachment(shared, true, event->count);
1877
1878 int index = menu_get_index(shared->adata->menu);
1879 if (index < 0)
1880 return FR_ERROR;
1881
1882 struct AttachCtx *actx = shared->adata->actx;
1883
1884 if (index == 0)
1885 {
1886 mutt_error(_("Attachment is already at top"));
1887 return FR_ERROR;
1888 }
1889 if (actx->idx[index - 1]->level < actx->idx[index]->level)
1890 {
1891 mutt_error(_("Attachment can't be moved out of group"));
1892 return FR_ERROR;
1893 }
1894
1895 // find previous attachment at current level
1896 int previdx = index - 1;
1897 while ((previdx > 0) && (actx->idx[previdx]->level > actx->idx[index]->level))
1898 previdx--;
1899
1900 compose_attach_swap(shared->email, actx, previdx, index);
1901 mutt_update_tree(actx);
1904 menu_set_index(shared->adata->menu, previdx);
1905 return FR_SUCCESS;
1906}
1907
1911static int op_attach_new_mime(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1912{
1913 struct ComposeSharedData *shared = fdata->shared;
1914 int rc = FR_NO_ACTION;
1915 struct Buffer *fname = buf_pool_get();
1916 struct Buffer *type = NULL;
1917 struct AttachPtr *ap = NULL;
1918
1919 struct FileCompletionData cdata = { false, shared->mailbox, NULL, NULL, NULL };
1920 if ((mw_get_field(_("New file: "), fname, MUTT_COMP_NONE, HC_FILE,
1921 &CompleteFileOps, &cdata) != 0) ||
1922 buf_is_empty(fname))
1923 {
1924 goto done;
1925 }
1926 expand_path(fname, false);
1927
1928 /* Call to lookup_mime_type () ? maybe later */
1929 type = buf_pool_get();
1930 if ((mw_get_field("Content-Type: ", type, MUTT_COMP_NONE, HC_OTHER, NULL, NULL) != 0) ||
1931 buf_is_empty(type))
1932 {
1933 goto done;
1934 }
1935
1936 rc = FR_ERROR;
1937 char *p = strchr(buf_string(type), '/');
1938 if (!p)
1939 {
1940 mutt_error(_("Content-Type is of the form base/sub"));
1941 goto done;
1942 }
1943 *p++ = 0;
1944 enum ContentType itype = mutt_check_mime_type(buf_string(type));
1945 if (itype == TYPE_OTHER)
1946 {
1947 mutt_error(_("Unknown Content-Type %s"), buf_string(type));
1948 goto done;
1949 }
1950
1951 ap = mutt_aptr_new();
1952 /* Touch the file */
1953 if (!mutt_file_touch(buf_string(fname)))
1954 {
1955 mutt_error(_("Can't create file %s"), buf_string(fname));
1956 goto done;
1957 }
1958
1959 ap->body = mutt_make_file_attach(buf_string(fname), shared->sub);
1960 if (!ap->body)
1961 {
1962 mutt_error(_("Error attaching file"));
1963 goto done;
1964 }
1965 update_idx(shared->adata->menu, shared->adata->actx, ap);
1966 ap = NULL; // shared->adata->actx has taken ownership
1967
1968 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
1969 shared->adata->menu);
1970 cur_att->body->type = itype;
1971 mutt_str_replace(&cur_att->body->subtype, p);
1972 cur_att->body->unlink = true;
1975
1976 if (mutt_compose_attachment(cur_att->body))
1977 {
1978 mutt_update_encoding(cur_att->body, shared->sub);
1980 }
1981 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1982 rc = FR_SUCCESS;
1983
1984done:
1985 mutt_aptr_free(&ap);
1986 buf_pool_release(&type);
1987 buf_pool_release(&fname);
1988 return rc;
1989}
1990
1994static int op_attach_print(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1995{
1996 struct ComposeSharedData *shared = fdata->shared;
1997 struct AttachCtx *actx = shared->adata->actx;
1998 if (!check_count(actx))
1999 return FR_ERROR;
2000
2001 struct Menu *menu = shared->adata->menu;
2002 struct AttachPtrArray aa = ARRAY_HEAD_INITIALIZER;
2003 aa_add_selection(&aa, actx, menu, menu->tag_prefix, event->count);
2004 struct AttachPtr **app = NULL;
2005 ARRAY_FOREACH(app, &aa)
2006 {
2007 if ((*app)->body->type == TYPE_MULTIPART)
2008 {
2009 ARRAY_FREE(&aa);
2010 mutt_error(_("Can't print multipart attachments"));
2011 return FR_ERROR;
2012 }
2013 }
2014
2016 ARRAY_FREE(&aa);
2017 /* no send2hook, since this doesn't modify the message */
2018 return FR_SUCCESS;
2019}
2020
2025 const struct KeyEvent *event)
2026{
2027 struct ComposeSharedData *shared = fdata->shared;
2028 if (!check_count(shared->adata->actx))
2029 return FR_ERROR;
2030 char *src = NULL;
2031 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
2032 shared->adata->menu);
2033 if (cur_att->body->d_filename)
2034 src = cur_att->body->d_filename;
2035 else
2036 src = cur_att->body->filename;
2037 struct Buffer *fname = buf_pool_get();
2038 buf_strcpy(fname, mutt_path_basename(NONULL(src)));
2039 struct FileCompletionData cdata = { false, shared->mailbox, NULL, NULL, NULL };
2040 int rc = mw_get_field(_("Send attachment with name: "), fname, MUTT_COMP_NONE,
2041 HC_FILE, &CompleteFileOps, &cdata);
2042 if (rc == 0)
2043 {
2044 // It's valid to set an empty string here, to erase what was set
2045 mutt_str_replace(&cur_att->body->d_filename, buf_string(fname));
2047 }
2048 buf_pool_release(&fname);
2049 return FR_SUCCESS;
2050}
2051
2055static int op_attach_save(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2056{
2057 struct ComposeSharedData *shared = fdata->shared;
2058 struct AttachCtx *actx = shared->adata->actx;
2059 if (!check_count(actx))
2060 return FR_ERROR;
2061
2062 struct Menu *menu = shared->adata->menu;
2063 struct AttachPtrArray aa = ARRAY_HEAD_INITIALIZER;
2064 aa_add_selection(&aa, actx, menu, menu->tag_prefix, event->count);
2065 struct AttachPtr **app = NULL;
2066 ARRAY_FOREACH(app, &aa)
2067 {
2068 if ((*app)->body->type == TYPE_MULTIPART)
2069 {
2070 ARRAY_FREE(&aa);
2071 mutt_error(_("Can't save multipart attachments"));
2072 return FR_ERROR;
2073 }
2074 }
2075
2076 mutt_save_attachment_list(&aa, NULL, menu);
2077 ARRAY_FREE(&aa);
2078 /* no send2hook, since this doesn't modify the message */
2079 return FR_SUCCESS;
2080}
2081
2086 const struct KeyEvent *event)
2087{
2088 struct ComposeSharedData *shared = fdata->shared;
2089 /* toggle the content-disposition between inline/attachment */
2090 struct AttachCtx *actx = shared->adata->actx;
2091 if (!check_count(actx))
2092 return FR_ERROR;
2093
2094 int rc = FR_NO_ACTION;
2095 struct Menu *menu = shared->adata->menu;
2096 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
2097 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
2098
2099 struct Body **bp = NULL;
2100 ARRAY_FOREACH(bp, &ba)
2101 {
2102 (*bp)->disposition = ((*bp)->disposition == DISP_INLINE) ? DISP_ATTACH : DISP_INLINE;
2103 rc = FR_SUCCESS;
2104 }
2105
2106 if (rc == FR_SUCCESS)
2108
2109 ARRAY_FREE(&ba);
2111 return rc;
2112}
2113
2118 const struct KeyEvent *event)
2119{
2120 struct ComposeSharedData *shared = fdata->shared;
2121 struct AttachCtx *actx = shared->adata->actx;
2122 if (!check_count(actx))
2123 return FR_ERROR;
2124
2125 int rc = FR_NO_ACTION;
2126 struct Menu *menu = shared->adata->menu;
2127 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
2128 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
2129
2130 struct Body **bp = NULL;
2131 ARRAY_FOREACH(bp, &ba)
2132 {
2133 if (!mutt_is_text_part(*bp))
2134 continue;
2135
2136 (*bp)->noconv = !(*bp)->noconv;
2137 rc = FR_SUCCESS;
2138 }
2139
2140 if (rc == FR_NO_ACTION)
2141 {
2142 mutt_error(_("Recoding only affects text attachments"));
2143 ARRAY_FREE(&ba);
2144 return FR_ERROR;
2145 }
2146
2147 if (ARRAY_SIZE(&ba) == 1)
2148 {
2149 struct AttachPtr *cur_att = current_attachment(actx, menu);
2150 if (cur_att->body->noconv)
2151 mutt_message(_("The current attachment won't be converted"));
2152 else
2153 mutt_message(_("The current attachment will be converted"));
2154 }
2155 else
2156 {
2158 }
2159
2161 ARRAY_FREE(&ba);
2162 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2163 return FR_SUCCESS;
2164}
2165
2170 const struct KeyEvent *event)
2171{
2172 struct ComposeSharedData *shared = fdata->shared;
2173 struct AttachCtx *actx = shared->adata->actx;
2174 if (!check_count(actx))
2175 return FR_ERROR;
2176
2177 int rc = FR_NO_ACTION;
2178 struct Menu *menu = shared->adata->menu;
2179 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
2180 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
2181
2182 struct Body **bp = NULL;
2183 ARRAY_FOREACH(bp, &ba)
2184 {
2185 (*bp)->unlink = !(*bp)->unlink;
2186 rc = FR_SUCCESS;
2187 }
2188
2189 if (rc == FR_SUCCESS)
2191
2192 ARRAY_FREE(&ba);
2193 /* No send2hook since this doesn't change the message. */
2194 return rc;
2195}
2196
2200static int op_attach_ungroup(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2201{
2202 struct ComposeSharedData *shared = fdata->shared;
2203 if (shared->adata->actx->idx[shared->adata->menu->current]->body->type != TYPE_MULTIPART)
2204 {
2205 mutt_error(_("Attachment is not 'multipart'"));
2206 return FR_ERROR;
2207 }
2208
2209 int aidx = shared->adata->menu->current;
2210 struct AttachCtx *actx = shared->adata->actx;
2211 struct Body *b = actx->idx[aidx]->body;
2212 struct Body *b_next = b->next;
2213 struct Body *b_previous = NULL;
2214 struct Body *b_parent = NULL;
2215 int parent_type = actx->idx[aidx]->parent_type;
2216 int level = actx->idx[aidx]->level;
2217
2218 // reorder body pointers
2219 if (attach_body_previous(shared->email->body, b, &b_previous))
2220 b_previous->next = b->parts;
2221 else if (attach_body_parent(shared->email->body, NULL, b, &b_parent))
2222 b_parent->parts = b->parts;
2223 else
2224 shared->email->body = b->parts;
2225
2226 // update attachment list
2227 int i = aidx + 1;
2228 while ((i < actx->idxlen) && (actx->idx[i]->level > level))
2229 {
2230 actx->idx[i]->level--;
2231 if (actx->idx[i]->level == level)
2232 {
2233 actx->idx[i]->parent_type = parent_type;
2234 // set body->next for final attachment in group
2235 if (!actx->idx[i]->body->next)
2236 actx->idx[i]->body->next = b_next;
2237 }
2238 i++;
2239 }
2240
2241 // free memory
2242 actx->idx[aidx]->body->parts = NULL;
2243 actx->idx[aidx]->body->next = NULL;
2244 actx->idx[aidx]->body->email = NULL;
2245 mutt_body_free(&actx->idx[aidx]->body);
2246 FREE(&actx->idx[aidx]->tree);
2247 FREE(&actx->idx[aidx]);
2248
2249 // reorder attachment list
2250 for (int j = aidx; j < (actx->idxlen - 1); j++)
2251 actx->idx[j] = actx->idx[j + 1];
2252 actx->idx[actx->idxlen - 1] = NULL;
2253 actx->idxlen--;
2254 update_menu(actx, shared->adata->menu, false);
2255
2256 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2257 return FR_SUCCESS;
2258}
2259
2264 const struct KeyEvent *event)
2265{
2266 struct ComposeSharedData *shared = fdata->shared;
2267 struct AttachCtx *actx = shared->adata->actx;
2268 if (!check_count(actx))
2269 return FR_ERROR;
2270
2271 int rc = FR_NO_ACTION;
2272 struct Menu *menu = shared->adata->menu;
2273 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
2274 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
2275 if (ARRAY_EMPTY(&ba))
2276 goto done;
2277
2278 struct Body **bp = NULL;
2279 ARRAY_FOREACH(bp, &ba)
2280 {
2281 mutt_update_encoding(*bp, shared->sub);
2282 }
2283
2286 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2287 rc = FR_SUCCESS;
2288
2289done:
2290 ARRAY_FREE(&ba);
2291 return rc;
2292}
2293
2294// -----------------------------------------------------------------------------
2295
2300 const struct KeyEvent *event)
2301{
2302 struct ComposeSharedData *shared = fdata->shared;
2304 const char *tag = NULL;
2305 char *err = NULL;
2306 mutt_env_to_local(shared->email->env);
2307 const char *const c_editor = cs_subset_string(shared->sub, "editor");
2308 if (shared->email->body->type == TYPE_MULTIPART)
2309 {
2310 struct Body *b = shared->email->body->parts;
2311 while (b && b->parts)
2312 b = b->parts;
2313 if (b)
2314 mutt_edit_headers(NONULL(c_editor), b->filename, shared->email, shared->fcc);
2315 }
2316 else
2317 {
2318 mutt_edit_headers(NONULL(c_editor), shared->email->body->filename,
2319 shared->email, shared->fcc);
2320 }
2321
2322 if (mutt_env_to_intl(shared->email->env, &tag, &err))
2323 {
2324 mutt_error(_("Bad IDN in '%s': '%s'"), tag, err);
2325 FREE(&err);
2326 }
2328
2330 mutt_update_encoding(shared->email->body, shared->sub);
2331
2332 /* attachments may have been added */
2333 if (shared->adata->actx->idxlen &&
2334 shared->adata->actx->idx[shared->adata->actx->idxlen - 1]->body->next)
2335 {
2337 update_menu(shared->adata->actx, shared->adata->menu, true);
2338 }
2339
2341 /* Unconditional hook since editor was invoked */
2342 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2343 return FR_SUCCESS;
2344}
2345
2349static int op_compose_edit_file(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2350{
2351 struct ComposeSharedData *shared = fdata->shared;
2352 if (!check_count(shared->adata->actx))
2353 return FR_ERROR;
2354 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
2355 shared->adata->menu);
2356 if (cur_att->body->type == TYPE_MULTIPART)
2357 {
2358 mutt_error(_("Can't edit multipart attachments"));
2359 return FR_ERROR;
2360 }
2361 const char *const c_editor = cs_subset_string(shared->sub, "editor");
2362 mutt_edit_file(NONULL(c_editor), cur_att->body->filename);
2363 mutt_update_encoding(cur_att->body, shared->sub);
2366 /* Unconditional hook since editor was invoked */
2367 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2368 return FR_SUCCESS;
2369}
2370
2375 const struct KeyEvent *event)
2376{
2377 struct ComposeSharedData *shared = fdata->shared;
2378 const bool c_edit_headers = cs_subset_bool(shared->sub, "edit_headers");
2379 if (!c_edit_headers)
2380 {
2382 const char *const c_editor = cs_subset_string(shared->sub, "editor");
2383 mutt_edit_file(c_editor, shared->email->body->filename);
2385 mutt_update_encoding(shared->email->body, shared->sub);
2387 /* Unconditional hook since editor was invoked */
2388 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2389 return FR_SUCCESS;
2390 }
2391
2392 return op_envelope_edit_headers(fdata, event);
2393}
2394
2398static int op_compose_ispell(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2399{
2400 struct ComposeSharedData *shared = fdata->shared;
2401 endwin();
2402 const char *const c_ispell = cs_subset_string(shared->sub, "ispell");
2403 struct Buffer *cmd = buf_pool_get();
2404 struct Buffer *quoted = buf_pool_get();
2405 buf_quote_filename(quoted, shared->email->body->filename, true);
2406 buf_printf(cmd, "%s -x %s", NONULL(c_ispell), buf_string(quoted));
2407 buf_pool_release(&quoted);
2408 if (mutt_system(buf_string(cmd)) == -1)
2409 {
2410 mutt_error(_("Error running \"%s\""), buf_string(cmd));
2411 buf_pool_release(&cmd);
2412 return FR_ERROR;
2413 }
2414 buf_pool_release(&cmd);
2415
2416 mutt_update_encoding(shared->email->body, shared->sub);
2418 return FR_SUCCESS;
2419}
2420
2425 const struct KeyEvent *event)
2426{
2427 struct ComposeSharedData *shared = fdata->shared;
2428 if (check_attachments(shared->adata->actx, shared->sub) != 0)
2429 {
2431 return FR_ERROR;
2432 }
2433
2434 shared->rc = 1;
2435 return FR_DONE;
2436}
2437
2442 const struct KeyEvent *event)
2443{
2444 struct ComposeSharedData *shared = fdata->shared;
2445 if (!check_count(shared->adata->actx))
2446 return FR_ERROR;
2447 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
2448 shared->adata->menu);
2449 if (cur_att->body->type == TYPE_MULTIPART)
2450 {
2451 mutt_error(_("Can't rename multipart attachments"));
2452 return FR_ERROR;
2453 }
2454 struct Buffer *fname = buf_pool_get();
2455 buf_strcpy(fname, cur_att->body->filename);
2456 pretty_mailbox(fname);
2457 struct FileCompletionData cdata = { false, shared->mailbox, NULL, NULL, NULL };
2458 if ((mw_get_field(_("Rename to: "), fname, MUTT_COMP_NONE, HC_FILE,
2459 &CompleteFileOps, &cdata) == 0) &&
2460 !buf_is_empty(fname))
2461 {
2462 struct stat st = { 0 };
2463 if (stat(cur_att->body->filename, &st) == -1)
2464 {
2465 /* L10N: "stat" is a system call. Do "man 2 stat" for more information. */
2466 mutt_error(_("Can't stat %s: %s"), buf_string(fname), strerror(errno));
2467 buf_pool_release(&fname);
2468 return FR_ERROR;
2469 }
2470
2471 expand_path(fname, false);
2472 if (mutt_file_rename(cur_att->body->filename, buf_string(fname)))
2473 {
2474 buf_pool_release(&fname);
2475 return FR_ERROR;
2476 }
2477
2478 mutt_str_replace(&cur_att->body->filename, buf_string(fname));
2480
2481 if (cur_att->body->stamp >= st.st_mtime)
2482 mutt_stamp_attachment(cur_att->body);
2483 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2484 }
2485 buf_pool_release(&fname);
2486 return FR_SUCCESS;
2487}
2488
2493 const struct KeyEvent *event)
2494{
2495 struct ComposeSharedData *shared = fdata->shared;
2496 /* Note: We don't invoke send2-hook here, since we want to leave
2497 * users an opportunity to change settings from the ":" prompt. */
2498 if (check_attachments(shared->adata->actx, shared->sub) != 0)
2499 {
2501 return FR_NO_ACTION;
2502 }
2503
2504 if (!shared->fcc_set && !buf_is_empty(shared->fcc))
2505 {
2506 enum QuadOption ans = query_quadoption(_("Save a copy of this message?"),
2507 shared->sub, "copy");
2508 if (ans == MUTT_ABORT)
2509 return FR_NO_ACTION;
2510 else if (ans == MUTT_NO)
2511 buf_reset(shared->fcc);
2512 }
2513
2514 shared->rc = 0;
2515 return FR_DONE;
2516}
2517
2522 const struct KeyEvent *event)
2523{
2524 struct ComposeSharedData *shared = fdata->shared;
2525 int rc = FR_NO_ACTION;
2526 struct Buffer *fname = buf_pool_get();
2527 if (shared->mailbox)
2528 {
2529 buf_strcpy(fname, mailbox_path(shared->mailbox));
2530 pretty_mailbox(fname);
2531 }
2532 if (shared->adata->actx->idxlen)
2533 shared->email->body = shared->adata->actx->idx[0]->body;
2534 if ((mw_enter_fname(_("Write message to mailbox"), fname, true,
2535 shared->mailbox, false, NULL, NULL, MUTT_SEL_NONE) != -1) &&
2536 !buf_is_empty(fname))
2537 {
2538 mutt_message(_("Writing message to %s ..."), buf_string(fname));
2539 expand_path(fname, false);
2540
2541 if (shared->email->body->next)
2542 shared->email->body = mutt_make_multipart(shared->email->body);
2543
2544 if (mutt_write_fcc(buf_string(fname), shared->email, NULL, false, NULL,
2545 NULL, shared->sub) == 0)
2546 mutt_message(_("Message written"));
2547
2548 shared->email->body = mutt_remove_multipart(shared->email->body);
2549 rc = FR_SUCCESS;
2550 }
2551 buf_pool_release(&fname);
2552 return rc;
2553}
2554
2565static int op_display_headers(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2566{
2567 struct ComposeSharedData *shared = fdata->shared;
2568 if (!check_count(shared->adata->actx))
2569 return FR_ERROR;
2570
2571 enum ViewAttachMode mode = MUTT_VA_REGULAR;
2572
2573 const int op = event->op;
2574 switch (op)
2575 {
2576 case OP_ATTACH_VIEW:
2577 case OP_DISPLAY_HEADERS:
2578 break;
2579
2580 case OP_ATTACH_VIEW_MAILCAP:
2581 mode = MUTT_VA_MAILCAP;
2582 break;
2583
2584 case OP_ATTACH_VIEW_PAGER:
2585 mode = MUTT_VA_PAGER;
2586 break;
2587
2588 case OP_ATTACH_VIEW_TEXT:
2589 mode = MUTT_VA_AS_TEXT;
2590 break;
2591 }
2592
2593 if (mode == MUTT_VA_REGULAR)
2594 {
2595 mutt_attach_display_loop(shared->sub, shared->adata->menu, op,
2596 shared->email, shared->adata->actx, false);
2597 }
2598 else
2599 {
2600 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
2601 shared->adata->menu);
2602 mutt_view_attachment(NULL, cur_att->body, mode, shared->email,
2603 shared->adata->actx, shared->adata->menu->win);
2604 }
2605
2607 /* no send2hook, since this doesn't modify the message */
2608 return FR_SUCCESS;
2609}
2610
2614static int op_exit(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2615{
2616 struct ComposeSharedData *shared = fdata->shared;
2617 enum QuadOption ans = query_quadoption(_("Save (postpone) draft message?"),
2618 shared->sub, "postpone");
2619 if (ans == MUTT_NO)
2620 {
2621 for (int i = 0; i < shared->adata->actx->idxlen; i++)
2622 if (shared->adata->actx->idx[i]->unowned)
2623 shared->adata->actx->idx[i]->body->unlink = false;
2624
2625 if (!(shared->flags & MUTT_COMPOSE_NOFREEHEADER))
2626 {
2627 for (int i = 0; i < shared->adata->actx->idxlen; i++)
2628 {
2629 /* avoid freeing other attachments */
2630 shared->adata->actx->idx[i]->body->next = NULL;
2631 if (!shared->adata->actx->idx[i]->body->email)
2632 shared->adata->actx->idx[i]->body->parts = NULL;
2633 mutt_body_free(&shared->adata->actx->idx[i]->body);
2634 }
2635 }
2636 shared->rc = -1;
2637 return FR_DONE;
2638 }
2639 else if (ans == MUTT_ABORT)
2640 {
2641 return FR_NO_ACTION;
2642 }
2643
2644 return op_compose_postpone_message(fdata, event);
2645}
2646
2650static int op_forget_passphrase(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2651{
2653 return FR_SUCCESS;
2654}
2655
2656// -----------------------------------------------------------------------------
2657
2661static const struct ComposeFunction ComposeFunctions[] = {
2662 // clang-format off
2663 { OP_ATTACH_ATTACH_FILE, op_attach_attach_file },
2664 { OP_ATTACH_ATTACH_KEY, op_attach_attach_key },
2665 { OP_ATTACH_ATTACH_MESSAGE, op_attach_attach_message },
2666 { OP_ATTACH_ATTACH_NEWS_MESSAGE, op_attach_attach_message },
2667 { OP_ATTACH_DETACH, op_attach_detach },
2668 { OP_ATTACH_EDIT_CONTENT_ID, op_attach_edit_content_id },
2669 { OP_ATTACH_EDIT_DESCRIPTION, op_attach_edit_description },
2670 { OP_ATTACH_EDIT_ENCODING, op_attach_edit_encoding },
2671 { OP_ATTACH_EDIT_LANGUAGE, op_attach_edit_language },
2672 { OP_ATTACH_EDIT_MIME, op_attach_edit_mime },
2673 { OP_ATTACH_EDIT_TYPE, op_attach_edit_type },
2674 { OP_ATTACH_FILTER, op_attach_filter },
2675 { OP_ATTACH_GET_ATTACHMENT, op_attach_get_attachment },
2676 { OP_ATTACH_GROUP_ALTS, op_attach_group_alts },
2677 { OP_ATTACH_GROUP_LINGUAL, op_attach_group_lingual },
2678 { OP_ATTACH_GROUP_RELATED, op_attach_group_related },
2679 { OP_ATTACH_MOVE_DOWN, op_attach_move_down },
2680 { OP_ATTACH_MOVE_UP, op_attach_move_up },
2681 { OP_ATTACH_NEW_MIME, op_attach_new_mime },
2682 { OP_ATTACH_PRINT, op_attach_print },
2683 { OP_ATTACH_RENAME_ATTACHMENT, op_attach_rename_attachment },
2684 { OP_ATTACH_SAVE, op_attach_save },
2685 { OP_ATTACH_TOGGLE_DISPOSITION, op_attach_toggle_disposition },
2686 { OP_ATTACH_TOGGLE_RECODE, op_attach_toggle_recode },
2687 { OP_ATTACH_TOGGLE_UNLINK, op_attach_toggle_unlink },
2688 { OP_ATTACH_UNGROUP, op_attach_ungroup },
2689 { OP_ATTACH_UPDATE_ENCODING, op_attach_update_encoding },
2690 { OP_ATTACH_VIEW, op_display_headers },
2691 { OP_ATTACH_VIEW_MAILCAP, op_display_headers },
2692 { OP_ATTACH_VIEW_PAGER, op_display_headers },
2693 { OP_ATTACH_VIEW_TEXT, op_display_headers },
2694 { OP_COMPOSE_EDIT_FILE, op_compose_edit_file },
2695 { OP_COMPOSE_EDIT_MESSAGE, op_compose_edit_message },
2696 { OP_COMPOSE_ISPELL, op_compose_ispell },
2697 { OP_COMPOSE_POSTPONE_MESSAGE, op_compose_postpone_message },
2698 { OP_COMPOSE_RENAME_FILE, op_compose_rename_file },
2699 { OP_COMPOSE_SEND_MESSAGE, op_compose_send_message },
2700 { OP_COMPOSE_WRITE_MESSAGE, op_compose_write_message },
2701 { OP_DISPLAY_HEADERS, op_display_headers },
2702 { OP_ENVELOPE_EDIT_HEADERS, op_envelope_edit_headers },
2703 { OP_EXIT, op_exit },
2704 { OP_FORGET_PASSPHRASE, op_forget_passphrase },
2705 { OP_PIPE, op_attach_filter },
2706 { 0, NULL },
2707 // clang-format on
2708};
2709
2713int compose_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
2714{
2715 // The Dispatcher may be called on any Window in the Dialog
2716 struct MuttWindow *dlg = dialog_find(win);
2717 if (!event || !dlg || !dlg->wdata)
2718 {
2720 return FR_ERROR;
2721 }
2722
2723 const int op = event->op;
2724
2726
2727 struct ComposeFunctionData fdata = {
2728 .n = NeoMutt,
2729 .mod_data = mod_data,
2730 .shared = dlg->wdata,
2731 };
2732
2733 int rc = FR_UNKNOWN;
2734 for (size_t i = 0; ComposeFunctions[i].op != OP_NULL; i++)
2735 {
2736 const struct ComposeFunction *fn = &ComposeFunctions[i];
2737 if (fn->op == op)
2738 {
2739 rc = fn->function(&fdata, event);
2740 break;
2741 }
2742 }
2743
2744 if (rc == FR_UNKNOWN) // Not our function
2745 return rc;
2746
2747 const char *result = dispatcher_get_retval_name(rc);
2748 mutt_debug(LL_DEBUG1, "Handled %s (%d) -> %s\n", opcodes_get_name(op), op, NONULL(result));
2749
2751 return rc;
2752}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition array.h:74
#define ARRAY_SIZE(head)
The number of elements stored.
Definition array.h:87
#define ARRAY_FREE(head)
Release all memory.
Definition array.h:209
#define ARRAY_GET(head, idx)
Return the element at index.
Definition array.h:109
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition array.h:58
void mutt_actx_add_attach(struct AttachCtx *actx, struct AttachPtr *attach)
Add an Attachment to an Attachment Context.
Definition attach.c:65
void mutt_actx_ins_attach(struct AttachCtx *actx, struct AttachPtr *attach, int aidx)
Insert an Attachment into an Attachment Context at Specified Index.
Definition attach.c:91
struct AttachPtr * mutt_aptr_new(void)
Create a new Attachment Pointer.
Definition attach.c:40
void mutt_actx_entries_free(struct AttachCtx *actx)
Free entries in an Attachment Context.
Definition attach.c:162
void mutt_aptr_free(struct AttachPtr **ptr)
Free an Attachment Pointer.
Definition attach.c:49
bool attach_body_parent(struct Body *start, struct Body *start_parent, struct Body *body, struct Body **body_parent)
Find the parent of a body.
Definition lib.c:71
int attach_body_count(struct Body *body, bool recurse)
Count bodies.
Definition lib.c:42
bool attach_body_previous(struct Body *start, struct Body *body, struct Body **previous)
Find the previous body of a body.
Definition lib.c:142
GUI display the mailboxes in a side panel.
Compose Attach Data.
const struct CompleteOps CompleteFileOps
Auto-Completion of Files.
Definition complete.c:152
Select a Mailbox from a list.
@ MUTT_SEL_MULTI
Multi-selection is enabled.
Definition lib.h:61
@ MUTT_SEL_NONE
No flags are set.
Definition lib.h:59
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
@ CMD_SEND2_HOOK
:send2-hook
Definition command.h:109
static bool check_count(struct AttachCtx *actx)
Check if there are any attachments.
Definition functions.c:231
void compose_init_keys(struct NeoMutt *n, struct SubMenu *sm_generic)
Initialise the Compose Keybindings - Implements ::init_keys_api.
Definition functions.c:209
static int body_index(struct AttachCtx *actx, const struct Body *b)
Find the index of an attachment body.
Definition functions.c:553
static const struct ComposeFunction ComposeFunctions[]
All the NeoMutt functions that the Compose supports.
Definition functions.c:2661
static int group_attachments(struct ComposeSharedData *shared, char *subtype, struct BodyArray *ba)
Group selected attachments into a multipart group.
Definition functions.c:771
static const struct MenuFuncOp OpCompose[]
Functions for the Compose Menu.
Definition functions.c:78
static int prev_sibling(struct AttachCtx *actx, int index)
Find the previous sibling of an attachment.
Definition functions.c:574
static void update_idx(struct Menu *menu, struct AttachCtx *actx, struct AttachPtr *ap)
Add a new attachment to the message.
Definition functions.c:462
static char * gen_cid(void)
Generate a random Content ID.
Definition functions.c:248
static MenuRedrawFlags selection_redraw_flags(size_t count)
Choose redraw flags for a selection.
Definition functions.c:671
static void compose_attach_swap(struct Email *e, struct AttachCtx *actx, int first, int second)
Swap two adjacent entries in the attachment list.
Definition functions.c:487
static bool body_array_contains(struct BodyArray *ba, const struct Body *b)
Does a selection contain a body?
Definition functions.c:616
static int next_sibling(struct AttachCtx *actx, int index)
Find the next sibling of an attachment.
Definition functions.c:594
static int delete_attachment(struct AttachCtx *actx, int aidx, struct ConfigSubset *sub)
Delete an attachment.
Definition functions.c:373
static bool selected_attachments_are_siblings(struct Email *e, struct BodyArray *ba)
Check whether selected attachments are siblings.
Definition functions.c:634
static bool check_cid(const char *cid)
Check if a Content-ID is valid.
Definition functions.c:264
static int check_attachments(struct AttachCtx *actx, struct ConfigSubset *sub)
Check if any attachments have changed or been deleted.
Definition functions.c:309
static int move_attachment(struct ComposeSharedData *shared, bool up, int count)
Move attachments in the attachment list.
Definition functions.c:683
static const struct MenuOpSeq ComposeDefaultBindings[]
Key bindings for the Compose Menu.
Definition functions.c:146
static bool content_id_exists(struct AttachCtx *actx, const struct Body *skip, const char *cid)
Check whether a Content-ID is already in use.
Definition functions.c:284
Compose functions.
GUI editor for an email's headers.
#define MUTT_COMPOSE_NOFREEHEADER
Don't free the header when closing compose dialog.
Definition lib.h:55
Compose private Module data.
Compose Private Data.
Compose Shared Data.
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
unsigned char cs_subset_enum(const struct ConfigSubset *sub, const char *name)
Get a enumeration config item by name.
Definition helpers.c:71
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition helpers.c:266
Convenience wrapper for the config headers.
Connection Library.
Convenience wrapper for the core headers.
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition mailbox.h:216
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition mailbox.h:50
@ MUTT_POP
'POP3' Mailbox type
Definition mailbox.h:51
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition mailbox.h:48
@ MUTT_IMAP
'IMAP' Mailbox type
Definition mailbox.h:49
void crypt_forget_passphrase(void)
Forget a passphrase and display a message.
Definition crypt.c:89
struct Body * crypt_pgp_make_key_attachment(void)
Wrapper for CryptModuleSpecs::pgp_make_key_attachment()
Definition cryptglue.c:349
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition curs_lib.c:118
void dialog_push(struct MuttWindow *dlg)
Display a Window to the user.
Definition dialog.c:109
void dialog_pop(void)
Hide a Window from the user.
Definition dialog.c:148
struct MuttWindow * dialog_find(struct MuttWindow *win)
Find the parent Dialog of a Window.
Definition dialog.c:89
const char * dispatcher_get_retval_name(int rv)
Get the name of a return value.
Definition dispatcher.c:55
void dispatcher_flush_on_error(int rv)
Flush pending keys after a dispatch error.
Definition dispatcher.c:65
@ FR_SUCCESS
Valid function - successfully performed.
Definition dispatcher.h:40
@ FR_DONE
Exit the Dialog.
Definition dispatcher.h:36
@ FR_UNKNOWN
Unknown function.
Definition dispatcher.h:34
@ FR_ERROR
Valid function - error occurred.
Definition dispatcher.h:39
@ FR_NOT_IMPL
Invalid function - feature not enabled.
Definition dispatcher.h:37
@ FR_NO_ACTION
Valid function - no action performed.
Definition dispatcher.h:38
void update_menu(struct AttachCtx *actx, struct Menu *menu, bool init)
Redraw the compose window.
struct MuttWindow * index_pager_init(void)
Allocate the Windows for the Index/Pager.
Definition dlg_index.c:1462
Edit a string.
@ MUTT_COMP_NONE
No flags are set.
Definition wdata.h:46
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition body.c:58
struct Body * mutt_body_new(void)
Create a new Body.
Definition body.c:44
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 header.c:181
Structs that make up an email.
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition parse.c:333
int mutt_check_encoding(const char *c)
Check the encoding type.
Definition parse.c:404
EmailSortType
Methods for sorting Emails.
Definition sort.h:53
@ NT_EMAIL_CHANGE_ATTACH
Email's Attachments have changed.
Definition email.h:188
@ NT_EMAIL_CHANGE_ENVELOPE
Email's Envelope has changed.
Definition email.h:187
int mutt_env_to_intl(struct Envelope *env, const char **tag, char **err)
Convert an Envelope's Address fields to Punycode format.
Definition envelope.c:350
void mutt_env_to_local(struct Envelope *env)
Convert an Envelope's Address fields to local format.
Definition envelope.c:316
bool mutt_edit_content_type(struct Email *e, struct Body *b, FILE *fp)
Edit the content type of an attachment.
Definition external.c:1073
Manage where the email is piped to external commands.
bool mutt_file_touch(const char *path)
Make sure a file exists.
Definition file.c:974
void buf_quote_filename(struct Buffer *buf, const char *filename, bool add_outer)
Quote a filename to survive the shell's quoting rules.
Definition file.c:803
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition file.c:1257
bool OptNews
(pseudo) used to change reader mode
Definition globals.c:53
Global variables.
static int op_exit(struct AliasFunctionData *fdata, const struct KeyEvent *event)
exit this menu - Implements alias_function_t -
Definition functions.c:312
static int op_attach_edit_type(struct AttachFunctionData *fdata, const struct KeyEvent *event)
edit attachment content type - Implements attach_function_t -
Definition functions.c:393
static int op_attach_save(struct AttachFunctionData *fdata, const struct KeyEvent *event)
save message/attachment to a mailbox/file - Implements attach_function_t -
Definition functions.c:430
static int op_attach_print(struct AttachFunctionData *fdata, const struct KeyEvent *event)
print the current entry - Implements attach_function_t -
Definition functions.c:417
static int op_forget_passphrase(struct AttachFunctionData *fdata, const struct KeyEvent *event)
wipe passphrases from memory - Implements attach_function_t -
Definition functions.c:644
static int op_attach_edit_description(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Edit attachment description - Implements compose_function_t -.
Definition functions.c:1412
static int op_compose_postpone_message(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Save this message to send later - Implements compose_function_t -.
Definition functions.c:2424
static int op_attach_edit_content_id(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Edit the 'Content-ID' of the attachment - Implements compose_function_t -.
Definition functions.c:1327
static int op_attach_group_lingual(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Group selected attachments as 'multipart/multilingual' - Implements compose_function_t -.
Definition functions.c:1736
static int op_compose_write_message(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Write the message to a folder - Implements compose_function_t -.
Definition functions.c:2521
static int op_compose_edit_message(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Edit the message - Implements compose_function_t -.
Definition functions.c:2374
static int op_attach_ungroup(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Ungroup a 'multipart' attachment - Implements compose_function_t -.
Definition functions.c:2200
static int op_compose_ispell(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Run ispell on the message - Implements compose_function_t -.
Definition functions.c:2398
static int op_attach_update_encoding(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Update an attachment's encoding info - Implements compose_function_t -.
Definition functions.c:2263
static int op_attach_get_attachment(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Get a temporary copy of an attachment - Implements compose_function_t -.
Definition functions.c:1672
static int op_attach_group_related(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Group selected attachments as 'multipart/related' - Implements compose_function_t -.
Definition functions.c:1777
static int op_compose_rename_file(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Rename/move an attached file - Implements compose_function_t -.
Definition functions.c:2441
static int op_attach_move_up(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Move an attachment up in the attachment list - Implements compose_function_t -.
Definition functions.c:1872
static int op_attach_filter(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Filter attachment through a shell command - Implements compose_function_t -.
Definition functions.c:1636
static int op_attach_edit_language(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Edit the 'Content-Language' of the attachment - Implements compose_function_t -.
Definition functions.c:1520
static int op_attach_toggle_disposition(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Toggle disposition between inline/attachment - Implements compose_function_t -.
Definition functions.c:2085
static int op_attach_rename_attachment(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Send attachment with a different name - Implements compose_function_t -.
Definition functions.c:2024
static int op_attach_move_down(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Move an attachment down in the attachment list - Implements compose_function_t -.
Definition functions.c:1812
static int op_attach_group_alts(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Group selected attachments as 'multipart/alternative' - Implements compose_function_t -.
Definition functions.c:1715
static int op_attach_attach_key(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Attach a PGP public key - Implements compose_function_t -.
Definition functions.c:1058
static int op_attach_toggle_recode(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Toggle recoding of this attachment - Implements compose_function_t -.
Definition functions.c:2117
static int op_envelope_edit_headers(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Edit the message with headers - Implements compose_function_t -.
Definition functions.c:2299
static int op_compose_send_message(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Send the message - Implements compose_function_t -.
Definition functions.c:2492
static int op_attach_edit_encoding(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Edit attachment transfer-encoding - Implements compose_function_t -.
Definition functions.c:1460
static int op_attach_toggle_unlink(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Toggle whether to delete file after sending it - Implements compose_function_t -.
Definition functions.c:2169
static int op_attach_detach(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Delete the current entry - Implements compose_function_t -.
Definition functions.c:1240
static int op_display_headers(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Display message and toggle header weeding - Implements compose_function_t -.
Definition functions.c:2565
static int op_attach_attach_message(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Attach messages to this message - Implements compose_function_t -.
Definition functions.c:1087
static int op_compose_edit_file(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Edit the file to be attached - Implements compose_function_t -.
Definition functions.c:2349
static int op_attach_edit_mime(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Edit attachment using mailcap entry - Implements compose_function_t -.
Definition functions.c:1573
static int op_attach_new_mime(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Compose new attachment using mailcap entry - Implements compose_function_t -.
Definition functions.c:1911
static int op_attach_attach_file(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Attach files to this message - Implements compose_function_t -.
Definition functions.c:992
int compose_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
Perform a Compose function - Implements function_dispatcher_t -.
Definition functions.c:2713
struct Mailbox * dlg_index(struct MuttWindow *dlg, struct Mailbox *m_init)
Display a list of emails -.
Definition dlg_index.c:1121
int mw_enter_fname(const char *prompt, struct Buffer *fname, bool mailbox, struct Mailbox *m, bool multiple, char ***files, int *numfiles, SelectFileFlags flags)
Ask the user to select a file -.
Definition curs_lib.c:238
int mw_get_field(const char *prompt, struct Buffer *buf, CompletionFlags complete, enum HistoryClass hclass, const struct CompleteOps *comp_api, void *cdata)
Ask the user for a string -.
Definition window.c:502
#define mutt_warning(...)
Definition logging2.h:92
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
#define mutt_perror(...)
Definition logging2.h:95
enum MailboxType nntp_path_probe(const char *path, const struct stat *st)
Is this an NNTP Mailbox?
Definition nntp.c:2787
enum MailboxType pop_path_probe(const char *path, const struct stat *st)
Is this a POP Mailbox?
Definition pop.c:1170
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox?
Definition imap.c:2681
Convenience wrapper for the gui headers.
Read/write command history from/to a file.
@ HC_FILE
Files.
Definition lib.h:59
@ HC_OTHER
Miscellaneous strings.
Definition lib.h:61
void exec_message_hook(struct Mailbox *m, struct Email *e, enum CommandId id)
Perform a message hook.
Definition exec.c:137
Hook Commands.
IMAP network mailbox.
GUI manage the main index (list of emails)
void km_menu_add_submenu(struct MenuDefinition *md, struct SubMenu *sm)
Add a SubMenu to a Menu Definition.
Definition init.c:121
struct SubMenu * km_register_submenu(const struct MenuFuncOp functions[])
Register a submenu.
Definition init.c:87
struct MenuDefinition * km_register_menu(int menu, const char *name)
Register a menu.
Definition init.c:104
void km_menu_add_bindings(struct MenuDefinition *md, const struct MenuOpSeq bindings[])
Add Keybindings to a Menu.
Definition init.c:134
Manage keymappings.
@ LL_DEBUG5
Log at debug level 5.
Definition logging2.h:49
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
GUI present the user with a selectable list.
void menu_queue_redraw(struct Menu *menu, MenuRedrawFlags redraw)
Queue a request for a redraw.
Definition menu.c:179
int menu_get_index(struct Menu *menu)
Get the current selection in the Menu.
Definition menu.c:155
uint8_t MenuRedrawFlags
Definition lib.h:66
@ MENU_REDRAW_INDEX
Redraw the index.
Definition lib.h:61
@ MENU_REDRAW_FULL
Redraw everything.
Definition lib.h:64
@ MENU_REDRAW_CURRENT
Redraw the current line of the menu.
Definition lib.h:63
MenuRedrawFlags menu_set_index(struct Menu *menu, int index)
Set the current selection in the Menu.
Definition menu.c:169
@ ENC_7BIT
7-bit text
Definition mime.h:49
@ ENC_UUENCODED
UUEncoded text.
Definition mime.h:54
@ ENC_OTHER
Encoding unknown.
Definition mime.h:48
ContentType
Content-Type.
Definition mime.h:30
@ TYPE_OTHER
Unknown Content-Type.
Definition mime.h:31
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition mime.h:37
@ DISP_ATTACH
Content is attached.
Definition mime.h:63
@ DISP_INLINE
Content is inline.
Definition mime.h:62
#define ENCODING(x)
Get the encoding name for an encoding type.
Definition mime.h:97
@ MODULE_ID_COMPOSE
ModuleCompose, Compose an Email
Definition module_api.h:57
@ MODULE_ID_NNTP
ModuleNntp, Nntp
Definition module_api.h:81
struct Body * mutt_remove_multipart(struct Body *b)
Extract the multipart body if it exists.
Definition multipart.c:133
struct Body * mutt_make_multipart(struct Body *b)
Create a multipart email.
Definition multipart.c:107
void mutt_generate_boundary(struct ParameterList *pl)
Create a unique boundary id for a MIME part.
Definition multipart.c:93
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition notify.c:173
const char * mutt_path_basename(const char *path)
Find the last component for a pathname.
Definition path.c:282
struct Regex * mutt_regex_new(const char *str, uint32_t flags, struct Buffer *err)
Create an Regex from a string.
Definition regex.c:80
void mutt_regex_free(struct Regex **ptr)
Free a Regex object.
Definition regex.c:118
bool mutt_regex_match(const struct Regex *regex, const char *str)
Shorthand to mutt_regex_capture()
Definition regex.c:614
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:665
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
Many unsorted constants and some structs.
int mutt_system(const char *cmd)
Run an external command.
Definition system.c:51
bool mutt_edit_attachment(struct Body *b)
Edit an attachment.
int mutt_get_tmp_attachment(struct Body *b)
Get a temporary copy of an attachment.
Definition mutt_attach.c:68
int mutt_compose_attachment(struct Body *b)
Create an attachment.
int mutt_view_attachment(FILE *fp, struct Body *b, enum ViewAttachMode mode, struct Email *e, struct AttachCtx *actx, struct MuttWindow *win)
View an attachment.
void mutt_save_attachment_list(struct AttachPtrArray *aa, struct Email *e, struct Menu *menu)
Save a list of selected attachments.
Definition recvattach.c:423
void mutt_pipe_attachment_list(struct AttachPtrArray *aa, bool filter)
Pipe selected attachments to a command.
Definition recvattach.c:697
ViewAttachMode
Options for mutt_view_attachment()
Definition mutt_attach.h:44
@ MUTT_VA_MAILCAP
Force viewing using mailcap entry.
Definition mutt_attach.h:46
@ MUTT_VA_REGULAR
View using default method.
Definition mutt_attach.h:45
@ MUTT_VA_PAGER
View attachment in pager using copiousoutput mailcap.
Definition mutt_attach.h:48
@ MUTT_VA_AS_TEXT
Force viewing as text.
Definition mutt_attach.h:47
int mutt_attach_display_loop(struct ConfigSubset *sub, struct Menu *menu, int op, struct Email *e, struct AttachCtx *actx, bool recv)
Event loop for the Attachment menu.
Definition recvattach.c:917
void mutt_print_attachment_list(struct AttachPtrArray *aa)
Print selected attachments.
Definition recvattach.c:846
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
NeoMutt Logging.
void mutt_window_free(struct MuttWindow **ptr)
Free a Window and its children.
bool mutt_is_text_part(const struct Body *b)
Is this part of an email in plain text?
Definition muttlib.c:396
void pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition muttlib.c:428
void expand_path(struct Buffer *buf, bool regex)
Create the canonical path.
Definition muttlib.c:122
Some miscellaneous functions.
bool message_is_tagged(struct Email *e)
Is a message in the index tagged (and within limit)
Definition mview.c:361
void mx_fastclose_mailbox(struct Mailbox *m, bool keep_account)
Free up memory associated with the Mailbox.
Definition mx.c:411
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition mx.c:285
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition mx.c:1323
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition mx.c:1647
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition mx.c:595
API for mailboxes.
@ MUTT_READONLY
Open in read-only mode.
Definition mxapi.h:45
API for encryption/signing of emails.
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition lib.h:106
#define WithCrypto
Definition lib.h:132
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:663
Nntp-specific Account data.
Usenet network mailbox type; talk to an NNTP server.
void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *acct)
Make fully qualified url from newsgroup name.
Definition newsrc.c:557
struct NntpAccountData * nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
Open a connection to an NNTP server.
Definition newsrc.c:953
@ NT_EMAIL
Email has changed, NotifyEmail, EventEmail.
Definition notify_type.h:44
const char * opcodes_get_name(int op)
Get the name of an opcode.
Definition opcodes.c:48
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
POP network mailbox.
QuadOption
Possible values for a quad-option.
Definition quad.h:36
@ MUTT_ABORT
User aborted the question (with Ctrl-G)
Definition quad.h:37
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition quad.h:38
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition quad.h:39
Ask the user a question.
enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def, struct ConfigSubset *sub, const char *name)
Ask the user a Yes/No question offering help.
Definition question.c:357
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition question.c:384
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition question.c:329
void mutt_rand_base32(char *buf, size_t buflen)
Fill a buffer with a base32-encoded random string.
Definition random.c:106
struct AttachPtr * current_attachment(struct AttachCtx *actx, struct Menu *menu)
Get the current attachment.
Definition recvattach.c:71
void mutt_update_tree(struct AttachCtx *actx)
Refresh the list of attachments.
Definition recvattach.c:116
int aa_add_selection(struct AttachPtrArray *aa, struct AttachCtx *actx, struct Menu *menu, bool use_tagged, int count)
Build a working set of Attachments for an action.
Definition selection.c:125
int ba_add_selection(struct BodyArray *ba, struct AttachCtx *actx, struct Menu *menu, bool use_tagged, int count)
Build a working set of attachment bodies for an action.
Definition selection.c:164
void mutt_rfc3676_space_unstuff(struct Email *e)
Remove RFC3676 space stuffing.
Definition rfc3676.c:503
void mutt_rfc3676_space_stuff(struct Email *e)
Perform RFC3676 space stuffing on an Email.
Definition rfc3676.c:490
Convenience wrapper for the send headers.
void mutt_update_encoding(struct Body *b, struct ConfigSubset *sub)
Update the encoding type.
Definition sendlib.c:421
struct Body * mutt_make_file_attach(const char *path, struct ConfigSubset *sub)
Create a file attachment.
Definition sendlib.c:613
struct Body * mutt_make_message_attach(struct Mailbox *m, struct Email *e, bool attach_msg, struct ConfigSubset *sub)
Create a message attachment.
Definition sendlib.c:453
int mutt_write_fcc(const char *path, struct Email *e, const char *msgid, bool post, const char *fcc, char **finalpath, struct ConfigSubset *sub)
Write email to FCC mailbox.
Definition sendlib.c:1024
void mutt_stamp_attachment(struct Body *b)
Timestamp an Attachment.
Definition sendlib.c:409
#define MUTT_RANDTAG_LEN
Length of random tag for message boundaries.
Definition sendlib.h:36
#define ASSERT(COND)
Definition signal2.h:59
int endwin(void)
#define NONULL(x)
Definition string2.h:44
A set of attachments.
Definition attach.h:65
short vcount
The number of virtual attachments.
Definition attach.h:74
struct AttachPtr ** idx
Array of attachments.
Definition attach.h:69
short idxlen
Number of attachmentes.
Definition attach.h:70
An email to which things will be attached.
Definition attach.h:36
struct Body * body
Attachment.
Definition attach.h:37
char * tree
Tree characters to display.
Definition attach.h:40
int level
Nesting depth of attachment.
Definition attach.h:41
bool unowned
Don't unlink on detach.
Definition attach.h:43
int parent_type
Type of parent attachment, e.g. TYPE_MULTIPART.
Definition attach.h:39
The body of an email.
Definition body.h:36
char * language
content-language (RFC8255)
Definition body.h:78
char * content_id
Content-Id (RFC2392)
Definition body.h:58
char * d_filename
filename to be used for the content-disposition header If NULL, filename is used instead.
Definition body.h:56
struct Body * parts
parts of a multipart or message/rfc822
Definition body.h:73
bool noconv
Don't do character set conversion.
Definition body.h:46
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition body.h:68
time_t stamp
Time stamp of last encoding update.
Definition body.h:77
struct ParameterList parameter
Parameters of the content-type.
Definition body.h:63
struct AttachPtr * aptr
Menu information, used in recvattach.c.
Definition body.h:75
struct Email * email
header information for message/rfc822
Definition body.h:74
char * description
content-description
Definition body.h:55
unsigned int disposition
content-disposition, ContentDisposition
Definition body.h:42
bool tagged
This attachment is tagged.
Definition body.h:90
struct Body * next
next attachment in the list
Definition body.h:72
char * subtype
content-type subtype
Definition body.h:61
unsigned int encoding
content-transfer-encoding, ContentEncoding
Definition body.h:41
unsigned int type
content-type primary type, ContentType
Definition body.h:40
char * filename
When sending a message, this is the file to which this structure refers.
Definition body.h:59
String manipulation buffer.
Definition buffer.h:36
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
struct Menu * menu
Menu displaying the attachments.
Definition attach_data.h:35
struct AttachCtx * actx
Set of attachments.
Definition attach_data.h:34
Data passed to Compose worker functions.
Definition functions.h:33
struct NeoMutt * n
NeoMutt application data.
Definition functions.h:34
struct ComposeSharedData * shared
Shared Compose data.
Definition functions.h:36
struct ComposeModuleData * mod_data
Compose module data.
Definition functions.h:35
A NeoMutt function.
Definition functions.h:59
int op
Op code, e.g. OP_COMPOSE_WRITE_MESSAGE.
Definition functions.h:60
compose_function_t function
Function to call.
Definition functions.h:61
Compose private Module data.
Definition module_data.h:30
struct MenuDefinition * md_compose
Compose Menu Definition.
Definition module_data.h:32
Shared Compose Data.
Definition shared_data.h:35
struct ConfigSubset * sub
Config set to use.
Definition shared_data.h:36
struct Mailbox * mailbox
Current Mailbox.
Definition shared_data.h:37
int flags
Flags, e.g. MUTT_COMPOSE_NOFREEHEADER.
Definition shared_data.h:45
bool fcc_set
User has edited the Fcc: field.
Definition shared_data.h:46
int rc
Return code to leave compose.
Definition shared_data.h:47
struct ComposeAttachData * adata
Attachments.
Definition shared_data.h:39
struct Email * email
Email being composed.
Definition shared_data.h:38
struct Buffer * fcc
Buffer to save FCC.
Definition shared_data.h:44
A set of inherited config items.
Definition subset.h:46
struct ConnAccount account
Account details: username, password, etc.
Definition connection.h:49
The envelope/body of an email.
Definition email.h:39
struct Envelope * env
Envelope information.
Definition email.h:68
struct Body * body
List of MIME parts.
Definition email.h:69
struct Notify * notify
Notifications: NotifyEmail, EventEmail.
Definition email.h:73
Input for the file completion function.
Definition curs_lib.h:39
Data shared between Index, Pager and Sidebar.
Definition shared_data.h:37
bool attach_msg
Are we in "attach message" mode?
Definition shared_data.h:46
An event such as a keypress.
Definition get.h:75
int count
Optional count prefix, e.g. 3 for 3j
Definition get.h:78
A mailbox.
Definition mailbox.h:81
int msg_count
Total number of messages.
Definition mailbox.h:90
enum MailboxType type
Mailbox type.
Definition mailbox.h:104
struct Email ** emails
Array of Emails.
Definition mailbox.h:98
bool readonly
Don't allow changes to the mailbox.
Definition mailbox.h:118
Functions for a Dialog or Window.
Definition menu.h:77
Mapping between a function and an operation.
Definition menu.h:35
Mapping between an operation and a key sequence.
Definition menu.h:45
Definition lib.h:86
struct MuttWindow * win
Window holding the Menu.
Definition lib.h:94
int current
Current entry.
Definition lib.h:87
int num_tagged
Number of tagged entries.
Definition lib.h:101
bool tag_prefix
User has pressed <tag-prefix>
Definition lib.h:92
void * wdata
Private data.
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
struct Connection * conn
Connection to NNTP Server.
Definition adata.h:63
Nntp private Module data.
Definition module_data.h:30
struct NntpAccountData * current_news_srv
Current NNTP news server.
Definition module_data.h:32
Cached regular expression.
Definition regex3.h:85
Collection of related functions.
Definition menu.h:65
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition subset.c:303
@ MENU_COMPOSE
Compose an email.
Definition type.h:40