NeoMutt  2022-04-29-247-gc6aae8
Teaching an old dog new tricks
DOXYGEN
functions.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <errno.h>
31#include <limits.h>
32#include <stdbool.h>
33#include <stdio.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 "enter/lib.h"
50#include "index/lib.h"
51#include "menu/lib.h"
52#include "ncrypt/lib.h"
53#include "question/lib.h"
54#include "send/lib.h"
55#include "attach_data.h"
56#include "commands.h"
57#include "hook.h"
58#include "mutt_header.h"
59#include "mutt_logging.h"
60#include "muttlib.h"
61#include "mview.h"
62#include "mx.h"
63#include "opcodes.h"
64#include "options.h"
65#include "protos.h"
66#include "rfc3676.h"
67#include "shared_data.h"
68#ifdef ENABLE_NLS
69#include <libintl.h>
70#endif
71#ifdef MIXMASTER
72#include "mixmaster/lib.h"
73#endif
74#ifdef USE_NNTP
75#include "nntp/lib.h"
76#include "nntp/adata.h" // IWYU pragma: keep
77#endif
78#ifdef USE_POP
79#include "pop/lib.h"
80#endif
81#ifdef USE_IMAP
82#include "imap/lib.h"
83#endif
84
90static bool check_count(struct AttachCtx *actx)
91{
92 if (actx->idxlen == 0)
93 {
94 mutt_error(_("There are no attachments"));
95 return false;
96 }
97
98 return true;
99}
100
107static char *gen_cid(void)
108{
109 char rndid[MUTT_RANDTAG_LEN + 1];
110
111 mutt_rand_base32(rndid, sizeof(rndid) - 1);
112 rndid[MUTT_RANDTAG_LEN] = 0;
113
114 return mutt_str_dup(rndid);
115}
116
123static bool check_cid(const char *cid)
124{
125 static const char *check = "^[-\\.0-9@A-Z_a-z]+$";
126
127 struct Regex *check_cid_regex = mutt_regex_new(check, 0, NULL);
128
129 const bool valid = mutt_regex_match(check_cid_regex, cid);
130
131 mutt_regex_free(&check_cid_regex);
132
133 return valid;
134}
135
143static int check_attachments(struct AttachCtx *actx, struct ConfigSubset *sub)
144{
145 int rc = -1;
146 struct stat st = { 0 };
147 struct Buffer *pretty = NULL, *msg = NULL;
148
149 for (int i = 0; i < actx->idxlen; i++)
150 {
151 if (actx->idx[i]->body->type == TYPE_MULTIPART)
152 continue;
153 if (stat(actx->idx[i]->body->filename, &st) != 0)
154 {
155 if (!pretty)
156 pretty = mutt_buffer_pool_get();
157 mutt_buffer_strcpy(pretty, actx->idx[i]->body->filename);
159 /* L10N: This message is displayed in the compose menu when an attachment
160 doesn't stat. %d is the attachment number and %s is the attachment
161 filename. The filename is located last to avoid a long path hiding
162 the error message. */
163 mutt_error(_("Attachment #%d no longer exists: %s"), i + 1,
164 mutt_buffer_string(pretty));
165 goto cleanup;
166 }
167
168 if (actx->idx[i]->body->stamp < st.st_mtime)
169 {
170 if (!pretty)
171 pretty = mutt_buffer_pool_get();
172 mutt_buffer_strcpy(pretty, actx->idx[i]->body->filename);
174
175 if (!msg)
176 msg = mutt_buffer_pool_get();
177 /* L10N: This message is displayed in the compose menu when an attachment
178 is modified behind the scenes. %d is the attachment number and %s is
179 the attachment filename. The filename is located last to avoid a long
180 path hiding the prompt question. */
181 mutt_buffer_printf(msg, _("Attachment #%d modified. Update encoding for %s?"),
182 i + 1, mutt_buffer_string(pretty));
183
185 if (ans == MUTT_YES)
186 mutt_update_encoding(actx->idx[i]->body, sub);
187 else if (ans == MUTT_ABORT)
188 goto cleanup;
189 }
190 }
191
192 rc = 0;
193
194cleanup:
197 return rc;
198}
199
207static int delete_attachment(struct AttachCtx *actx, int aidx)
208{
209 if (!actx || (aidx < 0) || (aidx >= actx->idxlen))
210 return -1;
211
212 struct AttachPtr **idx = actx->idx;
213 struct Body *bptr_previous = NULL;
214 struct Body *bptr_parent = NULL;
215
216 if (aidx == 0)
217 {
218 struct Body *b = actx->idx[0]->body;
219 if (!b->next) // There's only one attachment left
220 {
221 mutt_error(_("You may not delete the only attachment"));
222 return -1;
223 }
224 }
225
226 if (idx[aidx]->level > 0)
227 {
228 if (attach_body_parent(idx[0]->body, NULL, idx[aidx]->body, &bptr_parent))
229 {
230 if (attach_body_count(bptr_parent->parts, false) < 3)
231 {
232 mutt_error(_("Can't leave group with only one attachment"));
233 return -1;
234 }
235 }
236 }
237
238 // reorder body pointers
239 if (aidx > 0)
240 {
241 if (attach_body_previous(idx[0]->body, idx[aidx]->body, &bptr_previous))
242 bptr_previous->next = idx[aidx]->body->next;
243 else if (attach_body_parent(idx[0]->body, NULL, idx[aidx]->body, &bptr_parent))
244 bptr_parent->parts = idx[aidx]->body->next;
245 }
246
247 // free memory
248 int part_count = 1;
249 if (aidx < (actx->idxlen - 1))
250 {
251 if ((idx[aidx]->body->type == TYPE_MULTIPART) &&
252 (idx[aidx + 1]->level > idx[aidx]->level))
253 {
254 part_count += attach_body_count(idx[aidx]->body->parts, true);
255 }
256 }
257 idx[aidx]->body->next = NULL;
258 mutt_body_free(&(idx[aidx]->body));
259 for (int i = 0; i < part_count; i++)
260 {
261 FREE(&idx[aidx + i]->tree);
262 FREE(&idx[aidx + i]);
263 }
264
265 // reorder attachment list
266 for (int i = aidx; i < (actx->idxlen - part_count); i++)
267 idx[i] = idx[i + part_count];
268 for (int i = 0; i < part_count; i++)
269 idx[actx->idxlen - i - 1] = NULL;
270 actx->idxlen -= part_count;
271
272 return 0;
273}
274
281static void update_idx(struct Menu *menu, struct AttachCtx *actx, struct AttachPtr *ap)
282{
283 ap->level = 0;
284 for (int i = actx->idxlen; i > 0; i--)
285 {
286 if (ap->level == actx->idx[i - 1]->level)
287 {
288 actx->idx[i - 1]->body->next = ap->body;
289 break;
290 }
291 }
292
293 ap->body->aptr = ap;
294 mutt_actx_add_attach(actx, ap);
295 update_menu(actx, menu, false);
296 menu_set_index(menu, actx->vcount - 1);
297}
298
306static void compose_attach_swap(struct Email *e, struct AttachCtx *actx, int first, int second)
307{
308 struct AttachPtr **idx = actx->idx;
309
310 // check that attachments really are adjacent
311 if (idx[first]->body->next != idx[second]->body)
312 return;
313
314 // reorder Body pointers
315 if (first == 0)
316 {
317 // first attachment is the fundamental part
318 idx[first]->body->next = idx[second]->body->next;
319 idx[second]->body->next = idx[first]->body;
320 e->body = idx[second]->body;
321 }
322 else
323 {
324 // find previous attachment
325 struct Body *bptr_previous = NULL;
326 struct Body *bptr_parent = NULL;
327 if (attach_body_previous(e->body, idx[first]->body, &bptr_previous))
328 {
329 idx[first]->body->next = idx[second]->body->next;
330 idx[second]->body->next = idx[first]->body;
331 bptr_previous->next = idx[second]->body;
332 }
333 else if (attach_body_parent(e->body, NULL, idx[first]->body, &bptr_parent))
334 {
335 idx[first]->body->next = idx[second]->body->next;
336 idx[second]->body->next = idx[first]->body;
337 bptr_parent->parts = idx[second]->body;
338 }
339 }
340
341 // reorder attachment list
342 struct AttachPtr *saved = idx[second];
343 for (int i = second; i > first; i--)
344 idx[i] = idx[i - 1];
345 idx[first] = saved;
346
347 // if moved attachment is a group then move subparts too
348 if ((idx[first]->body->type == TYPE_MULTIPART) && (second < actx->idxlen - 1))
349 {
350 int i = second + 1;
351 while (idx[i]->level > idx[first]->level)
352 {
353 saved = idx[i];
354 int destidx = i - second + first;
355 for (int j = i; j > destidx; j--)
356 idx[j] = idx[j - 1];
357 idx[destidx] = saved;
358 i++;
359 if (i >= actx->idxlen)
360 break;
361 }
362 }
363}
364
372static int group_attachments(struct ComposeSharedData *shared, char *subtype)
373{
374 struct AttachCtx *actx = shared->adata->actx;
375 int group_level = -1;
376 struct Body *bptr_parent = NULL;
377
378 // Attachments to be grouped must have the same parent
379 for (int i = 0; i < actx->idxlen; i++)
380 {
381 // check if all tagged attachments are at same level
382 if (actx->idx[i]->body->tagged)
383 {
384 if (group_level == -1)
385 {
386 group_level = actx->idx[i]->level;
387 }
388 else
389 {
390 if (group_level != actx->idx[i]->level)
391 {
392 mutt_error(_("Attachments to be grouped must have the same parent"));
393 return FR_ERROR;
394 }
395 }
396 // if not at top level check if all tagged attachments have same parent
397 if (group_level > 0)
398 {
399 if (bptr_parent)
400 {
401 struct Body *bptr_test = NULL;
402 if (!attach_body_parent(actx->idx[0]->body, NULL, actx->idx[i]->body, &bptr_test))
403 mutt_debug(LL_DEBUG5, "can't find parent\n");
404 if (bptr_test != bptr_parent)
405 {
406 mutt_error(_("Attachments to be grouped must have the same parent"));
407 return FR_ERROR;
408 }
409 }
410 else
411 {
412 if (!attach_body_parent(actx->idx[0]->body, NULL, actx->idx[i]->body, &bptr_parent))
413 mutt_debug(LL_DEBUG5, "can't find parent\n");
414 }
415 }
416 }
417 }
418
419 // Can't group all attachments unless at top level
420 if (bptr_parent)
421 {
422 if (shared->adata->menu->num_tagged == attach_body_count(bptr_parent->parts, false))
423 {
424 mutt_error(_("Can't leave group with only one attachment"));
425 return FR_ERROR;
426 }
427 }
428
429 struct Body *group = mutt_body_new();
430 group->type = TYPE_MULTIPART;
431 group->subtype = mutt_str_dup(subtype);
432 group->encoding = ENC_7BIT;
433
434 struct Body *bptr_first = NULL; // first tagged attachment
435 struct Body *bptr = NULL; // current tagged attachment
436 struct Body *group_parent = NULL; // parent of group
437 struct Body *group_previous = NULL; // previous body to group
438 struct Body *group_part = NULL; // current attachment in group
439 int group_idx = 0; // index in attachment list where group will be inserted
440 int group_last_idx = 0; // index of last part of previous found group
441 int group_parent_type = TYPE_OTHER;
442
443 for (int i = 0; i < actx->idxlen; i++)
444 {
445 bptr = actx->idx[i]->body;
446 if (bptr->tagged)
447 {
448 // set group properties based on first tagged attachment
449 if (!bptr_first)
450 {
451 group->disposition = bptr->disposition;
452 if (bptr->language && !mutt_str_equal(subtype, "multilingual"))
453 group->language = mutt_str_dup(bptr->language);
454 group_parent_type = bptr->aptr->parent_type;
455 bptr_first = bptr;
456 if (i > 0)
457 {
458 if (!attach_body_previous(shared->email->body, bptr, &group_previous))
459 {
460 mutt_debug(LL_DEBUG5, "couldn't find previous\n");
461 }
462 if (!attach_body_parent(shared->email->body, NULL, bptr, &group_parent))
463 {
464 mutt_debug(LL_DEBUG5, "couldn't find parent\n");
465 }
466 }
467 }
468
469 shared->adata->menu->num_tagged--;
470 bptr->tagged = false;
471 bptr->aptr->level++;
473
474 // append bptr to the group parts list and remove from email body list
475 struct Body *bptr_previous = NULL;
476 if (attach_body_previous(shared->email->body, bptr, &bptr_previous))
477 bptr_previous->next = bptr->next;
478 else if (attach_body_parent(shared->email->body, NULL, bptr, &bptr_parent))
479 bptr_parent->parts = bptr->next;
480 else
481 shared->email->body = bptr->next;
482
483 if (group_part)
484 {
485 // add bptr to group parts list
486 group_part->next = bptr;
487 group_part = group_part->next;
488 group_part->next = NULL;
489
490 // reorder attachments and set levels
491 int bptr_attachments = attach_body_count(bptr, true);
492 for (int j = i + 1; j < (i + bptr_attachments); j++)
493 actx->idx[j]->level++;
494 if (i > (group_last_idx + 1))
495 {
496 for (int j = 0; j < bptr_attachments; j++)
497 {
498 struct AttachPtr *saved = actx->idx[i + bptr_attachments - 1];
499 for (int k = i + bptr_attachments - 1; k > (group_last_idx + 1); k--)
500 actx->idx[k] = actx->idx[k - 1];
501 actx->idx[group_last_idx + 1] = saved;
502 }
503 }
504 i += bptr_attachments - 1;
505 group_last_idx += bptr_attachments;
506 }
507 else
508 {
509 group_idx = i;
510 group->parts = bptr;
511 group_part = bptr;
512 group_part->next = NULL;
513 int bptr_attachments = attach_body_count(bptr, true);
514 for (int j = i + 1; j < (i + bptr_attachments); j++)
515 actx->idx[j]->level++;
516 i += bptr_attachments - 1;
517 group_last_idx = i;
518 }
519 }
520 }
521
522 if (!bptr_first)
523 {
524 mutt_body_free(&group);
525 return FR_ERROR;
526 }
527
528 // set group->next
529 int next_aidx = group_idx + attach_body_count(group->parts, true);
530 if (group_parent)
531 {
532 // find next attachment with the same parent as the group
533 struct Body *b = NULL;
534 struct Body *b_parent = NULL;
535 while (next_aidx < actx->idxlen)
536 {
537 b = actx->idx[next_aidx]->body;
538 b_parent = NULL;
539 if (attach_body_parent(shared->email->body, NULL, b, &b_parent))
540 {
541 if (group_parent == b_parent)
542 {
543 group->next = b;
544 break;
545 }
546 }
547 next_aidx++;
548 }
549 }
550 else if (next_aidx < actx->idxlen)
551 {
552 // group is at top level
553 group->next = actx->idx[next_aidx]->body;
554 }
555
556 // set previous or parent for group
557 if (group_previous)
558 group_previous->next = group;
559 else if (group_parent)
560 group_parent->parts = group;
561
563
564 struct AttachPtr *group_ap = mutt_aptr_new();
565 group_ap->body = group;
566 group_ap->body->aptr = group_ap;
567 group_ap->level = group_level;
568 group_ap->parent_type = group_parent_type;
569
570 // insert group into attachment list
571 mutt_actx_ins_attach(actx, group_ap, group_idx);
572
573 // update email body and last attachment pointers
574 shared->email->body = actx->idx[0]->body;
575 actx->idx[actx->idxlen - 1]->body->next = NULL;
576
577 update_menu(actx, shared->adata->menu, false);
578 shared->adata->menu->current = group_idx;
580
582 return FR_SUCCESS;
583}
584
585// -----------------------------------------------------------------------------
586
590static int op_attachment_attach_file(struct ComposeSharedData *shared, int op)
591{
592 char *prompt = _("Attach file");
593 int numfiles = 0;
594 char **files = NULL;
595
596 struct Buffer *fname = mutt_buffer_pool_get();
597 if ((mutt_buffer_enter_fname(prompt, fname, false, NULL, true, &files,
598 &numfiles, MUTT_SEL_MULTI) == -1) ||
600 {
601 for (int i = 0; i < numfiles; i++)
602 FREE(&files[i]);
603
604 FREE(&files);
606 return FR_NO_ACTION;
607 }
608
609 bool error = false;
610 bool added_attachment = false;
611 if (numfiles > 1)
612 {
613 mutt_message(ngettext("Attaching selected file...",
614 "Attaching selected files...", numfiles));
615 }
616 for (int i = 0; i < numfiles; i++)
617 {
618 char *att = files[i];
619 if (!att)
620 continue;
621
622 struct AttachPtr *ap = mutt_aptr_new();
623 ap->unowned = true;
624 ap->body = mutt_make_file_attach(att, shared->sub);
625 if (ap->body)
626 {
627 added_attachment = true;
628 update_idx(shared->adata->menu, shared->adata->actx, ap);
629 }
630 else
631 {
632 error = true;
633 mutt_error(_("Unable to attach %s"), att);
634 mutt_aptr_free(&ap);
635 }
636 FREE(&files[i]);
637 }
638
639 FREE(&files);
641
642 if (!error)
644
647 if (added_attachment)
649 return FR_SUCCESS;
650}
651
655static int op_attachment_attach_key(struct ComposeSharedData *shared, int op)
656{
658 return FR_NOT_IMPL;
659 struct AttachPtr *ap = mutt_aptr_new();
661 if (ap->body)
662 {
663 update_idx(shared->adata->menu, shared->adata->actx, ap);
666 }
667 else
668 {
669 mutt_aptr_free(&ap);
670 }
671
673 return FR_SUCCESS;
674}
675
683static int op_attachment_attach_message(struct ComposeSharedData *shared, int op)
684{
685 char *prompt = _("Open mailbox to attach message from");
686
687#ifdef USE_NNTP
688 OptNews = false;
689 if (shared->mailbox && (op == OP_ATTACHMENT_ATTACH_NEWS_MESSAGE))
690 {
691 const char *const c_news_server = cs_subset_string(shared->sub, "news_server");
692 CurrentNewsSrv = nntp_select_server(shared->mailbox, c_news_server, false);
693 if (!CurrentNewsSrv)
694 return FR_NO_ACTION;
695
696 prompt = _("Open newsgroup to attach message from");
697 OptNews = true;
698 }
699#endif
700
701 struct Buffer *fname = mutt_buffer_pool_get();
702 if (shared->mailbox)
703 {
704#ifdef USE_NNTP
705 if ((op == OP_ATTACHMENT_ATTACH_MESSAGE) ^ (shared->mailbox->type == MUTT_NNTP))
706#endif
707 {
708 mutt_buffer_strcpy(fname, mailbox_path(shared->mailbox));
710 }
711 }
712
713 if ((mutt_buffer_enter_fname(prompt, fname, true, shared->mailbox, false,
714 NULL, NULL, MUTT_SEL_NO_FLAGS) == -1) ||
716 {
718 return FR_NO_ACTION;
719 }
720
721#ifdef USE_NNTP
722 if (OptNews)
724 else
725#endif
727#ifdef USE_IMAP
728 if (imap_path_probe(mutt_buffer_string(fname), NULL) != MUTT_IMAP)
729#endif
730#ifdef USE_POP
731 if (pop_path_probe(mutt_buffer_string(fname), NULL) != MUTT_POP)
732#endif
733#ifdef USE_NNTP
734 if (!OptNews && (nntp_path_probe(mutt_buffer_string(fname), NULL) != MUTT_NNTP))
735#endif
737 {
738 /* check to make sure the file exists and is readable */
739 if (access(mutt_buffer_string(fname), R_OK) == -1)
740 {
743 return FR_ERROR;
744 }
745 }
746
748
749 struct Mailbox *m_attach = mx_path_resolve(mutt_buffer_string(fname));
750 const bool old_readonly = m_attach->readonly;
751 if (!mx_mbox_open(m_attach, MUTT_READONLY))
752 {
753 mutt_error(_("Unable to open mailbox %s"), mutt_buffer_string(fname));
754 mx_fastclose_mailbox(m_attach, false);
755 m_attach = NULL;
757 return FR_ERROR;
758 }
760
761 if (m_attach->msg_count == 0)
762 {
763 mx_mbox_close(m_attach);
764 mutt_error(_("No messages in that folder"));
765 return FR_NO_ACTION;
766 }
767
768 /* `$sort`, `$sort_aux`, `$use_threads` could be changed in mutt_index_menu() */
769 const enum SortType old_sort = cs_subset_sort(shared->sub, "sort");
770 const enum SortType old_sort_aux = cs_subset_sort(shared->sub, "sort_aux");
771 const unsigned char old_use_threads = cs_subset_enum(shared->sub, "use_threads");
772
773 OptAttachMsg = true;
774 mutt_message(_("Tag the messages you want to attach"));
775 struct MuttWindow *dlg_index = index_pager_init();
776 dialog_push(dlg_index);
777 struct Mailbox *m_attach_new = mutt_index_menu(dlg_index, m_attach);
778 dialog_pop();
779 mutt_window_free(&dlg_index);
780 OptAttachMsg = false;
781
782 if (!shared->mailbox)
783 {
784 /* Restore old $sort variables */
785 cs_subset_str_native_set(shared->sub, "sort", old_sort, NULL);
786 cs_subset_str_native_set(shared->sub, "sort_aux", old_sort_aux, NULL);
787 cs_subset_str_native_set(shared->sub, "use_threads", old_use_threads, NULL);
790 return FR_SUCCESS;
791 }
792
793 bool added_attachment = false;
794 for (int i = 0; i < m_attach_new->msg_count; i++)
795 {
796 if (!m_attach_new->emails[i])
797 break;
798 if (!message_is_tagged(m_attach_new->emails[i]))
799 continue;
800
801 struct AttachPtr *ap = mutt_aptr_new();
802 ap->body = mutt_make_message_attach(m_attach_new, m_attach_new->emails[i],
803 true, shared->sub);
804 if (ap->body)
805 {
806 added_attachment = true;
807 update_idx(shared->adata->menu, shared->adata->actx, ap);
808 }
809 else
810 {
811 mutt_error(_("Unable to attach"));
812 mutt_aptr_free(&ap);
813 }
814 }
816
817 if (m_attach_new == m_attach)
818 {
819 m_attach->readonly = old_readonly;
820 }
821 mx_fastclose_mailbox(m_attach_new, false);
822
823 /* Restore old $sort variables */
824 cs_subset_str_native_set(shared->sub, "sort", old_sort, NULL);
825 cs_subset_str_native_set(shared->sub, "sort_aux", old_sort_aux, NULL);
826 cs_subset_str_native_set(shared->sub, "use_threads", old_use_threads, NULL);
827 if (added_attachment)
829 return FR_SUCCESS;
830}
831
835static int op_attachment_detach(struct ComposeSharedData *shared, int op)
836{
837 struct AttachCtx *actx = shared->adata->actx;
838 if (!check_count(actx))
839 return FR_NO_ACTION;
840
841 struct Menu *menu = shared->adata->menu;
842 struct AttachPtr *cur_att = current_attachment(actx, menu);
843 if (cur_att->unowned)
844 cur_att->body->unlink = false;
845
846 int index = menu_get_index(menu);
847 if (delete_attachment(actx, index) == -1)
848 return FR_ERROR;
849
850 menu->num_tagged = 0;
851 for (int i = 0; i < actx->idxlen; i++)
852 {
853 if (actx->idx[i]->body->tagged)
854 menu->num_tagged++;
855 }
856
857 update_menu(actx, menu, false);
859
860 index = menu_get_index(menu);
861 if (index == 0)
862 shared->email->body = actx->idx[0]->body;
863
865 return FR_SUCCESS;
866}
867
871static int op_attachment_edit_content_id(struct ComposeSharedData *shared, int op)
872{
873 if (!check_count(shared->adata->actx))
874 return FR_NO_ACTION;
875
876 int rc = FR_NO_ACTION;
877 struct Buffer *buf = mutt_buffer_pool_get();
878 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
879 shared->adata->menu);
880
881 char *id = mutt_param_get(&cur_att->body->parameter, "content-id");
882 if (id)
883 {
884 mutt_buffer_strcpy(buf, id);
885 }
886 else
887 {
888 id = gen_cid();
889 mutt_buffer_strcpy(buf, id);
890 FREE(&id);
891 }
892
893 if (mutt_buffer_get_field("Content-ID: ", buf, MUTT_COMP_NO_FLAGS, false,
894 NULL, NULL, NULL) == 0)
895 {
896 if (!mutt_str_equal(id, mutt_buffer_string(buf)))
897 {
899 {
900 mutt_param_set(&cur_att->body->parameter, "content-id", mutt_buffer_string(buf));
904 rc = FR_SUCCESS;
905 }
906 else
907 {
908 mutt_error(_("Content-ID can only contain the characters: -.0-9@A-Z_a-z"));
909 rc = FR_ERROR;
910 }
911 }
912 }
913
915
916 if (rc != FR_ERROR)
918
919 return rc;
920}
921
925static int op_attachment_edit_description(struct ComposeSharedData *shared, int op)
926{
927 if (!check_count(shared->adata->actx))
928 return FR_NO_ACTION;
929
930 int rc = FR_NO_ACTION;
931 struct Buffer *buf = mutt_buffer_pool_get();
932
933 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
934 shared->adata->menu);
935 mutt_buffer_strcpy(buf, cur_att->body->description);
936
937 /* header names should not be translated */
938 if (mutt_buffer_get_field("Description: ", buf, MUTT_COMP_NO_FLAGS, false,
939 NULL, NULL, NULL) == 0)
940 {
942 {
946 rc = FR_SUCCESS;
947 }
948 }
949
951 return rc;
952}
953
957static int op_attachment_edit_encoding(struct ComposeSharedData *shared, int op)
958{
959 if (!check_count(shared->adata->actx))
960 return FR_NO_ACTION;
961
962 int rc = FR_NO_ACTION;
963 struct Buffer *buf = mutt_buffer_pool_get();
964
965 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
966 shared->adata->menu);
967 mutt_buffer_strcpy(buf, ENCODING(cur_att->body->encoding));
968
969 if ((mutt_buffer_get_field("Content-Transfer-Encoding: ", buf,
970 MUTT_COMP_NO_FLAGS, false, NULL, NULL, NULL) == 0) &&
972 {
974 if ((enc != ENC_OTHER) && (enc != ENC_UUENCODED))
975 {
976 if (enc != cur_att->body->encoding)
977 {
978 cur_att->body->encoding = enc;
983 rc = FR_SUCCESS;
984 }
985 }
986 else
987 {
988 mutt_error(_("Invalid encoding"));
989 rc = FR_ERROR;
990 }
991 }
992
994 return rc;
995}
996
1000static int op_attachment_edit_language(struct ComposeSharedData *shared, int op)
1001{
1002 if (!check_count(shared->adata->actx))
1003 return FR_NO_ACTION;
1004
1005 int rc = FR_NO_ACTION;
1006 struct Buffer *buf = mutt_buffer_pool_get();
1007 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
1008 shared->adata->menu);
1009
1010 mutt_buffer_strcpy(buf, cur_att->body->language);
1011 if (mutt_buffer_get_field("Content-Language: ", buf, MUTT_COMP_NO_FLAGS,
1012 false, NULL, NULL, NULL) == 0)
1013 {
1014 if (!mutt_str_equal(cur_att->body->language, mutt_buffer_string(buf)))
1015 {
1019 mutt_message_hook(NULL, shared->email, MUTT_SEND2_HOOK);
1020 rc = FR_SUCCESS;
1021 }
1023 }
1024 else
1025 {
1026 mutt_warning(_("Empty 'Content-Language'"));
1027 rc = FR_ERROR;
1028 }
1029
1031 return rc;
1032}
1033
1037static int op_attachment_edit_mime(struct ComposeSharedData *shared, int op)
1038{
1039 if (!check_count(shared->adata->actx))
1040 return FR_NO_ACTION;
1041 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
1042 shared->adata->menu);
1043 if (!mutt_edit_attachment(cur_att->body))
1044 return FR_NO_ACTION;
1045
1046 mutt_update_encoding(cur_att->body, shared->sub);
1048 mutt_message_hook(NULL, shared->email, MUTT_SEND2_HOOK);
1049 return FR_SUCCESS;
1050}
1051
1055static int op_attachment_edit_type(struct ComposeSharedData *shared, int op)
1056{
1057 if (!check_count(shared->adata->actx))
1058 return FR_NO_ACTION;
1059
1060 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
1061 shared->adata->menu);
1062 if (!mutt_edit_content_type(NULL, cur_att->body, NULL))
1063 return FR_NO_ACTION;
1064
1065 /* this may have been a change to text/something */
1066 mutt_update_encoding(cur_att->body, shared->sub);
1068 mutt_message_hook(NULL, shared->email, MUTT_SEND2_HOOK);
1069 return FR_SUCCESS;
1070}
1071
1079static int op_attachment_filter(struct ComposeSharedData *shared, int op)
1080{
1081 struct AttachCtx *actx = shared->adata->actx;
1082 if (!check_count(actx))
1083 return FR_NO_ACTION;
1084
1085 struct Menu *menu = shared->adata->menu;
1086 struct AttachPtr *cur_att = current_attachment(actx, menu);
1087 if (cur_att->body->type == TYPE_MULTIPART)
1088 {
1089 mutt_error(_("Can't filter multipart attachments"));
1090 return FR_ERROR;
1091 }
1092 mutt_pipe_attachment_list(actx, NULL, menu->tag_prefix, cur_att->body,
1093 (op == OP_ATTACHMENT_FILTER));
1094 if (op == OP_ATTACHMENT_FILTER) /* cte might have changed */
1095 {
1097 }
1099 mutt_message_hook(NULL, shared->email, MUTT_SEND2_HOOK);
1100 return FR_SUCCESS;
1101}
1102
1106static int op_attachment_get_attachment(struct ComposeSharedData *shared, int op)
1107{
1108 struct AttachCtx *actx = shared->adata->actx;
1109 if (!check_count(actx))
1110 return FR_NO_ACTION;
1111
1112 int rc = FR_ERROR;
1113 struct Menu *menu = shared->adata->menu;
1114 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1115 ba_add_tagged(&ba, actx, menu);
1116 if (ARRAY_EMPTY(&ba))
1117 goto done;
1118
1119 struct Body **bp = NULL;
1120 ARRAY_FOREACH(bp, &ba)
1121 {
1122 if ((*bp)->type == TYPE_MULTIPART)
1123 {
1124 mutt_warning(_("Can't get multipart attachments"));
1125 continue;
1126 }
1128 }
1129
1131 rc = FR_SUCCESS;
1132
1133done:
1134 ARRAY_FREE(&ba);
1135 /* No send2hook since this doesn't change the message. */
1136 return rc;
1137}
1138
1142static int op_attachment_group_alts(struct ComposeSharedData *shared, int op)
1143{
1144 if (shared->adata->menu->num_tagged < 2)
1145 {
1146 mutt_error(_("Grouping 'alternatives' requires at least 2 tagged messages"));
1147 return FR_ERROR;
1148 }
1149
1150 return group_attachments(shared, "alternative");
1151}
1152
1156static int op_attachment_group_lingual(struct ComposeSharedData *shared, int op)
1157{
1158 if (shared->adata->menu->num_tagged < 2)
1159 {
1160 mutt_error(_("Grouping 'multilingual' requires at least 2 tagged messages"));
1161 return FR_ERROR;
1162 }
1163
1164 /* traverse to see whether all the parts have Content-Language: set */
1165 int tagged_with_lang_num = 0;
1166 for (struct Body *b = shared->email->body; b; b = b->next)
1167 if (b->tagged && b->language && *b->language)
1168 tagged_with_lang_num++;
1169
1170 if (shared->adata->menu->num_tagged != tagged_with_lang_num)
1171 {
1172 if (mutt_yesorno(_("Not all parts have 'Content-Language' set, continue?"), MUTT_YES) != MUTT_YES)
1173 {
1174 mutt_message(_("Not sending this message"));
1175 return FR_ERROR;
1176 }
1177 }
1178
1179 return group_attachments(shared, "multilingual");
1180}
1181
1185static int op_attachment_group_related(struct ComposeSharedData *shared, int op)
1186{
1187 if (shared->adata->menu->num_tagged < 2)
1188 {
1189 mutt_error(_("Grouping 'related' requires at least 2 tagged messages"));
1190 return FR_ERROR;
1191 }
1192
1193 // ensure Content-ID is set for tagged attachments
1194 for (struct Body *b = shared->email->body; b; b = b->next)
1195 {
1196 if (!b->tagged || (b->type == TYPE_MULTIPART))
1197 continue;
1198
1199 char *id = mutt_param_get(&b->parameter, "content-id");
1200 if (id)
1201 continue;
1202
1203 id = gen_cid();
1204 if (id)
1205 {
1206 mutt_param_set(&b->parameter, "content-id", id);
1207 FREE(&id);
1208 }
1209 }
1210
1211 return group_attachments(shared, "related");
1212}
1213
1217static int op_attachment_move_down(struct ComposeSharedData *shared, int op)
1218{
1219 int index = menu_get_index(shared->adata->menu);
1220
1221 struct AttachCtx *actx = shared->adata->actx;
1222
1223 if (index < 0)
1224 return FR_ERROR;
1225
1226 if (index == (actx->idxlen - 1))
1227 {
1228 mutt_error(_("Attachment is already at bottom"));
1229 return FR_NO_ACTION;
1230 }
1231 if ((actx->idx[index]->parent_type == TYPE_MULTIPART) &&
1232 !actx->idx[index]->body->next)
1233 {
1234 mutt_error(_("Attachment can't be moved out of group"));
1235 return FR_ERROR;
1236 }
1237
1238 // find next attachment at current level
1239 int nextidx = index + 1;
1240 while ((nextidx < actx->idxlen) &&
1241 (actx->idx[nextidx]->level > actx->idx[index]->level))
1242 {
1243 nextidx++;
1244 }
1245 if (nextidx == actx->idxlen)
1246 {
1247 mutt_error(_("Attachment is already at bottom"));
1248 return FR_NO_ACTION;
1249 }
1250
1251 // find final position
1252 int finalidx = index + 1;
1253 if (nextidx < actx->idxlen - 1)
1254 {
1255 if ((actx->idx[nextidx]->body->type == TYPE_MULTIPART) &&
1256 (actx->idx[nextidx + 1]->level > actx->idx[nextidx]->level))
1257 {
1258 finalidx += attach_body_count(actx->idx[nextidx]->body->parts, true);
1259 }
1260 }
1261
1262 compose_attach_swap(shared->email, shared->adata->actx, index, nextidx);
1263 mutt_update_tree(shared->adata->actx);
1265 menu_set_index(shared->adata->menu, finalidx);
1266 return FR_SUCCESS;
1267}
1268
1272static int op_attachment_move_up(struct ComposeSharedData *shared, int op)
1273{
1274 int index = menu_get_index(shared->adata->menu);
1275 if (index < 0)
1276 return FR_ERROR;
1277
1278 struct AttachCtx *actx = shared->adata->actx;
1279
1280 if (index == 0)
1281 {
1282 mutt_error(_("Attachment is already at top"));
1283 return FR_NO_ACTION;
1284 }
1285 if (actx->idx[index - 1]->level < actx->idx[index]->level)
1286 {
1287 mutt_error(_("Attachment can't be moved out of group"));
1288 return FR_ERROR;
1289 }
1290
1291 // find previous attachment at current level
1292 int previdx = index - 1;
1293 while ((previdx > 0) && (actx->idx[previdx]->level > actx->idx[index]->level))
1294 previdx--;
1295
1296 compose_attach_swap(shared->email, actx, previdx, index);
1297 mutt_update_tree(actx);
1299 menu_set_index(shared->adata->menu, previdx);
1300 return FR_SUCCESS;
1301}
1302
1306static int op_attachment_new_mime(struct ComposeSharedData *shared, int op)
1307{
1308 int rc = FR_NO_ACTION;
1309 struct Buffer *fname = mutt_buffer_pool_get();
1310 struct Buffer *type = NULL;
1311 struct AttachPtr *ap = NULL;
1312
1313 if ((mutt_buffer_get_field(_("New file: "), fname, MUTT_COMP_FILE, false,
1314 NULL, NULL, NULL) != 0) ||
1315 mutt_buffer_is_empty(fname))
1316 {
1317 goto done;
1318 }
1320
1321 /* Call to lookup_mime_type () ? maybe later */
1322 type = mutt_buffer_pool_get();
1323 if ((mutt_buffer_get_field("Content-Type: ", type, MUTT_COMP_NO_FLAGS, false,
1324 NULL, NULL, NULL) != 0) ||
1326 {
1327 goto done;
1328 }
1329
1330 rc = FR_ERROR;
1331 char *p = strchr(mutt_buffer_string(type), '/');
1332 if (!p)
1333 {
1334 mutt_error(_("Content-Type is of the form base/sub"));
1335 goto done;
1336 }
1337 *p++ = 0;
1339 if (itype == TYPE_OTHER)
1340 {
1341 mutt_error(_("Unknown Content-Type %s"), mutt_buffer_string(type));
1342 goto done;
1343 }
1344
1345 ap = mutt_aptr_new();
1346 /* Touch the file */
1347 FILE *fp = mutt_file_fopen(mutt_buffer_string(fname), "w");
1348 if (!fp)
1349 {
1350 mutt_error(_("Can't create file %s"), mutt_buffer_string(fname));
1351 goto done;
1352 }
1354
1355 ap->body = mutt_make_file_attach(mutt_buffer_string(fname), shared->sub);
1356 if (!ap->body)
1357 {
1358 mutt_error(_("What we have here is a failure to make an attachment"));
1359 goto done;
1360 }
1361 update_idx(shared->adata->menu, shared->adata->actx, ap);
1362 ap = NULL; // shared->adata->actx has taken ownership
1363
1364 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
1365 shared->adata->menu);
1366 cur_att->body->type = itype;
1367 mutt_str_replace(&cur_att->body->subtype, p);
1368 cur_att->body->unlink = true;
1371
1372 if (mutt_compose_attachment(cur_att->body))
1373 {
1374 mutt_update_encoding(cur_att->body, shared->sub);
1376 }
1377 mutt_message_hook(NULL, shared->email, MUTT_SEND2_HOOK);
1378 rc = FR_SUCCESS;
1379
1380done:
1381 mutt_aptr_free(&ap);
1384 return rc;
1385}
1386
1390static int op_attachment_print(struct ComposeSharedData *shared, int op)
1391{
1392 struct AttachCtx *actx = shared->adata->actx;
1393 if (!check_count(actx))
1394 return FR_NO_ACTION;
1395
1396 struct Menu *menu = shared->adata->menu;
1397 struct AttachPtr *cur_att = current_attachment(actx, menu);
1398 if (cur_att->body->type == TYPE_MULTIPART)
1399 {
1400 mutt_error(_("Can't print multipart attachments"));
1401 return FR_ERROR;
1402 }
1403
1404 mutt_print_attachment_list(actx, NULL, menu->tag_prefix, cur_att->body);
1405 /* no send2hook, since this doesn't modify the message */
1406 return FR_SUCCESS;
1407}
1408
1412static int op_attachment_rename_attachment(struct ComposeSharedData *shared, int op)
1413{
1414 if (!check_count(shared->adata->actx))
1415 return FR_NO_ACTION;
1416 char *src = NULL;
1417 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
1418 shared->adata->menu);
1419 if (cur_att->body->d_filename)
1420 src = cur_att->body->d_filename;
1421 else
1422 src = cur_att->body->filename;
1423 struct Buffer *fname = mutt_buffer_pool_get();
1425 int rc = mutt_buffer_get_field(_("Send attachment with name: "), fname,
1426 MUTT_COMP_FILE, false, NULL, NULL, NULL);
1427 if (rc == 0)
1428 {
1429 // It's valid to set an empty string here, to erase what was set
1432 }
1434 return FR_SUCCESS;
1435}
1436
1440static int op_attachment_save(struct ComposeSharedData *shared, int op)
1441{
1442 struct AttachCtx *actx = shared->adata->actx;
1443 if (!check_count(actx))
1444 return FR_NO_ACTION;
1445
1446 struct Menu *menu = shared->adata->menu;
1447 struct AttachPtr *cur_att = current_attachment(actx, menu);
1448 if (cur_att->body->type == TYPE_MULTIPART)
1449 {
1450 mutt_error(_("Can't save multipart attachments"));
1451 return FR_ERROR;
1452 }
1453
1454 mutt_save_attachment_list(actx, NULL, menu->tag_prefix, cur_att->body, NULL, menu);
1455 /* no send2hook, since this doesn't modify the message */
1456 return FR_SUCCESS;
1457}
1458
1462static int op_attachment_toggle_disposition(struct ComposeSharedData *shared, int op)
1463{
1464 /* toggle the content-disposition between inline/attachment */
1465 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
1466 shared->adata->menu);
1467 cur_att->body->disposition = (cur_att->body->disposition == DISP_INLINE) ?
1468 DISP_ATTACH :
1471 return FR_SUCCESS;
1472}
1473
1477static int op_attachment_toggle_recode(struct ComposeSharedData *shared, int op)
1478{
1479 if (!check_count(shared->adata->actx))
1480 return FR_NO_ACTION;
1481 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
1482 shared->adata->menu);
1483 if (!mutt_is_text_part(cur_att->body))
1484 {
1485 mutt_error(_("Recoding only affects text attachments"));
1486 return FR_ERROR;
1487 }
1488 cur_att->body->noconv = !cur_att->body->noconv;
1489 if (cur_att->body->noconv)
1490 mutt_message(_("The current attachment won't be converted"));
1491 else
1492 mutt_message(_("The current attachment will be converted"));
1494 mutt_message_hook(NULL, shared->email, MUTT_SEND2_HOOK);
1495 return FR_SUCCESS;
1496}
1497
1501static int op_attachment_toggle_unlink(struct ComposeSharedData *shared, int op)
1502{
1503 if (!check_count(shared->adata->actx))
1504 return FR_NO_ACTION;
1505 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
1506 shared->adata->menu);
1507 cur_att->body->unlink = !cur_att->body->unlink;
1508
1510 /* No send2hook since this doesn't change the message. */
1511 return FR_SUCCESS;
1512}
1513
1517static int op_attachment_ungroup(struct ComposeSharedData *shared, int op)
1518{
1519 if (shared->adata->actx->idx[shared->adata->menu->current]->body->type != TYPE_MULTIPART)
1520 {
1521 mutt_error(_("Attachment is not 'multipart'"));
1522 return FR_ERROR;
1523 }
1524
1525 int aidx = shared->adata->menu->current;
1526 struct AttachCtx *actx = shared->adata->actx;
1527 struct Body *bptr = actx->idx[aidx]->body;
1528 struct Body *bptr_next = bptr->next;
1529 struct Body *bptr_previous = NULL;
1530 struct Body *bptr_parent = NULL;
1531 int parent_type = actx->idx[aidx]->parent_type;
1532 int level = actx->idx[aidx]->level;
1533
1534 // reorder body pointers
1535 if (attach_body_previous(shared->email->body, bptr, &bptr_previous))
1536 bptr_previous->next = bptr->parts;
1537 else if (attach_body_parent(shared->email->body, NULL, bptr, &bptr_parent))
1538 bptr_parent->parts = bptr->parts;
1539 else
1540 shared->email->body = bptr->parts;
1541
1542 // update attachment list
1543 int i = aidx + 1;
1544 while (actx->idx[i]->level > level)
1545 {
1546 actx->idx[i]->level--;
1547 if (actx->idx[i]->level == level)
1548 {
1549 actx->idx[i]->parent_type = parent_type;
1550 // set body->next for final attachment in group
1551 if (!actx->idx[i]->body->next)
1552 actx->idx[i]->body->next = bptr_next;
1553 }
1554 i++;
1555 if (i == actx->idxlen)
1556 break;
1557 }
1558
1559 // free memory
1560 actx->idx[aidx]->body->parts = NULL;
1561 actx->idx[aidx]->body->next = NULL;
1562 actx->idx[aidx]->body->email = NULL;
1563 mutt_body_free(&actx->idx[aidx]->body);
1564 FREE(&actx->idx[aidx]->tree);
1565 FREE(&actx->idx[aidx]);
1566
1567 // reorder attachment list
1568 for (int j = aidx; j < (actx->idxlen - 1); j++)
1569 actx->idx[j] = actx->idx[j + 1];
1570 actx->idx[actx->idxlen - 1] = NULL;
1571 actx->idxlen--;
1572 update_menu(actx, shared->adata->menu, false);
1573
1574 mutt_message_hook(NULL, shared->email, MUTT_SEND2_HOOK);
1575 return FR_SUCCESS;
1576}
1577
1581static int op_attachment_update_encoding(struct ComposeSharedData *shared, int op)
1582{
1583 struct AttachCtx *actx = shared->adata->actx;
1584 if (!check_count(actx))
1585 return FR_NO_ACTION;
1586
1587 int rc = FR_NO_ACTION;
1588 struct Menu *menu = shared->adata->menu;
1589 struct BodyArray ba = ARRAY_HEAD_INITIALIZER;
1590 ba_add_tagged(&ba, actx, menu);
1591 if (ARRAY_EMPTY(&ba))
1592 goto done;
1593
1594 struct Body **bp = NULL;
1595 ARRAY_FOREACH(bp, &ba)
1596 {
1597 mutt_update_encoding(*bp, shared->sub);
1598 }
1599
1602 mutt_message_hook(NULL, shared->email, MUTT_SEND2_HOOK);
1603 rc = FR_SUCCESS;
1604
1605done:
1606 ARRAY_FREE(&ba);
1607 return rc;
1608}
1609
1610// -----------------------------------------------------------------------------
1611
1615static int op_envelope_edit_headers(struct ComposeSharedData *shared, int op)
1616{
1618 const char *tag = NULL;
1619 char *err = NULL;
1620 mutt_env_to_local(shared->email->env);
1621 const char *const c_editor = cs_subset_string(shared->sub, "editor");
1622 if (shared->email->body->type == TYPE_MULTIPART)
1623 {
1624 struct Body *b = shared->email->body->parts;
1625 while (b->parts)
1626 b = b->parts;
1627 mutt_edit_headers(NONULL(c_editor), b->filename, shared->email, shared->fcc);
1628 }
1629 else
1630 {
1631 mutt_edit_headers(NONULL(c_editor), shared->email->body->filename,
1632 shared->email, shared->fcc);
1633 }
1634
1635 if (mutt_env_to_intl(shared->email->env, &tag, &err))
1636 {
1637 mutt_error(_("Bad IDN in '%s': '%s'"), tag, err);
1638 FREE(&err);
1639 }
1641
1643 mutt_update_encoding(shared->email->body, shared->sub);
1644
1645 /* attachments may have been added */
1646 if (shared->adata->actx->idxlen &&
1647 shared->adata->actx->idx[shared->adata->actx->idxlen - 1]->body->next)
1648 {
1650 update_menu(shared->adata->actx, shared->adata->menu, true);
1651 }
1652
1654 /* Unconditional hook since editor was invoked */
1655 mutt_message_hook(NULL, shared->email, MUTT_SEND2_HOOK);
1656 return FR_SUCCESS;
1657}
1658
1662static int op_compose_edit_file(struct ComposeSharedData *shared, int op)
1663{
1664 if (!check_count(shared->adata->actx))
1665 return FR_NO_ACTION;
1666 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
1667 shared->adata->menu);
1668 if (cur_att->body->type == TYPE_MULTIPART)
1669 {
1670 mutt_error(_("Can't edit multipart attachments"));
1671 return FR_ERROR;
1672 }
1673 const char *const c_editor = cs_subset_string(shared->sub, "editor");
1674 mutt_edit_file(NONULL(c_editor), cur_att->body->filename);
1675 mutt_update_encoding(cur_att->body, shared->sub);
1678 /* Unconditional hook since editor was invoked */
1679 mutt_message_hook(NULL, shared->email, MUTT_SEND2_HOOK);
1680 return FR_SUCCESS;
1681}
1682
1686static int op_compose_edit_message(struct ComposeSharedData *shared, int op)
1687{
1688 const bool c_edit_headers = cs_subset_bool(shared->sub, "edit_headers");
1689 if (!c_edit_headers)
1690 {
1692 const char *const c_editor = cs_subset_string(shared->sub, "editor");
1693 mutt_edit_file(c_editor, shared->email->body->filename);
1695 mutt_update_encoding(shared->email->body, shared->sub);
1697 /* Unconditional hook since editor was invoked */
1698 mutt_message_hook(NULL, shared->email, MUTT_SEND2_HOOK);
1699 return FR_SUCCESS;
1700 }
1701
1702 return op_envelope_edit_headers(shared, op);
1703}
1704
1708static int op_compose_ispell(struct ComposeSharedData *shared, int op)
1709{
1710 endwin();
1711 const char *const c_ispell = cs_subset_string(shared->sub, "ispell");
1712 char buf[PATH_MAX] = { 0 };
1713 snprintf(buf, sizeof(buf), "%s -x %s", NONULL(c_ispell), shared->email->body->filename);
1714 if (mutt_system(buf) == -1)
1715 {
1716 mutt_error(_("Error running \"%s\""), buf);
1717 return FR_ERROR;
1718 }
1719
1720 mutt_update_encoding(shared->email->body, shared->sub);
1722 return FR_SUCCESS;
1723}
1724
1728static int op_compose_postpone_message(struct ComposeSharedData *shared, int op)
1729{
1730 if (check_attachments(shared->adata->actx, shared->sub) != 0)
1731 {
1733 return FR_ERROR;
1734 }
1735
1736 shared->rc = 1;
1737 return FR_DONE;
1738}
1739
1743static int op_compose_rename_file(struct ComposeSharedData *shared, int op)
1744{
1745 if (!check_count(shared->adata->actx))
1746 return FR_NO_ACTION;
1747 struct AttachPtr *cur_att = current_attachment(shared->adata->actx,
1748 shared->adata->menu);
1749 if (cur_att->body->type == TYPE_MULTIPART)
1750 {
1751 mutt_error(_("Can't rename multipart attachments"));
1752 return FR_ERROR;
1753 }
1754 struct Buffer *fname = mutt_buffer_pool_get();
1755 mutt_buffer_strcpy(fname, cur_att->body->filename);
1757 if ((mutt_buffer_get_field(_("Rename to: "), fname, MUTT_COMP_FILE, false,
1758 NULL, NULL, NULL) == 0) &&
1759 !mutt_buffer_is_empty(fname))
1760 {
1761 struct stat st = { 0 };
1762 if (stat(cur_att->body->filename, &st) == -1)
1763 {
1764 /* L10N: "stat" is a system call. Do "man 2 stat" for more information. */
1765 mutt_error(_("Can't stat %s: %s"), mutt_buffer_string(fname), strerror(errno));
1767 return FR_ERROR;
1768 }
1769
1771 if (mutt_file_rename(cur_att->body->filename, mutt_buffer_string(fname)))
1772 {
1774 return FR_ERROR;
1775 }
1776
1779
1780 if (cur_att->body->stamp >= st.st_mtime)
1781 mutt_stamp_attachment(cur_att->body);
1782 mutt_message_hook(NULL, shared->email, MUTT_SEND2_HOOK);
1783 }
1785 return FR_SUCCESS;
1786}
1787
1791static int op_compose_send_message(struct ComposeSharedData *shared, int op)
1792{
1793 /* Note: We don't invoke send2-hook here, since we want to leave
1794 * users an opportunity to change settings from the ":" prompt. */
1795 if (check_attachments(shared->adata->actx, shared->sub) != 0)
1796 {
1798 return FR_NO_ACTION;
1799 }
1800
1801#ifdef MIXMASTER
1802 if (!STAILQ_EMPTY(&shared->email->chain) && (mix_check_message(shared->email) != 0))
1803 return FR_NO_ACTION;
1804#endif
1805
1806 if (!shared->fcc_set && !mutt_buffer_is_empty(shared->fcc))
1807 {
1808 const enum QuadOption c_copy = cs_subset_quad(shared->sub, "copy");
1809 enum QuadOption ans = query_quadoption(c_copy, _("Save a copy of this message?"));
1810 if (ans == MUTT_ABORT)
1811 return FR_NO_ACTION;
1812 else if (ans == MUTT_NO)
1813 mutt_buffer_reset(shared->fcc);
1814 }
1815
1816 shared->rc = 0;
1817 return FR_DONE;
1818}
1819
1823static int op_compose_write_message(struct ComposeSharedData *shared, int op)
1824{
1825 int rc = FR_NO_ACTION;
1826 struct Buffer *fname = mutt_buffer_pool_get();
1827 if (shared->mailbox)
1828 {
1829 mutt_buffer_strcpy(fname, mailbox_path(shared->mailbox));
1831 }
1832 if (shared->adata->actx->idxlen)
1833 shared->email->body = shared->adata->actx->idx[0]->body;
1834 if ((mutt_buffer_enter_fname(_("Write message to mailbox"), fname, true,
1835 shared->mailbox, false, NULL, NULL, MUTT_SEL_NO_FLAGS) != -1) &&
1836 !mutt_buffer_is_empty(fname))
1837 {
1838 mutt_message(_("Writing message to %s ..."), mutt_buffer_string(fname));
1840
1841 if (shared->email->body->next)
1842 shared->email->body = mutt_make_multipart(shared->email->body);
1843
1844 if (mutt_write_fcc(mutt_buffer_string(fname), shared->email, NULL, false,
1845 NULL, NULL, shared->sub) == 0)
1846 mutt_message(_("Message written"));
1847
1848 shared->email->body = mutt_remove_multipart(shared->email->body);
1849 rc = FR_SUCCESS;
1850 }
1852 return rc;
1853}
1854
1862static int op_display_headers(struct ComposeSharedData *shared, int op)
1863{
1864 if (!check_count(shared->adata->actx))
1865 return FR_NO_ACTION;
1866 mutt_attach_display_loop(shared->sub, shared->adata->menu, op, shared->email,
1867 shared->adata->actx, false);
1869 /* no send2hook, since this doesn't modify the message */
1870 return FR_SUCCESS;
1871}
1872
1876static int op_exit(struct ComposeSharedData *shared, int op)
1877{
1878 const enum QuadOption c_postpone = cs_subset_quad(shared->sub, "postpone");
1879 enum QuadOption ans = query_quadoption(c_postpone, _("Save (postpone) draft message?"));
1880 if (ans == MUTT_NO)
1881 {
1882 for (int i = 0; i < shared->adata->actx->idxlen; i++)
1883 if (shared->adata->actx->idx[i]->unowned)
1884 shared->adata->actx->idx[i]->body->unlink = false;
1885
1886 if (!(shared->flags & MUTT_COMPOSE_NOFREEHEADER))
1887 {
1888 for (int i = 0; i < shared->adata->actx->idxlen; i++)
1889 {
1890 /* avoid freeing other attachments */
1891 shared->adata->actx->idx[i]->body->next = NULL;
1892 /* See the comment in delete_attachment() */
1893 if (!shared->adata->actx->idx[i]->body->email)
1894 shared->adata->actx->idx[i]->body->parts = NULL;
1895 mutt_body_free(&shared->adata->actx->idx[i]->body);
1896 }
1897 }
1898 shared->rc = -1;
1899 return FR_DONE;
1900 }
1901 else if (ans == MUTT_ABORT)
1902 return FR_NO_ACTION;
1903
1904 return op_compose_postpone_message(shared, op);
1905}
1906
1910static int op_forget_passphrase(struct ComposeSharedData *shared, int op)
1911{
1913 return FR_SUCCESS;
1914}
1915
1916// -----------------------------------------------------------------------------
1917
1922 // clang-format off
1923 { OP_ATTACHMENT_ATTACH_FILE, op_attachment_attach_file },
1924 { OP_ATTACHMENT_ATTACH_KEY, op_attachment_attach_key },
1925 { OP_ATTACHMENT_ATTACH_MESSAGE, op_attachment_attach_message },
1926 { OP_ATTACHMENT_ATTACH_NEWS_MESSAGE, op_attachment_attach_message },
1927 { OP_ATTACHMENT_DETACH, op_attachment_detach },
1928 { OP_ATTACHMENT_EDIT_CONTENT_ID, op_attachment_edit_content_id },
1929 { OP_ATTACHMENT_EDIT_DESCRIPTION, op_attachment_edit_description },
1930 { OP_ATTACHMENT_EDIT_ENCODING, op_attachment_edit_encoding },
1931 { OP_ATTACHMENT_EDIT_LANGUAGE, op_attachment_edit_language },
1932 { OP_ATTACHMENT_EDIT_MIME, op_attachment_edit_mime },
1933 { OP_ATTACHMENT_EDIT_TYPE, op_attachment_edit_type },
1934 { OP_ATTACHMENT_FILTER, op_attachment_filter },
1935 { OP_ATTACHMENT_GET_ATTACHMENT, op_attachment_get_attachment },
1936 { OP_ATTACHMENT_GROUP_ALTS, op_attachment_group_alts },
1937 { OP_ATTACHMENT_GROUP_LINGUAL, op_attachment_group_lingual },
1938 { OP_ATTACHMENT_GROUP_RELATED, op_attachment_group_related },
1939 { OP_ATTACHMENT_MOVE_DOWN, op_attachment_move_down },
1940 { OP_ATTACHMENT_MOVE_UP, op_attachment_move_up },
1941 { OP_ATTACHMENT_NEW_MIME, op_attachment_new_mime },
1942 { OP_PIPE, op_attachment_filter },
1943 { OP_ATTACHMENT_PRINT, op_attachment_print },
1944 { OP_ATTACHMENT_RENAME_ATTACHMENT, op_attachment_rename_attachment },
1945 { OP_ATTACHMENT_SAVE, op_attachment_save },
1946 { OP_ATTACHMENT_TOGGLE_DISPOSITION, op_attachment_toggle_disposition },
1947 { OP_ATTACHMENT_TOGGLE_RECODE, op_attachment_toggle_recode },
1948 { OP_ATTACHMENT_TOGGLE_UNLINK, op_attachment_toggle_unlink },
1949 { OP_ATTACHMENT_UNGROUP, op_attachment_ungroup },
1950 { OP_ATTACHMENT_UPDATE_ENCODING, op_attachment_update_encoding },
1951 { OP_ATTACHMENT_VIEW, op_display_headers },
1952 { OP_COMPOSE_EDIT_FILE, op_compose_edit_file },
1953 { OP_COMPOSE_EDIT_MESSAGE, op_compose_edit_message },
1954 { OP_COMPOSE_ISPELL, op_compose_ispell },
1955 { OP_COMPOSE_POSTPONE_MESSAGE, op_compose_postpone_message },
1956 { OP_COMPOSE_RENAME_FILE, op_compose_rename_file },
1957 { OP_COMPOSE_SEND_MESSAGE, op_compose_send_message },
1958 { OP_COMPOSE_WRITE_MESSAGE, op_compose_write_message },
1959 { OP_DISPLAY_HEADERS, op_display_headers },
1960 { OP_ENVELOPE_EDIT_HEADERS, op_envelope_edit_headers },
1961 { OP_EXIT, op_exit },
1962 { OP_FORGET_PASSPHRASE, op_forget_passphrase },
1963 { 0, NULL },
1964 // clang-format on
1965};
1966
1971{
1972 if (!win)
1973 return FR_UNKNOWN;
1974
1975 struct MuttWindow *dlg = dialog_find(win);
1976 if (!dlg || !dlg->wdata)
1977 return FR_UNKNOWN;
1978
1979 int rc = FR_UNKNOWN;
1980 for (size_t i = 0; ComposeFunctions[i].op != OP_NULL; i++)
1981 {
1982 const struct ComposeFunction *fn = &ComposeFunctions[i];
1983 if (fn->op == op)
1984 {
1985 struct ComposeSharedData *shared = dlg->wdata;
1986 rc = fn->function(shared, op);
1987 break;
1988 }
1989 }
1990
1991 if (rc == FR_UNKNOWN) // Not our function
1992 return rc;
1993
1994 const char *result = dispacher_get_retval_name(rc);
1995 mutt_debug(LL_DEBUG1, "Handled %s (%d) -> %s\n", opcodes_get_name(op), op, NONULL(result));
1996
1997 return rc;
1998}
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:211
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition: array.h:73
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:203
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:57
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.
Select a Mailbox from a list.
#define MUTT_SEL_MULTI
Multi-selection is enabled.
Definition: lib.h:57
#define MUTT_SEL_NO_FLAGS
No flags are set.
Definition: lib.h:55
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:260
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:327
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:168
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:85
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
bool mutt_edit_content_type(struct Email *e, struct Body *b, FILE *fp)
Edit the content type of an attachment.
Definition: commands.c:1105
Manage where the email is piped to external commands.
static bool check_count(struct AttachCtx *actx)
Check if there are any attachments.
Definition: functions.c:90
static int group_attachments(struct ComposeSharedData *shared, char *subtype)
Group tagged attachments into a multipart group.
Definition: functions.c:372
static void update_idx(struct Menu *menu, struct AttachCtx *actx, struct AttachPtr *ap)
Add a new attachment to the message.
Definition: functions.c:281
static char * gen_cid(void)
Generate a random Content ID.
Definition: functions.c:107
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:306
static bool check_cid(const char *cid)
Check if a Content-ID is valid.
Definition: functions.c:123
static int check_attachments(struct AttachCtx *actx, struct ConfigSubset *sub)
Check if any attachments have changed or been deleted.
Definition: functions.c:143
struct ComposeFunction ComposeFunctions[]
All the NeoMutt functions that the Compose supports.
Definition: functions.c:1921
static int delete_attachment(struct AttachCtx *actx, int aidx)
Delete an attachment.
Definition: functions.c:207
#define MUTT_COMPOSE_NOFREEHEADER
Definition: lib.h:50
void update_menu(struct AttachCtx *actx, struct Menu *menu, bool init)
Redraw the compose window.
Definition: compose.c:222
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:218
unsigned char cs_subset_enum(const struct ConfigSubset *sub, const char *name)
Get a enumeration config item by name.
Definition: helpers.c:97
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:292
Convenience wrapper for the config headers.
Connection Library.
Convenience wrapper for the core headers.
void crypt_forget_passphrase(void)
Forget a passphrase and display a message.
Definition: crypt.c:92
struct Body * crypt_pgp_make_key_attachment(void)
Wrapper for CryptModuleSpecs::pgp_make_key_attachment()
Definition: cryptglue.c:305
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:312
int mutt_buffer_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:445
void dialog_push(struct MuttWindow *dlg)
Display a Window to the user.
Definition: dialog.c:103
void dialog_pop(void)
Hide a Window from the user.
Definition: dialog.c:137
struct MuttWindow * dialog_find(struct MuttWindow *win)
Find the parent Dialog of a Window.
Definition: dialog.c:83
const char * dispacher_get_retval_name(int rv)
Get the name of a return value.
Definition: dispatcher.c:54
@ FR_SUCCESS
Valid function - successfully performed.
Definition: dispatcher.h:39
@ FR_DONE
Exit the Dialog.
Definition: dispatcher.h:35
@ FR_UNKNOWN
Unknown function.
Definition: dispatcher.h:33
@ FR_ERROR
Valid function - error occurred.
Definition: dispatcher.h:38
@ FR_NOT_IMPL
Invalid function - feature not enabled.
Definition: dispatcher.h:36
@ FR_NO_ACTION
Valid function - no action performed.
Definition: dispatcher.h:37
struct Mailbox * mutt_index_menu(struct MuttWindow *dlg, struct Mailbox *m_init)
Display a list of emails.
Definition: dlg_index.c:1058
struct MuttWindow * index_pager_init(void)
Allocate the Windows for the Index/Pager.
Definition: dlg_index.c:1403
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
struct Body * mutt_body_new(void)
Create a new Body.
Definition: body.c:43
Structs that make up an email.
@ NT_EMAIL_CHANGE_ATTACH
Email's Attachments have changed.
Definition: email.h:153
@ NT_EMAIL_CHANGE_ENVELOPE
Email's Envelope has changed.
Definition: email.h:152
Enter a string.
int mutt_buffer_get_field(const char *field, struct Buffer *buf, CompletionFlags complete, bool multiple, struct Mailbox *m, char ***files, int *numfiles)
Ask the user for a string.
Definition: window.c:180
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:326
void mutt_env_to_local(struct Envelope *env)
Convert an Envelope's Address fields to local format.
Definition: envelope.c:288
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:618
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
int mutt_file_rename(const char *oldfile, const char *newfile)
Rename a file.
Definition: file.c:1403
static int op_attachment_edit_type(struct ComposeSharedData *shared, int op)
Edit attachment content type - Implements compose_function_t -.
Definition: functions.c:1055
static int op_attachment_group_lingual(struct ComposeSharedData *shared, int op)
Group tagged attachments as 'multipart/multilingual' - Implements compose_function_t -.
Definition: functions.c:1156
static int op_attachment_group_alts(struct ComposeSharedData *shared, int op)
Group tagged attachments as 'multipart/alternative' - Implements compose_function_t -.
Definition: functions.c:1142
static int op_attachment_filter(struct ComposeSharedData *shared, int op)
Filter attachment through a shell command - Implements compose_function_t -.
Definition: functions.c:1079
static int op_compose_rename_file(struct ComposeSharedData *shared, int op)
Rename/move an attached file - Implements compose_function_t -.
Definition: functions.c:1743
static int op_compose_ispell(struct ComposeSharedData *shared, int op)
Run ispell on the message - Implements compose_function_t -.
Definition: functions.c:1708
static int op_attachment_edit_description(struct ComposeSharedData *shared, int op)
Edit attachment description - Implements compose_function_t -.
Definition: functions.c:925
static int op_attachment_group_related(struct ComposeSharedData *shared, int op)
Group tagged attachments as 'multipart/related' - Implements compose_function_t -.
Definition: functions.c:1185
static int op_display_headers(struct ComposeSharedData *shared, int op)
Display message and toggle header weeding - Implements compose_function_t -.
Definition: functions.c:1862
static int op_attachment_attach_key(struct ComposeSharedData *shared, int op)
Attach a PGP public key - Implements compose_function_t -.
Definition: functions.c:655
static int op_compose_edit_file(struct ComposeSharedData *shared, int op)
Edit the file to be attached - Implements compose_function_t -.
Definition: functions.c:1662
static int op_forget_passphrase(struct ComposeSharedData *shared, int op)
Wipe passphrases from memory - Implements compose_function_t -.
Definition: functions.c:1910
static int op_attachment_rename_attachment(struct ComposeSharedData *shared, int op)
Send attachment with a different name - Implements compose_function_t -.
Definition: functions.c:1412
static int op_attachment_save(struct ComposeSharedData *shared, int op)
Save message/attachment to a mailbox/file - Implements compose_function_t -.
Definition: functions.c:1440
static int op_attachment_move_down(struct ComposeSharedData *shared, int op)
Move an attachment down in the attachment list - Implements compose_function_t -.
Definition: functions.c:1217
static int op_compose_send_message(struct ComposeSharedData *shared, int op)
Send the message - Implements compose_function_t -.
Definition: functions.c:1791
static int op_attachment_attach_file(struct ComposeSharedData *shared, int op)
Attach files to this message - Implements compose_function_t -.
Definition: functions.c:590
static int op_attachment_toggle_unlink(struct ComposeSharedData *shared, int op)
Toggle whether to delete file after sending it - Implements compose_function_t -.
Definition: functions.c:1501
static int op_attachment_toggle_recode(struct ComposeSharedData *shared, int op)
Toggle recoding of this attachment - Implements compose_function_t -.
Definition: functions.c:1477
static int op_attachment_move_up(struct ComposeSharedData *shared, int op)
Move an attachment up in the attachment list - Implements compose_function_t -.
Definition: functions.c:1272
static int op_attachment_edit_language(struct ComposeSharedData *shared, int op)
Edit the 'Content-Language' of the attachment - Implements compose_function_t -.
Definition: functions.c:1000
static int op_envelope_edit_headers(struct ComposeSharedData *shared, int op)
Edit the message with headers - Implements compose_function_t -.
Definition: functions.c:1615
static int op_attachment_toggle_disposition(struct ComposeSharedData *shared, int op)
Toggle disposition between inline/attachment - Implements compose_function_t -.
Definition: functions.c:1462
static int op_attachment_edit_encoding(struct ComposeSharedData *shared, int op)
Edit attachment transfer-encoding - Implements compose_function_t -.
Definition: functions.c:957
static int op_attachment_update_encoding(struct ComposeSharedData *shared, int op)
Update an attachment's encoding info - Implements compose_function_t -.
Definition: functions.c:1581
static int op_attachment_print(struct ComposeSharedData *shared, int op)
Print the current entry - Implements compose_function_t -.
Definition: functions.c:1390
static int op_attachment_attach_message(struct ComposeSharedData *shared, int op)
Attach messages to this message - Implements compose_function_t -.
Definition: functions.c:683
static int op_exit(struct ComposeSharedData *shared, int op)
Exit this menu - Implements compose_function_t -.
Definition: functions.c:1876
static int op_attachment_ungroup(struct ComposeSharedData *shared, int op)
Ungroup a 'multipart' attachment - Implements compose_function_t -.
Definition: functions.c:1517
static int op_attachment_edit_content_id(struct ComposeSharedData *shared, int op)
Edit the 'Content-ID' of the attachment - Implements compose_function_t -.
Definition: functions.c:871
static int op_attachment_get_attachment(struct ComposeSharedData *shared, int op)
Get a temporary copy of an attachment - Implements compose_function_t -.
Definition: functions.c:1106
static int op_compose_write_message(struct ComposeSharedData *shared, int op)
Write the message to a folder - Implements compose_function_t -.
Definition: functions.c:1823
static int op_attachment_edit_mime(struct ComposeSharedData *shared, int op)
Edit attachment using mailcap entry - Implements compose_function_t -.
Definition: functions.c:1037
static int op_compose_edit_message(struct ComposeSharedData *shared, int op)
Edit the message - Implements compose_function_t -.
Definition: functions.c:1686
static int op_attachment_detach(struct ComposeSharedData *shared, int op)
Delete the current entry - Implements compose_function_t -.
Definition: functions.c:835
static int op_compose_postpone_message(struct ComposeSharedData *shared, int op)
Save this message to send later - Implements compose_function_t -.
Definition: functions.c:1728
static int op_attachment_new_mime(struct ComposeSharedData *shared, int op)
Compose new attachment using mailcap entry - Implements compose_function_t -.
Definition: functions.c:1306
int compose_function_dispatcher(struct MuttWindow *win, int op)
Perform a Compose function - Implements function_dispatcher_t -.
Definition: functions.c:1970
#define mutt_warning(...)
Definition: logging.h:85
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
#define mutt_perror(...)
Definition: logging.h:88
enum MailboxType nntp_path_probe(const char *path, const struct stat *st)
Is this an NNTP Mailbox? - Implements MxOps::path_probe() -.
Definition: nntp.c:2701
enum MailboxType pop_path_probe(const char *path, const struct stat *st)
Is this a POP Mailbox? - Implements MxOps::path_probe() -.
Definition: pop.c:1157
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2400
Convenience wrapper for the gui headers.
void mutt_message_hook(struct Mailbox *m, struct Email *e, HookFlags type)
Perform a message hook.
Definition: hook.c:658
Parse and execute user-defined hooks.
#define MUTT_SEND2_HOOK
send2-hook: when changing fields in the compose menu
Definition: hook.h:49
IMAP network mailbox.
GUI manage the main index (list of emails)
Data shared between Index, Pager and Sidebar.
@ LL_DEBUG5
Log at debug level 5.
Definition: logging.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:210
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:51
@ MUTT_POP
'POP3' Mailbox type
Definition: mailbox.h:52
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition: mailbox.h:49
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:50
#define FREE(x)
Definition: memory.h:43
GUI present the user with a selectable list.
#define MENU_REDRAW_FULL
Redraw everything.
Definition: lib.h:59
#define MENU_REDRAW_INDEX
Redraw the index.
Definition: lib.h:56
void menu_queue_redraw(struct Menu *menu, MenuRedrawFlags redraw)
Queue a request for a redraw.
Definition: menu.c:178
int menu_get_index(struct Menu *menu)
Get the current selection in the Menu.
Definition: menu.c:154
#define MENU_REDRAW_CURRENT
Redraw the current line of the menu.
Definition: lib.h:58
MenuRedrawFlags menu_set_index(struct Menu *menu, int index)
Set the current selection in the Menu.
Definition: menu.c:168
@ 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)
Definition: mime.h:92
Support of Mixmaster anonymous remailer.
int mix_check_message(struct Email *e)
Safety-check the message before passing it to mixmaster.
Definition: mixmaster.c:51
struct Body * mutt_remove_multipart(struct Body *b)
Extract the multipart body if it exists.
Definition: multipart.c:126
struct Body * mutt_make_multipart(struct Body *b)
Create a multipart email.
Definition: multipart.c:100
void mutt_generate_boundary(struct ParameterList *pl)
Create a unique boundary id for a MIME part.
Definition: multipart.c:86
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:171
const char * mutt_path_basename(const char *f)
Find the last component for a pathname.
Definition: path.c:329
struct Regex * mutt_regex_new(const char *str, uint32_t flags, struct Buffer *err)
Create an Regex from a string.
Definition: regex.c:76
void mutt_regex_free(struct Regex **r)
Free a Regex object.
Definition: regex.c:114
bool mutt_regex_match(const struct Regex *regex, const char *str)
Shorthand to mutt_regex_capture()
Definition: regex.c:631
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
Many unsorted constants and some structs.
#define MUTT_COMP_FILE
File completion (in browser)
Definition: mutt.h:55
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:52
#define PATH_MAX
Definition: mutt.h:40
bool mutt_edit_attachment(struct Body *a)
Edit an attachment.
Definition: mutt_attach.c:260
int mutt_get_tmp_attachment(struct Body *a)
Get a temporary copy of an attachment.
Definition: mutt_attach.c:69
int mutt_compose_attachment(struct Body *a)
Create an attachment.
Definition: mutt_attach.c:115
void mutt_save_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top, struct Email *e, struct Menu *menu)
Save a list of attachments.
Definition: recvattach.c:413
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:938
void mutt_pipe_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top, bool filter)
Pipe a list of attachments to a command.
Definition: recvattach.c:705
void mutt_print_attachment_list(struct AttachCtx *actx, FILE *fp, bool tag, struct Body *top)
Print a list of Attachments.
Definition: recvattach.c:863
void mutt_edit_headers(const char *editor, const char *body, struct Email *e, struct Buffer *fcc)
Let the user edit the message header and body.
Definition: mutt_header.c:172
Representation of the email's header.
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:74
NeoMutt Logging.
void mutt_window_free(struct MuttWindow **ptr)
Free a Window and its children.
Definition: mutt_window.c:201
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:599
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:322
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:433
Some miscellaneous functions.
bool message_is_tagged(struct Email *e)
Is a message in the index tagged (and within limit)
Definition: mview.c:352
The "currently-open" mailbox.
void mx_fastclose_mailbox(struct Mailbox *m, bool keep_account)
Free up memory associated with the Mailbox.
Definition: mx.c:430
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:304
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1327
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1677
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition: mx.c:615
API for mailboxes.
#define MUTT_READONLY
Open in read-only mode.
Definition: mxapi.h:64
API for encryption/signing of emails.
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:90
#define WithCrypto
Definition: lib.h:116
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:561
struct NntpAccountData * CurrentNewsSrv
Current NNTP news server.
Definition: nntp.c:77
struct NntpAccountData * nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
Open a connection to an NNTP server.
Definition: newsrc.c:1019
@ 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:46
All user-callable functions.
Handling of global boolean variables.
bool OptNews
(pseudo) used to change reader mode
Definition: options.h:50
bool OptAttachMsg
(pseudo) used by attach-message
Definition: options.h:37
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition: parameter.c:84
void mutt_param_set(struct ParameterList *pl, const char *attribute, const char *value)
Set a Parameter.
Definition: parameter.c:110
enum ContentType mutt_check_mime_type(const char *s)
Check a MIME type string.
Definition: parse.c:323
int mutt_check_encoding(const char *c)
Check the encoding type.
Definition: parse.c:396
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
POP network mailbox.
Prototypes for many functions.
int mutt_system(const char *cmd)
Run an external command.
Definition: system.c:51
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 mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:194
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: question.c:386
#define STAILQ_EMPTY(head)
Definition: queue.h:348
void mutt_rand_base32(char *buf, size_t buflen)
Fill a buffer with a base32-encoded random string.
Definition: random.c:102
struct AttachPtr * current_attachment(struct AttachCtx *actx, struct Menu *menu)
Get the current attachment.
Definition: recvattach.c:69
void mutt_update_tree(struct AttachCtx *actx)
Refresh the list of attachments.
Definition: recvattach.c:112
int ba_add_tagged(struct BodyArray *ba, struct AttachCtx *actx, struct Menu *menu)
Get an array of tagged Attachments.
Definition: recvattach.c:1219
void mutt_rfc3676_space_unstuff(struct Email *e)
Remove RFC3676 space stuffing.
Definition: rfc3676.c:494
void mutt_rfc3676_space_stuff(struct Email *e)
Perform RFC3676 space stuffing on an Email.
Definition: rfc3676.c:481
RFC3676 Format Flowed routines.
Convenience wrapper for the send headers.
void mutt_stamp_attachment(struct Body *a)
Timestamp an Attachment.
Definition: sendlib.c:401
struct Body * mutt_make_file_attach(const char *path, struct ConfigSubset *sub)
Create a file attachment.
Definition: sendlib.c:598
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:445
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:1030
void mutt_update_encoding(struct Body *a, struct ConfigSubset *sub)
Update the encoding type.
Definition: sendlib.c:413
#define MUTT_RANDTAG_LEN
Definition: sendlib.h:35
Sidebar functions.
GUI display the mailboxes in a side panel.
SortType
Methods for sorting.
Definition: sort2.h:42
Key value store.
#define NONULL(x)
Definition: string2.h:37
A set of attachments.
Definition: attach.h:51
short vcount
The number of virtual attachments.
Definition: attach.h:60
struct AttachPtr ** idx
Array of attachments.
Definition: attach.h:55
short idxlen
Number of attachmentes.
Definition: attach.h:56
An email to which things will be attached.
Definition: attach.h:35
struct Body * body
Attachment.
Definition: attach.h:36
char * tree
Tree characters to display.
Definition: attach.h:39
int level
Nesting depth of attachment.
Definition: attach.h:40
FILE * fp
Used in the recvattach menu.
Definition: attach.h:37
bool unowned
Don't unlink on detach.
Definition: attach.h:42
int parent_type
Type of parent attachment, e.g. TYPE_MULTIPART.
Definition: attach.h:38
The body of an email.
Definition: body.h:36
char * language
content-language (RFC8255)
Definition: body.h:77
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:72
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:67
time_t stamp
Time stamp of last encoding update.
Definition: body.h:76
struct ParameterList parameter
Parameters of the content-type.
Definition: body.h:62
struct AttachPtr * aptr
Menu information, used in recvattach.c.
Definition: body.h:74
struct Email * email
header information for message/rfc822
Definition: body.h:73
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:89
struct Body * next
next attachment in the list
Definition: body.h:71
char * subtype
content-type subtype
Definition: body.h:60
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:58
String manipulation buffer.
Definition: buffer.h:34
size_t dsize
Length of data.
Definition: buffer.h:37
char * data
Pointer to data.
Definition: buffer.h:35
struct Menu * menu
Menu displaying the attachments.
Definition: attach_data.h:35
struct AttachCtx * actx
Set of attachments.
Definition: attach_data.h:34
A NeoMutt function.
Definition: functions.h:45
int op
Op code, e.g. OP_COMPOSE_WRITE_MESSAGE.
Definition: functions.h:46
compose_function_t function
Function to call.
Definition: functions.h:47
Shared Compose Data.
Definition: shared_data.h:33
struct ConfigSubset * sub
Config set to use.
Definition: shared_data.h:34
struct Mailbox * mailbox
Current Mailbox.
Definition: shared_data.h:35
int flags
Flags, e.g. MUTT_COMPOSE_NOFREEHEADER.
Definition: shared_data.h:41
bool fcc_set
User has edited the Fcc: field.
Definition: shared_data.h:42
int rc
Return code to leave compose.
Definition: shared_data.h:43
struct ComposeAttachData * adata
Attachments.
Definition: shared_data.h:37
struct Email * email
Email being composed.
Definition: shared_data.h:36
struct Buffer * fcc
Buffer to save FCC.
Definition: shared_data.h:40
A set of inherited config items.
Definition: subset.h:47
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
The envelope/body of an email.
Definition: email.h:37
struct Envelope * env
Envelope information.
Definition: email.h:66
struct Body * body
List of MIME parts.
Definition: email.h:67
struct ListHead chain
Mixmaster chain.
Definition: email.h:90
struct Notify * notify
Notifications: NotifyEmail, EventEmail.
Definition: email.h:71
A mailbox.
Definition: mailbox.h:79
int msg_count
Total number of messages.
Definition: mailbox.h:88
enum MailboxType type
Mailbox type.
Definition: mailbox.h:102
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
bool readonly
Don't allow changes to the mailbox.
Definition: mailbox.h:115
Definition: lib.h:69
int current
Current entry.
Definition: lib.h:70
int num_tagged
Number of tagged entries.
Definition: lib.h:83
bool tag_prefix
User has pressed <tag-prefix>
Definition: lib.h:75
void * wdata
Private data.
Definition: mutt_window.h:145
struct Connection * conn
Connection to NNTP Server.
Definition: adata.h:63
Cached regular expression.
Definition: regex3.h:89
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:305