NeoMutt  2025-12-11-980-ge38c27
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 { "filter-entry", OP_ATTACH_FILTER },
109 { "get-attachment", OP_ATTACH_GET_ATTACHMENT },
110 { "group-alternatives", OP_ATTACH_GROUP_ALTS },
111 { "group-multilingual", OP_ATTACH_GROUP_LINGUAL },
112 { "group-related", OP_ATTACH_GROUP_RELATED },
113 { "ispell", OP_COMPOSE_ISPELL },
114 { "move-down", OP_ATTACH_MOVE_DOWN },
115 { "move-up", OP_ATTACH_MOVE_UP },
116 { "new-mime", OP_ATTACH_NEW_MIME },
117 { "pgp-menu", OP_COMPOSE_PGP_MENU },
118 { "pipe-entry", OP_PIPE },
119 { "pipe-message", OP_PIPE },
120 { "postpone-message", OP_COMPOSE_POSTPONE_MESSAGE },
121 { "preview-scroll-half-down", OP_PREVIEW_SCROLL_HALF_DOWN },
122 { "preview-scroll-half-up", OP_PREVIEW_SCROLL_HALF_UP },
123 { "preview-scroll-line-down", OP_PREVIEW_SCROLL_LINE_DOWN },
124 { "preview-scroll-line-up", OP_PREVIEW_SCROLL_LINE_UP },
125 { "preview-scroll-page-down", OP_PREVIEW_SCROLL_PAGE_DOWN },
126 { "preview-scroll-page-up", OP_PREVIEW_SCROLL_PAGE_UP },
127 { "preview-select-first-entry", OP_PREVIEW_SELECT_FIRST_ENTRY },
128 { "preview-select-last-entry", OP_PREVIEW_SELECT_LAST_ENTRY },
129 { "print-entry", OP_ATTACH_PRINT },
130 { "rename-attachment", OP_ATTACH_RENAME_ATTACHMENT },
131 { "rename-file", OP_COMPOSE_RENAME_FILE },
132 { "send-message", OP_COMPOSE_SEND_MESSAGE },
133 { "smime-menu", OP_COMPOSE_SMIME_MENU },
134 { "toggle-disposition", OP_ATTACH_TOGGLE_DISPOSITION },
135 { "toggle-recode", OP_ATTACH_TOGGLE_RECODE },
136 { "toggle-unlink", OP_ATTACH_TOGGLE_UNLINK },
137 { "ungroup-attachment", OP_ATTACH_UNGROUP },
138 { "update-encoding", OP_ATTACH_UPDATE_ENCODING },
139 { "view-attach", OP_ATTACH_VIEW },
140 { "view-mailcap", OP_ATTACH_VIEW_MAILCAP },
141 { "view-pager", OP_ATTACH_VIEW_PAGER },
142 { "view-text", OP_ATTACH_VIEW_TEXT },
143 { "write-fcc", OP_COMPOSE_WRITE_MESSAGE },
144 { NULL, 0 },
145};
146
150static const struct MenuOpSeq ComposeDefaultBindings[] = { /* map: compose */
151 { OP_ATTACH_ATTACH_FILE, "a" },
152 { OP_ATTACH_ATTACH_KEY, "\033k" }, // <Alt-k>
153 { OP_ATTACH_ATTACH_MESSAGE, "A" },
154 { OP_ATTACH_DETACH, "D" },
155 { OP_ATTACH_EDIT_CONTENT_ID, "\033i" }, // <Alt-i>
156 { OP_ATTACH_EDIT_DESCRIPTION, "d" },
157 { OP_ATTACH_EDIT_ENCODING, "\005" }, // <Ctrl-E>
158 { OP_ATTACH_EDIT_LANGUAGE, "\014" }, // <Ctrl-L>
159 { OP_ATTACH_EDIT_MIME, "m" },
160 { OP_ATTACH_EDIT_TYPE, "\024" }, // <Ctrl-T>
161 { OP_ATTACH_FILTER, "F" },
162 { OP_ATTACH_GET_ATTACHMENT, "G" },
163 { OP_ATTACH_GROUP_ALTS, "&" },
164 { OP_ATTACH_GROUP_LINGUAL, "^" },
165 { OP_ATTACH_GROUP_RELATED, "%" },
166 { OP_ATTACH_MOVE_DOWN, "+" },
167 { OP_ATTACH_MOVE_UP, "-" },
168 { OP_ATTACH_NEW_MIME, "n" },
169 { OP_PIPE, "|" },
170 { OP_ATTACH_PRINT, "l" },
171 { OP_ATTACH_RENAME_ATTACHMENT, "\017" }, // <Ctrl-O>
172 { OP_ATTACH_SAVE, "C" },
173 { OP_ATTACH_TOGGLE_DISPOSITION, "\004" }, // <Ctrl-D>
174 { OP_ATTACH_TOGGLE_UNLINK, "u" },
175 { OP_ATTACH_UNGROUP, "#" },
176 { OP_ATTACH_UPDATE_ENCODING, "U" },
177 { OP_ATTACH_VIEW, "<keypadenter>" },
178 { OP_ATTACH_VIEW, "\n" }, // <Enter>
179 { OP_ATTACH_VIEW, "\r" }, // <Return>
180#ifdef USE_AUTOCRYPT
181 { OP_COMPOSE_AUTOCRYPT_MENU, "o" },
182#endif
183 { OP_COMPOSE_EDIT_FILE, "\033e" }, // <Alt-e>
184 { OP_COMPOSE_EDIT_MESSAGE, "e" },
185 { OP_COMPOSE_ISPELL, "i" },
186 { OP_COMPOSE_PGP_MENU, "p" },
187 { OP_COMPOSE_POSTPONE_MESSAGE, "P" },
188 { OP_COMPOSE_RENAME_FILE, "R" },
189 { OP_COMPOSE_SEND_MESSAGE, "y" },
190 { OP_COMPOSE_SMIME_MENU, "S" },
191 { OP_COMPOSE_WRITE_MESSAGE, "w" },
192 { OP_DISPLAY_HEADERS, "h" },
193 { OP_ENVELOPE_EDIT_BCC, "b" },
194 { OP_ENVELOPE_EDIT_CC, "c" },
195 { OP_ENVELOPE_EDIT_FCC, "f" },
196 { OP_ENVELOPE_EDIT_FROM, "\033f" }, // <Alt-f>
197 { OP_ENVELOPE_EDIT_HEADERS, "E" },
198 { OP_ENVELOPE_EDIT_REPLY_TO, "r" },
199 { OP_ENVELOPE_EDIT_SUBJECT, "s" },
200 { OP_ENVELOPE_EDIT_TO, "t" },
201 { OP_PREVIEW_SCROLL_PAGE_DOWN, "<pagedown>" },
202 { OP_PREVIEW_SCROLL_PAGE_UP, "<pageup>" },
203 { OP_FORGET_PASSPHRASE, "\006" }, // <Ctrl-F>
204 { OP_TAG, "T" },
205 { 0, NULL },
206};
207// clang-format on
208
212void compose_init_keys(struct NeoMutt *n, struct SubMenu *sm_generic)
213{
215 ASSERT(mod_data);
216
217 struct MenuDefinition *md = NULL;
218 struct SubMenu *sm = NULL;
219
221 md = km_register_menu(MENU_COMPOSE, "compose");
222 km_menu_add_submenu(md, sm);
223 km_menu_add_submenu(md, sm_generic);
225
226 mod_data->md_compose = md;
227}
228
234static bool check_count(struct AttachCtx *actx)
235{
236 if (actx->idxlen == 0)
237 {
238 mutt_error(_("There are no attachments"));
239 return false;
240 }
241
242 return true;
243}
244
251static char *gen_cid(void)
252{
253 char rndid[MUTT_RANDTAG_LEN + 1] = { 0 };
254
255 mutt_rand_base32(rndid, sizeof(rndid) - 1);
256 rndid[MUTT_RANDTAG_LEN] = 0;
257
258 return mutt_str_dup(rndid);
259}
260
267static bool check_cid(const char *cid)
268{
269 static const char *check = "^[-\\.0-9@A-Z_a-z]+$";
270
271 struct Regex *check_cid_regex = mutt_regex_new(check, 0, NULL);
272
273 const bool valid = mutt_regex_match(check_cid_regex, cid);
274
275 mutt_regex_free(&check_cid_regex);
276
277 return valid;
278}
279
287static bool content_id_exists(struct AttachCtx *actx, const struct Body *skip, const char *cid)
288{
289 if (!actx || !cid)
290 return false;
291
292 for (int i = 0; i < actx->idxlen; i++)
293 {
294 struct Body *b = actx->idx[i]->body;
295 if ((b == skip) || !b->content_id)
296 continue;
297
298 if (mutt_str_equal(b->content_id, cid))
299 return true;
300 }
301
302 return false;
303}
304
312static int check_attachments(struct AttachCtx *actx, struct ConfigSubset *sub)
313{
314 int rc = -1;
315 struct stat st = { 0 };
316 struct Buffer *pretty = NULL;
317 struct Buffer *msg = NULL;
318
319 for (int i = 0; i < actx->idxlen; i++)
320 {
321 if (actx->idx[i]->body->type == TYPE_MULTIPART)
322 continue;
323 if (stat(actx->idx[i]->body->filename, &st) != 0)
324 {
325 if (!pretty)
326 pretty = buf_pool_get();
327 buf_strcpy(pretty, actx->idx[i]->body->filename);
328 pretty_mailbox(pretty);
329 /* L10N: This message is displayed in the compose menu when an attachment
330 doesn't stat. %d is the attachment number and %s is the attachment
331 filename. The filename is located last to avoid a long path hiding
332 the error message. */
333 mutt_error(_("Attachment #%d no longer exists: %s"), i + 1, buf_string(pretty));
334 goto cleanup;
335 }
336
337 if (actx->idx[i]->body->stamp < st.st_mtime)
338 {
339 if (!pretty)
340 pretty = buf_pool_get();
341 buf_strcpy(pretty, actx->idx[i]->body->filename);
342 pretty_mailbox(pretty);
343
344 if (!msg)
345 msg = buf_pool_get();
346 /* L10N: This message is displayed in the compose menu when an attachment
347 is modified behind the scenes. %d is the attachment number and %s is
348 the attachment filename. The filename is located last to avoid a long
349 path hiding the prompt question. */
350 buf_printf(msg, _("Attachment #%d modified. Update encoding for %s?"),
351 i + 1, buf_string(pretty));
352
354 if (ans == MUTT_YES)
355 mutt_update_encoding(actx->idx[i]->body, sub);
356 else if (ans == MUTT_ABORT)
357 goto cleanup;
358 }
359 }
360
361 rc = 0;
362
363cleanup:
364 buf_pool_release(&pretty);
365 buf_pool_release(&msg);
366 return rc;
367}
368
377static int delete_attachment(struct AttachCtx *actx, int aidx, struct ConfigSubset *sub)
378{
379 if (!actx || (aidx < 0) || (aidx >= actx->idxlen))
380 return -1;
381
382 struct AttachPtr **idx = actx->idx;
383 struct Body *b_previous = NULL;
384 struct Body *b_parent = NULL;
385
386 if (aidx == 0)
387 {
388 struct Body *b = actx->idx[0]->body;
389 if (!b->next) // There's only one attachment left
390 {
391 mutt_error(_("You may not delete the only attachment"));
392 return -1;
393 }
394
395 if (cs_subset_bool(sub, "compose_confirm_detach_first"))
396 {
397 /* L10N: Prompt when trying to hit <detach-file> on the first entry in
398 the compose menu. This entry is most likely the message they just
399 typed. Hitting yes will remove the entry and unlink the file, so
400 it's worth confirming they really meant to do it. */
401 enum QuadOption ans = query_yesorno_help(_("Really delete the main message?"), MUTT_NO,
402 sub, "compose_confirm_detach_first");
403 if (ans == MUTT_NO)
404 {
405 idx[aidx]->body->tagged = false;
406 return -1;
407 }
408 }
409 }
410
411 if (idx[aidx]->level > 0)
412 {
413 if (attach_body_parent(idx[0]->body, NULL, idx[aidx]->body, &b_parent))
414 {
415 if (attach_body_count(b_parent->parts, false) < 3)
416 {
417 mutt_error(_("Can't leave group with only one attachment"));
418 return -1;
419 }
420 }
421 }
422
423 // reorder body pointers
424 if (aidx > 0)
425 {
426 if (attach_body_previous(idx[0]->body, idx[aidx]->body, &b_previous))
427 b_previous->next = idx[aidx]->body->next;
428 else if (attach_body_parent(idx[0]->body, NULL, idx[aidx]->body, &b_parent))
429 b_parent->parts = idx[aidx]->body->next;
430 }
431
432 // free memory
433 int part_count = 1;
434 if (aidx < (actx->idxlen - 1))
435 {
436 if ((idx[aidx]->body->type == TYPE_MULTIPART) &&
437 (idx[aidx + 1]->level > idx[aidx]->level))
438 {
439 part_count += attach_body_count(idx[aidx]->body->parts, true);
440 }
441 }
442 idx[aidx]->body->next = NULL;
443 mutt_body_free(&(idx[aidx]->body));
444 for (int i = 0; i < part_count; i++)
445 {
446 FREE(&idx[aidx + i]->tree);
447 FREE(&idx[aidx + i]);
448 }
449
450 // reorder attachment list
451 for (int i = aidx; i < (actx->idxlen - part_count); i++)
452 idx[i] = idx[i + part_count];
453 for (int i = 0; i < part_count; i++)
454 idx[actx->idxlen - i - 1] = NULL;
455 actx->idxlen -= part_count;
456
457 return 0;
458}
459
466static void update_idx(struct Menu *menu, struct AttachCtx *actx, struct AttachPtr *ap)
467{
468 ap->level = 0;
469 for (int i = actx->idxlen; i > 0; i--)
470 {
471 if (ap->level == actx->idx[i - 1]->level)
472 {
473 actx->idx[i - 1]->body->next = ap->body;
474 break;
475 }
476 }
477
478 ap->body->aptr = ap;
479 mutt_actx_add_attach(actx, ap);
480 update_menu(actx, menu, false);
481 menu_set_index(menu, actx->vcount - 1);
482}
483
491static void compose_attach_swap(struct Email *e, struct AttachCtx *actx, int first, int second)
492{
493 struct AttachPtr **idx = actx->idx;
494
495 // check that attachments really are adjacent
496 if (idx[first]->body->next != idx[second]->body)
497 return;
498
499 // reorder Body pointers
500 if (first == 0)
501 {
502 // first attachment is the fundamental part
503 idx[first]->body->next = idx[second]->body->next;
504 idx[second]->body->next = idx[first]->body;
505 e->body = idx[second]->body;
506 }
507 else
508 {
509 // find previous attachment
510 struct Body *b_previous = NULL;
511 struct Body *b_parent = NULL;
512 if (attach_body_previous(e->body, idx[first]->body, &b_previous))
513 {
514 idx[first]->body->next = idx[second]->body->next;
515 idx[second]->body->next = idx[first]->body;
516 b_previous->next = idx[second]->body;
517 }
518 else if (attach_body_parent(e->body, NULL, idx[first]->body, &b_parent))
519 {
520 idx[first]->body->next = idx[second]->body->next;
521 idx[second]->body->next = idx[first]->body;
522 b_parent->parts = idx[second]->body;
523 }
524 }
525
526 // reorder attachment list
527 struct AttachPtr *saved = idx[second];
528 for (int i = second; i > first; i--)
529 idx[i] = idx[i - 1];
530 idx[first] = saved;
531
532 // if moved attachment is a group then move subparts too
533 if ((idx[first]->body->type == TYPE_MULTIPART) && (second < actx->idxlen - 1))
534 {
535 int i = second + 1;
536 while (idx[i]->level > idx[first]->level)
537 {
538 saved = idx[i];
539 int destidx = i - second + first;
540 for (int j = i; j > destidx; j--)
541 idx[j] = idx[j - 1];
542 idx[destidx] = saved;
543 i++;
544 if (i >= actx->idxlen)
545 break;
546 }
547 }
548}
549
557static int body_index(struct AttachCtx *actx, const struct Body *b)
558{
559 if (!actx || !b)
560 return -1;
561
562 for (int i = 0; i < actx->idxlen; i++)
563 {
564 if (actx->idx[i]->body == b)
565 return i;
566 }
567
568 return -1;
569}
570
578static int prev_sibling(struct AttachCtx *actx, int index)
579{
580 if (!actx || (index <= 0) || (index >= actx->idxlen))
581 return -1;
582
583 const int level = actx->idx[index]->level;
584 int previdx = index - 1;
585 while ((previdx > 0) && (actx->idx[previdx]->level > level))
586 previdx--;
587
588 return (actx->idx[previdx]->level == level) ? previdx : -1;
589}
590
598static int next_sibling(struct AttachCtx *actx, int index)
599{
600 if (!actx || (index < 0) || (index >= actx->idxlen))
601 return -1;
602
603 const int level = actx->idx[index]->level;
604 int nextidx = index + 1;
605 while ((nextidx < actx->idxlen) && (actx->idx[nextidx]->level > level))
606 nextidx++;
607
608 if ((nextidx >= actx->idxlen) || (actx->idx[nextidx]->level != level))
609 return -1;
610
611 return nextidx;
612}
613
620static bool body_array_contains(struct BodyArray *ba, const struct Body *b)
621{
622 struct Body **bp = NULL;
623 ARRAY_FOREACH(bp, ba)
624 {
625 if (*bp == b)
626 return true;
627 }
628
629 return false;
630}
631
638static bool selected_attachments_are_siblings(struct Email *e, struct BodyArray *ba)
639{
640 if (!e || !ba)
641 return false;
642
643 int level = -1;
644 struct Body *parent = NULL;
645 struct Body **bp = NULL;
646 ARRAY_FOREACH(bp, ba)
647 {
648 struct Body *this_parent = NULL;
649 struct Body *body = *bp;
650 if (body->aptr && (body->aptr->level > 0))
651 {
652 if (!attach_body_parent(e->body, NULL, body, &this_parent))
653 return false;
654 }
655
656 if (level == -1)
657 {
658 level = body->aptr ? body->aptr->level : 0;
659 parent = this_parent;
660 continue;
661 }
662
663 if ((level != (body->aptr ? body->aptr->level : 0)) || (parent != this_parent))
664 return false;
665 }
666
667 return true;
668}
669
676{
677 return (count > 1) ? MENU_REDRAW_FULL : MENU_REDRAW_CURRENT;
678}
679
692static void resolve_attachment(struct Menu *menu, const struct ConfigSubset *sub,
693 int index, int num)
694{
695 if (!menu || menu->tag_prefix || (num <= 0))
696 return;
697
698 const bool c_resolve = cs_subset_bool(sub, "resolve");
699 if (!c_resolve)
700 return;
701
702 const int new_index = index + num;
703 if (new_index < menu->max)
704 menu_set_index(menu, new_index);
705}
706
714static int move_attachment(struct ComposeSharedData *shared, bool up, int count)
715{
716 struct AttachCtx *actx = shared->adata->actx;
717 if (!check_count(actx))
718 return FR_ERROR;
719
720 struct Menu *menu = shared->adata->menu;
721 struct AttachPtr *cur_att = current_attachment(actx, menu);
722 if (!cur_att)
723 return FR_ERROR;
724
725 struct Body *cur_body = cur_att->body;
726 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
727 ba_add_selection(&ba, actx, menu, menu->tag_prefix, count);
728 if (ARRAY_EMPTY(&ba))
729 goto done;
730
731 if ((ARRAY_SIZE(&ba) > 1) && !selected_attachments_are_siblings(shared->email, &ba))
732 {
733 mutt_error(_("Selected attachments must be siblings"));
734 ARRAY_FREE(&ba);
735 return FR_ERROR;
736 }
737
738 bool moved = false;
739 if (up)
740 {
741 struct Body **bp = NULL;
742 ARRAY_FOREACH(bp, &ba)
743 {
744 const int index = body_index(actx, *bp);
745 const int previdx = prev_sibling(actx, index);
746 if (previdx < 0)
747 continue;
748 if (body_array_contains(&ba, actx->idx[previdx]->body))
749 continue;
750
751 compose_attach_swap(shared->email, actx, previdx, index);
752 moved = true;
753 }
754 }
755 else
756 {
757 for (int i = ARRAY_SIZE(&ba) - 1; i >= 0; i--)
758 {
759 struct Body **bp = ARRAY_GET(&ba, i);
760 const int index = body_index(actx, *bp);
761 const int nextidx = next_sibling(actx, index);
762 if (nextidx < 0)
763 continue;
764 if (body_array_contains(&ba, actx->idx[nextidx]->body))
765 continue;
766
767 compose_attach_swap(shared->email, actx, index, nextidx);
768 moved = true;
769 }
770 }
771
772 if (!moved)
773 {
774 mutt_error(up ? _("Attachment is already at top") : _("Attachment is already at bottom"));
775 goto done;
776 }
777
778 mutt_update_tree(actx);
781
782 const int index = body_index(actx, cur_body);
783 if (index >= 0)
784 menu_set_index(menu, index);
785
786 ARRAY_FREE(&ba);
787 return FR_SUCCESS;
788
789done:
790 ARRAY_FREE(&ba);
791 return FR_ERROR;
792}
793
802static int group_attachments(struct ComposeSharedData *shared, char *subtype,
803 struct BodyArray *ba)
804{
805 struct AttachCtx *actx = shared->adata->actx;
806 int group_level = -1;
807 struct Body *bptr_parent = NULL;
808
809 // Attachments to be grouped must have the same parent
810 for (int i = 0; i < actx->idxlen; i++)
811 {
812 if (body_array_contains(ba, actx->idx[i]->body))
813 {
814 if (group_level == -1)
815 {
816 group_level = actx->idx[i]->level;
817 }
818 else
819 {
820 if (group_level != actx->idx[i]->level)
821 {
822 mutt_error(_("Attachments to be grouped must have the same parent"));
823 return FR_ERROR;
824 }
825 }
826 // if not at top level check if all tagged attachments have same parent
827 if (group_level > 0)
828 {
829 if (bptr_parent)
830 {
831 struct Body *bptr_test = NULL;
832 if (!attach_body_parent(actx->idx[0]->body, NULL, actx->idx[i]->body, &bptr_test))
833 mutt_debug(LL_DEBUG5, "can't find parent\n");
834 if (bptr_test != bptr_parent)
835 {
836 mutt_error(_("Attachments to be grouped must have the same parent"));
837 return FR_ERROR;
838 }
839 }
840 else
841 {
842 if (!attach_body_parent(actx->idx[0]->body, NULL, actx->idx[i]->body, &bptr_parent))
843 mutt_debug(LL_DEBUG5, "can't find parent\n");
844 }
845 }
846 }
847 }
848
849 // Can't group all attachments unless at top level
850 if (bptr_parent)
851 {
852 if (ARRAY_SIZE(ba) == attach_body_count(bptr_parent->parts, false))
853 {
854 mutt_error(_("Can't leave group with only one attachment"));
855 return FR_ERROR;
856 }
857 }
858
859 struct Body *group = mutt_body_new();
860 group->type = TYPE_MULTIPART;
861 group->subtype = mutt_str_dup(subtype);
862 group->encoding = ENC_7BIT;
863
864 struct Body *bptr_first = NULL; // first selected attachment
865 struct Body *bptr = NULL; // current selected attachment
866 struct Body *group_parent = NULL; // parent of group
867 struct Body *group_previous = NULL; // previous body to group
868 struct Body *group_part = NULL; // current attachment in group
869 int group_idx = 0; // index in attachment list where group will be inserted
870 int group_last_idx = 0; // index of last part of previous found group
871 int group_parent_type = TYPE_OTHER;
872
873 for (int i = 0; i < actx->idxlen; i++)
874 {
875 bptr = actx->idx[i]->body;
876 if (body_array_contains(ba, bptr))
877 {
878 // set group properties based on first selected attachment
879 if (!bptr_first)
880 {
881 group->disposition = bptr->disposition;
882 if (bptr->language && !mutt_str_equal(subtype, "multilingual"))
883 group->language = mutt_str_dup(bptr->language);
884 group_parent_type = bptr->aptr->parent_type;
885 bptr_first = bptr;
886 if (i > 0)
887 {
888 if (!attach_body_previous(shared->email->body, bptr, &group_previous))
889 {
890 mutt_debug(LL_DEBUG5, "couldn't find previous\n");
891 }
892 if (!attach_body_parent(shared->email->body, NULL, bptr, &group_parent))
893 {
894 mutt_debug(LL_DEBUG5, "couldn't find parent\n");
895 }
896 }
897 }
898
899 if (bptr->tagged)
900 {
901 shared->adata->menu->num_tagged--;
902 bptr->tagged = false;
903 }
904 bptr->aptr->level++;
906
907 // append bptr to the group parts list and remove from email body list
908 struct Body *bptr_previous = NULL;
909 if (attach_body_previous(shared->email->body, bptr, &bptr_previous))
910 bptr_previous->next = bptr->next;
911 else if (attach_body_parent(shared->email->body, NULL, bptr, &bptr_parent))
912 bptr_parent->parts = bptr->next;
913 else
914 shared->email->body = bptr->next;
915
916 if (group_part)
917 {
918 // add bptr to group parts list
919 group_part->next = bptr;
920 group_part = group_part->next;
921 group_part->next = NULL;
922
923 // reorder attachments and set levels
924 int bptr_attachments = attach_body_count(bptr, true);
925 for (int j = i + 1; j < (i + bptr_attachments); j++)
926 actx->idx[j]->level++;
927 if (i > (group_last_idx + 1))
928 {
929 for (int j = 0; j < bptr_attachments; j++)
930 {
931 struct AttachPtr *saved = actx->idx[i + bptr_attachments - 1];
932 for (int k = i + bptr_attachments - 1; k > (group_last_idx + 1); k--)
933 actx->idx[k] = actx->idx[k - 1];
934 actx->idx[group_last_idx + 1] = saved;
935 }
936 }
937 i += bptr_attachments - 1;
938 group_last_idx += bptr_attachments;
939 }
940 else
941 {
942 group_idx = i;
943 group->parts = bptr;
944 group_part = bptr;
945 group_part->next = NULL;
946 int bptr_attachments = attach_body_count(bptr, true);
947 for (int j = i + 1; j < (i + bptr_attachments); j++)
948 actx->idx[j]->level++;
949 i += bptr_attachments - 1;
950 group_last_idx = i;
951 }
952 }
953 }
954
955 if (!bptr_first)
956 {
957 mutt_body_free(&group);
958 return FR_ERROR;
959 }
960
961 // set group->next
962 int next_aidx = group_idx + attach_body_count(group->parts, true);
963 if (group_parent)
964 {
965 // find next attachment with the same parent as the group
966 struct Body *b = NULL;
967 struct Body *b_parent = NULL;
968 while (next_aidx < actx->idxlen)
969 {
970 b = actx->idx[next_aidx]->body;
971 b_parent = NULL;
972 if (attach_body_parent(shared->email->body, NULL, b, &b_parent))
973 {
974 if (group_parent == b_parent)
975 {
976 group->next = b;
977 break;
978 }
979 }
980 next_aidx++;
981 }
982 }
983 else if (next_aidx < actx->idxlen)
984 {
985 // group is at top level
986 group->next = actx->idx[next_aidx]->body;
987 }
988
989 // set previous or parent for group
990 if (group_previous)
991 group_previous->next = group;
992 else if (group_parent)
993 group_parent->parts = group;
994
996
997 struct AttachPtr *group_ap = mutt_aptr_new();
998 group_ap->body = group;
999 group_ap->body->aptr = group_ap;
1000 group_ap->level = group_level;
1001 group_ap->parent_type = group_parent_type;
1002
1003 // insert group into attachment list
1004 mutt_actx_ins_attach(actx, group_ap, group_idx);
1005
1006 // update email body and last attachment pointers
1007 shared->email->body = actx->idx[0]->body;
1008 actx->idx[actx->idxlen - 1]->body->next = NULL;
1009
1010 update_menu(actx, shared->adata->menu, false);
1011 shared->adata->menu->current = group_idx;
1013
1014 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1015 return FR_SUCCESS;
1016}
1017
1018// -----------------------------------------------------------------------------
1019
1023static int op_attach_attach_file(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1024{
1025 struct ComposeSharedData *shared = fdata->shared;
1026 char *prompt = _("Attach file");
1027 int numfiles = 0;
1028 char **files = NULL;
1029
1030 struct Buffer *fname = buf_pool_get();
1031 if ((mw_enter_fname(prompt, fname, false, NULL, true, &files, &numfiles,
1032 MUTT_SEL_MULTI) == -1) ||
1033 buf_is_empty(fname))
1034 {
1035 for (int i = 0; i < numfiles; i++)
1036 FREE(&files[i]);
1037
1038 FREE(&files);
1039 buf_pool_release(&fname);
1040 return FR_NO_ACTION;
1041 }
1042
1043 bool error = false;
1044 bool added_attachment = false;
1045 if (numfiles > 1)
1046 {
1047 mutt_message(ngettext("Attaching selected file...",
1048 "Attaching selected files...", numfiles));
1049 }
1050 for (int i = 0; i < numfiles; i++)
1051 {
1052 char *att = files[i];
1053 if (!att)
1054 continue;
1055
1056 struct AttachPtr *ap = mutt_aptr_new();
1057 ap->unowned = true;
1058 ap->body = mutt_make_file_attach(att, shared->sub);
1059 if (ap->body)
1060 {
1061 added_attachment = true;
1062 update_idx(shared->adata->menu, shared->adata->actx, ap);
1063 }
1064 else
1065 {
1066 error = true;
1067 mutt_error(_("Unable to attach %s"), att);
1068 mutt_aptr_free(&ap);
1069 }
1070 FREE(&files[i]);
1071 }
1072
1073 FREE(&files);
1074 buf_pool_release(&fname);
1075
1076 if (!error)
1078
1081 if (added_attachment)
1082 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1083 return added_attachment ? FR_SUCCESS : (error ? FR_ERROR : FR_NO_ACTION);
1084}
1085
1089static int op_attach_attach_key(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1090{
1091 struct ComposeSharedData *shared = fdata->shared;
1092 if (!(WithCrypto & APPLICATION_PGP))
1093 return FR_NOT_IMPL;
1094 struct AttachPtr *ap = mutt_aptr_new();
1096 if (ap->body)
1097 {
1098 update_idx(shared->adata->menu, shared->adata->actx, ap);
1100 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1101 }
1102 else
1103 {
1104 mutt_aptr_free(&ap);
1105 }
1106
1108 return FR_SUCCESS;
1109}
1110
1119 const struct KeyEvent *event)
1120{
1122 struct ComposeSharedData *shared = fdata->shared;
1123 char *prompt = _("Open mailbox to attach message from");
1124
1125 OptNews = false;
1126 const int op = event->op;
1127 if (shared->mailbox && (op == OP_ATTACH_ATTACH_NEWS_MESSAGE))
1128 {
1129 const char *const c_news_server = cs_subset_string(shared->sub, "news_server");
1130 mod_data->current_news_srv = nntp_select_server(shared->mailbox, c_news_server, false);
1131 if (!mod_data->current_news_srv)
1132 return FR_NO_ACTION;
1133
1134 prompt = _("Open newsgroup to attach message from");
1135 OptNews = true;
1136 }
1137
1138 struct Buffer *fname = buf_pool_get();
1139 if (shared->mailbox)
1140 {
1141 if ((op == OP_ATTACH_ATTACH_MESSAGE) ^ (shared->mailbox->type == MUTT_NNTP))
1142 {
1143 buf_strcpy(fname, mailbox_path(shared->mailbox));
1144 pretty_mailbox(fname);
1145 }
1146 }
1147
1148 if ((mw_enter_fname(prompt, fname, true, shared->mailbox, false, NULL, NULL,
1149 MUTT_SEL_NONE) == -1) ||
1150 buf_is_empty(fname))
1151 {
1152 buf_pool_release(&fname);
1153 return FR_NO_ACTION;
1154 }
1155
1156 if (OptNews)
1157 nntp_expand_path(fname->data, fname->dsize,
1158 &mod_data->current_news_srv->conn->account);
1159 else
1160 expand_path(fname, false);
1161
1162 if (imap_path_probe(buf_string(fname), NULL) != MUTT_IMAP)
1163 {
1164 if (pop_path_probe(buf_string(fname), NULL) != MUTT_POP)
1165 {
1166 if (!OptNews && (nntp_path_probe(buf_string(fname), NULL) != MUTT_NNTP))
1167 {
1168 if (mx_path_probe(buf_string(fname)) != MUTT_NOTMUCH)
1169 {
1170 /* check to make sure the file exists and is readable */
1171 if (access(buf_string(fname), R_OK) == -1)
1172 {
1173 mutt_perror("%s", buf_string(fname));
1174 buf_pool_release(&fname);
1175 return FR_ERROR;
1176 }
1177 }
1178 }
1179 }
1180 }
1181
1183
1184 struct Mailbox *m_attach = mx_path_resolve(buf_string(fname));
1185 const bool old_readonly = m_attach->readonly;
1186 if (!mx_mbox_open(m_attach, MUTT_READONLY))
1187 {
1188 mutt_error(_("Unable to open mailbox %s"), buf_string(fname));
1189 mx_fastclose_mailbox(m_attach, false);
1190 m_attach = NULL;
1191 buf_pool_release(&fname);
1192 return FR_ERROR;
1193 }
1194 buf_pool_release(&fname);
1195
1196 if (m_attach->msg_count == 0)
1197 {
1198 mx_mbox_close(m_attach);
1199 mutt_error(_("No messages in that folder"));
1200 return FR_ERROR;
1201 }
1202
1203 /* `$sort`, `$sort_aux`, `$use_threads` could be changed in dlg_index() */
1204 const enum EmailSortType old_sort = cs_subset_sort(shared->sub, "sort");
1205 const enum EmailSortType old_sort_aux = cs_subset_sort(shared->sub, "sort_aux");
1206 const unsigned char old_use_threads = cs_subset_enum(shared->sub, "use_threads");
1207
1208 mutt_message(_("Tag the messages you want to attach"));
1209 struct MuttWindow *dlg = index_pager_init();
1210 struct IndexSharedData *index_shared = dlg->wdata;
1211 index_shared->attach_msg = true;
1212 dialog_push(dlg);
1213 struct Mailbox *m_attach_new = dlg_index(dlg, m_attach);
1214 dialog_pop();
1215 mutt_window_free(&dlg);
1216
1217 if (!shared->mailbox)
1218 {
1219 /* Restore old $sort variables */
1220 cs_subset_str_native_set(shared->sub, "sort", old_sort, NULL);
1221 cs_subset_str_native_set(shared->sub, "sort_aux", old_sort_aux, NULL);
1222 cs_subset_str_native_set(shared->sub, "use_threads", old_use_threads, NULL);
1225 return FR_SUCCESS;
1226 }
1227
1228 bool added_attachment = false;
1229 for (int i = 0; i < m_attach_new->msg_count; i++)
1230 {
1231 if (!m_attach_new->emails[i])
1232 break;
1233 if (!message_is_tagged(m_attach_new->emails[i]))
1234 continue;
1235
1236 struct AttachPtr *ap = mutt_aptr_new();
1237 ap->body = mutt_make_message_attach(m_attach_new, m_attach_new->emails[i],
1238 true, shared->sub);
1239 if (ap->body)
1240 {
1241 added_attachment = true;
1242 update_idx(shared->adata->menu, shared->adata->actx, ap);
1243 }
1244 else
1245 {
1246 mutt_error(_("Unable to attach"));
1247 mutt_aptr_free(&ap);
1248 }
1249 }
1251
1252 if (m_attach_new == m_attach)
1253 {
1254 m_attach->readonly = old_readonly;
1255 }
1256 mx_fastclose_mailbox(m_attach_new, false);
1257
1258 /* Restore old $sort variables */
1259 cs_subset_str_native_set(shared->sub, "sort", old_sort, NULL);
1260 cs_subset_str_native_set(shared->sub, "sort_aux", old_sort_aux, NULL);
1261 cs_subset_str_native_set(shared->sub, "use_threads", old_use_threads, NULL);
1263 if (added_attachment)
1264 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1265 return added_attachment ? FR_SUCCESS : FR_ERROR;
1266}
1267
1271static int op_attach_detach(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1272{
1273 struct ComposeSharedData *shared = fdata->shared;
1274 struct AttachCtx *actx = shared->adata->actx;
1275 if (!check_count(actx))
1276 return FR_ERROR;
1277
1278 struct Menu *menu = shared->adata->menu;
1279 int rc = FR_NO_ACTION;
1280 struct AttachPtrArray aa = ARRAY_HEAD_INITIALIZER;
1281 aa_add_selection(&aa, actx, menu, menu->tag_prefix, event->count);
1282
1283 if (ARRAY_SIZE(&aa) > 1)
1284 {
1285 for (int i = ARRAY_SIZE(&aa) - 1; i >= 0; i--)
1286 {
1287 struct AttachPtr **app = ARRAY_GET(&aa, i);
1288 if (!app || !*app)
1289 continue;
1290 struct AttachPtr *ap = *app;
1291
1292 if (ap->unowned)
1293 ap->body->unlink = false;
1294
1295 const int index = body_index(actx, ap->body);
1296 if ((index >= 0) && (delete_attachment(actx, index, fdata->n->sub) == 0))
1297 rc = FR_SUCCESS;
1298 }
1299
1300 if (rc != FR_SUCCESS)
1301 {
1302 menu->num_tagged = 0;
1303 for (int i = 0; i < actx->idxlen; i++)
1304 {
1305 if (actx->idx[i]->body->tagged)
1306 menu->num_tagged++;
1307 }
1308 update_menu(actx, menu, false);
1309 ARRAY_FREE(&aa);
1310 return FR_ERROR;
1311 }
1312 }
1313 else
1314 {
1315 struct AttachPtr *cur_att = current_attachment(actx, menu);
1316 if (cur_att->unowned)
1317 cur_att->body->unlink = false;
1318
1319 const int index = menu_get_index(menu);
1320 if (delete_attachment(actx, index, fdata->n->sub) == -1)
1321 {
1322 menu->num_tagged = 0;
1323 for (int i = 0; i < actx->idxlen; i++)
1324 {
1325 if (actx->idx[i]->body->tagged)
1326 menu->num_tagged++;
1327 }
1328 update_menu(actx, menu, false);
1329 ARRAY_FREE(&aa);
1330 return FR_ERROR;
1331 }
1332
1333 rc = FR_SUCCESS;
1334 }
1335
1336 if (rc == FR_SUCCESS)
1338
1339 menu->num_tagged = 0;
1340 for (int i = 0; i < actx->idxlen; i++)
1341 {
1342 if (actx->idx[i]->body->tagged)
1343 menu->num_tagged++;
1344 }
1345
1346 update_menu(actx, menu, false);
1348
1349 const int index = menu_get_index(menu);
1350 if (index == 0)
1351 shared->email->body = actx->idx[0]->body;
1352
1353 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1354 ARRAY_FREE(&aa);
1355 return rc;
1356}
1357
1362 const struct KeyEvent *event)
1363{
1364 struct ComposeSharedData *shared = fdata->shared;
1365 struct AttachCtx *actx = shared->adata->actx;
1366 if (!check_count(actx))
1367 return FR_ERROR;
1368
1369 int rc = FR_NO_ACTION;
1370 struct Menu *menu = shared->adata->menu;
1371 const int index = menu_get_index(menu);
1372 struct Buffer *buf = buf_pool_get();
1373 struct Buffer *cid = buf_pool_get();
1374 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1375 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
1376 const int num = ARRAY_SIZE(&ba);
1377
1378 struct AttachPtr *cur_att = current_attachment(actx, menu);
1379
1380 char *id = cur_att->body->content_id;
1381 if (id)
1382 {
1383 buf_strcpy(buf, id);
1384 }
1385 else
1386 {
1387 id = gen_cid();
1388 buf_strcpy(buf, id);
1389 FREE(&id);
1390 }
1391
1392 if (mw_get_field("Content-ID: ", buf, MUTT_COMP_NONE, HC_OTHER, NULL, NULL) == 0)
1393 {
1394 if (check_cid(buf_string(buf)))
1395 {
1396 bool changed = false;
1397 struct Body **bp = NULL;
1398 ARRAY_FOREACH(bp, &ba)
1399 {
1400 const bool multi = (ARRAY_SIZE(&ba) > 1);
1401 int suffix = multi ? (ARRAY_FOREACH_IDX_bp + 1) : 0;
1402
1403 do
1404 {
1405 if (suffix == 0)
1406 buf_strcpy(cid, buf_string(buf));
1407 else
1408 buf_printf(cid, "%s-%d", buf_string(buf), suffix);
1409
1410 suffix++;
1411 } while (content_id_exists(actx, *bp, buf_string(cid)));
1412
1413 if (!mutt_str_equal((*bp)->content_id, buf_string(cid)))
1414 {
1415 mutt_str_replace(&(*bp)->content_id, buf_string(cid));
1416 changed = true;
1417 }
1418 }
1419
1420 if (changed)
1421 {
1423 resolve_attachment(menu, shared->sub, index, num);
1425 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1426 rc = FR_SUCCESS;
1427 }
1428 }
1429 else
1430 {
1431 mutt_error(_("Content-ID can only contain the characters: -.0-9@A-Z_a-z"));
1432 rc = FR_ERROR;
1433 }
1434 }
1435
1436 ARRAY_FREE(&ba);
1437 buf_pool_release(&cid);
1438 buf_pool_release(&buf);
1439
1440 if (rc != FR_ERROR)
1442
1443 return rc;
1444}
1445
1450 const struct KeyEvent *event)
1451{
1452 struct ComposeSharedData *shared = fdata->shared;
1453 struct AttachCtx *actx = shared->adata->actx;
1454 if (!check_count(actx))
1455 return FR_ERROR;
1456
1457 int rc = FR_NO_ACTION;
1458 struct Menu *menu = shared->adata->menu;
1459 const int index = menu_get_index(menu);
1460 struct Buffer *buf = buf_pool_get();
1461 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1462
1463 struct AttachPtr *cur_att = current_attachment(actx, menu);
1464 buf_strcpy(buf, cur_att->body->description);
1465
1466 /* header names should not be translated */
1467 if (mw_get_field("Description: ", buf, MUTT_COMP_NONE, HC_OTHER, NULL, NULL) == 0)
1468 {
1469 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
1470 const int num = ARRAY_SIZE(&ba);
1471
1472 bool changed = false;
1473 struct Body **bp = NULL;
1474 ARRAY_FOREACH(bp, &ba)
1475 {
1476 if (!mutt_str_equal((*bp)->description, buf_string(buf)))
1477 {
1478 mutt_str_replace(&(*bp)->description, buf_string(buf));
1479 changed = true;
1480 }
1481 }
1482
1483 if (changed)
1484 {
1486 resolve_attachment(menu, shared->sub, index, num);
1487 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1488 rc = FR_SUCCESS;
1489 }
1490 }
1491
1492 ARRAY_FREE(&ba);
1493 buf_pool_release(&buf);
1494 return rc;
1495}
1496
1501 const struct KeyEvent *event)
1502{
1503 struct ComposeSharedData *shared = fdata->shared;
1504 struct AttachCtx *actx = shared->adata->actx;
1505 if (!check_count(actx))
1506 return FR_ERROR;
1507
1508 int rc = FR_NO_ACTION;
1509 struct Menu *menu = shared->adata->menu;
1510 const int index = menu_get_index(menu);
1511 struct Buffer *buf = buf_pool_get();
1512 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1513
1514 struct AttachPtr *cur_att = current_attachment(actx, menu);
1515 buf_strcpy(buf, ENCODING(cur_att->body->encoding));
1516
1517 if ((mw_get_field("Content-Transfer-Encoding: ", buf, MUTT_COMP_NONE,
1518 HC_OTHER, NULL, NULL) == 0) &&
1519 !buf_is_empty(buf))
1520 {
1521 int enc = mutt_check_encoding(buf_string(buf));
1522 if ((enc != ENC_OTHER) && (enc != ENC_UUENCODED))
1523 {
1524 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
1525 const int num = ARRAY_SIZE(&ba);
1526
1527 bool changed = false;
1528 struct Body **bp = NULL;
1529 ARRAY_FOREACH(bp, &ba)
1530 {
1531 if ((*bp)->encoding != enc)
1532 {
1533 (*bp)->encoding = enc;
1534 changed = true;
1535 }
1536 }
1537
1538 if (changed)
1539 {
1541 resolve_attachment(menu, shared->sub, index, num);
1544 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1545 rc = FR_SUCCESS;
1546 }
1547 }
1548 else
1549 {
1550 mutt_error(_("Invalid encoding"));
1551 rc = FR_ERROR;
1552 }
1553 }
1554
1555 ARRAY_FREE(&ba);
1556 buf_pool_release(&buf);
1557 return rc;
1558}
1559
1564 const struct KeyEvent *event)
1565{
1566 struct ComposeSharedData *shared = fdata->shared;
1567 struct AttachCtx *actx = shared->adata->actx;
1568 if (!check_count(actx))
1569 return FR_ERROR;
1570
1571 int rc = FR_NO_ACTION;
1572 struct Menu *menu = shared->adata->menu;
1573 const int index = menu_get_index(menu);
1574 struct Buffer *buf = buf_pool_get();
1575 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1576 struct AttachPtr *cur_att = current_attachment(actx, menu);
1577
1578 buf_strcpy(buf, cur_att->body->language);
1579 if (mw_get_field("Content-Language: ", buf, MUTT_COMP_NONE, HC_OTHER, NULL, NULL) == 0)
1580 {
1581 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
1582 const int num = ARRAY_SIZE(&ba);
1583
1584 bool changed = false;
1585 struct Body **bp = NULL;
1586 ARRAY_FOREACH(bp, &ba)
1587 {
1588 if (!mutt_str_equal((*bp)->language, buf_string(buf)))
1589 {
1590 mutt_str_replace(&(*bp)->language, buf_string(buf));
1591 changed = true;
1592 }
1593 }
1594
1595 if (changed)
1596 {
1598 resolve_attachment(menu, shared->sub, index, num);
1600 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1601 rc = FR_SUCCESS;
1602 }
1604 }
1605 else
1606 {
1607 mutt_warning(_("Empty 'Content-Language'"));
1608 rc = FR_ERROR;
1609 }
1610
1611 ARRAY_FREE(&ba);
1612 buf_pool_release(&buf);
1613 return rc;
1614}
1615
1619static int op_attach_edit_mime(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1620{
1621 struct ComposeSharedData *shared = fdata->shared;
1622 struct AttachCtx *actx = shared->adata->actx;
1623 if (!check_count(actx))
1624 return FR_ERROR;
1625
1626 int rc = FR_NO_ACTION;
1627 struct Menu *menu = shared->adata->menu;
1628 const int index = menu_get_index(menu);
1629 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1630 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
1631 const int num = ARRAY_SIZE(&ba);
1632 if (ARRAY_EMPTY(&ba))
1633 goto done;
1634
1635 struct Body **bp = NULL;
1636 ARRAY_FOREACH(bp, &ba)
1637 {
1638 if (!mutt_edit_attachment(*bp))
1639 continue;
1640
1641 mutt_update_encoding(*bp, shared->sub);
1642 rc = FR_SUCCESS;
1643 }
1644
1645 if (rc != FR_SUCCESS)
1646 goto done;
1647
1649 resolve_attachment(menu, shared->sub, index, num);
1650 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1651
1652done:
1653 ARRAY_FREE(&ba);
1654 return rc;
1655}
1656
1660static int op_attach_edit_type(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1661{
1662 struct ComposeSharedData *shared = fdata->shared;
1663 if (!check_count(shared->adata->actx))
1664 return FR_ERROR;
1665
1666 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
1667 shared->adata->menu);
1668 if (!mutt_edit_content_type(NULL, cur_att->body, NULL))
1669 return FR_NO_ACTION;
1670
1671 /* this may have been a change to text/something */
1672 mutt_update_encoding(cur_att->body, shared->sub);
1674 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1675 return FR_SUCCESS;
1676}
1677
1685static int op_attach_filter(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1686{
1687 struct ComposeSharedData *shared = fdata->shared;
1688 struct AttachCtx *actx = shared->adata->actx;
1689 if (!check_count(actx))
1690 return FR_ERROR;
1691
1692 struct Menu *menu = shared->adata->menu;
1693 const int index = menu_get_index(menu);
1694 struct AttachPtrArray aa = ARRAY_HEAD_INITIALIZER;
1695 aa_add_selection(&aa, actx, menu, menu->tag_prefix, event->count);
1696 const int num = ARRAY_SIZE(&aa);
1697 struct AttachPtr **app = NULL;
1698 ARRAY_FOREACH(app, &aa)
1699 {
1700 if ((*app)->body->type == TYPE_MULTIPART)
1701 {
1702 ARRAY_FREE(&aa);
1703 mutt_error(_("Can't filter multipart attachments"));
1704 return FR_ERROR;
1705 }
1706 }
1707
1708 const int op = event->op;
1709 mutt_pipe_attachment_list(&aa, (op == OP_ATTACH_FILTER));
1710 if (op == OP_ATTACH_FILTER) /* cte might have changed */
1711 {
1713 }
1714 resolve_attachment(menu, shared->sub, index, num);
1715 ARRAY_FREE(&aa);
1717 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1718 return FR_SUCCESS;
1719}
1720
1725 const struct KeyEvent *event)
1726{
1727 struct ComposeSharedData *shared = fdata->shared;
1728 struct AttachCtx *actx = shared->adata->actx;
1729 if (!check_count(actx))
1730 return FR_ERROR;
1731
1732 int rc = FR_ERROR;
1733 struct Menu *menu = shared->adata->menu;
1734 const int index = menu_get_index(menu);
1735 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1736 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
1737 const int num = ARRAY_SIZE(&ba);
1738 if (ARRAY_EMPTY(&ba))
1739 goto done;
1740
1741 struct Body **bp = NULL;
1742 bool got_attachment = false;
1743 ARRAY_FOREACH(bp, &ba)
1744 {
1745 if ((*bp)->type == TYPE_MULTIPART)
1746 {
1747 mutt_warning(_("Can't get multipart attachments"));
1748 continue;
1749 }
1751 got_attachment = true;
1752 }
1753
1754 if (got_attachment)
1755 {
1757 resolve_attachment(menu, shared->sub, index, num);
1758 rc = FR_SUCCESS;
1759 }
1760
1761done:
1762 ARRAY_FREE(&ba);
1763 /* No send2hook since this doesn't change the message. */
1764 return rc;
1765}
1766
1770static int op_attach_group_alts(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1771{
1772 struct ComposeSharedData *shared = fdata->shared;
1773 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1774 ba_add_selection(&ba, shared->adata->actx, shared->adata->menu,
1775 shared->adata->menu->tag_prefix, event->count);
1776 if (ARRAY_SIZE(&ba) < 2)
1777 {
1778 ARRAY_FREE(&ba);
1779 mutt_error(_("Grouping 'alternatives' requires at least 2 selected attachments"));
1780 return FR_ERROR;
1781 }
1782
1783 const int rc = group_attachments(shared, "alternative", &ba);
1784 ARRAY_FREE(&ba);
1785 return rc;
1786}
1787
1792 const struct KeyEvent *event)
1793{
1794 struct ComposeSharedData *shared = fdata->shared;
1795 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1796 ba_add_selection(&ba, shared->adata->actx, shared->adata->menu,
1797 shared->adata->menu->tag_prefix, event->count);
1798 if (ARRAY_SIZE(&ba) < 2)
1799 {
1800 ARRAY_FREE(&ba);
1801 mutt_error(_("Grouping 'multilingual' requires at least 2 selected attachments"));
1802 return FR_ERROR;
1803 }
1804
1805 int tagged_with_lang_num = 0;
1806 struct Body **bp = NULL;
1807 ARRAY_FOREACH(bp, &ba)
1808 {
1809 if ((*bp)->language && *(*bp)->language)
1810 tagged_with_lang_num++;
1811 }
1812
1813 if (ARRAY_SIZE(&ba) != tagged_with_lang_num)
1814 {
1815 if (query_yesorno(_("Not all parts have 'Content-Language' set, continue?"),
1816 MUTT_YES) != MUTT_YES)
1817 {
1818 ARRAY_FREE(&ba);
1819 mutt_message(_("Not sending this message"));
1820 return FR_ERROR;
1821 }
1822 }
1823
1824 const int rc = group_attachments(shared, "multilingual", &ba);
1825 ARRAY_FREE(&ba);
1826 return rc;
1827}
1828
1833 const struct KeyEvent *event)
1834{
1835 struct ComposeSharedData *shared = fdata->shared;
1836 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1837 ba_add_selection(&ba, shared->adata->actx, shared->adata->menu,
1838 shared->adata->menu->tag_prefix, event->count);
1839 if (ARRAY_SIZE(&ba) < 2)
1840 {
1841 ARRAY_FREE(&ba);
1842 mutt_error(_("Grouping 'related' requires at least 2 selected attachments"));
1843 return FR_ERROR;
1844 }
1845
1846 // ensure Content-ID is set for selected attachments
1847 struct Body **bp = NULL;
1848 ARRAY_FOREACH(bp, &ba)
1849 {
1850 if ((*bp)->type == TYPE_MULTIPART)
1851 continue;
1852
1853 if (!(*bp)->content_id)
1854 {
1855 (*bp)->content_id = gen_cid();
1856 }
1857 }
1858
1859 const int rc = group_attachments(shared, "related", &ba);
1860 ARRAY_FREE(&ba);
1861 return rc;
1862}
1863
1867static int op_attach_move_down(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1868{
1869 struct ComposeSharedData *shared = fdata->shared;
1870 if (shared->adata->menu->tag_prefix || (event->count > 1))
1871 return move_attachment(shared, false, event->count);
1872
1873 int index = menu_get_index(shared->adata->menu);
1874
1875 struct AttachCtx *actx = shared->adata->actx;
1876
1877 if (index < 0)
1878 return FR_ERROR;
1879
1880 if (index == (actx->idxlen - 1))
1881 {
1882 mutt_error(_("Attachment is already at bottom"));
1883 return FR_ERROR;
1884 }
1885 if ((actx->idx[index]->parent_type == TYPE_MULTIPART) &&
1886 !actx->idx[index]->body->next)
1887 {
1888 mutt_error(_("Attachment can't be moved out of group"));
1889 return FR_ERROR;
1890 }
1891
1892 // find next attachment at current level
1893 int nextidx = index + 1;
1894 while ((nextidx < actx->idxlen) &&
1895 (actx->idx[nextidx]->level > actx->idx[index]->level))
1896 {
1897 nextidx++;
1898 }
1899 if (nextidx == actx->idxlen)
1900 {
1901 mutt_error(_("Attachment is already at bottom"));
1902 return FR_ERROR;
1903 }
1904
1905 // find final position
1906 int finalidx = index + 1;
1907 if (nextidx < actx->idxlen - 1)
1908 {
1909 if ((actx->idx[nextidx]->body->type == TYPE_MULTIPART) &&
1910 (actx->idx[nextidx + 1]->level > actx->idx[nextidx]->level))
1911 {
1912 finalidx += attach_body_count(actx->idx[nextidx]->body->parts, true);
1913 }
1914 }
1915
1916 compose_attach_swap(shared->email, shared->adata->actx, index, nextidx);
1917 mutt_update_tree(shared->adata->actx);
1920 menu_set_index(shared->adata->menu, finalidx);
1921 return FR_SUCCESS;
1922}
1923
1927static int op_attach_move_up(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1928{
1929 struct ComposeSharedData *shared = fdata->shared;
1930 if (shared->adata->menu->tag_prefix || (event->count > 1))
1931 return move_attachment(shared, true, event->count);
1932
1933 int index = menu_get_index(shared->adata->menu);
1934 if (index < 0)
1935 return FR_ERROR;
1936
1937 struct AttachCtx *actx = shared->adata->actx;
1938
1939 if (index == 0)
1940 {
1941 mutt_error(_("Attachment is already at top"));
1942 return FR_ERROR;
1943 }
1944 if (actx->idx[index - 1]->level < actx->idx[index]->level)
1945 {
1946 mutt_error(_("Attachment can't be moved out of group"));
1947 return FR_ERROR;
1948 }
1949
1950 // find previous attachment at current level
1951 int previdx = index - 1;
1952 while ((previdx > 0) && (actx->idx[previdx]->level > actx->idx[index]->level))
1953 previdx--;
1954
1955 compose_attach_swap(shared->email, actx, previdx, index);
1956 mutt_update_tree(actx);
1959 menu_set_index(shared->adata->menu, previdx);
1960 return FR_SUCCESS;
1961}
1962
1966static int op_attach_new_mime(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1967{
1968 struct ComposeSharedData *shared = fdata->shared;
1969 int rc = FR_NO_ACTION;
1970 struct Buffer *fname = buf_pool_get();
1971 struct Buffer *type = NULL;
1972 struct AttachPtr *ap = NULL;
1973
1974 struct FileCompletionData cdata = { false, shared->mailbox, NULL, NULL, NULL };
1975 if ((mw_get_field(_("New file: "), fname, MUTT_COMP_NONE, HC_FILE,
1976 &CompleteFileOps, &cdata) != 0) ||
1977 buf_is_empty(fname))
1978 {
1979 goto done;
1980 }
1981 expand_path(fname, false);
1982
1983 /* Call to lookup_mime_type () ? maybe later */
1984 type = buf_pool_get();
1985 if ((mw_get_field("Content-Type: ", type, MUTT_COMP_NONE, HC_OTHER, NULL, NULL) != 0) ||
1986 buf_is_empty(type))
1987 {
1988 goto done;
1989 }
1990
1991 rc = FR_ERROR;
1992 char *p = strchr(buf_string(type), '/');
1993 if (!p)
1994 {
1995 mutt_error(_("Content-Type is of the form base/sub"));
1996 goto done;
1997 }
1998 *p++ = 0;
1999 enum ContentType itype = mutt_check_mime_type(buf_string(type));
2000 if (itype == TYPE_OTHER)
2001 {
2002 mutt_error(_("Unknown Content-Type %s"), buf_string(type));
2003 goto done;
2004 }
2005
2006 ap = mutt_aptr_new();
2007 /* Touch the file */
2008 if (!mutt_file_touch(buf_string(fname)))
2009 {
2010 mutt_error(_("Can't create file %s"), buf_string(fname));
2011 goto done;
2012 }
2013
2014 ap->body = mutt_make_file_attach(buf_string(fname), shared->sub);
2015 if (!ap->body)
2016 {
2017 mutt_error(_("Error attaching file"));
2018 goto done;
2019 }
2020 update_idx(shared->adata->menu, shared->adata->actx, ap);
2021 ap = NULL; // shared->adata->actx has taken ownership
2022
2023 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
2024 shared->adata->menu);
2025 cur_att->body->type = itype;
2026 mutt_str_replace(&cur_att->body->subtype, p);
2027 cur_att->body->unlink = true;
2030
2031 if (mutt_compose_attachment(cur_att->body))
2032 {
2033 mutt_update_encoding(cur_att->body, shared->sub);
2035 }
2036 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2037 rc = FR_SUCCESS;
2038
2039done:
2040 mutt_aptr_free(&ap);
2041 buf_pool_release(&type);
2042 buf_pool_release(&fname);
2043 return rc;
2044}
2045
2049static int op_attach_print(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2050{
2051 struct ComposeSharedData *shared = fdata->shared;
2052 struct AttachCtx *actx = shared->adata->actx;
2053 if (!check_count(actx))
2054 return FR_ERROR;
2055
2056 struct Menu *menu = shared->adata->menu;
2057 const int index = menu_get_index(menu);
2058 struct AttachPtrArray aa = ARRAY_HEAD_INITIALIZER;
2059 aa_add_selection(&aa, actx, menu, menu->tag_prefix, event->count);
2060 const int num = ARRAY_SIZE(&aa);
2061 struct AttachPtr **app = NULL;
2062 ARRAY_FOREACH(app, &aa)
2063 {
2064 if ((*app)->body->type == TYPE_MULTIPART)
2065 {
2066 ARRAY_FREE(&aa);
2067 mutt_error(_("Can't print multipart attachments"));
2068 return FR_ERROR;
2069 }
2070 }
2071
2073 ARRAY_FREE(&aa);
2074 resolve_attachment(menu, shared->sub, index, num);
2075 /* no send2hook, since this doesn't modify the message */
2076 return FR_SUCCESS;
2077}
2078
2083 const struct KeyEvent *event)
2084{
2085 struct ComposeSharedData *shared = fdata->shared;
2086 if (!check_count(shared->adata->actx))
2087 return FR_ERROR;
2088 char *src = NULL;
2089 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
2090 shared->adata->menu);
2091 if (cur_att->body->d_filename)
2092 src = cur_att->body->d_filename;
2093 else
2094 src = cur_att->body->filename;
2095 struct Buffer *fname = buf_pool_get();
2096 buf_strcpy(fname, mutt_path_basename(NONULL(src)));
2097 struct FileCompletionData cdata = { false, shared->mailbox, NULL, NULL, NULL };
2098 int rc = mw_get_field(_("Send attachment with name: "), fname, MUTT_COMP_NONE,
2099 HC_FILE, &CompleteFileOps, &cdata);
2100 if (rc == 0)
2101 {
2102 // It's valid to set an empty string here, to erase what was set
2103 mutt_str_replace(&cur_att->body->d_filename, buf_string(fname));
2105 }
2106 buf_pool_release(&fname);
2107 return FR_SUCCESS;
2108}
2109
2113static int op_attach_save(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2114{
2115 struct ComposeSharedData *shared = fdata->shared;
2116 struct AttachCtx *actx = shared->adata->actx;
2117 if (!check_count(actx))
2118 return FR_ERROR;
2119
2120 struct Menu *menu = shared->adata->menu;
2121 const int index = menu_get_index(menu);
2122 struct AttachPtrArray aa = ARRAY_HEAD_INITIALIZER;
2123 aa_add_selection(&aa, actx, menu, menu->tag_prefix, event->count);
2124 const int num = ARRAY_SIZE(&aa);
2125 struct AttachPtr **app = NULL;
2126 ARRAY_FOREACH(app, &aa)
2127 {
2128 if ((*app)->body->type == TYPE_MULTIPART)
2129 {
2130 ARRAY_FREE(&aa);
2131 mutt_error(_("Can't save multipart attachments"));
2132 return FR_ERROR;
2133 }
2134 }
2135
2136 mutt_save_attachment_list(&aa, NULL, menu);
2137 ARRAY_FREE(&aa);
2138 resolve_attachment(menu, shared->sub, index, num);
2139 /* no send2hook, since this doesn't modify the message */
2140 return FR_SUCCESS;
2141}
2142
2147 const struct KeyEvent *event)
2148{
2149 struct ComposeSharedData *shared = fdata->shared;
2150 /* toggle the content-disposition between inline/attachment */
2151 struct AttachCtx *actx = shared->adata->actx;
2152 if (!check_count(actx))
2153 return FR_ERROR;
2154
2155 int rc = FR_NO_ACTION;
2156 struct Menu *menu = shared->adata->menu;
2157 const int index = menu_get_index(menu);
2158 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
2159 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
2160 const int num = ARRAY_SIZE(&ba);
2161
2162 struct Body **bp = NULL;
2163 ARRAY_FOREACH(bp, &ba)
2164 {
2165 (*bp)->disposition = ((*bp)->disposition == DISP_INLINE) ? DISP_ATTACH : DISP_INLINE;
2166 rc = FR_SUCCESS;
2167 }
2168
2169 if (rc == FR_SUCCESS)
2170 {
2172 resolve_attachment(menu, shared->sub, index, num);
2173 }
2174
2175 ARRAY_FREE(&ba);
2177 return rc;
2178}
2179
2184 const struct KeyEvent *event)
2185{
2186 struct ComposeSharedData *shared = fdata->shared;
2187 struct AttachCtx *actx = shared->adata->actx;
2188 if (!check_count(actx))
2189 return FR_ERROR;
2190
2191 int rc = FR_NO_ACTION;
2192 struct Menu *menu = shared->adata->menu;
2193 const int index = menu_get_index(menu);
2194 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
2195 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
2196 const int num = ARRAY_SIZE(&ba);
2197
2198 struct Body **bp = NULL;
2199 ARRAY_FOREACH(bp, &ba)
2200 {
2201 if (!mutt_is_text_part(*bp))
2202 continue;
2203
2204 (*bp)->noconv = !(*bp)->noconv;
2205 rc = FR_SUCCESS;
2206 }
2207
2208 if (rc == FR_NO_ACTION)
2209 {
2210 mutt_error(_("Recoding only affects text attachments"));
2211 ARRAY_FREE(&ba);
2212 return FR_ERROR;
2213 }
2214
2215 if (ARRAY_SIZE(&ba) == 1)
2216 {
2217 struct AttachPtr *cur_att = current_attachment(actx, menu);
2218 if (cur_att->body->noconv)
2219 mutt_message(_("The current attachment won't be converted"));
2220 else
2221 mutt_message(_("The current attachment will be converted"));
2222 }
2223 else
2224 {
2226 }
2227
2229 resolve_attachment(menu, shared->sub, index, num);
2230 ARRAY_FREE(&ba);
2231 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2232 return FR_SUCCESS;
2233}
2234
2239 const struct KeyEvent *event)
2240{
2241 struct ComposeSharedData *shared = fdata->shared;
2242 struct AttachCtx *actx = shared->adata->actx;
2243 if (!check_count(actx))
2244 return FR_ERROR;
2245
2246 int rc = FR_NO_ACTION;
2247 struct Menu *menu = shared->adata->menu;
2248 const int index = menu_get_index(menu);
2249 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
2250 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
2251 const int num = ARRAY_SIZE(&ba);
2252
2253 struct Body **bp = NULL;
2254 ARRAY_FOREACH(bp, &ba)
2255 {
2256 (*bp)->unlink = !(*bp)->unlink;
2257 rc = FR_SUCCESS;
2258 }
2259
2260 if (rc == FR_SUCCESS)
2261 {
2263 resolve_attachment(menu, shared->sub, index, num);
2264 }
2265
2266 ARRAY_FREE(&ba);
2267 /* No send2hook since this doesn't change the message. */
2268 return rc;
2269}
2270
2274static int op_attach_ungroup(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2275{
2276 struct ComposeSharedData *shared = fdata->shared;
2277 if (shared->adata->actx->idx[shared->adata->menu->current]->body->type != TYPE_MULTIPART)
2278 {
2279 mutt_error(_("Attachment is not 'multipart'"));
2280 return FR_ERROR;
2281 }
2282
2283 int aidx = shared->adata->menu->current;
2284 struct AttachCtx *actx = shared->adata->actx;
2285 struct Body *b = actx->idx[aidx]->body;
2286 struct Body *b_next = b->next;
2287 struct Body *b_previous = NULL;
2288 struct Body *b_parent = NULL;
2289 int parent_type = actx->idx[aidx]->parent_type;
2290 int level = actx->idx[aidx]->level;
2291
2292 // reorder body pointers
2293 if (attach_body_previous(shared->email->body, b, &b_previous))
2294 b_previous->next = b->parts;
2295 else if (attach_body_parent(shared->email->body, NULL, b, &b_parent))
2296 b_parent->parts = b->parts;
2297 else
2298 shared->email->body = b->parts;
2299
2300 // update attachment list
2301 int i = aidx + 1;
2302 while ((i < actx->idxlen) && (actx->idx[i]->level > level))
2303 {
2304 actx->idx[i]->level--;
2305 if (actx->idx[i]->level == level)
2306 {
2307 actx->idx[i]->parent_type = parent_type;
2308 // set body->next for final attachment in group
2309 if (!actx->idx[i]->body->next)
2310 actx->idx[i]->body->next = b_next;
2311 }
2312 i++;
2313 }
2314
2315 // free memory
2316 actx->idx[aidx]->body->parts = NULL;
2317 actx->idx[aidx]->body->next = NULL;
2318 actx->idx[aidx]->body->email = NULL;
2319 mutt_body_free(&actx->idx[aidx]->body);
2320 FREE(&actx->idx[aidx]->tree);
2321 FREE(&actx->idx[aidx]);
2322
2323 // reorder attachment list
2324 for (int j = aidx; j < (actx->idxlen - 1); j++)
2325 actx->idx[j] = actx->idx[j + 1];
2326 actx->idx[actx->idxlen - 1] = NULL;
2327 actx->idxlen--;
2328 update_menu(actx, shared->adata->menu, false);
2329
2330 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2331 return FR_SUCCESS;
2332}
2333
2338 const struct KeyEvent *event)
2339{
2340 struct ComposeSharedData *shared = fdata->shared;
2341 struct AttachCtx *actx = shared->adata->actx;
2342 if (!check_count(actx))
2343 return FR_ERROR;
2344
2345 int rc = FR_NO_ACTION;
2346 struct Menu *menu = shared->adata->menu;
2347 const int index = menu_get_index(menu);
2348 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
2349 ba_add_selection(&ba, actx, menu, menu->tag_prefix, event->count);
2350 const int num = ARRAY_SIZE(&ba);
2351 if (ARRAY_EMPTY(&ba))
2352 goto done;
2353
2354 struct Body **bp = NULL;
2355 ARRAY_FOREACH(bp, &ba)
2356 {
2357 mutt_update_encoding(*bp, shared->sub);
2358 }
2359
2361 resolve_attachment(menu, shared->sub, index, num);
2363 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2364 rc = FR_SUCCESS;
2365
2366done:
2367 ARRAY_FREE(&ba);
2368 return rc;
2369}
2370
2371// -----------------------------------------------------------------------------
2372
2377 const struct KeyEvent *event)
2378{
2379 struct ComposeSharedData *shared = fdata->shared;
2381 const char *tag = NULL;
2382 char *err = NULL;
2383 mutt_env_to_local(shared->email->env);
2384 const char *const c_editor = cs_subset_string(shared->sub, "editor");
2385 if (shared->email->body->type == TYPE_MULTIPART)
2386 {
2387 struct Body *b = shared->email->body->parts;
2388 while (b && b->parts)
2389 b = b->parts;
2390 if (b)
2391 mutt_edit_headers(NONULL(c_editor), b->filename, shared->email, shared->fcc);
2392 }
2393 else
2394 {
2395 mutt_edit_headers(NONULL(c_editor), shared->email->body->filename,
2396 shared->email, shared->fcc);
2397 }
2398
2399 if (mutt_env_to_intl(shared->email->env, &tag, &err))
2400 {
2401 mutt_error(_("Bad IDN in '%s': '%s'"), tag, err);
2402 FREE(&err);
2403 }
2405
2407 mutt_update_encoding(shared->email->body, shared->sub);
2408
2409 /* attachments may have been added */
2410 if (shared->adata->actx->idxlen &&
2411 shared->adata->actx->idx[shared->adata->actx->idxlen - 1]->body->next)
2412 {
2414 update_menu(shared->adata->actx, shared->adata->menu, true);
2415 }
2416
2418 /* Unconditional hook since editor was invoked */
2419 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2420 return FR_SUCCESS;
2421}
2422
2426static int op_compose_edit_file(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2427{
2428 struct ComposeSharedData *shared = fdata->shared;
2429 if (!check_count(shared->adata->actx))
2430 return FR_ERROR;
2431 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
2432 shared->adata->menu);
2433 if (cur_att->body->type == TYPE_MULTIPART)
2434 {
2435 mutt_error(_("Can't edit multipart attachments"));
2436 return FR_ERROR;
2437 }
2438 const char *const c_editor = cs_subset_string(shared->sub, "editor");
2439 mutt_edit_file(NONULL(c_editor), cur_att->body->filename);
2440 mutt_update_encoding(cur_att->body, shared->sub);
2443 /* Unconditional hook since editor was invoked */
2444 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2445 return FR_SUCCESS;
2446}
2447
2452 const struct KeyEvent *event)
2453{
2454 struct ComposeSharedData *shared = fdata->shared;
2455 const bool c_edit_headers = cs_subset_bool(shared->sub, "edit_headers");
2456 if (!c_edit_headers)
2457 {
2459 const char *const c_editor = cs_subset_string(shared->sub, "editor");
2460 mutt_edit_file(c_editor, shared->email->body->filename);
2462 mutt_update_encoding(shared->email->body, shared->sub);
2464 /* Unconditional hook since editor was invoked */
2465 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2466 return FR_SUCCESS;
2467 }
2468
2469 return op_envelope_edit_headers(fdata, event);
2470}
2471
2475static int op_compose_ispell(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2476{
2477 struct ComposeSharedData *shared = fdata->shared;
2478 endwin();
2479 const char *const c_ispell = cs_subset_string(shared->sub, "ispell");
2480 struct Buffer *cmd = buf_pool_get();
2481 struct Buffer *quoted = buf_pool_get();
2482 buf_quote_filename(quoted, shared->email->body->filename, true);
2483 buf_printf(cmd, "%s -x %s", NONULL(c_ispell), buf_string(quoted));
2484 buf_pool_release(&quoted);
2485 if (mutt_system(buf_string(cmd)) == -1)
2486 {
2487 mutt_error(_("Error running \"%s\""), buf_string(cmd));
2488 buf_pool_release(&cmd);
2489 return FR_ERROR;
2490 }
2491 buf_pool_release(&cmd);
2492
2493 mutt_update_encoding(shared->email->body, shared->sub);
2495 return FR_SUCCESS;
2496}
2497
2502 const struct KeyEvent *event)
2503{
2504 struct ComposeSharedData *shared = fdata->shared;
2505 if (check_attachments(shared->adata->actx, shared->sub) != 0)
2506 {
2508 return FR_ERROR;
2509 }
2510
2511 shared->rc = 1;
2512 return FR_DONE;
2513}
2514
2519 const struct KeyEvent *event)
2520{
2521 struct ComposeSharedData *shared = fdata->shared;
2522 if (!check_count(shared->adata->actx))
2523 return FR_ERROR;
2524 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
2525 shared->adata->menu);
2526 if (cur_att->body->type == TYPE_MULTIPART)
2527 {
2528 mutt_error(_("Can't rename multipart attachments"));
2529 return FR_ERROR;
2530 }
2531 struct Buffer *fname = buf_pool_get();
2532 buf_strcpy(fname, cur_att->body->filename);
2533 pretty_mailbox(fname);
2534 struct FileCompletionData cdata = { false, shared->mailbox, NULL, NULL, NULL };
2535 if ((mw_get_field(_("Rename to: "), fname, MUTT_COMP_NONE, HC_FILE,
2536 &CompleteFileOps, &cdata) == 0) &&
2537 !buf_is_empty(fname))
2538 {
2539 struct stat st = { 0 };
2540 if (stat(cur_att->body->filename, &st) == -1)
2541 {
2542 /* L10N: "stat" is a system call. Do "man 2 stat" for more information. */
2543 mutt_error(_("Can't stat %s: %s"), buf_string(fname), strerror(errno));
2544 buf_pool_release(&fname);
2545 return FR_ERROR;
2546 }
2547
2548 expand_path(fname, false);
2549 if (mutt_file_rename(cur_att->body->filename, buf_string(fname)))
2550 {
2551 buf_pool_release(&fname);
2552 return FR_ERROR;
2553 }
2554
2555 mutt_str_replace(&cur_att->body->filename, buf_string(fname));
2557
2558 if (cur_att->body->stamp >= st.st_mtime)
2559 mutt_stamp_attachment(cur_att->body);
2560 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2561 }
2562 buf_pool_release(&fname);
2563 return FR_SUCCESS;
2564}
2565
2570 const struct KeyEvent *event)
2571{
2572 struct ComposeSharedData *shared = fdata->shared;
2573 /* Note: We don't invoke send2-hook here, since we want to leave
2574 * users an opportunity to change settings from the ":" prompt. */
2575 if (check_attachments(shared->adata->actx, shared->sub) != 0)
2576 {
2578 return FR_NO_ACTION;
2579 }
2580
2581 if (!shared->fcc_set && !buf_is_empty(shared->fcc))
2582 {
2583 enum QuadOption ans = query_quadoption(_("Save a copy of this message?"),
2584 shared->sub, "copy");
2585 if (ans == MUTT_ABORT)
2586 return FR_NO_ACTION;
2587 else if (ans == MUTT_NO)
2588 buf_reset(shared->fcc);
2589 }
2590
2591 shared->rc = 0;
2592 return FR_DONE;
2593}
2594
2599 const struct KeyEvent *event)
2600{
2601 struct ComposeSharedData *shared = fdata->shared;
2602 int rc = FR_NO_ACTION;
2603 struct Buffer *fname = buf_pool_get();
2604 if (shared->mailbox)
2605 {
2606 buf_strcpy(fname, mailbox_path(shared->mailbox));
2607 pretty_mailbox(fname);
2608 }
2609 if (shared->adata->actx->idxlen)
2610 shared->email->body = shared->adata->actx->idx[0]->body;
2611 if ((mw_enter_fname(_("Write message to mailbox"), fname, true,
2612 shared->mailbox, false, NULL, NULL, MUTT_SEL_NONE) != -1) &&
2613 !buf_is_empty(fname))
2614 {
2615 mutt_message(_("Writing message to %s ..."), buf_string(fname));
2616 expand_path(fname, false);
2617
2618 if (shared->email->body->next)
2619 shared->email->body = mutt_make_multipart(shared->email->body);
2620
2621 if (mutt_write_fcc(buf_string(fname), shared->email, NULL, false, NULL,
2622 NULL, shared->sub) == 0)
2623 mutt_message(_("Message written"));
2624
2625 shared->email->body = mutt_remove_multipart(shared->email->body);
2626 rc = FR_SUCCESS;
2627 }
2628 buf_pool_release(&fname);
2629 return rc;
2630}
2631
2642static int op_display_headers(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2643{
2644 struct ComposeSharedData *shared = fdata->shared;
2645 if (!check_count(shared->adata->actx))
2646 return FR_ERROR;
2647
2648 enum ViewAttachMode mode = MUTT_VA_REGULAR;
2649
2650 const int op = event->op;
2651 switch (op)
2652 {
2653 case OP_ATTACH_VIEW:
2654 case OP_DISPLAY_HEADERS:
2655 break;
2656
2657 case OP_ATTACH_VIEW_MAILCAP:
2658 mode = MUTT_VA_MAILCAP;
2659 break;
2660
2661 case OP_ATTACH_VIEW_PAGER:
2662 mode = MUTT_VA_PAGER;
2663 break;
2664
2665 case OP_ATTACH_VIEW_TEXT:
2666 mode = MUTT_VA_AS_TEXT;
2667 break;
2668 }
2669
2670 if (mode == MUTT_VA_REGULAR)
2671 {
2672 mutt_attach_display_loop(shared->sub, shared->adata->menu, op,
2673 shared->email, shared->adata->actx, false);
2674 }
2675 else
2676 {
2677 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
2678 shared->adata->menu);
2679 mutt_view_attachment(NULL, cur_att->body, mode, shared->email,
2680 shared->adata->actx, shared->adata->menu->win);
2681 }
2682
2684 /* no send2hook, since this doesn't modify the message */
2685 return FR_SUCCESS;
2686}
2687
2691static int op_quit(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2692{
2693 struct ComposeSharedData *shared = fdata->shared;
2694 enum QuadOption ans = query_quadoption(_("Save (postpone) draft message?"),
2695 shared->sub, "postpone");
2696 if (ans == MUTT_NO)
2697 {
2698 for (int i = 0; i < shared->adata->actx->idxlen; i++)
2699 if (shared->adata->actx->idx[i]->unowned)
2700 shared->adata->actx->idx[i]->body->unlink = false;
2701
2702 if (!(shared->flags & MUTT_COMPOSE_NOFREEHEADER))
2703 {
2704 for (int i = 0; i < shared->adata->actx->idxlen; i++)
2705 {
2706 /* avoid freeing other attachments */
2707 shared->adata->actx->idx[i]->body->next = NULL;
2708 if (!shared->adata->actx->idx[i]->body->email)
2709 shared->adata->actx->idx[i]->body->parts = NULL;
2710 mutt_body_free(&shared->adata->actx->idx[i]->body);
2711 }
2712 }
2713 shared->rc = -1;
2714 return FR_DONE;
2715 }
2716 else if (ans == MUTT_ABORT)
2717 {
2718 return FR_NO_ACTION;
2719 }
2720
2721 return op_compose_postpone_message(fdata, event);
2722}
2723
2724// -----------------------------------------------------------------------------
2725
2729static const struct ComposeFunction ComposeFunctions[] = {
2730 // clang-format off
2731 { OP_ATTACH_ATTACH_FILE, op_attach_attach_file },
2732 { OP_ATTACH_ATTACH_KEY, op_attach_attach_key },
2733 { OP_ATTACH_ATTACH_MESSAGE, op_attach_attach_message },
2734 { OP_ATTACH_ATTACH_NEWS_MESSAGE, op_attach_attach_message },
2735 { OP_ATTACH_DETACH, op_attach_detach },
2736 { OP_ATTACH_EDIT_CONTENT_ID, op_attach_edit_content_id },
2737 { OP_ATTACH_EDIT_DESCRIPTION, op_attach_edit_description },
2738 { OP_ATTACH_EDIT_ENCODING, op_attach_edit_encoding },
2739 { OP_ATTACH_EDIT_LANGUAGE, op_attach_edit_language },
2740 { OP_ATTACH_EDIT_MIME, op_attach_edit_mime },
2741 { OP_ATTACH_EDIT_TYPE, op_attach_edit_type },
2742 { OP_ATTACH_FILTER, op_attach_filter },
2743 { OP_ATTACH_GET_ATTACHMENT, op_attach_get_attachment },
2744 { OP_ATTACH_GROUP_ALTS, op_attach_group_alts },
2745 { OP_ATTACH_GROUP_LINGUAL, op_attach_group_lingual },
2746 { OP_ATTACH_GROUP_RELATED, op_attach_group_related },
2747 { OP_ATTACH_MOVE_DOWN, op_attach_move_down },
2748 { OP_ATTACH_MOVE_UP, op_attach_move_up },
2749 { OP_ATTACH_NEW_MIME, op_attach_new_mime },
2750 { OP_ATTACH_PRINT, op_attach_print },
2751 { OP_ATTACH_RENAME_ATTACHMENT, op_attach_rename_attachment },
2752 { OP_ATTACH_SAVE, op_attach_save },
2753 { OP_ATTACH_TOGGLE_DISPOSITION, op_attach_toggle_disposition },
2754 { OP_ATTACH_TOGGLE_RECODE, op_attach_toggle_recode },
2755 { OP_ATTACH_TOGGLE_UNLINK, op_attach_toggle_unlink },
2756 { OP_ATTACH_UNGROUP, op_attach_ungroup },
2757 { OP_ATTACH_UPDATE_ENCODING, op_attach_update_encoding },
2758 { OP_ATTACH_VIEW, op_display_headers },
2759 { OP_ATTACH_VIEW_MAILCAP, op_display_headers },
2760 { OP_ATTACH_VIEW_PAGER, op_display_headers },
2761 { OP_ATTACH_VIEW_TEXT, op_display_headers },
2762 { OP_COMPOSE_EDIT_FILE, op_compose_edit_file },
2763 { OP_COMPOSE_EDIT_MESSAGE, op_compose_edit_message },
2764 { OP_COMPOSE_ISPELL, op_compose_ispell },
2765 { OP_COMPOSE_POSTPONE_MESSAGE, op_compose_postpone_message },
2766 { OP_COMPOSE_RENAME_FILE, op_compose_rename_file },
2767 { OP_COMPOSE_SEND_MESSAGE, op_compose_send_message },
2768 { OP_COMPOSE_WRITE_MESSAGE, op_compose_write_message },
2769 { OP_DISPLAY_HEADERS, op_display_headers },
2770 { OP_ENVELOPE_EDIT_HEADERS, op_envelope_edit_headers },
2771 { OP_EXIT, op_quit },
2772 { OP_PIPE, op_attach_filter },
2773 { OP_QUIT, op_quit },
2774 { 0, NULL },
2775 // clang-format on
2776};
2777
2781int compose_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
2782{
2783 // The Dispatcher may be called on any Window in the Dialog
2784 struct MuttWindow *dlg = dialog_find(win);
2785 if (!event || !dlg || !dlg->wdata)
2786 {
2788 return FR_ERROR;
2789 }
2790
2791 const int op = event->op;
2792
2794
2795 struct ComposeFunctionData fdata = {
2796 .n = NeoMutt,
2797 .mod_data = mod_data,
2798 .shared = dlg->wdata,
2799 };
2800
2801 int rc = FR_UNKNOWN;
2802 for (size_t i = 0; ComposeFunctions[i].op != OP_NULL; i++)
2803 {
2804 const struct ComposeFunction *fn = &ComposeFunctions[i];
2805 if (fn->op == op)
2806 {
2807 rc = fn->function(&fdata, event);
2808 break;
2809 }
2810 }
2811
2812 if (rc == FR_UNKNOWN) // Not our function
2813 return rc;
2814
2815 const char *result = dispatcher_get_retval_name(rc);
2816 mutt_debug(LL_DEBUG1, "Handled %s (%d) -> %s\n", opcodes_get_name(op), op, NONULL(result));
2817
2819 return rc;
2820}
#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:168
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:89
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:298
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:401
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:234
void compose_init_keys(struct NeoMutt *n, struct SubMenu *sm_generic)
Initialise the Compose Keybindings - Implements ::init_keys_api.
Definition functions.c:212
static int body_index(struct AttachCtx *actx, const struct Body *b)
Find the index of an attachment body.
Definition functions.c:557
static const struct ComposeFunction ComposeFunctions[]
All the NeoMutt functions that the Compose supports.
Definition functions.c:2729
static int group_attachments(struct ComposeSharedData *shared, char *subtype, struct BodyArray *ba)
Group selected attachments into a multipart group.
Definition functions.c:802
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:578
static void update_idx(struct Menu *menu, struct AttachCtx *actx, struct AttachPtr *ap)
Add a new attachment to the message.
Definition functions.c:466
static char * gen_cid(void)
Generate a random Content ID.
Definition functions.c:251
static MenuRedrawFlags selection_redraw_flags(size_t count)
Choose redraw flags for a selection.
Definition functions.c:675
static void resolve_attachment(struct Menu *menu, const struct ConfigSubset *sub, int index, int num)
Advance the Attachment List selection after an action.
Definition functions.c:692
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:491
static bool body_array_contains(struct BodyArray *ba, const struct Body *b)
Does a selection contain a body?
Definition functions.c:620
static int next_sibling(struct AttachCtx *actx, int index)
Find the next sibling of an attachment.
Definition functions.c:598
static int delete_attachment(struct AttachCtx *actx, int aidx, struct ConfigSubset *sub)
Delete an attachment.
Definition functions.c:377
static bool selected_attachments_are_siblings(struct Email *e, struct BodyArray *ba)
Check whether selected attachments are siblings.
Definition functions.c:638
static bool check_cid(const char *cid)
Check if a Content-ID is valid.
Definition functions.c:267
static int check_attachments(struct AttachCtx *actx, struct ConfigSubset *sub)
Check if any attachments have changed or been deleted.
Definition functions.c:312
static int move_attachment(struct ComposeSharedData *shared, bool up, int count)
Move attachments in the attachment list.
Definition functions.c:714
static const struct MenuOpSeq ComposeDefaultBindings[]
Key bindings for the Compose Menu.
Definition functions.c:150
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:287
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
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:117
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:1258
bool OptNews
(pseudo) used to change reader mode
Definition globals.c:53
Global variables.
static int op_quit(struct AliasFunctionData *fdata, const struct KeyEvent *event)
Save changes and exit this dialog - Implements alias_function_t -.
Definition functions.c:308
static int op_attach_edit_type(struct AttachFunctionData *fdata, const struct KeyEvent *event)
edit attachment content type - Implements attach_function_t -
Definition functions.c:391
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:428
static int op_attach_print(struct AttachFunctionData *fdata, const struct KeyEvent *event)
print the current entry - Implements attach_function_t -
Definition functions.c:415
static int op_attach_edit_description(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Edit attachment description - Implements compose_function_t -.
Definition functions.c:1449
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:2501
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:1361
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:1791
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:2598
static int op_compose_edit_message(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Edit the message - Implements compose_function_t -.
Definition functions.c:2451
static int op_attach_ungroup(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Ungroup a 'multipart' attachment - Implements compose_function_t -.
Definition functions.c:2274
static int op_compose_ispell(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Run ispell on the message - Implements compose_function_t -.
Definition functions.c:2475
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:2337
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:1724
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:1832
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:2518
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:1927
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:1685
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:1563
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:2146
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:2082
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:1867
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:1770
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:1089
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:2183
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:2376
static int op_compose_send_message(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Send the message - Implements compose_function_t -.
Definition functions.c:2569
static int op_attach_edit_encoding(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Edit attachment transfer-encoding - Implements compose_function_t -.
Definition functions.c:1500
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:2238
static int op_attach_detach(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Delete the current entry - Implements compose_function_t -.
Definition functions.c:1271
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:2642
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:1118
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:2426
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:1619
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:1966
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:1023
int compose_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
Perform a Compose function - Implements function_dispatcher_t -.
Definition functions.c:2781
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:237
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:2797
enum MailboxType pop_path_probe(const char *path, const struct stat *st)
Is this a POP Mailbox?
Definition pop.c:1177
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox?
Definition imap.c:2683
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:123
struct SubMenu * km_register_submenu(const struct MenuFuncOp functions[])
Register a submenu.
Definition init.c:88
struct MenuDefinition * km_register_menu(int menu, const char *name)
Register a menu.
Definition init.c:105
void km_menu_add_bindings(struct MenuDefinition *md, const struct MenuOpSeq bindings[])
Add Keybindings to a Menu.
Definition init.c:136
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:177
int menu_get_index(struct Menu *menu)
Get the current selection in the Menu.
Definition menu.c:153
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:167
@ 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:83
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:618
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:666
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:425
void mutt_pipe_attachment_list(struct AttachPtrArray *aa, bool filter)
Pipe selected attachments to a command.
Definition recvattach.c:699
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:919
void mutt_print_attachment_list(struct AttachPtrArray *aa)
Print selected attachments.
Definition recvattach.c:848
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:363
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:1326
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition mx.c:1650
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:666
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:559
struct NntpAccountData * nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
Open a connection to an NNTP server.
Definition newsrc.c:957
@ 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:118
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:505
void mutt_rfc3676_space_stuff(struct Email *e)
Perform RFC3676 space stuffing on an Email.
Definition rfc3676.c:492
Convenience wrapper for the send headers.
void mutt_update_encoding(struct Body *b, struct ConfigSubset *sub)
Update the encoding type.
Definition sendlib.c:424
struct Body * mutt_make_file_attach(const char *path, struct ConfigSubset *sub)
Create a file attachment.
Definition sendlib.c:617
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:456
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:1028
void mutt_stamp_attachment(struct Body *b)
Timestamp an Attachment.
Definition sendlib.c:412
#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 num
Attachment index number.
Definition attach.h:42
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 menudef.h:44
Mapping between a function and an operation.
Definition menu.h:37
Mapping between an operation and a key sequence.
Definition menu.h:47
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 menudef.h:33
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