NeoMutt  2025-12-11-1039-g550ac6
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 "expando/lib.h"
51#include "history/lib.h"
52#include "hooks/lib.h"
53#include "imap/lib.h"
54#include "index/lib.h"
55#include "key/lib.h"
56#include "menu/lib.h"
57#include "ncrypt/lib.h"
58#include "nntp/lib.h"
59#include "pop/lib.h"
60#include "question/lib.h"
61#include "send/lib.h"
62#include "attach_data.h"
63#include "external.h"
64#include "globals.h"
65#include "module_data.h"
66#include "mutt_logging.h"
67#include "muttlib.h"
68#include "mx.h"
69#include "nntp/adata.h" // IWYU pragma: keep
70#include "shared_data.h"
71#ifdef ENABLE_NLS
72#include <libintl.h>
73#endif
74
75// clang-format off
79static const struct MenuFuncOp OpCompose[] = { /* map: compose */
80 { "attach-file", OP_ATTACH_ATTACH_FILE },
81 { "attach-key", OP_ATTACH_ATTACH_KEY },
82 { "attach-message", OP_ATTACH_ATTACH_MESSAGE },
83 { "attach-news-message", OP_ATTACH_ATTACH_NEWS_MESSAGE },
84#ifdef USE_AUTOCRYPT
85 { "autocrypt-menu", OP_COMPOSE_AUTOCRYPT_MENU },
86#endif
87 { "copy-file", OP_ATTACH_SAVE },
88 { "detach-file", OP_ATTACH_DETACH },
89 { "display-toggle-weed", OP_DISPLAY_HEADERS },
90 { "edit-bcc", OP_ENVELOPE_EDIT_BCC },
91 { "edit-cc", OP_ENVELOPE_EDIT_CC },
92 { "edit-content-id", OP_ATTACH_EDIT_CONTENT_ID },
93 { "edit-description", OP_ATTACH_EDIT_DESCRIPTION },
94 { "edit-encoding", OP_ATTACH_EDIT_ENCODING },
95 { "edit-fcc", OP_ENVELOPE_EDIT_FCC },
96 { "edit-file", OP_COMPOSE_EDIT_FILE },
97 { "edit-followup-to", OP_ENVELOPE_EDIT_FOLLOWUP_TO },
98 { "edit-from", OP_ENVELOPE_EDIT_FROM },
99 { "edit-headers", OP_ENVELOPE_EDIT_HEADERS },
100 { "edit-language", OP_ATTACH_EDIT_LANGUAGE },
101 { "edit-message", OP_COMPOSE_EDIT_MESSAGE },
102 { "edit-mime", OP_ATTACH_EDIT_MIME },
103 { "edit-newsgroups", OP_ENVELOPE_EDIT_NEWSGROUPS },
104 { "edit-reply-to", OP_ENVELOPE_EDIT_REPLY_TO },
105 { "edit-subject", OP_ENVELOPE_EDIT_SUBJECT },
106 { "edit-to", OP_ENVELOPE_EDIT_TO },
107 { "edit-type", OP_ATTACH_EDIT_TYPE },
108 { "edit-x-comment-to", OP_ENVELOPE_EDIT_X_COMMENT_TO },
109 { "filter-entry", OP_ATTACH_FILTER },
110 { "get-attachment", OP_ATTACH_GET_ATTACHMENT },
111 { "group-alternatives", OP_ATTACH_GROUP_ALTS },
112 { "group-multilingual", OP_ATTACH_GROUP_LINGUAL },
113 { "group-related", OP_ATTACH_GROUP_RELATED },
114 { "ispell", OP_COMPOSE_CHECK_SPELLING },
115 { "move-down", OP_ATTACH_MOVE_DOWN },
116 { "move-up", OP_ATTACH_MOVE_UP },
117 { "new-mime", OP_ATTACH_NEW_MIME },
118 { "pgp-menu", OP_COMPOSE_PGP_MENU },
119 { "pipe-entry", OP_PIPE },
120 { "pipe-message", OP_PIPE },
121 { "postpone-message", OP_COMPOSE_POSTPONE_MESSAGE },
122 { "preview-scroll-half-down", OP_PREVIEW_SCROLL_HALF_DOWN },
123 { "preview-scroll-half-up", OP_PREVIEW_SCROLL_HALF_UP },
124 { "preview-scroll-line-down", OP_PREVIEW_SCROLL_LINE_DOWN },
125 { "preview-scroll-line-up", OP_PREVIEW_SCROLL_LINE_UP },
126 { "preview-scroll-page-down", OP_PREVIEW_SCROLL_PAGE_DOWN },
127 { "preview-scroll-page-up", OP_PREVIEW_SCROLL_PAGE_UP },
128 { "preview-select-first-entry", OP_PREVIEW_SELECT_FIRST_ENTRY },
129 { "preview-select-last-entry", OP_PREVIEW_SELECT_LAST_ENTRY },
130 { "print-entry", OP_ATTACH_PRINT },
131 { "rename-attachment", OP_ATTACH_RENAME_ATTACHMENT },
132 { "rename-file", OP_COMPOSE_RENAME_FILE },
133 { "send-message", OP_COMPOSE_SEND_MESSAGE },
134 { "smime-menu", OP_COMPOSE_SMIME_MENU },
135 { "toggle-disposition", OP_ATTACH_TOGGLE_DISPOSITION },
136 { "toggle-recode", OP_ATTACH_TOGGLE_RECODE },
137 { "toggle-unlink", OP_ATTACH_TOGGLE_UNLINK },
138 { "ungroup-attachment", OP_ATTACH_UNGROUP },
139 { "update-encoding", OP_ATTACH_UPDATE_ENCODING },
140 { "view-attach", OP_ATTACH_VIEW },
141 { "view-mailcap", OP_ATTACH_VIEW_MAILCAP },
142 { "view-pager", OP_ATTACH_VIEW_PAGER },
143 { "view-text", OP_ATTACH_VIEW_TEXT },
144 { "write-fcc", OP_COMPOSE_WRITE_MESSAGE },
145 { NULL, 0 },
146};
147
151static const struct MenuOpSeq ComposeDefaultBindings[] = { /* map: compose */
152 { OP_ATTACH_ATTACH_FILE, "a" },
153 { OP_ATTACH_ATTACH_KEY, "\033k" }, // <Alt-k>
154 { OP_ATTACH_ATTACH_MESSAGE, "A" },
155 { OP_ATTACH_DETACH, "D" },
156 { OP_ATTACH_EDIT_CONTENT_ID, "\033i" }, // <Alt-i>
157 { OP_ATTACH_EDIT_DESCRIPTION, "d" },
158 { OP_ATTACH_EDIT_ENCODING, "\005" }, // <Ctrl-E>
159 { OP_ATTACH_EDIT_LANGUAGE, "\014" }, // <Ctrl-L>
160 { OP_ATTACH_EDIT_MIME, "m" },
161 { OP_ATTACH_EDIT_TYPE, "\024" }, // <Ctrl-T>
162 { OP_ATTACH_FILTER, "F" },
163 { OP_ATTACH_GET_ATTACHMENT, "G" },
164 { OP_ATTACH_GROUP_ALTS, "&" },
165 { OP_ATTACH_GROUP_LINGUAL, "^" },
166 { OP_ATTACH_GROUP_RELATED, "%" },
167 { OP_ATTACH_MOVE_DOWN, "+" },
168 { OP_ATTACH_MOVE_UP, "-" },
169 { OP_ATTACH_NEW_MIME, "n" },
170 { OP_PIPE, "|" },
171 { OP_ATTACH_PRINT, "l" },
172 { OP_ATTACH_RENAME_ATTACHMENT, "\017" }, // <Ctrl-O>
173 { OP_ATTACH_SAVE, "C" },
174 { OP_ATTACH_TOGGLE_DISPOSITION, "\004" }, // <Ctrl-D>
175 { OP_ATTACH_TOGGLE_UNLINK, "u" },
176 { OP_ATTACH_UNGROUP, "#" },
177 { OP_ATTACH_UPDATE_ENCODING, "U" },
178 { OP_ATTACH_VIEW, "<keypadenter>" },
179 { OP_ATTACH_VIEW, "\n" }, // <Enter>
180 { OP_ATTACH_VIEW, "\r" }, // <Return>
181#ifdef USE_AUTOCRYPT
182 { OP_COMPOSE_AUTOCRYPT_MENU, "o" },
183#endif
184 { OP_COMPOSE_EDIT_FILE, "\033e" }, // <Alt-e>
185 { OP_COMPOSE_EDIT_MESSAGE, "e" },
186 { OP_COMPOSE_CHECK_SPELLING, "i" },
187 { OP_COMPOSE_PGP_MENU, "p" },
188 { OP_COMPOSE_POSTPONE_MESSAGE, "P" },
189 { OP_COMPOSE_RENAME_FILE, "R" },
190 { OP_COMPOSE_SEND_MESSAGE, "y" },
191 { OP_COMPOSE_SMIME_MENU, "S" },
192 { OP_COMPOSE_WRITE_MESSAGE, "w" },
193 { OP_DISPLAY_HEADERS, "h" },
194 { OP_ENVELOPE_EDIT_BCC, "b" },
195 { OP_ENVELOPE_EDIT_CC, "c" },
196 { OP_ENVELOPE_EDIT_FCC, "f" },
197 { OP_ENVELOPE_EDIT_FROM, "\033f" }, // <Alt-f>
198 { OP_ENVELOPE_EDIT_HEADERS, "E" },
199 { OP_ENVELOPE_EDIT_REPLY_TO, "r" },
200 { OP_ENVELOPE_EDIT_SUBJECT, "s" },
201 { OP_ENVELOPE_EDIT_TO, "t" },
202 { OP_PREVIEW_SCROLL_PAGE_DOWN, "<pagedown>" },
203 { OP_PREVIEW_SCROLL_PAGE_UP, "<pageup>" },
204 { OP_FORGET_PASSPHRASE, "\006" }, // <Ctrl-F>
205 { OP_TAG, "T" },
206 { 0, NULL },
207};
208// clang-format on
209
213void compose_init_keys(struct NeoMutt *n, struct SubMenu *sm_generic)
214{
216 ASSERT(mod_data);
217
218 struct MenuDefinition *md = NULL;
219 struct SubMenu *sm = NULL;
220
222 md = km_register_menu(MENU_COMPOSE, "compose");
223 km_menu_add_submenu(md, sm);
224 km_menu_add_submenu(md, sm_generic);
226
227 mod_data->md_compose = md;
228}
229
235static bool check_count(struct AttachCtx *actx)
236{
237 if (actx->idxlen == 0)
238 {
239 mutt_error(_("There are no attachments"));
240 return false;
241 }
242
243 return true;
244}
245
252static char *gen_cid(void)
253{
254 char rndid[MUTT_RANDTAG_LEN + 1] = { 0 };
255
256 mutt_rand_base32(rndid, sizeof(rndid) - 1);
257 rndid[MUTT_RANDTAG_LEN] = 0;
258
259 return mutt_str_dup(rndid);
260}
261
268static bool check_cid(const char *cid)
269{
270 static const char *check = "^[-\\.0-9@A-Z_a-z]+$";
271
272 struct Regex *check_cid_regex = mutt_regex_new(check, 0, NULL);
273
274 const bool valid = mutt_regex_match(check_cid_regex, cid);
275
276 mutt_regex_free(&check_cid_regex);
277
278 return valid;
279}
280
288static bool content_id_exists(struct AttachCtx *actx, const struct Body *skip, const char *cid)
289{
290 if (!actx || !cid)
291 return false;
292
293 for (int i = 0; i < actx->idxlen; i++)
294 {
295 struct Body *b = actx->idx[i]->body;
296 if ((b == skip) || !b->content_id)
297 continue;
298
299 if (mutt_str_equal(b->content_id, cid))
300 return true;
301 }
302
303 return false;
304}
305
313static int check_attachments(struct AttachCtx *actx, struct ConfigSubset *sub)
314{
315 int rc = -1;
316 struct stat st = { 0 };
317 struct Buffer *pretty = NULL;
318 struct Buffer *msg = NULL;
319
320 for (int i = 0; i < actx->idxlen; i++)
321 {
322 if (actx->idx[i]->body->type == TYPE_MULTIPART)
323 continue;
324 if (stat(actx->idx[i]->body->filename, &st) != 0)
325 {
326 if (!pretty)
327 pretty = buf_pool_get();
328 buf_strcpy(pretty, actx->idx[i]->body->filename);
329 pretty_mailbox(pretty);
330 /* L10N: This message is displayed in the compose menu when an attachment
331 doesn't stat. %d is the attachment number and %s is the attachment
332 filename. The filename is located last to avoid a long path hiding
333 the error message. */
334 mutt_error(_("Attachment #%d no longer exists: %s"), i + 1, buf_string(pretty));
335 goto cleanup;
336 }
337
338 if (actx->idx[i]->body->stamp < st.st_mtime)
339 {
340 if (!pretty)
341 pretty = buf_pool_get();
342 buf_strcpy(pretty, actx->idx[i]->body->filename);
343 pretty_mailbox(pretty);
344
345 if (!msg)
346 msg = buf_pool_get();
347 /* L10N: This message is displayed in the compose menu when an attachment
348 is modified behind the scenes. %d is the attachment number and %s is
349 the attachment filename. The filename is located last to avoid a long
350 path hiding the prompt question. */
351 buf_printf(msg, _("Attachment #%d modified. Update encoding for %s?"),
352 i + 1, buf_string(pretty));
353
355 if (ans == MUTT_YES)
356 mutt_update_encoding(actx->idx[i]->body, sub);
357 else if (ans == MUTT_ABORT)
358 goto cleanup;
359 }
360 }
361
362 rc = 0;
363
364cleanup:
365 buf_pool_release(&pretty);
366 buf_pool_release(&msg);
367 return rc;
368}
369
378static int delete_attachment(struct AttachCtx *actx, int aidx, struct ConfigSubset *sub)
379{
380 if (!actx || (aidx < 0) || (aidx >= actx->idxlen))
381 return -1;
382
383 struct AttachPtr **idx = actx->idx;
384 struct Body *b_previous = NULL;
385 struct Body *b_parent = NULL;
386
387 if (aidx == 0)
388 {
389 struct Body *b = actx->idx[0]->body;
390 if (!b->next) // There's only one attachment left
391 {
392 mutt_error(_("You may not delete the only attachment"));
393 return -1;
394 }
395
396 if (cs_subset_bool(sub, "compose_confirm_detach_first"))
397 {
398 /* L10N: Prompt when trying to hit <detach-file> on the first entry in
399 the compose menu. This entry is most likely the message they just
400 typed. Hitting yes will remove the entry and unlink the file, so
401 it's worth confirming they really meant to do it. */
402 enum QuadOption ans = query_yesorno_help(_("Really delete the main message?"), MUTT_NO,
403 sub, "compose_confirm_detach_first");
404 if (ans == MUTT_NO)
405 {
406 idx[aidx]->body->tagged = false;
407 return -1;
408 }
409 }
410 }
411
412 if (idx[aidx]->level > 0)
413 {
414 if (attach_body_parent(idx[0]->body, NULL, idx[aidx]->body, &b_parent))
415 {
416 if (attach_body_count(b_parent->parts, false) < 3)
417 {
418 mutt_error(_("Can't leave group with only one attachment"));
419 return -1;
420 }
421 }
422 }
423
424 // reorder body pointers
425 if (aidx > 0)
426 {
427 if (attach_body_previous(idx[0]->body, idx[aidx]->body, &b_previous))
428 b_previous->next = idx[aidx]->body->next;
429 else if (attach_body_parent(idx[0]->body, NULL, idx[aidx]->body, &b_parent))
430 b_parent->parts = idx[aidx]->body->next;
431 }
432
433 // free memory
434 int part_count = 1;
435 if (aidx < (actx->idxlen - 1))
436 {
437 if ((idx[aidx]->body->type == TYPE_MULTIPART) &&
438 (idx[aidx + 1]->level > idx[aidx]->level))
439 {
440 part_count += attach_body_count(idx[aidx]->body->parts, true);
441 }
442 }
443 idx[aidx]->body->next = NULL;
444 mutt_body_free(&(idx[aidx]->body));
445 for (int i = 0; i < part_count; i++)
446 {
447 FREE(&idx[aidx + i]->tree);
448 FREE(&idx[aidx + i]);
449 }
450
451 // reorder attachment list
452 for (int i = aidx; i < (actx->idxlen - part_count); i++)
453 idx[i] = idx[i + part_count];
454 for (int i = 0; i < part_count; i++)
455 idx[actx->idxlen - i - 1] = NULL;
456 actx->idxlen -= part_count;
457
458 return 0;
459}
460
467static void update_idx(struct Menu *menu, struct AttachCtx *actx, struct AttachPtr *ap)
468{
469 ap->level = 0;
470 for (int i = actx->idxlen; i > 0; i--)
471 {
472 if (ap->level == actx->idx[i - 1]->level)
473 {
474 actx->idx[i - 1]->body->next = ap->body;
475 break;
476 }
477 }
478
479 ap->body->aptr = ap;
480 mutt_actx_add_attach(actx, ap);
481 update_menu(actx, menu, false);
482 menu_set_index(menu, actx->vcount - 1);
483}
484
492static void compose_attach_swap(struct Email *e, struct AttachCtx *actx, int first, int second)
493{
494 struct AttachPtr **idx = actx->idx;
495
496 // check that attachments really are adjacent
497 if (idx[first]->body->next != idx[second]->body)
498 return;
499
500 // reorder Body pointers
501 if (first == 0)
502 {
503 // first attachment is the fundamental part
504 idx[first]->body->next = idx[second]->body->next;
505 idx[second]->body->next = idx[first]->body;
506 e->body = idx[second]->body;
507 }
508 else
509 {
510 // find previous attachment
511 struct Body *b_previous = NULL;
512 struct Body *b_parent = NULL;
513 if (attach_body_previous(e->body, idx[first]->body, &b_previous))
514 {
515 idx[first]->body->next = idx[second]->body->next;
516 idx[second]->body->next = idx[first]->body;
517 b_previous->next = idx[second]->body;
518 }
519 else if (attach_body_parent(e->body, NULL, idx[first]->body, &b_parent))
520 {
521 idx[first]->body->next = idx[second]->body->next;
522 idx[second]->body->next = idx[first]->body;
523 b_parent->parts = idx[second]->body;
524 }
525 }
526
527 // reorder attachment list
528 struct AttachPtr *saved = idx[second];
529 for (int i = second; i > first; i--)
530 idx[i] = idx[i - 1];
531 idx[first] = saved;
532
533 // if moved attachment is a group then move subparts too
534 if ((idx[first]->body->type == TYPE_MULTIPART) && (second < actx->idxlen - 1))
535 {
536 int i = second + 1;
537 while (idx[i]->level > idx[first]->level)
538 {
539 saved = idx[i];
540 int destidx = i - second + first;
541 for (int j = i; j > destidx; j--)
542 idx[j] = idx[j - 1];
543 idx[destidx] = saved;
544 i++;
545 if (i >= actx->idxlen)
546 break;
547 }
548 }
549}
550
558static int body_index(struct AttachCtx *actx, const struct Body *b)
559{
560 if (!actx || !b)
561 return -1;
562
563 for (int i = 0; i < actx->idxlen; i++)
564 {
565 if (actx->idx[i]->body == b)
566 return i;
567 }
568
569 return -1;
570}
571
579static int prev_sibling(struct AttachCtx *actx, int index)
580{
581 if (!actx || (index <= 0) || (index >= actx->idxlen))
582 return -1;
583
584 const int level = actx->idx[index]->level;
585 int previdx = index - 1;
586 while ((previdx > 0) && (actx->idx[previdx]->level > level))
587 previdx--;
588
589 return (actx->idx[previdx]->level == level) ? previdx : -1;
590}
591
599static int next_sibling(struct AttachCtx *actx, int index)
600{
601 if (!actx || (index < 0) || (index >= actx->idxlen))
602 return -1;
603
604 const int level = actx->idx[index]->level;
605 int nextidx = index + 1;
606 while ((nextidx < actx->idxlen) && (actx->idx[nextidx]->level > level))
607 nextidx++;
608
609 if ((nextidx >= actx->idxlen) || (actx->idx[nextidx]->level != level))
610 return -1;
611
612 return nextidx;
613}
614
621static bool body_array_contains(struct BodyArray *ba, const struct Body *b)
622{
623 struct Body **bp = NULL;
624 ARRAY_FOREACH(bp, ba)
625 {
626 if (*bp == b)
627 return true;
628 }
629
630 return false;
631}
632
639static bool selected_attachments_are_siblings(struct Email *e, struct BodyArray *ba)
640{
641 if (!e || !ba)
642 return false;
643
644 int level = -1;
645 struct Body *parent = NULL;
646 struct Body **bp = NULL;
647 ARRAY_FOREACH(bp, ba)
648 {
649 struct Body *this_parent = NULL;
650 struct Body *body = *bp;
651 if (body->aptr && (body->aptr->level > 0))
652 {
653 if (!attach_body_parent(e->body, NULL, body, &this_parent))
654 return false;
655 }
656
657 if (level == -1)
658 {
659 level = body->aptr ? body->aptr->level : 0;
660 parent = this_parent;
661 continue;
662 }
663
664 if ((level != (body->aptr ? body->aptr->level : 0)) || (parent != this_parent))
665 return false;
666 }
667
668 return true;
669}
670
677{
678 return (count > 1) ? MENU_REDRAW_FULL : MENU_REDRAW_CURRENT;
679}
680
693static void resolve_attachment(struct Menu *menu, const struct ConfigSubset *sub,
694 int index, int num)
695{
696 if (!menu || menu->tag_prefix || (num <= 0))
697 return;
698
699 const bool c_resolve = cs_subset_bool(sub, "resolve");
700 if (!c_resolve)
701 return;
702
703 const int new_index = index + num;
704 if (new_index < menu->max)
705 menu_set_index(menu, new_index);
706}
707
715static int move_attachment(struct ComposeSharedData *shared, bool up, int count)
716{
717 struct AttachCtx *actx = shared->adata->actx;
718 if (!check_count(actx))
719 return FR_ERROR;
720
721 struct Menu *menu = shared->adata->menu;
722 struct AttachPtr *cur_att = current_attachment(actx, menu);
723 if (!cur_att)
724 return FR_ERROR;
725
726 struct Body *cur_body = cur_att->body;
727 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
728 ba_add_selection(&ba, actx, menu, menu->tag_prefix, count);
729 if (ARRAY_EMPTY(&ba))
730 goto done;
731
732 if ((ARRAY_SIZE(&ba) > 1) && !selected_attachments_are_siblings(shared->email, &ba))
733 {
734 mutt_error(_("Selected attachments must be siblings"));
735 ARRAY_FREE(&ba);
736 return FR_ERROR;
737 }
738
739 bool moved = false;
740 if (up)
741 {
742 struct Body **bp = NULL;
743 ARRAY_FOREACH(bp, &ba)
744 {
745 const int index = body_index(actx, *bp);
746 const int previdx = prev_sibling(actx, index);
747 if (previdx < 0)
748 continue;
749 if (body_array_contains(&ba, actx->idx[previdx]->body))
750 continue;
751
752 compose_attach_swap(shared->email, actx, previdx, index);
753 moved = true;
754 }
755 }
756 else
757 {
758 for (int i = ARRAY_SIZE(&ba) - 1; i >= 0; i--)
759 {
760 struct Body **bp = ARRAY_GET(&ba, i);
761 const int index = body_index(actx, *bp);
762 const int nextidx = next_sibling(actx, index);
763 if (nextidx < 0)
764 continue;
765 if (body_array_contains(&ba, actx->idx[nextidx]->body))
766 continue;
767
768 compose_attach_swap(shared->email, actx, index, nextidx);
769 moved = true;
770 }
771 }
772
773 if (!moved)
774 {
775 mutt_error(up ? _("Attachment is already at top") : _("Attachment is already at bottom"));
776 goto done;
777 }
778
779 mutt_update_tree(actx);
782
783 const int index = body_index(actx, cur_body);
784 if (index >= 0)
785 menu_set_index(menu, index);
786
787 ARRAY_FREE(&ba);
788 return FR_SUCCESS;
789
790done:
791 ARRAY_FREE(&ba);
792 return FR_ERROR;
793}
794
803static int group_attachments(struct ComposeSharedData *shared, char *subtype,
804 struct BodyArray *ba)
805{
806 struct AttachCtx *actx = shared->adata->actx;
807 int group_level = -1;
808 struct Body *bptr_parent = NULL;
809
810 // Attachments to be grouped must have the same parent
811 for (int i = 0; i < actx->idxlen; i++)
812 {
813 if (body_array_contains(ba, actx->idx[i]->body))
814 {
815 if (group_level == -1)
816 {
817 group_level = actx->idx[i]->level;
818 }
819 else
820 {
821 if (group_level != actx->idx[i]->level)
822 {
823 mutt_error(_("Attachments to be grouped must have the same parent"));
824 return FR_ERROR;
825 }
826 }
827 // if not at top level check if all tagged attachments have same parent
828 if (group_level > 0)
829 {
830 if (bptr_parent)
831 {
832 struct Body *bptr_test = NULL;
833 if (!attach_body_parent(actx->idx[0]->body, NULL, actx->idx[i]->body, &bptr_test))
834 mutt_debug(LL_DEBUG5, "can't find parent\n");
835 if (bptr_test != bptr_parent)
836 {
837 mutt_error(_("Attachments to be grouped must have the same parent"));
838 return FR_ERROR;
839 }
840 }
841 else
842 {
843 if (!attach_body_parent(actx->idx[0]->body, NULL, actx->idx[i]->body, &bptr_parent))
844 mutt_debug(LL_DEBUG5, "can't find parent\n");
845 }
846 }
847 }
848 }
849
850 // Can't group all attachments unless at top level
851 if (bptr_parent)
852 {
853 if (ARRAY_SIZE(ba) == attach_body_count(bptr_parent->parts, false))
854 {
855 mutt_error(_("Can't leave group with only one attachment"));
856 return FR_ERROR;
857 }
858 }
859
860 struct Body *group = mutt_body_new();
861 group->type = TYPE_MULTIPART;
862 group->subtype = mutt_str_dup(subtype);
863 group->encoding = ENC_7BIT;
864
865 struct Body *bptr_first = NULL; // first selected attachment
866 struct Body *bptr = NULL; // current selected attachment
867 struct Body *group_parent = NULL; // parent of group
868 struct Body *group_previous = NULL; // previous body to group
869 struct Body *group_part = NULL; // current attachment in group
870 int group_idx = 0; // index in attachment list where group will be inserted
871 int group_last_idx = 0; // index of last part of previous found group
872 int group_parent_type = TYPE_OTHER;
873
874 for (int i = 0; i < actx->idxlen; i++)
875 {
876 bptr = actx->idx[i]->body;
877 if (body_array_contains(ba, bptr))
878 {
879 // set group properties based on first selected attachment
880 if (!bptr_first)
881 {
882 group->disposition = bptr->disposition;
883 if (bptr->language && !mutt_str_equal(subtype, "multilingual"))
884 group->language = mutt_str_dup(bptr->language);
885 group_parent_type = bptr->aptr->parent_type;
886 bptr_first = bptr;
887 if (i > 0)
888 {
889 if (!attach_body_previous(shared->email->body, bptr, &group_previous))
890 {
891 mutt_debug(LL_DEBUG5, "couldn't find previous\n");
892 }
893 if (!attach_body_parent(shared->email->body, NULL, bptr, &group_parent))
894 {
895 mutt_debug(LL_DEBUG5, "couldn't find parent\n");
896 }
897 }
898 }
899
900 if (bptr->tagged)
901 {
902 shared->adata->menu->num_tagged--;
903 bptr->tagged = false;
904 }
905 bptr->aptr->level++;
907
908 // append bptr to the group parts list and remove from email body list
909 struct Body *bptr_previous = NULL;
910 if (attach_body_previous(shared->email->body, bptr, &bptr_previous))
911 bptr_previous->next = bptr->next;
912 else if (attach_body_parent(shared->email->body, NULL, bptr, &bptr_parent))
913 bptr_parent->parts = bptr->next;
914 else
915 shared->email->body = bptr->next;
916
917 if (group_part)
918 {
919 // add bptr to group parts list
920 group_part->next = bptr;
921 group_part = group_part->next;
922 group_part->next = NULL;
923
924 // reorder attachments and set levels
925 int bptr_attachments = attach_body_count(bptr, true);
926 for (int j = i + 1; j < (i + bptr_attachments); j++)
927 actx->idx[j]->level++;
928 if (i > (group_last_idx + 1))
929 {
930 for (int j = 0; j < bptr_attachments; j++)
931 {
932 struct AttachPtr *saved = actx->idx[i + bptr_attachments - 1];
933 for (int k = i + bptr_attachments - 1; k > (group_last_idx + 1); k--)
934 actx->idx[k] = actx->idx[k - 1];
935 actx->idx[group_last_idx + 1] = saved;
936 }
937 }
938 i += bptr_attachments - 1;
939 group_last_idx += bptr_attachments;
940 }
941 else
942 {
943 group_idx = i;
944 group->parts = bptr;
945 group_part = bptr;
946 group_part->next = NULL;
947 int bptr_attachments = attach_body_count(bptr, true);
948 for (int j = i + 1; j < (i + bptr_attachments); j++)
949 actx->idx[j]->level++;
950 i += bptr_attachments - 1;
951 group_last_idx = i;
952 }
953 }
954 }
955
956 if (!bptr_first)
957 {
958 mutt_body_free(&group);
959 return FR_ERROR;
960 }
961
962 // set group->next
963 int next_aidx = group_idx + attach_body_count(group->parts, true);
964 if (group_parent)
965 {
966 // find next attachment with the same parent as the group
967 struct Body *b = NULL;
968 struct Body *b_parent = NULL;
969 while (next_aidx < actx->idxlen)
970 {
971 b = actx->idx[next_aidx]->body;
972 b_parent = NULL;
973 if (attach_body_parent(shared->email->body, NULL, b, &b_parent))
974 {
975 if (group_parent == b_parent)
976 {
977 group->next = b;
978 break;
979 }
980 }
981 next_aidx++;
982 }
983 }
984 else if (next_aidx < actx->idxlen)
985 {
986 // group is at top level
987 group->next = actx->idx[next_aidx]->body;
988 }
989
990 // set previous or parent for group
991 if (group_previous)
992 group_previous->next = group;
993 else if (group_parent)
994 group_parent->parts = group;
995
997
998 struct AttachPtr *group_ap = mutt_aptr_new();
999 group_ap->body = group;
1000 group_ap->body->aptr = group_ap;
1001 group_ap->level = group_level;
1002 group_ap->parent_type = group_parent_type;
1003
1004 // insert group into attachment list
1005 mutt_actx_ins_attach(actx, group_ap, group_idx);
1006
1007 // update email body and last attachment pointers
1008 shared->email->body = actx->idx[0]->body;
1009 actx->idx[actx->idxlen - 1]->body->next = NULL;
1010
1011 update_menu(actx, shared->adata->menu, false);
1012 shared->adata->menu->current = group_idx;
1014
1015 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1016 return FR_SUCCESS;
1017}
1018
1019// -----------------------------------------------------------------------------
1020
1024static int op_attach_attach_file(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1025{
1026 struct ComposeSharedData *shared = fdata->shared;
1027 char *prompt = _("Attach file");
1028 int numfiles = 0;
1029 char **files = NULL;
1030
1031 struct Buffer *fname = buf_pool_get();
1032 if ((mw_enter_fname(prompt, fname, false, NULL, true, &files, &numfiles,
1033 MUTT_SEL_MULTI) == -1) ||
1034 buf_is_empty(fname))
1035 {
1036 for (int i = 0; i < numfiles; i++)
1037 FREE(&files[i]);
1038
1039 FREE(&files);
1040 buf_pool_release(&fname);
1041 return FR_NO_ACTION;
1042 }
1043
1044 bool error = false;
1045 bool added_attachment = false;
1046 if (numfiles > 1)
1047 {
1048 mutt_message(ngettext("Attaching selected file...",
1049 "Attaching selected files...", numfiles));
1050 }
1051 for (int i = 0; i < numfiles; i++)
1052 {
1053 char *att = files[i];
1054 if (!att)
1055 continue;
1056
1057 struct AttachPtr *ap = mutt_aptr_new();
1058 ap->unowned = true;
1059 ap->body = mutt_make_file_attach(att, shared->sub);
1060 if (ap->body)
1061 {
1062 added_attachment = true;
1063 update_idx(shared->adata->menu, shared->adata->actx, ap);
1064 }
1065 else
1066 {
1067 error = true;
1068 mutt_error(_("Unable to attach %s"), att);
1069 mutt_aptr_free(&ap);
1070 }
1071 FREE(&files[i]);
1072 }
1073
1074 FREE(&files);
1075 buf_pool_release(&fname);
1076
1077 if (!error)
1079
1082 if (added_attachment)
1083 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1084 return added_attachment ? FR_SUCCESS : (error ? FR_ERROR : FR_NO_ACTION);
1085}
1086
1090static int op_attach_attach_key(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
1091{
1092 struct ComposeSharedData *shared = fdata->shared;
1093 if (!(WithCrypto & APPLICATION_PGP))
1094 return FR_NOT_IMPL;
1095 struct AttachPtr *ap = mutt_aptr_new();
1097 if (ap->body)
1098 {
1099 update_idx(shared->adata->menu, shared->adata->actx, ap);
1101 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
1102 }
1103 else
1104 {
1105 mutt_aptr_free(&ap);
1106 }
1107
1109 return FR_SUCCESS;
1110}
1111
1120 const struct KeyEvent *event)
1121{
1123 struct ComposeSharedData *shared = fdata->shared;
1124 char *prompt = _("Open mailbox to attach message from");
1125
1126 OptNews = false;
1127 const int op = event->op;
1128 if (shared->mailbox && (op == OP_ATTACH_ATTACH_NEWS_MESSAGE))
1129 {
1130 const char *const c_news_server = cs_subset_string(shared->sub, "news_server");
1131 mod_data->current_news_srv = nntp_select_server(shared->mailbox, c_news_server, false);
1132 if (!mod_data->current_news_srv)
1133 return FR_NO_ACTION;
1134
1135 prompt = _("Open newsgroup to attach message from");
1136 OptNews = true;
1137 }
1138
1139 struct Buffer *fname = buf_pool_get();
1140 if (shared->mailbox)
1141 {
1142 if ((op == OP_ATTACH_ATTACH_MESSAGE) ^ (shared->mailbox->type == MUTT_NNTP))
1143 {
1144 buf_strcpy(fname, mailbox_path(shared->mailbox));
1145 pretty_mailbox(fname);
1146 }
1147 }
1148
1149 if ((mw_enter_fname(prompt, fname, true, shared->mailbox, false, NULL, NULL,
1150 MUTT_SEL_NONE) == -1) ||
1151 buf_is_empty(fname))
1152 {
1153 buf_pool_release(&fname);
1154 return FR_NO_ACTION;
1155 }
1156
1157 if (OptNews)
1158 nntp_expand_path(fname->data, fname->dsize,
1159 &mod_data->current_news_srv->conn->account);
1160 else
1161 expand_path(fname, false);
1162
1163 if (imap_path_probe(buf_string(fname), NULL) != MUTT_IMAP)
1164 {
1165 if (pop_path_probe(buf_string(fname), NULL) != MUTT_POP)
1166 {
1167 if (!OptNews && (nntp_path_probe(buf_string(fname), NULL) != MUTT_NNTP))
1168 {
1169 if (mx_path_probe(buf_string(fname)) != MUTT_NOTMUCH)
1170 {
1171 /* check to make sure the file exists and is readable */
1172 if (access(buf_string(fname), R_OK) == -1)
1173 {
1174 mutt_perror("%s", buf_string(fname));
1175 buf_pool_release(&fname);
1176 return FR_ERROR;
1177 }
1178 }
1179 }
1180 }
1181 }
1182
1184
1185 struct Mailbox *m_attach = mx_path_resolve(buf_string(fname));
1186 const bool old_readonly = m_attach->readonly;
1187 if (!mx_mbox_open(m_attach, MUTT_READONLY))
1188 {
1189 mutt_error(_("Unable to open mailbox %s"), buf_string(fname));
1190 mx_fastclose_mailbox(m_attach, false);
1191 m_attach = NULL;
1192 buf_pool_release(&fname);
1193 return FR_ERROR;
1194 }
1195 buf_pool_release(&fname);
1196
1197 if (m_attach->msg_count == 0)
1198 {
1199 mx_mbox_close(m_attach);
1200 mutt_error(_("No messages in that folder"));
1201 return FR_ERROR;
1202 }
1203
1204 /* `$sort`, `$sort_aux`, `$use_threads` could be changed in dlg_index() */
1205 const enum EmailSortType old_sort = cs_subset_sort(shared->sub, "sort");
1206 const enum EmailSortType old_sort_aux = cs_subset_sort(shared->sub, "sort_aux");
1207 const unsigned char old_use_threads = cs_subset_enum(shared->sub, "use_threads");
1208
1209 mutt_message(_("Tag the messages you want to attach"));
1210 struct MuttWindow *dlg = index_pager_init();
1211 struct IndexSharedData *index_shared = dlg->wdata;
1212 index_shared->attach_msg = true;
1213 dialog_push(dlg);
1214 struct Mailbox *m_attach_new = dlg_index(dlg, m_attach);
1215 dialog_pop();
1216 mutt_window_free(&dlg);
1217
1218 if (shared->mailbox)
1219 {
1220 /* Restore old $sort variables */
1221 cs_subset_str_native_set(shared->sub, "sort", old_sort, NULL);
1222 cs_subset_str_native_set(shared->sub, "sort_aux", old_sort_aux, NULL);
1223 cs_subset_str_native_set(shared->sub, "use_threads", old_use_threads, NULL);
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
2476{
2477 const char *filename;
2478};
2479
2483static void spelling_command_file(const struct ExpandoNode *node, void *data,
2484 MuttFormatFlags flags, struct Buffer *buf)
2485{
2486 struct SpellingCommandData *scd = data;
2487
2488 buf_quote_filename(buf, scd->filename, true);
2489}
2490
2495 const struct KeyEvent *event)
2496{
2497 struct ComposeSharedData *shared = fdata->shared;
2498 endwin();
2499 const struct Expando *c_spelling_command = cs_subset_expando(shared->sub, "spelling_command");
2500 struct Buffer *cmd = buf_pool_get();
2501 struct SpellingCommandData scd = { shared->email->body->filename };
2502 const struct ExpandoRenderCallback spelling_command_render_callbacks[] = {
2504 { -1, -1, NULL, NULL },
2505 };
2506 expando_render(c_spelling_command, spelling_command_render_callbacks, &scd,
2507 MUTT_FORMAT_NONE, -1, cmd);
2508 if (!expando_find_node(c_spelling_command, ED_COMPOSE, ED_COM_SPELLING_FILE))
2509 {
2510 if (!buf_is_empty(cmd))
2511 buf_addch(cmd, ' ');
2512 struct Buffer *quoted = buf_pool_get();
2513 buf_quote_filename(quoted, scd.filename, true);
2514 buf_addstr(cmd, buf_string(quoted));
2515 buf_pool_release(&quoted);
2516 }
2517 if (mutt_system(buf_string(cmd)) == -1)
2518 {
2519 mutt_error(_("Error running \"%s\""), buf_string(cmd));
2520 buf_pool_release(&cmd);
2521 return FR_ERROR;
2522 }
2523 buf_pool_release(&cmd);
2524
2525 mutt_update_encoding(shared->email->body, shared->sub);
2527 return FR_SUCCESS;
2528}
2529
2534 const struct KeyEvent *event)
2535{
2536 struct ComposeSharedData *shared = fdata->shared;
2537 if (check_attachments(shared->adata->actx, shared->sub) != 0)
2538 {
2540 return FR_ERROR;
2541 }
2542
2543 shared->rc = 1;
2544 return FR_DONE;
2545}
2546
2551 const struct KeyEvent *event)
2552{
2553 struct ComposeSharedData *shared = fdata->shared;
2554 if (!check_count(shared->adata->actx))
2555 return FR_ERROR;
2556 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
2557 shared->adata->menu);
2558 if (cur_att->body->type == TYPE_MULTIPART)
2559 {
2560 mutt_error(_("Can't rename multipart attachments"));
2561 return FR_ERROR;
2562 }
2563 struct Buffer *fname = buf_pool_get();
2564 buf_strcpy(fname, cur_att->body->filename);
2565 pretty_mailbox(fname);
2566 struct FileCompletionData cdata = { false, shared->mailbox, NULL, NULL, NULL };
2567 if ((mw_get_field(_("Rename to: "), fname, MUTT_COMP_NONE, HC_FILE,
2568 &CompleteFileOps, &cdata) == 0) &&
2569 !buf_is_empty(fname))
2570 {
2571 struct stat st = { 0 };
2572 if (stat(cur_att->body->filename, &st) == -1)
2573 {
2574 /* L10N: "stat" is a system call. Do "man 2 stat" for more information. */
2575 mutt_error(_("Can't stat %s: %s"), buf_string(fname), strerror(errno));
2576 buf_pool_release(&fname);
2577 return FR_ERROR;
2578 }
2579
2580 expand_path(fname, false);
2581 if (mutt_file_rename(cur_att->body->filename, buf_string(fname)))
2582 {
2583 buf_pool_release(&fname);
2584 return FR_ERROR;
2585 }
2586
2587 mutt_str_replace(&cur_att->body->filename, buf_string(fname));
2589
2590 if (cur_att->body->stamp >= st.st_mtime)
2591 mutt_stamp_attachment(cur_att->body);
2592 exec_message_hook(NULL, shared->email, CMD_SEND2_HOOK);
2593 }
2594 buf_pool_release(&fname);
2595 return FR_SUCCESS;
2596}
2597
2602 const struct KeyEvent *event)
2603{
2604 struct ComposeSharedData *shared = fdata->shared;
2605 /* Note: We don't invoke send2-hook here, since we want to leave
2606 * users an opportunity to change settings from the ":" prompt. */
2607 if (check_attachments(shared->adata->actx, shared->sub) != 0)
2608 {
2610 return FR_NO_ACTION;
2611 }
2612
2613 if (!shared->fcc_set && !buf_is_empty(shared->fcc))
2614 {
2615 enum QuadOption ans = query_quadoption(_("Save a copy of this message?"),
2616 shared->sub, "copy");
2617 if (ans == MUTT_ABORT)
2618 return FR_NO_ACTION;
2619 else if (ans == MUTT_NO)
2620 buf_reset(shared->fcc);
2621 }
2622
2623 shared->rc = 0;
2624 return FR_DONE;
2625}
2626
2631 const struct KeyEvent *event)
2632{
2633 struct ComposeSharedData *shared = fdata->shared;
2634 int rc = FR_NO_ACTION;
2635 struct Buffer *fname = buf_pool_get();
2636 if (shared->mailbox)
2637 {
2638 buf_strcpy(fname, mailbox_path(shared->mailbox));
2639 pretty_mailbox(fname);
2640 }
2641 if (shared->adata->actx->idxlen)
2642 shared->email->body = shared->adata->actx->idx[0]->body;
2643 if ((mw_enter_fname(_("Write message to mailbox"), fname, true,
2644 shared->mailbox, false, NULL, NULL, MUTT_SEL_NONE) != -1) &&
2645 !buf_is_empty(fname))
2646 {
2647 mutt_message(_("Writing message to %s ..."), buf_string(fname));
2648 expand_path(fname, false);
2649
2650 if (shared->email->body->next)
2651 shared->email->body = mutt_make_multipart(shared->email->body);
2652
2653 if (mutt_write_fcc(buf_string(fname), shared->email, NULL, false, NULL,
2654 NULL, shared->sub) == 0)
2655 mutt_message(_("Message written"));
2656
2657 shared->email->body = mutt_remove_multipart(shared->email->body);
2658 rc = FR_SUCCESS;
2659 }
2660 buf_pool_release(&fname);
2661 return rc;
2662}
2663
2674static int op_display_headers(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2675{
2676 struct ComposeSharedData *shared = fdata->shared;
2677 if (!check_count(shared->adata->actx))
2678 return FR_ERROR;
2679
2680 enum ViewAttachMode mode = MUTT_VA_REGULAR;
2681
2682 const int op = event->op;
2683 switch (op)
2684 {
2685 case OP_ATTACH_VIEW:
2686 case OP_DISPLAY_HEADERS:
2687 break;
2688
2689 case OP_ATTACH_VIEW_MAILCAP:
2690 mode = MUTT_VA_MAILCAP;
2691 break;
2692
2693 case OP_ATTACH_VIEW_PAGER:
2694 mode = MUTT_VA_PAGER;
2695 break;
2696
2697 case OP_ATTACH_VIEW_TEXT:
2698 mode = MUTT_VA_AS_TEXT;
2699 break;
2700
2701 default:
2702 break;
2703 }
2704
2705 if (mode == MUTT_VA_REGULAR)
2706 {
2707 mutt_attach_display_loop(shared->sub, shared->adata->menu, op,
2708 shared->email, shared->adata->actx, false);
2709 }
2710 else
2711 {
2712 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
2713 shared->adata->menu);
2714 mutt_view_attachment(NULL, cur_att->body, mode, shared->email,
2715 shared->adata->actx, shared->adata->menu->win);
2716 }
2717
2719 /* no send2hook, since this doesn't modify the message */
2720 return FR_SUCCESS;
2721}
2722
2726static int op_quit(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
2727{
2728 struct ComposeSharedData *shared = fdata->shared;
2729 enum QuadOption ans = query_quadoption(_("Save (postpone) draft message?"),
2730 shared->sub, "postpone");
2731 if (ans == MUTT_NO)
2732 {
2733 for (int i = 0; i < shared->adata->actx->idxlen; i++)
2734 if (shared->adata->actx->idx[i]->unowned)
2735 shared->adata->actx->idx[i]->body->unlink = false;
2736
2737 if (!(shared->flags & MUTT_COMPOSE_NOFREEHEADER))
2738 {
2739 for (int i = 0; i < shared->adata->actx->idxlen; i++)
2740 {
2741 /* avoid freeing other attachments */
2742 shared->adata->actx->idx[i]->body->next = NULL;
2743 if (!shared->adata->actx->idx[i]->body->email)
2744 shared->adata->actx->idx[i]->body->parts = NULL;
2745 mutt_body_free(&shared->adata->actx->idx[i]->body);
2746 }
2747 }
2748 shared->rc = -1;
2749 return FR_DONE;
2750 }
2751 else if (ans == MUTT_ABORT)
2752 {
2753 return FR_NO_ACTION;
2754 }
2755
2756 return op_compose_postpone_message(fdata, event);
2757}
2758
2759// -----------------------------------------------------------------------------
2760
2764static const struct ComposeFunction ComposeFunctions[] = {
2765 // clang-format off
2766 { OP_ATTACH_ATTACH_FILE, op_attach_attach_file },
2767 { OP_ATTACH_ATTACH_KEY, op_attach_attach_key },
2768 { OP_ATTACH_ATTACH_MESSAGE, op_attach_attach_message },
2769 { OP_ATTACH_ATTACH_NEWS_MESSAGE, op_attach_attach_message },
2770 { OP_ATTACH_DETACH, op_attach_detach },
2771 { OP_ATTACH_EDIT_CONTENT_ID, op_attach_edit_content_id },
2772 { OP_ATTACH_EDIT_DESCRIPTION, op_attach_edit_description },
2773 { OP_ATTACH_EDIT_ENCODING, op_attach_edit_encoding },
2774 { OP_ATTACH_EDIT_LANGUAGE, op_attach_edit_language },
2775 { OP_ATTACH_EDIT_MIME, op_attach_edit_mime },
2776 { OP_ATTACH_EDIT_TYPE, op_attach_edit_type },
2777 { OP_ATTACH_FILTER, op_attach_filter },
2778 { OP_ATTACH_GET_ATTACHMENT, op_attach_get_attachment },
2779 { OP_ATTACH_GROUP_ALTS, op_attach_group_alts },
2780 { OP_ATTACH_GROUP_LINGUAL, op_attach_group_lingual },
2781 { OP_ATTACH_GROUP_RELATED, op_attach_group_related },
2782 { OP_ATTACH_MOVE_DOWN, op_attach_move_down },
2783 { OP_ATTACH_MOVE_UP, op_attach_move_up },
2784 { OP_ATTACH_NEW_MIME, op_attach_new_mime },
2785 { OP_ATTACH_PRINT, op_attach_print },
2786 { OP_ATTACH_RENAME_ATTACHMENT, op_attach_rename_attachment },
2787 { OP_ATTACH_SAVE, op_attach_save },
2788 { OP_ATTACH_TOGGLE_DISPOSITION, op_attach_toggle_disposition },
2789 { OP_ATTACH_TOGGLE_RECODE, op_attach_toggle_recode },
2790 { OP_ATTACH_TOGGLE_UNLINK, op_attach_toggle_unlink },
2791 { OP_ATTACH_UNGROUP, op_attach_ungroup },
2792 { OP_ATTACH_UPDATE_ENCODING, op_attach_update_encoding },
2793 { OP_ATTACH_VIEW, op_display_headers },
2794 { OP_ATTACH_VIEW_MAILCAP, op_display_headers },
2795 { OP_ATTACH_VIEW_PAGER, op_display_headers },
2796 { OP_ATTACH_VIEW_TEXT, op_display_headers },
2797 { OP_COMPOSE_EDIT_FILE, op_compose_edit_file },
2798 { OP_COMPOSE_EDIT_MESSAGE, op_compose_edit_message },
2799 { OP_COMPOSE_CHECK_SPELLING, op_compose_check_spelling },
2800 { OP_COMPOSE_POSTPONE_MESSAGE, op_compose_postpone_message },
2801 { OP_COMPOSE_RENAME_FILE, op_compose_rename_file },
2802 { OP_COMPOSE_SEND_MESSAGE, op_compose_send_message },
2803 { OP_COMPOSE_WRITE_MESSAGE, op_compose_write_message },
2804 { OP_DISPLAY_HEADERS, op_display_headers },
2805 { OP_ENVELOPE_EDIT_HEADERS, op_envelope_edit_headers },
2806 { OP_EXIT, op_quit },
2807 { OP_PIPE, op_attach_filter },
2808 { OP_QUIT, op_quit },
2809 { 0, NULL },
2810 // clang-format on
2811};
2812
2816int compose_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
2817{
2818 // The Dispatcher may be called on any Window in the Dialog
2819 struct MuttWindow *dlg = dialog_find(win);
2820 if (!event || !dlg || !dlg->wdata)
2821 {
2823 return FR_ERROR;
2824 }
2825
2826 const int op = event->op;
2827
2829
2830 struct ComposeFunctionData fdata = {
2831 .n = NeoMutt,
2832 .mod_data = mod_data,
2833 .shared = dlg->wdata,
2834 };
2835
2836 int rc = FR_UNKNOWN;
2837 for (size_t i = 0; ComposeFunctions[i].op != OP_NULL; i++)
2838 {
2839 const struct ComposeFunction *fn = &ComposeFunctions[i];
2840 if (fn->op == op)
2841 {
2842 rc = fn->function(&fdata, event);
2843 break;
2844 }
2845 }
2846
2847 if (rc == FR_UNKNOWN) // Not our function
2848 return rc;
2849
2850 const char *result = dispatcher_get_retval_name(rc);
2851 mutt_debug(LL_DEBUG1, "Handled %s (%d) -> %s\n", opcodes_get_name(op), op, NONULL(result));
2852
2854 return rc;
2855}
#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_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:248
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:233
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:235
void compose_init_keys(struct NeoMutt *n, struct SubMenu *sm_generic)
Initialise the Compose Keybindings - Implements init_keys_api.
Definition functions.c:213
static int body_index(struct AttachCtx *actx, const struct Body *b)
Find the index of an attachment body.
Definition functions.c:558
static const struct ComposeFunction ComposeFunctions[]
All the NeoMutt functions that the Compose supports.
Definition functions.c:2764
static int group_attachments(struct ComposeSharedData *shared, char *subtype, struct BodyArray *ba)
Group selected attachments into a multipart group.
Definition functions.c:803
static const struct MenuFuncOp OpCompose[]
Functions for the Compose Menu.
Definition functions.c:79
static int prev_sibling(struct AttachCtx *actx, int index)
Find the previous sibling of an attachment.
Definition functions.c:579
static void update_idx(struct Menu *menu, struct AttachCtx *actx, struct AttachPtr *ap)
Add a new attachment to the message.
Definition functions.c:467
static char * gen_cid(void)
Generate a random Content ID.
Definition functions.c:252
static MenuRedrawFlags selection_redraw_flags(size_t count)
Choose redraw flags for a selection.
Definition functions.c:676
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:693
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:492
static bool body_array_contains(struct BodyArray *ba, const struct Body *b)
Does a selection contain a body?
Definition functions.c:621
static int next_sibling(struct AttachCtx *actx, int index)
Find the next sibling of an attachment.
Definition functions.c:599
static int delete_attachment(struct AttachCtx *actx, int aidx, struct ConfigSubset *sub)
Delete an attachment.
Definition functions.c:378
static bool selected_attachments_are_siblings(struct Email *e, struct BodyArray *ba)
Check whether selected attachments are siblings.
Definition functions.c:639
static bool check_cid(const char *cid)
Check if a Content-ID is valid.
Definition functions.c:268
static int check_attachments(struct AttachCtx *actx, struct ConfigSubset *sub)
Check if any attachments have changed or been deleted.
Definition functions.c:313
static int move_attachment(struct ComposeSharedData *shared, bool up, int count)
Move attachments in the attachment list.
Definition functions.c:715
static const struct MenuOpSeq ComposeDefaultBindings[]
Key bindings for the Compose Menu.
Definition functions.c:151
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:288
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.
@ ED_COM_SPELLING_FILE
Spelling command's filename.
Definition shared_data.h:59
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
const struct Expando * cs_subset_expando(const struct ConfigSubset *sub, const char *name)
Get an Expando config item by name.
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
@ ED_COMPOSE
Compose ED_COM_ ExpandoDataCompose.
Definition domain.h:39
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:380
int mutt_check_encoding(const char *c)
Check the encoding type.
Definition parse.c:436
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
const struct ExpandoNode * expando_find_node(const struct Expando *exp, int did, int uid)
Find a Node in an Expando tree.
Definition expando.c:109
int expando_render(const struct Expando *exp, const struct ExpandoRenderCallback *erc, void *data, MuttFormatFlags flags, int max_cols, struct Buffer *buf)
Render an Expando + data into a string.
Definition expando.c:161
Parse Expando string.
bool mutt_edit_content_type(struct Email *e, struct Body *b, FILE *fp)
Edit the content type of an attachment.
Definition external.c:1077
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:2533
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:2630
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_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_check_spelling(struct ComposeFunctionData *fdata, const struct KeyEvent *event)
Check the spelling of the message - Implements compose_function_t -.
Definition functions.c:2494
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:2550
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:1090
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:2601
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:2674
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:1119
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:1024
int compose_function_dispatcher(struct MuttWindow *win, const struct KeyEvent *event)
Perform a Compose function - Implements function_dispatcher_t -.
Definition functions.c:2816
static void spelling_command_file(const struct ExpandoNode *node, void *data, MuttFormatFlags flags, struct Buffer *buf)
Spelling command: Filename - Implements get_string_t -.
Definition functions.c:2483
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:2802
enum MailboxType pop_path_probe(const char *path, const struct stat *st)
Is this a POP Mailbox?
Definition pop.c:1182
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:621
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:399
void pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition muttlib.c:431
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
@ MUTT_FORMAT_NONE
No flags are set.
Definition render.h:37
uint8_t MuttFormatFlags
Definition render.h:45
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:618
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:1029
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
Basic Expando Node.
Definition node.h:67
Parsed Expando trees.
Definition expando.h:41
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
Data for the spelling command.
Definition functions.c:2476
const char * filename
Filename of the message being composed.
Definition functions.c:2477
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