NeoMutt  2021-02-05-329-g9e03b7
Teaching an old dog new tricks
DOXYGEN
index.c
Go to the documentation of this file.
1 
30 #include "config.h"
31 #include <assert.h>
32 #include <ctype.h>
33 #include <limits.h>
34 #include <stdbool.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include "private.h"
39 #include "mutt/lib.h"
40 #include "config/lib.h"
41 #include "email/lib.h"
42 #include "core/lib.h"
43 #include "alias/lib.h"
44 #include "conn/lib.h"
45 #include "gui/lib.h"
46 #include "mutt.h"
47 #include "lib.h"
48 #include "menu/lib.h"
49 #include "ncrypt/lib.h"
50 #include "pager/lib.h"
51 #include "pattern/lib.h"
52 #include "send/lib.h"
53 #include "browser.h"
54 #include "commands.h"
55 #include "context.h"
56 #include "format_flags.h"
57 #include "hdrline.h"
58 #include "hook.h"
59 #include "keymap.h"
60 #include "mutt_globals.h"
61 #include "mutt_header.h"
62 #include "mutt_logging.h"
63 #include "mutt_mailbox.h"
64 #include "mutt_thread.h"
65 #include "muttlib.h"
66 #include "mx.h"
67 #include "opcodes.h"
68 #include "options.h"
69 #include "private_data.h"
70 #include "progress.h"
71 #include "protos.h"
72 #include "recvattach.h"
73 #include "score.h"
74 #include "shared_data.h"
75 #include "sort.h"
76 #include "status.h"
77 #ifdef USE_SIDEBAR
78 #include "sidebar/lib.h"
79 #endif
80 #ifdef USE_POP
81 #include "pop/lib.h"
82 #endif
83 #ifdef USE_IMAP
84 #include "imap/lib.h"
85 #endif
86 #ifdef USE_NOTMUCH
87 #include "notmuch/lib.h"
88 #endif
89 #ifdef USE_NNTP
90 #include "nntp/lib.h"
91 #include "nntp/adata.h" // IWYU pragma: keep
92 #include "nntp/mdata.h" // IWYU pragma: keep
93 #endif
94 #ifdef ENABLE_NLS
95 #include <libintl.h>
96 #endif
97 #ifdef USE_INOTIFY
98 #include "monitor.h"
99 #endif
100 #ifdef USE_AUTOCRYPT
101 #include "autocrypt/lib.h"
102 #endif
103 #ifdef USE_DEBUG_GRAPHVIZ
104 #include "debug/lib.h"
105 #endif
106 
108 static const struct Mapping IndexHelp[] = {
109  // clang-format off
110  { N_("Quit"), OP_QUIT },
111  { N_("Del"), OP_DELETE },
112  { N_("Undel"), OP_UNDELETE },
113  { N_("Save"), OP_SAVE },
114  { N_("Mail"), OP_MAIL },
115  { N_("Reply"), OP_REPLY },
116  { N_("Group"), OP_GROUP_REPLY },
117  { N_("Help"), OP_HELP },
118  { NULL, 0 },
119  // clang-format on
120 };
121 
122 #ifdef USE_NNTP
123 static const struct Mapping IndexNewsHelp[] = {
125  // clang-format off
126  { N_("Quit"), OP_QUIT },
127  { N_("Del"), OP_DELETE },
128  { N_("Undel"), OP_UNDELETE },
129  { N_("Save"), OP_SAVE },
130  { N_("Post"), OP_POST },
131  { N_("Followup"), OP_FOLLOWUP },
132  { N_("Catchup"), OP_CATCHUP },
133  { N_("Help"), OP_HELP },
134  { NULL, 0 },
135  // clang-format on
136 };
137 #endif
138 
139 // clang-format off
143 typedef uint8_t CheckFlags;
144 #define CHECK_NO_FLAGS 0
145 #define CHECK_IN_MAILBOX (1 << 0)
146 #define CHECK_MSGCOUNT (1 << 1)
147 #define CHECK_VISIBLE (1 << 2)
148 #define CHECK_READONLY (1 << 3)
149 #define CHECK_ATTACH (1 << 4)
150 // clang-format on
151 
159 static bool prereq(struct Context *ctx, struct Menu *menu, CheckFlags checks)
160 {
161  bool result = true;
162 
163  if (checks & (CHECK_MSGCOUNT | CHECK_VISIBLE | CHECK_READONLY))
164  checks |= CHECK_IN_MAILBOX;
165 
166  if ((checks & CHECK_IN_MAILBOX) && (!ctx || !ctx->mailbox))
167  {
168  mutt_error(_("No mailbox is open"));
169  result = false;
170  }
171 
172  if (result && (checks & CHECK_MSGCOUNT) && (ctx->mailbox->msg_count == 0))
173  {
174  mutt_error(_("There are no messages"));
175  result = false;
176  }
177 
178  int index = menu_get_index(menu);
179  if (result && (checks & CHECK_VISIBLE) && (index >= ctx->mailbox->vcount))
180  {
181  mutt_error(_("No visible messages"));
182  result = false;
183  }
184 
185  if (result && (checks & CHECK_READONLY) && ctx->mailbox->readonly)
186  {
187  mutt_error(_("Mailbox is read-only"));
188  result = false;
189  }
190 
191  if (result && (checks & CHECK_ATTACH) && OptAttachMsg)
192  {
193  mutt_error(_("Function not permitted in attach-message mode"));
194  result = false;
195  }
196 
197  if (!result)
198  mutt_flushinp();
199 
200  return result;
201 }
202 
210 static bool check_acl(struct Mailbox *m, AclFlags acl, const char *msg)
211 {
212  if (!m)
213  return false;
214 
215  if (!(m->rights & acl))
216  {
217  /* L10N: %s is one of the CHECK_ACL entries below. */
218  mutt_error(_("%s: Operation not permitted by ACL"), msg);
219  return false;
220  }
221 
222  return true;
223 }
224 
237 static void collapse_all(struct Context *ctx, struct Menu *menu, int toggle)
238 {
239  if (!ctx || !ctx->mailbox || (ctx->mailbox->msg_count == 0) || !menu)
240  return;
241 
242  struct Email *e_cur = mutt_get_virt_email(ctx->mailbox, menu_get_index(menu));
243  if (!e_cur)
244  return;
245 
246  int final;
247 
248  /* Figure out what the current message would be after folding / unfolding,
249  * so that we can restore the cursor in a sane way afterwards. */
250  if (e_cur->collapsed && toggle)
251  final = mutt_uncollapse_thread(e_cur);
252  else if (mutt_thread_can_collapse(e_cur))
253  final = mutt_collapse_thread(e_cur);
254  else
255  final = e_cur->vnum;
256 
257  if (final == -1)
258  return;
259 
260  struct Email *base = mutt_get_virt_email(ctx->mailbox, final);
261  if (!base)
262  return;
263 
264  /* Iterate all threads, perform collapse/uncollapse as needed */
265  ctx->collapsed = toggle ? !ctx->collapsed : true;
267 
268  /* Restore the cursor */
269  mutt_set_vnum(ctx->mailbox);
270  for (int i = 0; i < ctx->mailbox->vcount; i++)
271  {
272  struct Email *e = mutt_get_virt_email(ctx->mailbox, i);
273  if (!e)
274  break;
275  if (e->index == base->index)
276  {
277  menu_set_index(menu, i);
278  break;
279  }
280  }
281 
283 }
284 
292 static int ci_next_undeleted(struct Mailbox *m, int msgno)
293 {
294  if (!m)
295  return -1;
296 
297  for (int i = msgno + 1; i < m->vcount; i++)
298  {
299  struct Email *e = mutt_get_virt_email(m, i);
300  if (!e)
301  continue;
302  if (!e->deleted)
303  return i;
304  }
305  return -1;
306 }
307 
315 static int ci_previous_undeleted(struct Mailbox *m, int msgno)
316 {
317  if (!m)
318  return -1;
319 
320  for (int i = msgno - 1; i >= 0; i--)
321  {
322  struct Email *e = mutt_get_virt_email(m, i);
323  if (!e)
324  continue;
325  if (!e->deleted)
326  return i;
327  }
328  return -1;
329 }
330 
339 static int ci_first_message(struct Mailbox *m)
340 {
341  if (!m || (m->msg_count == 0))
342  return 0;
343 
344  int old = -1;
345  for (int i = 0; i < m->vcount; i++)
346  {
347  struct Email *e = mutt_get_virt_email(m, i);
348  if (!e)
349  continue;
350  if (!e->read && !e->deleted)
351  {
352  if (!e->old)
353  return i;
354  if (old == -1)
355  old = i;
356  }
357  }
358  if (old != -1)
359  return old;
360 
361  /* If `$sort` is reverse and not threaded, the latest message is first.
362  * If `$sort` is threaded, the latest message is first if exactly one
363  * of `$sort` and `$sort_aux` are reverse. */
364  const short c_sort = cs_subset_sort(m->sub, "sort");
365  const short c_sort_aux = cs_subset_sort(m->sub, "sort_aux");
366  if (((c_sort & SORT_REVERSE) && ((c_sort & SORT_MASK) != SORT_THREADS)) ||
367  (((c_sort & SORT_MASK) == SORT_THREADS) && ((c_sort ^ c_sort_aux) & SORT_REVERSE)))
368  {
369  return 0;
370  }
371  else
372  {
373  return m->vcount ? m->vcount - 1 : 0;
374  }
375 
376  return 0;
377 }
378 
387 static int mx_toggle_write(struct Mailbox *m)
388 {
389  if (!m)
390  return -1;
391 
392  if (m->readonly)
393  {
394  mutt_error(_("Can't toggle write on a readonly mailbox"));
395  return -1;
396  }
397 
398  if (m->dontwrite)
399  {
400  m->dontwrite = false;
401  mutt_message(_("Changes to folder will be written on folder exit"));
402  }
403  else
404  {
405  m->dontwrite = true;
406  mutt_message(_("Changes to folder will not be written"));
407  }
408 
409  return 0;
410 }
411 
417 static void resort_index(struct Context *ctx, struct Menu *menu)
418 {
419  if (!ctx || !ctx->mailbox || !menu)
420  return;
421 
422  struct Mailbox *m = ctx->mailbox;
423  const int old_index = menu_get_index(menu);
424  struct Email *e_cur = mutt_get_virt_email(m, old_index);
425 
426  int new_index = -1;
427  mutt_sort_headers(m, ctx->threads, false, &ctx->vsize);
428  /* Restore the current message */
429 
430  for (int i = 0; i < m->vcount; i++)
431  {
432  struct Email *e = mutt_get_virt_email(m, i);
433  if (!e)
434  continue;
435  if (e == e_cur)
436  {
437  new_index = i;
438  break;
439  }
440  }
441 
442  const short c_sort = cs_subset_sort(m->sub, "sort");
443  if (((c_sort & SORT_MASK) == SORT_THREADS) && (old_index < 0))
444  new_index = mutt_parent_message(e_cur, false);
445 
446  if (old_index < 0)
447  new_index = ci_first_message(m);
448 
449  menu_set_index(menu, new_index);
451 }
452 
459 static void update_index_threaded(struct Context *ctx, enum MxStatus check, int oldcount)
460 {
461  struct Email **save_new = NULL;
462  const bool lmt = ctx_has_limit(ctx);
463 
464  struct Mailbox *m = ctx->mailbox;
465  int num_new = MAX(0, m->msg_count - oldcount);
466 
467  const bool c_uncollapse_new = cs_subset_bool(m->sub, "uncollapse_new");
468  /* save the list of new messages */
469  if ((check != MX_STATUS_REOPENED) && (oldcount > 0) &&
470  (lmt || c_uncollapse_new) && (num_new > 0))
471  {
472  save_new = mutt_mem_malloc(num_new * sizeof(struct Email *));
473  for (int i = oldcount; i < m->msg_count; i++)
474  save_new[i - oldcount] = m->emails[i];
475  }
476 
477  /* Sort first to thread the new messages, because some patterns
478  * require the threading information.
479  *
480  * If the mailbox was reopened, need to rethread from scratch. */
481  mutt_sort_headers(m, ctx->threads, (check == MX_STATUS_REOPENED), &ctx->vsize);
482 
483  if (lmt)
484  {
485  /* Because threading changes the order in m->emails, we don't
486  * know which emails are new. Hence, we need to re-apply the limit to the
487  * whole set.
488  */
489  for (int i = 0; i < m->msg_count; i++)
490  {
491  struct Email *e = m->emails[i];
492  if ((e->vnum != -1) || mutt_pattern_exec(SLIST_FIRST(ctx->limit_pattern),
493  MUTT_MATCH_FULL_ADDRESS, m, e, NULL))
494  {
495  /* vnum will get properly set by mutt_set_vnum(), which
496  * is called by mutt_sort_headers() just below. */
497  e->vnum = 1;
498  e->visible = true;
499  }
500  else
501  {
502  e->vnum = -1;
503  e->visible = false;
504  }
505  }
506  /* Need a second sort to set virtual numbers and redraw the tree */
507  mutt_sort_headers(m, ctx->threads, false, &ctx->vsize);
508  }
509 
510  /* uncollapse threads with new mail */
511  if (c_uncollapse_new)
512  {
513  if (check == MX_STATUS_REOPENED)
514  {
515  ctx->collapsed = false;
517  mutt_set_vnum(m);
518  }
519  else if (oldcount > 0)
520  {
521  for (int j = 0; j < num_new; j++)
522  {
523  if (save_new[j]->visible)
524  {
525  mutt_uncollapse_thread(save_new[j]);
526  }
527  }
528  mutt_set_vnum(m);
529  }
530  }
531 
532  FREE(&save_new);
533 }
534 
540 static void update_index_unthreaded(struct Context *ctx, enum MxStatus check)
541 {
542  /* We are in a limited view. Check if the new message(s) satisfy
543  * the limit criteria. If they do, set their virtual msgno so that
544  * they will be visible in the limited view */
545  if (ctx_has_limit(ctx))
546  {
547  int padding = mx_msg_padding_size(ctx->mailbox);
548  ctx->mailbox->vcount = ctx->vsize = 0;
549  for (int i = 0; i < ctx->mailbox->msg_count; i++)
550  {
551  struct Email *e = ctx->mailbox->emails[i];
552  if (!e)
553  break;
555  MUTT_MATCH_FULL_ADDRESS, ctx->mailbox, e, NULL))
556  {
557  assert(ctx->mailbox->vcount < ctx->mailbox->msg_count);
558  e->vnum = ctx->mailbox->vcount;
559  ctx->mailbox->v2r[ctx->mailbox->vcount] = i;
560  e->visible = true;
561  ctx->mailbox->vcount++;
562  struct Body *b = e->body;
563  ctx->vsize += b->length + b->offset - b->hdr_offset + padding;
564  }
565  else
566  {
567  e->visible = false;
568  }
569  }
570  }
571 
572  /* if the mailbox was reopened, need to rethread from scratch */
573  mutt_sort_headers(ctx->mailbox, ctx->threads, (check == MX_STATUS_REOPENED), &ctx->vsize);
574 }
575 
584 static void update_index(struct Menu *menu, struct Context *ctx, enum MxStatus check,
585  int oldcount, const struct IndexSharedData *shared)
586 {
587  if (!menu || !ctx)
588  return;
589 
590  struct Mailbox *m = ctx->mailbox;
591  const short c_sort = cs_subset_sort(m->sub, "sort");
592  if ((c_sort & SORT_MASK) == SORT_THREADS)
593  update_index_threaded(ctx, check, oldcount);
594  else
595  update_index_unthreaded(ctx, check);
596 
597  const int old_index = menu_get_index(menu);
598  int index = -1;
599  if (oldcount)
600  {
601  /* restore the current message to the message it was pointing to */
602  for (int i = 0; i < m->vcount; i++)
603  {
604  struct Email *e = mutt_get_virt_email(m, i);
605  if (!e)
606  continue;
607  if (index_shared_data_is_cur_email(shared, e))
608  {
609  index = i;
610  break;
611  }
612  }
613  }
614 
615  if (index < 0)
616  {
617  index = (old_index < m->vcount) ? old_index : ci_first_message(m);
618  }
619  menu_set_index(menu, index);
620 }
621 
630 void mutt_update_index(struct Menu *menu, struct Context *ctx, enum MxStatus check,
631  int oldcount, struct IndexSharedData *shared)
632 {
633  update_index(menu, ctx, check, oldcount, shared);
634 }
635 
642 {
643  if (!nc->global_data)
644  return -1;
645  if ((nc->event_type != NT_MAILBOX) || (nc->event_subtype != NT_MAILBOX_CLOSED))
646  return 0;
647 
648  struct Mailbox **ptr = nc->global_data;
649  if (!ptr || !*ptr)
650  return 0;
651 
652  *ptr = NULL;
653  return 0;
654 }
655 
664 static void change_folder_mailbox(struct Menu *menu, struct Mailbox *m, int *oldcount,
665  struct IndexSharedData *shared, bool read_only)
666 {
667  if (!m)
668  return;
669 
670  /* keepalive failure in mutt_enter_fname may kill connection. */
671  if (shared->mailbox && (mutt_buffer_is_empty(&shared->mailbox->pathbuf)))
672  {
673  struct Context *ctx = shared->ctx;
674  index_shared_data_set_context(shared, NULL);
675  ctx_free(&ctx);
676  }
677 
678  if (shared->mailbox)
679  {
680  char *new_last_folder = NULL;
681 #ifdef USE_INOTIFY
682  int monitor_remove_rc = mutt_monitor_remove(NULL);
683 #endif
684 #ifdef USE_COMP_MBOX
685  if (shared->mailbox->compress_info && (shared->mailbox->realpath[0] != '\0'))
686  new_last_folder = mutt_str_dup(shared->mailbox->realpath);
687  else
688 #endif
689  new_last_folder = mutt_str_dup(mailbox_path(shared->mailbox));
690  *oldcount = shared->mailbox->msg_count;
691 
692  const enum MxStatus check = mx_mbox_close(shared->mailbox);
693  if (check == MX_STATUS_OK)
694  {
695  struct Context *ctx = shared->ctx;
696  index_shared_data_set_context(shared, NULL);
697  ctx_free(&ctx);
698  }
699  else
700  {
701 #ifdef USE_INOTIFY
702  if (monitor_remove_rc == 0)
703  mutt_monitor_add(NULL);
704 #endif
705  if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
706  update_index(menu, shared->ctx, check, *oldcount, shared);
707 
708  FREE(&new_last_folder);
709  OptSearchInvalid = true;
711  return;
712  }
713  FREE(&LastFolder);
714  LastFolder = new_last_folder;
715  }
717 
718  /* If the `folder-hook` were to call `unmailboxes`, then the Mailbox (`m`)
719  * could be deleted, leaving `m` dangling. */
720  // TODO: Refactor this function to avoid the need for an observer
722  char *dup_path = mutt_str_dup(mailbox_path(m));
723  char *dup_name = mutt_str_dup(m->name);
724 
725  mutt_folder_hook(dup_path, dup_name);
726  if (m)
727  {
728  /* `m` is still valid, but we won't need the observer again before the end
729  * of the function. */
731  }
732 
733  // Recreate the Mailbox as the folder-hook might have invoked `mailboxes`
734  // and/or `unmailboxes`.
735  m = mx_path_resolve(dup_path);
736  FREE(&dup_path);
737  FREE(&dup_name);
738 
739  if (!m)
740  return;
741 
742  const OpenMailboxFlags flags = read_only ? MUTT_READONLY : MUTT_OPEN_NO_FLAGS;
743  if (mx_mbox_open(m, flags))
744  {
745  struct Context *ctx = ctx_new(m);
746  index_shared_data_set_context(shared, ctx);
747 
748  menu_set_index(menu, ci_first_message(shared->mailbox));
749 #ifdef USE_INOTIFY
750  mutt_monitor_add(NULL);
751 #endif
752  }
753  else
754  {
755  index_shared_data_set_context(shared, NULL);
756  menu_set_index(menu, 0);
757  }
758 
759  const short c_sort = cs_subset_sort(shared->sub, "sort");
760  const bool c_collapse_all = cs_subset_bool(shared->sub, "collapse_all");
761  if (((c_sort & SORT_MASK) == SORT_THREADS) && c_collapse_all)
762  collapse_all(shared->ctx, menu, 0);
763 
764  struct MuttWindow *dlg = dialog_find(menu->win_index);
765  struct EventMailbox em = { shared->mailbox };
767 
769  /* force the mailbox check after we have changed the folder */
772  OptSearchInvalid = true;
773 }
774 
775 #ifdef USE_NOTMUCH
776 
785 static struct Mailbox *change_folder_notmuch(struct Menu *menu, char *buf,
786  int buflen, int *oldcount,
787  struct IndexSharedData *shared, bool read_only)
788 {
789  if (!nm_url_from_query(NULL, buf, buflen))
790  {
791  mutt_message(_("Failed to create query, aborting"));
792  return NULL;
793  }
794 
795  struct Mailbox *m_query = mx_path_resolve(buf);
796  change_folder_mailbox(menu, m_query, oldcount, shared, read_only);
797  return m_query;
798 }
799 #endif
800 
811 static void change_folder_string(struct Menu *menu, char *buf, size_t buflen,
812  int *oldcount, struct IndexSharedData *shared,
813  bool *pager_return, bool read_only)
814 {
815 #ifdef USE_NNTP
816  if (OptNews)
817  {
818  OptNews = false;
819  nntp_expand_path(buf, buflen, &CurrentNewsSrv->conn->account);
820  }
821  else
822 #endif
823  {
824  const char *const c_folder = cs_subset_string(shared->sub, "folder");
825  mx_path_canon(buf, buflen, c_folder, NULL);
826  }
827 
828  enum MailboxType type = mx_path_probe(buf);
829  if ((type == MUTT_MAILBOX_ERROR) || (type == MUTT_UNKNOWN))
830  {
831  // Look for a Mailbox by its description, before failing
832  struct Mailbox *m = mailbox_find_name(buf);
833  if (m)
834  {
835  change_folder_mailbox(menu, m, oldcount, shared, read_only);
836  *pager_return = false;
837  }
838  else
839  mutt_error(_("%s is not a mailbox"), buf);
840  return;
841  }
842 
843  /* past this point, we don't return to the pager on error */
844  *pager_return = false;
845 
846  struct Mailbox *m = mx_path_resolve(buf);
847  change_folder_mailbox(menu, m, oldcount, shared, read_only);
848 }
849 
853 void index_make_entry(struct Menu *menu, char *buf, size_t buflen, int line)
854 {
855  buf[0] = '\0';
856 
857  struct IndexSharedData *shared = menu->mdata;
858  struct Mailbox *m = shared->mailbox;
859 
860  if (!m || !menu || (line < 0) || (line >= m->email_max))
861  return;
862 
863  struct Email *e = mutt_get_virt_email(m, line);
864  if (!e)
865  return;
866 
868  struct MuttThread *tmp = NULL;
869 
870  const short c_sort = cs_subset_sort(shared->sub, "sort");
871  if (((c_sort & SORT_MASK) == SORT_THREADS) && e->tree)
872  {
873  flags |= MUTT_FORMAT_TREE; /* display the thread tree */
874  if (e->display_subject)
875  flags |= MUTT_FORMAT_FORCESUBJ;
876  else
877  {
878  const int reverse = c_sort & SORT_REVERSE;
879  int edgemsgno;
880  if (reverse)
881  {
882  if (menu->top + menu->pagelen > menu->max)
883  edgemsgno = m->v2r[menu->max - 1];
884  else
885  edgemsgno = m->v2r[menu->top + menu->pagelen - 1];
886  }
887  else
888  edgemsgno = m->v2r[menu->top];
889 
890  for (tmp = e->thread->parent; tmp; tmp = tmp->parent)
891  {
892  if (!tmp->message)
893  continue;
894 
895  /* if no ancestor is visible on current screen, provisionally force
896  * subject... */
897  if (reverse ? (tmp->message->msgno > edgemsgno) : (tmp->message->msgno < edgemsgno))
898  {
899  flags |= MUTT_FORMAT_FORCESUBJ;
900  break;
901  }
902  else if (tmp->message->vnum >= 0)
903  break;
904  }
905  if (flags & MUTT_FORMAT_FORCESUBJ)
906  {
907  for (tmp = e->thread->prev; tmp; tmp = tmp->prev)
908  {
909  if (!tmp->message)
910  continue;
911 
912  /* ...but if a previous sibling is available, don't force it */
913  if (reverse ? (tmp->message->msgno > edgemsgno) : (tmp->message->msgno < edgemsgno))
914  break;
915  else if (tmp->message->vnum >= 0)
916  {
917  flags &= ~MUTT_FORMAT_FORCESUBJ;
918  break;
919  }
920  }
921  }
922  }
923  }
924 
925  const char *const c_index_format =
926  cs_subset_string(shared->sub, "index_format");
927  mutt_make_string(buf, buflen, menu->win_index->state.cols, NONULL(c_index_format),
928  m, shared->ctx->msg_in_pager, e, flags, NULL);
929 }
930 
934 int index_color(struct Menu *menu, int line)
935 {
936  struct IndexSharedData *shared = menu->mdata;
937  struct Mailbox *m = shared->mailbox;
938  if (!m || (line < 0))
939  return 0;
940 
941  struct Email *e = mutt_get_virt_email(m, line);
942  if (!e)
943  return 0;
944 
945  if (e->pair)
946  return e->pair;
947 
948  mutt_set_header_color(m, e);
949  return e->pair;
950 }
951 
964 void mutt_draw_statusline(int cols, const char *buf, size_t buflen)
965 {
966  if (!buf || !stdscr)
967  return;
968 
969  size_t i = 0;
970  size_t offset = 0;
971  bool found = false;
972  size_t chunks = 0;
973  size_t len = 0;
974 
975  struct StatusSyntax
976  {
977  int color;
978  int first;
979  int last;
980  } *syntax = NULL;
981 
982  do
983  {
984  struct ColorLine *cl = NULL;
985  found = false;
986 
987  if (!buf[offset])
988  break;
989 
990  /* loop through each "color status regex" */
991  STAILQ_FOREACH(cl, mutt_color_status_line(), entries)
992  {
993  regmatch_t pmatch[cl->match + 1];
994 
995  if (regexec(&cl->regex, buf + offset, cl->match + 1, pmatch, 0) != 0)
996  continue; /* regex doesn't match the status bar */
997 
998  int first = pmatch[cl->match].rm_so + offset;
999  int last = pmatch[cl->match].rm_eo + offset;
1000 
1001  if (first == last)
1002  continue; /* ignore an empty regex */
1003 
1004  if (!found)
1005  {
1006  chunks++;
1007  mutt_mem_realloc(&syntax, chunks * sizeof(struct StatusSyntax));
1008  }
1009 
1010  i = chunks - 1;
1011  if (!found || (first < syntax[i].first) ||
1012  ((first == syntax[i].first) && (last > syntax[i].last)))
1013  {
1014  syntax[i].color = cl->pair;
1015  syntax[i].first = first;
1016  syntax[i].last = last;
1017  }
1018  found = true;
1019  }
1020 
1021  if (syntax)
1022  {
1023  offset = syntax[i].last;
1024  }
1025  } while (found);
1026 
1027  /* Only 'len' bytes will fit into 'cols' screen columns */
1028  len = mutt_wstr_trunc(buf, buflen, cols, NULL);
1029 
1030  offset = 0;
1031 
1032  if ((chunks > 0) && (syntax[0].first > 0))
1033  {
1034  /* Text before the first highlight */
1035  mutt_window_addnstr(buf, MIN(len, syntax[0].first));
1036  attrset(mutt_color(MT_COLOR_STATUS));
1037  if (len <= syntax[0].first)
1038  goto dsl_finish; /* no more room */
1039 
1040  offset = syntax[0].first;
1041  }
1042 
1043  for (i = 0; i < chunks; i++)
1044  {
1045  /* Highlighted text */
1046  attrset(syntax[i].color);
1047  mutt_window_addnstr(buf + offset, MIN(len, syntax[i].last) - offset);
1048  if (len <= syntax[i].last)
1049  goto dsl_finish; /* no more room */
1050 
1051  size_t next;
1052  if ((i + 1) == chunks)
1053  {
1054  next = len;
1055  }
1056  else
1057  {
1058  next = MIN(len, syntax[i + 1].first);
1059  }
1060 
1061  attrset(mutt_color(MT_COLOR_STATUS));
1062  offset = syntax[i].last;
1063  mutt_window_addnstr(buf + offset, next - offset);
1064 
1065  offset = next;
1066  if (offset >= len)
1067  goto dsl_finish; /* no more room */
1068  }
1069 
1070  attrset(mutt_color(MT_COLOR_STATUS));
1071  if (offset < len)
1072  {
1073  /* Text after the last highlight */
1074  mutt_window_addnstr(buf + offset, len - offset);
1075  }
1076 
1077  int width = mutt_strwidth(buf);
1078  if (width < cols)
1079  {
1080  /* Pad the rest of the line with whitespace */
1081  mutt_paddstr(cols - width, "");
1082  }
1083 dsl_finish:
1084  FREE(&syntax);
1085 }
1086 
1090 static void index_custom_redraw(struct Menu *menu)
1091 {
1092  if (menu->redraw & REDRAW_FULL)
1093  {
1094  menu_redraw_full(menu);
1095  mutt_show_error();
1096  }
1097 
1098  struct IndexSharedData *shared = menu->mdata;
1099  struct Mailbox *m = shared->mailbox;
1100  const int index = menu_get_index(menu);
1101  if (m && m->emails && (index < m->vcount))
1102  {
1103  menu_check_recenter(menu);
1104 
1105  if (menu->redraw & REDRAW_INDEX)
1106  {
1107  menu_redraw_index(menu);
1109  }
1110  else if (menu->redraw & REDRAW_MOTION)
1111  menu_redraw_motion(menu);
1112  else if (menu->redraw & REDRAW_CURRENT)
1113  menu_redraw_current(menu);
1114  }
1115 
1116  if (menu->redraw & REDRAW_STATUS)
1117  {
1118  char buf[1024];
1119  const char *const c_status_format =
1120  cs_subset_string(shared->sub, "status_format");
1121  menu_status_line(buf, sizeof(buf), shared, menu, menu->win_ibar->state.cols,
1122  NONULL(c_status_format));
1123  mutt_window_move(menu->win_ibar, 0, 0);
1125  mutt_draw_statusline(menu->win_ibar->state.cols, buf, sizeof(buf));
1127  menu->redraw &= ~REDRAW_STATUS;
1128  const bool c_ts_enabled = cs_subset_bool(shared->sub, "ts_enabled");
1129  if (c_ts_enabled && TsSupported)
1130  {
1131  const char *const c_ts_status_format =
1132  cs_subset_string(shared->sub, "ts_status_format");
1133  menu_status_line(buf, sizeof(buf), shared, menu, sizeof(buf),
1134  NONULL(c_ts_status_format));
1135  mutt_ts_status(buf);
1136  const char *const c_ts_icon_format =
1137  cs_subset_string(shared->sub, "ts_icon_format");
1138  menu_status_line(buf, sizeof(buf), shared, menu, sizeof(buf), NONULL(c_ts_icon_format));
1139  mutt_ts_icon(buf);
1140  }
1141  }
1142 
1143  menu->redraw = REDRAW_NO_FLAGS;
1144 }
1145 
1155 struct Mailbox *mutt_index_menu(struct MuttWindow *dlg, struct Mailbox *m_init)
1156 {
1157  struct Context *ctx_old = Context;
1158  struct IndexSharedData *shared = dlg->wdata;
1159  index_shared_data_set_context(shared, ctx_new(m_init));
1160 
1161  struct MuttWindow *panel_index = mutt_window_find(dlg, WT_INDEX);
1162  struct MuttWindow *panel_pager = mutt_window_find(dlg, WT_PAGER);
1163 
1164  struct IndexPrivateData *priv = panel_index->wdata;
1165  priv->attach_msg = OptAttachMsg;
1166  priv->win_index = mutt_window_find(panel_index, WT_MENU);
1167  priv->win_ibar = mutt_window_find(panel_index, WT_INDEX_BAR);
1168  priv->win_pager = mutt_window_find(panel_pager, WT_MENU);
1169  priv->win_pbar = mutt_window_find(panel_pager, WT_PAGER_BAR);
1170 
1171  int op = OP_NULL;
1172 
1173 #ifdef USE_NNTP
1174  if (shared->mailbox && (shared->mailbox->type == MUTT_NNTP))
1175  dlg->help_data = IndexNewsHelp;
1176  else
1177 #endif
1178  dlg->help_data = IndexHelp;
1179  dlg->help_menu = MENU_MAIN;
1180 
1181  priv->menu = priv->win_index->wdata;
1182  priv->menu->win_ibar = priv->win_ibar;
1183  priv->menu->mdata = shared;
1184  priv->menu->make_entry = index_make_entry;
1185  priv->menu->color = index_color;
1187  menu_set_index(priv->menu, ci_first_message(shared->mailbox));
1188  mutt_window_reflow(NULL);
1189 
1190  if (!priv->attach_msg)
1191  {
1192  /* force the mailbox check after we enter the folder */
1194  }
1195 #ifdef USE_INOTIFY
1196  mutt_monitor_add(NULL);
1197 #endif
1198 
1199  {
1200  const short c_sort = cs_subset_sort(shared->sub, "sort");
1201  const bool c_collapse_all = cs_subset_bool(shared->sub, "collapse_all");
1202  if (((c_sort & SORT_MASK) == SORT_THREADS) && c_collapse_all)
1203  {
1204  collapse_all(shared->ctx, priv->menu, 0);
1206  }
1207  }
1208 
1209  while (true)
1210  {
1211  /* Clear the tag prefix unless we just started it. Don't clear
1212  * the prefix on a timeout (op==-2), but do clear on an abort (op==-1) */
1213  if (priv->tag && (op != OP_TAG_PREFIX) && (op != OP_TAG_PREFIX_COND) && (op != -2))
1214  priv->tag = false;
1215 
1216  /* check if we need to resort the index because just about
1217  * any 'op' below could do mutt_enter_command(), either here or
1218  * from any new priv->menu launched, and change $sort/$sort_aux */
1219  if (OptNeedResort && shared->mailbox && (shared->mailbox->msg_count != 0) &&
1220  (menu_get_index(priv->menu) >= 0))
1221  {
1222  resort_index(shared->ctx, priv->menu);
1223  }
1224 
1225  priv->menu->max = shared->mailbox ? shared->mailbox->vcount : 0;
1226  priv->oldcount = shared->mailbox ? shared->mailbox->msg_count : 0;
1227 
1228  {
1229  const short c_sort = cs_subset_sort(shared->sub, "sort");
1230  if (OptRedrawTree && shared->mailbox && (shared->mailbox->msg_count != 0) &&
1231  ((c_sort & SORT_MASK) == SORT_THREADS))
1232  {
1233  mutt_draw_tree(shared->ctx->threads);
1235  OptRedrawTree = false;
1236  }
1237  }
1238 
1239  if (shared->mailbox)
1240  {
1241  mailbox_gc_run();
1242 
1243  shared->ctx->menu = priv->menu;
1244  /* check for new mail in the mailbox. If nonzero, then something has
1245  * changed about the file (either we got new mail or the file was
1246  * modified underneath us.) */
1247  enum MxStatus check = mx_mbox_check(shared->mailbox);
1248 
1249  if (check == MX_STATUS_ERROR)
1250  {
1251  if (mutt_buffer_is_empty(&shared->mailbox->pathbuf))
1252  {
1253  /* fatal error occurred */
1254  struct Context *ctx = shared->ctx;
1255  index_shared_data_set_context(shared, NULL);
1256  ctx_free(&ctx);
1258  }
1259 
1260  OptSearchInvalid = true;
1261  }
1262  else if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED) ||
1263  (check == MX_STATUS_FLAGS))
1264  {
1265  /* notify the user of new mail */
1266  if (check == MX_STATUS_REOPENED)
1267  {
1268  mutt_error(
1269  _("Mailbox was externally modified. Flags may be wrong."));
1270  }
1271  else if (check == MX_STATUS_NEW_MAIL)
1272  {
1273  for (size_t i = 0; i < shared->mailbox->msg_count; i++)
1274  {
1275  const struct Email *e = shared->mailbox->emails[i];
1276  if (e && !e->read && !e->old)
1277  {
1278  mutt_message(_("New mail in this mailbox"));
1279  const bool c_beep_new = cs_subset_bool(shared->sub, "beep_new");
1280  if (c_beep_new)
1281  mutt_beep(true);
1282  const char *const c_new_mail_command =
1283  cs_subset_string(shared->sub, "new_mail_command");
1284  if (c_new_mail_command)
1285  {
1286  char cmd[1024];
1287  menu_status_line(cmd, sizeof(cmd), shared, priv->menu,
1288  sizeof(cmd), NONULL(c_new_mail_command));
1289  if (mutt_system(cmd) != 0)
1290  mutt_error(_("Error running \"%s\""), cmd);
1291  }
1292  break;
1293  }
1294  }
1295  }
1296  else if (check == MX_STATUS_FLAGS)
1297  {
1298  mutt_message(_("Mailbox was externally modified"));
1299  }
1300 
1301  /* avoid the message being overwritten by mailbox */
1302  priv->do_mailbox_notify = false;
1303 
1304  bool verbose = shared->mailbox->verbose;
1305  shared->mailbox->verbose = false;
1306  update_index(priv->menu, shared->ctx, check, priv->oldcount, shared);
1307  shared->mailbox->verbose = verbose;
1308  priv->menu->max = shared->mailbox->vcount;
1310  OptSearchInvalid = true;
1311  }
1312 
1313  if (shared->mailbox)
1314  {
1316  shared, mutt_get_virt_email(shared->mailbox, menu_get_index(priv->menu)));
1317  }
1318  }
1319 
1320  if (!priv->attach_msg)
1321  {
1322  /* check for new mail in the incoming folders */
1323  priv->oldcount = priv->newcount;
1324  priv->newcount = mutt_mailbox_check(shared->mailbox, 0);
1325  if (priv->newcount != priv->oldcount)
1327  if (priv->do_mailbox_notify)
1328  {
1329  if (mutt_mailbox_notify(shared->mailbox))
1330  {
1332  const bool c_beep_new = cs_subset_bool(shared->sub, "beep_new");
1333  if (c_beep_new)
1334  mutt_beep(true);
1335  const char *const c_new_mail_command =
1336  cs_subset_string(shared->sub, "new_mail_command");
1337  if (c_new_mail_command)
1338  {
1339  char cmd[1024];
1340  menu_status_line(cmd, sizeof(cmd), shared, priv->menu, sizeof(cmd),
1341  NONULL(c_new_mail_command));
1342  if (mutt_system(cmd) != 0)
1343  mutt_error(_("Error running \"%s\""), cmd);
1344  }
1345  }
1346  }
1347  else
1348  priv->do_mailbox_notify = true;
1349  }
1350 
1351  if (op >= 0)
1353 
1354  if (priv->in_pager)
1355  {
1356  mutt_curses_set_cursor(MUTT_CURSOR_VISIBLE); /* fallback from the pager */
1357  }
1358  else
1359  {
1360  index_custom_redraw(priv->menu);
1361  window_redraw(RootWindow, false);
1362 
1363  /* give visual indication that the next command is a tag- command */
1364  if (priv->tag)
1365  {
1366  mutt_window_mvaddstr(MessageWindow, 0, 0, "tag-");
1368  }
1369 
1370  const bool c_arrow_cursor = cs_subset_bool(shared->sub, "arrow_cursor");
1371  const bool c_braille_friendly =
1372  cs_subset_bool(shared->sub, "braille_friendly");
1373  const int index = menu_get_index(priv->menu);
1374  if (c_arrow_cursor)
1375  {
1376  mutt_window_move(priv->menu->win_index, 2, index - priv->menu->top);
1377  }
1378  else if (c_braille_friendly)
1379  {
1380  mutt_window_move(priv->menu->win_index, 0, index - priv->menu->top);
1381  }
1382  else
1383  {
1384  mutt_window_move(priv->menu->win_index, priv->menu->win_index->state.cols - 1,
1385  index - priv->menu->top);
1386  }
1387  mutt_refresh();
1388 
1389  if (SigWinch)
1390  {
1391  SigWinch = 0;
1393  priv->menu->top = 0; /* so we scroll the right amount */
1394  /* force a real complete redraw. clrtobot() doesn't seem to be able
1395  * to handle every case without this. */
1396  clearok(stdscr, true);
1398  continue;
1399  }
1400 
1401  op = km_dokey(MENU_MAIN);
1402 
1403  /* either user abort or timeout */
1404  if (op < 0)
1405  {
1407  if (priv->tag)
1409  continue;
1410  }
1411 
1412  mutt_debug(LL_DEBUG1, "Got op %s (%d)\n", OpStrings[op][0], op);
1413 
1415 
1416  /* special handling for the priv->tag-prefix function */
1417  const bool c_auto_tag = cs_subset_bool(shared->sub, "auto_tag");
1418  if ((op == OP_TAG_PREFIX) || (op == OP_TAG_PREFIX_COND))
1419  {
1420  /* A second priv->tag-prefix command aborts */
1421  if (priv->tag)
1422  {
1423  priv->tag = false;
1425  continue;
1426  }
1427 
1428  if (!shared->mailbox)
1429  {
1430  mutt_error(_("No mailbox is open"));
1431  continue;
1432  }
1433 
1434  if (shared->mailbox->msg_tagged == 0)
1435  {
1436  if (op == OP_TAG_PREFIX)
1437  mutt_error(_("No tagged messages"));
1438  else if (op == OP_TAG_PREFIX_COND)
1439  {
1441  mutt_message(_("Nothing to do"));
1442  }
1443  continue;
1444  }
1445 
1446  /* get the real command */
1447  priv->tag = true;
1448  continue;
1449  }
1450  else if (c_auto_tag && shared->mailbox && (shared->mailbox->msg_tagged != 0))
1451  {
1452  priv->tag = true;
1453  }
1454 
1455  mutt_clear_error();
1456  }
1457 
1458 #ifdef USE_NNTP
1459  OptNews = false; /* for any case */
1460 #endif
1461 
1462 #ifdef USE_NOTMUCH
1463  nm_db_debug_check(shared->mailbox);
1464 #endif
1465 
1466  switch (op)
1467  {
1468  /* ----------------------------------------------------------------------
1469  * movement commands
1470  */
1471 
1472  case OP_BOTTOM_PAGE:
1473  menu_bottom_page(priv->menu);
1474  break;
1475  case OP_CURRENT_BOTTOM:
1476  menu_current_bottom(priv->menu);
1477  break;
1478  case OP_CURRENT_MIDDLE:
1479  menu_current_middle(priv->menu);
1480  break;
1481  case OP_CURRENT_TOP:
1482  menu_current_top(priv->menu);
1483  break;
1484  case OP_FIRST_ENTRY:
1485  menu_first_entry(priv->menu);
1486  break;
1487  case OP_HALF_DOWN:
1488  menu_half_down(priv->menu);
1489  break;
1490  case OP_HALF_UP:
1491  menu_half_up(priv->menu);
1492  break;
1493  case OP_LAST_ENTRY:
1494  menu_last_entry(priv->menu);
1495  break;
1496  case OP_MIDDLE_PAGE:
1497  menu_middle_page(priv->menu);
1498  break;
1499  case OP_NEXT_LINE:
1500  menu_next_line(priv->menu);
1501  break;
1502  case OP_NEXT_PAGE:
1503  menu_next_page(priv->menu);
1504  break;
1505  case OP_PREV_LINE:
1506  menu_prev_line(priv->menu);
1507  break;
1508  case OP_PREV_PAGE:
1509  menu_prev_page(priv->menu);
1510  break;
1511  case OP_TOP_PAGE:
1512  menu_top_page(priv->menu);
1513  break;
1514 
1515 #ifdef USE_NNTP
1516  case OP_GET_PARENT:
1517  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
1518  break;
1519  /* fallthrough */
1520 
1521  case OP_GET_MESSAGE:
1522  {
1523  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_READONLY | CHECK_ATTACH))
1524  break;
1525  char buf[PATH_MAX] = { 0 };
1526  if (shared->mailbox->type == MUTT_NNTP)
1527  {
1528  if (op == OP_GET_MESSAGE)
1529  {
1530  if ((mutt_get_field(_("Enter Message-Id: "), buf, sizeof(buf),
1531  MUTT_COMP_NO_FLAGS, false, NULL, NULL) != 0) ||
1532  (buf[0] == '\0'))
1533  {
1534  break;
1535  }
1536  }
1537  else
1538  {
1539  if (!shared->email || STAILQ_EMPTY(&shared->email->env->references))
1540  {
1541  mutt_error(_("Article has no parent reference"));
1542  break;
1543  }
1544  mutt_str_copy(buf, STAILQ_FIRST(&shared->email->env->references)->data,
1545  sizeof(buf));
1546  }
1547  if (!shared->mailbox->id_hash)
1548  shared->mailbox->id_hash = mutt_make_id_hash(shared->mailbox);
1549  struct Email *e = mutt_hash_find(shared->mailbox->id_hash, buf);
1550  if (e)
1551  {
1552  if (e->vnum != -1)
1553  {
1554  menu_set_index(priv->menu, e->vnum);
1555  }
1556  else if (e->collapsed)
1557  {
1559  mutt_set_vnum(shared->mailbox);
1560  menu_set_index(priv->menu, e->vnum);
1561  }
1562  else
1563  mutt_error(_("Message is not visible in limited view"));
1564  }
1565  else
1566  {
1567  mutt_message(_("Fetching %s from server..."), buf);
1568  int rc = nntp_check_msgid(shared->mailbox, buf);
1569  if (rc == 0)
1570  {
1571  e = shared->mailbox->emails[shared->mailbox->msg_count - 1];
1572  mutt_sort_headers(shared->mailbox, shared->ctx->threads, false,
1573  &shared->ctx->vsize);
1574  menu_set_index(priv->menu, e->vnum);
1576  }
1577  else if (rc > 0)
1578  mutt_error(_("Article %s not found on the server"), buf);
1579  }
1580  }
1581  break;
1582  }
1583 
1584  case OP_GET_CHILDREN:
1585  case OP_RECONSTRUCT_THREAD:
1586  {
1587  if (!prereq(shared->ctx, priv->menu,
1589  {
1590  break;
1591  }
1592  if (shared->mailbox->type != MUTT_NNTP)
1593  break;
1594 
1595  if (!shared->email)
1596  break;
1597 
1598  char buf[PATH_MAX] = { 0 };
1599  int oldmsgcount = shared->mailbox->msg_count;
1600  int oldindex = shared->email->index;
1601  int rc = 0;
1602 
1603  if (!shared->email->env->message_id)
1604  {
1605  mutt_error(_("No Message-Id. Unable to perform operation."));
1606  break;
1607  }
1608 
1609  mutt_message(_("Fetching message headers..."));
1610  if (!shared->mailbox->id_hash)
1611  shared->mailbox->id_hash = mutt_make_id_hash(shared->mailbox);
1612  mutt_str_copy(buf, shared->email->env->message_id, sizeof(buf));
1613 
1614  /* trying to find msgid of the root message */
1615  if (op == OP_RECONSTRUCT_THREAD)
1616  {
1617  struct ListNode *ref = NULL;
1618  STAILQ_FOREACH(ref, &shared->email->env->references, entries)
1619  {
1620  if (!mutt_hash_find(shared->mailbox->id_hash, ref->data))
1621  {
1622  rc = nntp_check_msgid(shared->mailbox, ref->data);
1623  if (rc < 0)
1624  break;
1625  }
1626 
1627  /* the last msgid in References is the root message */
1628  if (!STAILQ_NEXT(ref, entries))
1629  mutt_str_copy(buf, ref->data, sizeof(buf));
1630  }
1631  }
1632 
1633  /* fetching all child messages */
1634  if (rc >= 0)
1635  rc = nntp_check_children(shared->mailbox, buf);
1636 
1637  /* at least one message has been loaded */
1638  if (shared->mailbox->msg_count > oldmsgcount)
1639  {
1640  const int index = menu_get_index(priv->menu);
1641  struct Email *e_oldcur = mutt_get_virt_email(shared->mailbox, index);
1642  bool verbose = shared->mailbox->verbose;
1643 
1644  if (rc < 0)
1645  shared->mailbox->verbose = false;
1646  mutt_sort_headers(shared->mailbox, shared->ctx->threads,
1647  (op == OP_RECONSTRUCT_THREAD), &shared->ctx->vsize);
1648  shared->mailbox->verbose = verbose;
1649 
1650  /* Similar to OP_MAIN_ENTIRE_THREAD, keep displaying the old message, but
1651  * update the index */
1652  if (priv->in_pager)
1653  {
1654  menu_set_index(priv->menu, e_oldcur->vnum);
1656  op = OP_DISPLAY_MESSAGE;
1657  continue;
1658  }
1659 
1660  /* if the root message was retrieved, move to it */
1661  struct Email *e = mutt_hash_find(shared->mailbox->id_hash, buf);
1662  if (e)
1663  menu_set_index(priv->menu, e->vnum);
1664  else
1665  {
1666  /* try to restore old position */
1667  for (int i = 0; i < shared->mailbox->msg_count; i++)
1668  {
1669  e = shared->mailbox->emails[i];
1670  if (!e)
1671  break;
1672  if (e->index == oldindex)
1673  {
1674  menu_set_index(priv->menu, e->vnum);
1675  /* as an added courtesy, recenter the priv->menu
1676  * with the current entry at the middle of the screen */
1677  menu_check_recenter(priv->menu);
1678  menu_current_middle(priv->menu);
1679  }
1680  }
1681  }
1683  }
1684  else if (rc >= 0)
1685  {
1686  mutt_error(_("No deleted messages found in the thread"));
1687  /* Similar to OP_MAIN_ENTIRE_THREAD, keep displaying the old message, but
1688  * update the index */
1689  if (priv->in_pager)
1690  {
1691  op = OP_DISPLAY_MESSAGE;
1692  continue;
1693  }
1694  }
1695  break;
1696  }
1697 #endif
1698 
1699  case OP_JUMP:
1700  {
1701  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
1702  break;
1703  char buf[PATH_MAX] = { 0 };
1704  int msg_num = 0;
1705  if (isdigit(LastKey))
1707  if ((mutt_get_field(_("Jump to message: "), buf, sizeof(buf),
1708  MUTT_COMP_NO_FLAGS, false, NULL, NULL) != 0) ||
1709  (buf[0] == '\0'))
1710  {
1711  mutt_error(_("Nothing to do"));
1712  }
1713  else if (mutt_str_atoi(buf, &msg_num) < 0)
1714  mutt_error(_("Argument must be a message number"));
1715  else if ((msg_num < 1) || (msg_num > shared->mailbox->msg_count))
1716  mutt_error(_("Invalid message number"));
1717  else if (!shared->mailbox->emails[msg_num - 1]->visible)
1718  mutt_error(_("That message is not visible"));
1719  else
1720  {
1721  struct Email *e = shared->mailbox->emails[msg_num - 1];
1722 
1723  if (mutt_messages_in_thread(shared->mailbox, e, MIT_POSITION) > 1)
1724  {
1726  mutt_set_vnum(shared->mailbox);
1727  }
1728  menu_set_index(priv->menu, e->vnum);
1729  }
1730 
1731  if (priv->in_pager)
1732  {
1733  op = OP_DISPLAY_MESSAGE;
1734  continue;
1735  }
1736  else
1738 
1739  break;
1740  }
1741 
1742  /* --------------------------------------------------------------------
1743  * 'index' specific commands
1744  */
1745 
1746  case OP_MAIN_DELETE_PATTERN:
1747  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_READONLY | CHECK_ATTACH))
1748  {
1749  break;
1750  }
1751  /* L10N: CHECK_ACL */
1752  /* L10N: Due to the implementation details we do not know whether we
1753  delete zero, 1, 12, ... messages. So in English we use
1754  "messages". Your language might have other means to express this. */
1755  if (!check_acl(shared->mailbox, MUTT_ACL_DELETE, _("Can't delete messages")))
1756  break;
1757 
1758  mutt_pattern_func(shared->ctx, MUTT_DELETE, _("Delete messages matching: "));
1760  break;
1761 
1762 #ifdef USE_POP
1763  case OP_MAIN_FETCH_MAIL:
1764  if (!prereq(shared->ctx, priv->menu, CHECK_ATTACH))
1765  break;
1766  pop_fetch_mail();
1768  break;
1769 #endif /* USE_POP */
1770 
1771  case OP_SHOW_LOG_MESSAGES:
1772  {
1773 #ifdef USE_DEBUG_GRAPHVIZ
1774  dump_graphviz("index", shared->ctx);
1775 #endif
1776  char tempfile[PATH_MAX];
1777  mutt_mktemp(tempfile, sizeof(tempfile));
1778 
1779  FILE *fp = mutt_file_fopen(tempfile, "a+");
1780  if (!fp)
1781  {
1782  mutt_perror("fopen");
1783  break;
1784  }
1785 
1786  log_queue_save(fp);
1787  mutt_file_fclose(&fp);
1788 
1789  struct PagerData pdata = { 0 };
1790  struct PagerView pview = { &pdata };
1791 
1792  pdata.fname = tempfile;
1793 
1794  pview.banner = "messages";
1796  pview.mode = PAGER_MODE_OTHER;
1797 
1798  mutt_do_pager(&pview);
1799  break;
1800  }
1801 
1802  case OP_HELP:
1805  break;
1806 
1807  case OP_MAIN_SHOW_LIMIT:
1808  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
1809  break;
1810  if (!ctx_has_limit(shared->ctx))
1811  mutt_message(_("No limit pattern is in effect"));
1812  else
1813  {
1814  char buf2[256];
1815  /* L10N: ask for a limit to apply */
1816  snprintf(buf2, sizeof(buf2), _("Limit: %s"), shared->ctx->pattern);
1817  mutt_message("%s", buf2);
1818  }
1819  break;
1820 
1821  case OP_LIMIT_CURRENT_THREAD:
1822  case OP_MAIN_LIMIT:
1823  case OP_TOGGLE_READ:
1824  {
1825  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
1826  break;
1827  const bool lmt = ctx_has_limit(shared->ctx);
1828  int old_index = shared->email ? shared->email->index : -1;
1829  if (op == OP_TOGGLE_READ)
1830  {
1831  char buf2[1024];
1832 
1833  if (!lmt || !mutt_strn_equal(shared->ctx->pattern, "!~R!~D~s", 8))
1834  {
1835  snprintf(buf2, sizeof(buf2), "!~R!~D~s%s", lmt ? shared->ctx->pattern : ".*");
1836  }
1837  else
1838  {
1839  mutt_str_copy(buf2, shared->ctx->pattern + 8, sizeof(buf2));
1840  if ((*buf2 == '\0') || mutt_strn_equal(buf2, ".*", 2))
1841  snprintf(buf2, sizeof(buf2), "~A");
1842  }
1843  mutt_str_replace(&shared->ctx->pattern, buf2);
1844  mutt_pattern_func(shared->ctx, MUTT_LIMIT, NULL);
1845  }
1846 
1847  if (((op == OP_LIMIT_CURRENT_THREAD) &&
1848  mutt_limit_current_thread(shared->ctx, shared->email)) ||
1849  (op == OP_TOGGLE_READ) ||
1850  ((op == OP_MAIN_LIMIT) &&
1851  (mutt_pattern_func(shared->ctx, MUTT_LIMIT, _("Limit to messages matching: ")) == 0)))
1852  {
1853  int index = 0;
1854  if (old_index >= 0)
1855  {
1856  /* try to find what used to be the current message */
1857  for (size_t i = 0; i < shared->mailbox->vcount; i++)
1858  {
1859  struct Email *e = mutt_get_virt_email(shared->mailbox, i);
1860  if (!e)
1861  continue;
1862  if (e->index == old_index)
1863  {
1864  index = i;
1865  break;
1866  }
1867  }
1868  }
1869  menu_set_index(priv->menu, index);
1870 
1871  const short c_sort = cs_subset_sort(shared->sub, "sort");
1872  if ((shared->mailbox->msg_count != 0) && ((c_sort & SORT_MASK) == SORT_THREADS))
1873  {
1874  const bool c_collapse_all =
1875  cs_subset_bool(shared->sub, "collapse_all");
1876  if (c_collapse_all)
1877  collapse_all(shared->ctx, priv->menu, 0);
1878  mutt_draw_tree(shared->ctx->threads);
1879  }
1881  }
1882  if (lmt)
1883  mutt_message(_("To view all messages, limit to \"all\""));
1884  break;
1885  }
1886 
1887  case OP_QUIT:
1888  {
1889  if (priv->attach_msg)
1890  {
1891  priv->done = true;
1892  break;
1893  }
1894 
1895  const enum QuadOption c_quit = cs_subset_quad(shared->sub, "quit");
1896  if (query_quadoption(c_quit, _("Quit NeoMutt?")) == MUTT_YES)
1897  {
1898  priv->oldcount = shared->mailbox ? shared->mailbox->msg_count : 0;
1899 
1902 
1903  enum MxStatus check = MX_STATUS_OK;
1904  if (!shared->ctx || ((check = mx_mbox_close(shared->mailbox)) == MX_STATUS_OK))
1905  {
1906  struct Context *ctx = shared->ctx;
1907  index_shared_data_set_context(shared, NULL);
1908  ctx_free(&ctx);
1909  priv->done = true;
1910  }
1911  else
1912  {
1913  if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
1914  {
1915  update_index(priv->menu, shared->ctx, check, priv->oldcount, shared);
1916  }
1917 
1918  menu_queue_redraw(priv->menu, REDRAW_FULL); /* new mail arrived? */
1919  OptSearchInvalid = true;
1920  }
1921  }
1922  break;
1923  }
1924 
1925  case OP_REDRAW:
1926  mutt_window_reflow(NULL);
1927  clearok(stdscr, true);
1929  break;
1930 
1931  // Initiating a search can happen on an empty mailbox, but
1932  // searching for next/previous/... needs to be on a message and
1933  // thus a non-empty mailbox
1934  case OP_SEARCH_REVERSE:
1935  case OP_SEARCH_NEXT:
1936  case OP_SEARCH_OPPOSITE:
1937  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
1938  break;
1939  /* fallthrough */
1940  case OP_SEARCH:
1941  {
1942  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
1943  break;
1944  int index = menu_get_index(priv->menu);
1945  index = mutt_search_command(shared->mailbox, priv->menu, index, op);
1946  if (index != -1)
1947  menu_set_index(priv->menu, index);
1948  else
1950  break;
1951  }
1952 
1953  case OP_SORT:
1954  case OP_SORT_REVERSE:
1955  if (!mutt_select_sort(op == OP_SORT_REVERSE))
1956  break;
1957 
1958  if (shared->mailbox && (shared->mailbox->msg_count != 0))
1959  {
1960  resort_index(shared->ctx, priv->menu);
1961  OptSearchInvalid = true;
1962  }
1963  if (priv->in_pager)
1964  {
1965  op = OP_DISPLAY_MESSAGE;
1966  continue;
1967  }
1969  break;
1970 
1971  case OP_TAG:
1972  {
1973  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
1974  break;
1975  const bool c_auto_tag = cs_subset_bool(shared->sub, "auto_tag");
1976  if (priv->tag && !c_auto_tag)
1977  {
1978  struct Mailbox *m = shared->mailbox;
1979  for (size_t i = 0; i < m->msg_count; i++)
1980  {
1981  struct Email *e = m->emails[i];
1982  if (!e)
1983  break;
1984  if (e->visible)
1985  mutt_set_flag(m, e, MUTT_TAG, false);
1986  }
1988  }
1989  else
1990  {
1991  if (!shared->email)
1992  break;
1993  mutt_set_flag(shared->mailbox, shared->email, MUTT_TAG, !shared->email->tagged);
1994 
1996  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
1997  const int index = menu_get_index(priv->menu) + 1;
1998  if (c_resolve && (index < shared->mailbox->vcount))
1999  {
2000  menu_set_index(priv->menu, index);
2001  }
2002  else
2004  }
2005  break;
2006  }
2007 
2008  case OP_MAIN_TAG_PATTERN:
2009  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
2010  break;
2011  mutt_pattern_func(shared->ctx, MUTT_TAG, _("Tag messages matching: "));
2013  break;
2014 
2015  case OP_MAIN_UNDELETE_PATTERN:
2016  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_READONLY))
2017  break;
2018  /* L10N: CHECK_ACL */
2019  /* L10N: Due to the implementation details we do not know whether we
2020  undelete zero, 1, 12, ... messages. So in English we use
2021  "messages". Your language might have other means to express this. */
2022  if (!check_acl(shared->mailbox, MUTT_ACL_DELETE, _("Can't undelete messages")))
2023  break;
2024 
2025  if (mutt_pattern_func(shared->ctx, MUTT_UNDELETE,
2026  _("Undelete messages matching: ")) == 0)
2027  {
2029  }
2030  break;
2031 
2032  case OP_MAIN_UNTAG_PATTERN:
2033  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
2034  break;
2035  if (mutt_pattern_func(shared->ctx, MUTT_UNTAG, _("Untag messages matching: ")) == 0)
2037  break;
2038 
2039  case OP_COMPOSE_TO_SENDER:
2040  {
2041  if (!prereq(shared->ctx, priv->menu,
2043  break;
2044  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
2045  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
2046  mutt_send_message(SEND_TO_SENDER, NULL, NULL, shared->mailbox, &el,
2047  shared->sub);
2048  emaillist_clear(&el);
2050  break;
2051  }
2052 
2053  /* --------------------------------------------------------------------
2054  * The following operations can be performed inside of the pager.
2055  */
2056 
2057 #ifdef USE_IMAP
2058  case OP_MAIN_IMAP_FETCH:
2059  if (shared->mailbox && (shared->mailbox->type == MUTT_IMAP))
2060  imap_check_mailbox(shared->mailbox, true);
2061  break;
2062 
2063  case OP_MAIN_IMAP_LOGOUT_ALL:
2064  if (shared->mailbox && (shared->mailbox->type == MUTT_IMAP))
2065  {
2066  const enum MxStatus check = mx_mbox_close(shared->mailbox);
2067  if (check == MX_STATUS_OK)
2068  {
2069  struct Context *ctx = shared->ctx;
2070  index_shared_data_set_context(shared, NULL);
2071  ctx_free(&ctx);
2072  }
2073  else
2074  {
2075  if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
2076  {
2077  update_index(priv->menu, shared->ctx, check, priv->oldcount, shared);
2078  }
2079  OptSearchInvalid = true;
2081  break;
2082  }
2083  }
2084  imap_logout_all();
2085  mutt_message(_("Logged out of IMAP servers"));
2086  OptSearchInvalid = true;
2088  break;
2089 #endif
2090 
2091  case OP_MAIN_SYNC_FOLDER:
2092  if (!shared->mailbox || (shared->mailbox->msg_count == 0))
2093  break;
2094 
2095  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_READONLY))
2096  break;
2097  {
2098  int ovc = shared->mailbox->vcount;
2099  int oc = shared->mailbox->msg_count;
2100  struct Email *e = NULL;
2101 
2102  /* don't attempt to move the cursor if there are no visible messages in the current limit */
2103  int index = menu_get_index(priv->menu);
2104  if (index < shared->mailbox->vcount)
2105  {
2106  /* threads may be reordered, so figure out what header the cursor
2107  * should be on. */
2108  int newidx = index;
2109  if (!shared->email)
2110  break;
2111  if (shared->email->deleted)
2112  newidx = ci_next_undeleted(shared->mailbox, index);
2113  if (newidx < 0)
2114  newidx = ci_previous_undeleted(shared->mailbox, index);
2115  if (newidx >= 0)
2116  e = mutt_get_virt_email(shared->mailbox, newidx);
2117  }
2118 
2119  enum MxStatus check = mx_mbox_sync(shared->mailbox);
2120  if (check == MX_STATUS_OK)
2121  {
2122  if (e && (shared->mailbox->vcount != ovc))
2123  {
2124  for (size_t i = 0; i < shared->mailbox->vcount; i++)
2125  {
2126  struct Email *e2 = mutt_get_virt_email(shared->mailbox, i);
2127  if (e2 == e)
2128  {
2129  menu_set_index(priv->menu, i);
2130  break;
2131  }
2132  }
2133  }
2134  OptSearchInvalid = true;
2135  }
2136  else if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
2137  {
2138  update_index(priv->menu, shared->ctx, check, oc, shared);
2139  }
2140 
2141  /* do a sanity check even if mx_mbox_sync failed. */
2142 
2143  index = menu_get_index(priv->menu);
2144  if ((index < 0) || (shared->mailbox && (index >= shared->mailbox->vcount)))
2145  {
2146  menu_set_index(priv->menu, ci_first_message(shared->mailbox));
2147  }
2148  }
2149 
2150  /* check for a fatal error, or all messages deleted */
2151  if (shared->mailbox && mutt_buffer_is_empty(&shared->mailbox->pathbuf))
2152  {
2153  struct Context *ctx = shared->ctx;
2154  index_shared_data_set_context(shared, NULL);
2155  ctx_free(&ctx);
2156  }
2157 
2158  /* if we were in the pager, redisplay the message */
2159  if (priv->in_pager)
2160  {
2161  op = OP_DISPLAY_MESSAGE;
2162  continue;
2163  }
2164  else
2166  break;
2167 
2168  case OP_MAIN_QUASI_DELETE:
2169  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2170  break;
2171  if (priv->tag)
2172  {
2173  struct Mailbox *m = shared->mailbox;
2174  for (size_t i = 0; i < m->msg_count; i++)
2175  {
2176  struct Email *e = m->emails[i];
2177  if (!e)
2178  break;
2179  if (message_is_tagged(e))
2180  {
2181  e->quasi_deleted = true;
2182  m->changed = true;
2183  }
2184  }
2185  }
2186  else
2187  {
2188  if (!shared->email)
2189  break;
2190  shared->email->quasi_deleted = true;
2191  shared->mailbox->changed = true;
2192  }
2193  break;
2194 
2195 #ifdef USE_NOTMUCH
2196  case OP_MAIN_ENTIRE_THREAD:
2197  {
2198  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2199  break;
2200  char buf[PATH_MAX] = { 0 };
2201  if (shared->mailbox->type != MUTT_NOTMUCH)
2202  {
2203  if (((shared->mailbox->type != MUTT_MH) && (shared->mailbox->type != MUTT_MAILDIR)) ||
2204  (!shared->email || !shared->email->env || !shared->email->env->message_id))
2205  {
2206  mutt_message(_("No virtual folder and no Message-Id, aborting"));
2207  break;
2208  } // no virtual folder, but we have message-id, reconstruct thread on-the-fly
2209  strncpy(buf, "id:", sizeof(buf));
2210  int msg_id_offset = 0;
2211  if ((shared->email->env->message_id)[0] == '<')
2212  msg_id_offset = 1;
2213  mutt_str_cat(buf, sizeof(buf), (shared->email->env->message_id) + msg_id_offset);
2214  if (buf[strlen(buf) - 1] == '>')
2215  buf[strlen(buf) - 1] = '\0';
2216 
2217  change_folder_notmuch(priv->menu, buf, sizeof(buf), &priv->oldcount, shared, false);
2218 
2219  // If notmuch doesn't contain the message, we're left in an empty
2220  // vfolder. No messages are found, but nm_read_entire_thread assumes
2221  // a valid message-id and will throw a segfault.
2222  //
2223  // To prevent that, stay in the empty vfolder and print an error.
2224  if (shared->mailbox->msg_count == 0)
2225  {
2226  mutt_error(_("failed to find message in notmuch database. try "
2227  "running 'notmuch new'."));
2228  break;
2229  }
2230  }
2231  priv->oldcount = shared->mailbox->msg_count;
2232  const int index = menu_get_index(priv->menu);
2233  struct Email *e_oldcur = mutt_get_virt_email(shared->mailbox, index);
2234  if (nm_read_entire_thread(shared->mailbox, e_oldcur) < 0)
2235  {
2236  mutt_message(_("Failed to read thread, aborting"));
2237  break;
2238  }
2239  if (priv->oldcount < shared->mailbox->msg_count)
2240  {
2241  /* nm_read_entire_thread() triggers mutt_sort_headers() if necessary */
2242  menu_set_index(priv->menu, e_oldcur->vnum);
2244 
2245  if (e_oldcur->collapsed || shared->ctx->collapsed)
2246  {
2247  menu_set_index(priv->menu, mutt_uncollapse_thread(e_oldcur));
2248  mutt_set_vnum(shared->mailbox);
2249  }
2250  }
2251  if (priv->in_pager)
2252  {
2253  op = OP_DISPLAY_MESSAGE;
2254  continue;
2255  }
2256  break;
2257  }
2258 #endif
2259 
2260  case OP_MAIN_MODIFY_TAGS:
2261  case OP_MAIN_MODIFY_TAGS_THEN_HIDE:
2262  {
2263  if (!shared->mailbox)
2264  break;
2265  struct Mailbox *m = shared->mailbox;
2266  if (!mx_tags_is_supported(m))
2267  {
2268  mutt_message(_("Folder doesn't support tagging, aborting"));
2269  break;
2270  }
2271  if (!prereq(shared->ctx, priv->menu,
2273  break;
2274  if (!shared->email)
2275  break;
2276  char *tags = NULL;
2277  if (!priv->tag)
2278  tags = driver_tags_get_with_hidden(&shared->email->tags);
2279  char buf[PATH_MAX] = { 0 };
2280  int rc = mx_tags_edit(m, tags, buf, sizeof(buf));
2281  FREE(&tags);
2282  if (rc < 0)
2283  break;
2284  else if (rc == 0)
2285  {
2286  mutt_message(_("No tag specified, aborting"));
2287  break;
2288  }
2289 
2290  if (priv->tag)
2291  {
2292  struct Progress progress;
2293 
2294  if (m->verbose)
2295  {
2296  mutt_progress_init(&progress, _("Update tags..."),
2298  }
2299 
2300 #ifdef USE_NOTMUCH
2301  if (m->type == MUTT_NOTMUCH)
2302  nm_db_longrun_init(m, true);
2303 #endif
2304  for (int px = 0, i = 0; i < m->msg_count; i++)
2305  {
2306  struct Email *e = m->emails[i];
2307  if (!e)
2308  break;
2309  if (!message_is_tagged(e))
2310  continue;
2311 
2312  if (m->verbose)
2313  mutt_progress_update(&progress, ++px, -1);
2314  mx_tags_commit(m, e, buf);
2315  if (op == OP_MAIN_MODIFY_TAGS_THEN_HIDE)
2316  {
2317  bool still_queried = false;
2318 #ifdef USE_NOTMUCH
2319  if (m->type == MUTT_NOTMUCH)
2320  still_queried = nm_message_is_still_queried(m, e);
2321 #endif
2322  e->quasi_deleted = !still_queried;
2323  m->changed = true;
2324  }
2325  }
2326 #ifdef USE_NOTMUCH
2327  if (m->type == MUTT_NOTMUCH)
2328  nm_db_longrun_done(m);
2329 #endif
2331  }
2332  else
2333  {
2334  if (mx_tags_commit(m, shared->email, buf))
2335  {
2336  mutt_message(_("Failed to modify tags, aborting"));
2337  break;
2338  }
2339  if (op == OP_MAIN_MODIFY_TAGS_THEN_HIDE)
2340  {
2341  bool still_queried = false;
2342 #ifdef USE_NOTMUCH
2343  if (m->type == MUTT_NOTMUCH)
2344  still_queried = nm_message_is_still_queried(m, shared->email);
2345 #endif
2346  shared->email->quasi_deleted = !still_queried;
2347  m->changed = true;
2348  }
2349  if (priv->in_pager)
2350  {
2351  op = OP_DISPLAY_MESSAGE;
2352  continue;
2353  }
2354  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
2355  if (c_resolve)
2356  {
2357  int index = menu_get_index(priv->menu);
2358  index = ci_next_undeleted(shared->mailbox, index);
2359  if (index == -1)
2360  {
2362  }
2363  else
2364  {
2365  menu_set_index(priv->menu, index);
2366  }
2367  }
2368  else
2370  }
2372  break;
2373  }
2374 
2375  case OP_CHECK_STATS:
2376  mutt_check_stats(shared->mailbox);
2377  break;
2378 
2379 #ifdef USE_NOTMUCH
2380  case OP_MAIN_VFOLDER_FROM_QUERY:
2381  case OP_MAIN_VFOLDER_FROM_QUERY_READONLY:
2382  {
2383  char buf[PATH_MAX] = { 0 };
2384  if ((mutt_get_field("Query: ", buf, sizeof(buf), MUTT_NM_QUERY, false, NULL, NULL) != 0) ||
2385  (buf[0] == '\0'))
2386  {
2387  mutt_message(_("No query, aborting"));
2388  break;
2389  }
2390 
2391  // Keep copy of user's query to name the mailbox
2392  char *query_unencoded = mutt_str_dup(buf);
2393 
2394  struct Mailbox *m_query =
2395  change_folder_notmuch(priv->menu, buf, sizeof(buf), &priv->oldcount, shared,
2396  (op == OP_MAIN_VFOLDER_FROM_QUERY_READONLY));
2397  if (m_query)
2398  {
2399  m_query->name = query_unencoded;
2400  query_unencoded = NULL;
2401  }
2402  else
2403  {
2404  FREE(&query_unencoded);
2405  }
2406 
2407  break;
2408  }
2409 
2410  case OP_MAIN_WINDOWED_VFOLDER_BACKWARD:
2411  {
2412  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
2413  break;
2414  const short c_nm_query_window_duration =
2415  cs_subset_number(shared->sub, "nm_query_window_duration");
2416  if (c_nm_query_window_duration <= 0)
2417  {
2418  mutt_message(_("Windowed queries disabled"));
2419  break;
2420  }
2421  const char *const c_nm_query_window_current_search =
2422  cs_subset_string(shared->sub, "nm_query_window_current_search");
2423  if (!c_nm_query_window_current_search)
2424  {
2425  mutt_message(_("No notmuch vfolder currently loaded"));
2426  break;
2427  }
2429  char buf[PATH_MAX] = { 0 };
2430  mutt_str_copy(buf, c_nm_query_window_current_search, sizeof(buf));
2431  change_folder_notmuch(priv->menu, buf, sizeof(buf), &priv->oldcount, shared, false);
2432  break;
2433  }
2434 
2435  case OP_MAIN_WINDOWED_VFOLDER_FORWARD:
2436  {
2437  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
2438  break;
2439  const short c_nm_query_window_duration =
2440  cs_subset_number(shared->sub, "nm_query_window_duration");
2441  if (c_nm_query_window_duration <= 0)
2442  {
2443  mutt_message(_("Windowed queries disabled"));
2444  break;
2445  }
2446  const char *const c_nm_query_window_current_search =
2447  cs_subset_string(shared->sub, "nm_query_window_current_search");
2448  if (!c_nm_query_window_current_search)
2449  {
2450  mutt_message(_("No notmuch vfolder currently loaded"));
2451  break;
2452  }
2454  char buf[PATH_MAX] = { 0 };
2455  mutt_str_copy(buf, c_nm_query_window_current_search, sizeof(buf));
2456  change_folder_notmuch(priv->menu, buf, sizeof(buf), &priv->oldcount, shared, false);
2457  break;
2458  }
2459 #endif
2460 
2461 #ifdef USE_SIDEBAR
2462  case OP_SIDEBAR_OPEN:
2463  {
2464  struct MuttWindow *win_sidebar = mutt_window_find(dlg, WT_SIDEBAR);
2465  change_folder_mailbox(priv->menu, sb_get_highlight(win_sidebar),
2466  &priv->oldcount, shared, false);
2467  break;
2468  }
2469 #endif
2470 
2471  case OP_MAIN_NEXT_UNREAD_MAILBOX:
2472  {
2473  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
2474  break;
2475 
2476  struct Mailbox *m = shared->mailbox;
2477 
2478  struct Buffer *folderbuf = mutt_buffer_pool_get();
2479  mutt_buffer_strcpy(folderbuf, mailbox_path(m));
2480  m = mutt_mailbox_next(m, folderbuf);
2481  mutt_buffer_pool_release(&folderbuf);
2482 
2483  if (!m)
2484  {
2485  mutt_error(_("No mailboxes have new mail"));
2486  break;
2487  }
2488 
2489  change_folder_mailbox(priv->menu, m, &priv->oldcount, shared, false);
2490  break;
2491  }
2492 
2493  case OP_MAIN_CHANGE_FOLDER:
2494  case OP_MAIN_CHANGE_FOLDER_READONLY:
2495 #ifdef USE_NOTMUCH
2496  case OP_MAIN_CHANGE_VFOLDER: // now an alias for OP_MAIN_CHANGE_FOLDER
2497 #endif
2498  {
2499  bool pager_return = true; /* return to display message in pager */
2500  struct Buffer *folderbuf = mutt_buffer_pool_get();
2501  mutt_buffer_alloc(folderbuf, PATH_MAX);
2502 
2503  char *cp = NULL;
2504  bool read_only;
2505  const bool c_read_only = cs_subset_bool(shared->sub, "read_only");
2506  if (priv->attach_msg || c_read_only || (op == OP_MAIN_CHANGE_FOLDER_READONLY))
2507  {
2508  cp = _("Open mailbox in read-only mode");
2509  read_only = true;
2510  }
2511  else
2512  {
2513  cp = _("Open mailbox");
2514  read_only = false;
2515  }
2516 
2517  const bool c_change_folder_next =
2518  cs_subset_bool(shared->sub, "change_folder_next");
2519  if (c_change_folder_next && shared->mailbox &&
2520  !mutt_buffer_is_empty(&shared->mailbox->pathbuf))
2521  {
2522  mutt_buffer_strcpy(folderbuf, mailbox_path(shared->mailbox));
2523  mutt_buffer_pretty_mailbox(folderbuf);
2524  }
2525  /* By default, fill buf with the next mailbox that contains unread mail */
2526  mutt_mailbox_next(shared->ctx ? shared->mailbox : NULL, folderbuf);
2527 
2528  if (mutt_buffer_enter_fname(cp, folderbuf, true, shared->mailbox, false,
2529  NULL, NULL, MUTT_SEL_NO_FLAGS) == -1)
2530  goto changefoldercleanup;
2531 
2532  /* Selected directory is okay, let's save it. */
2534 
2535  if (mutt_buffer_is_empty(folderbuf))
2536  {
2538  goto changefoldercleanup;
2539  }
2540 
2541  struct Mailbox *m = mx_mbox_find2(mutt_buffer_string(folderbuf));
2542  if (m)
2543  {
2544  change_folder_mailbox(priv->menu, m, &priv->oldcount, shared, read_only);
2545  pager_return = false;
2546  }
2547  else
2548  {
2549  change_folder_string(priv->menu, folderbuf->data, folderbuf->dsize,
2550  &priv->oldcount, shared, &pager_return, read_only);
2551  }
2552 
2553  changefoldercleanup:
2554  mutt_buffer_pool_release(&folderbuf);
2555  if (priv->in_pager && pager_return)
2556  {
2557  op = OP_DISPLAY_MESSAGE;
2558  continue;
2559  }
2560  break;
2561  }
2562 
2563 #ifdef USE_NNTP
2564  case OP_MAIN_CHANGE_GROUP:
2565  case OP_MAIN_CHANGE_GROUP_READONLY:
2566  {
2567  bool pager_return = true; /* return to display message in pager */
2568  struct Buffer *folderbuf = mutt_buffer_pool_get();
2569  mutt_buffer_alloc(folderbuf, PATH_MAX);
2570 
2571  OptNews = false;
2572  bool read_only;
2573  char *cp = NULL;
2574  const bool c_read_only = cs_subset_bool(shared->sub, "read_only");
2575  if (priv->attach_msg || c_read_only || (op == OP_MAIN_CHANGE_GROUP_READONLY))
2576  {
2577  cp = _("Open newsgroup in read-only mode");
2578  read_only = true;
2579  }
2580  else
2581  {
2582  cp = _("Open newsgroup");
2583  read_only = false;
2584  }
2585 
2586  const bool c_change_folder_next =
2587  cs_subset_bool(shared->sub, "change_folder_next");
2588  if (c_change_folder_next && shared->mailbox &&
2589  !mutt_buffer_is_empty(&shared->mailbox->pathbuf))
2590  {
2591  mutt_buffer_strcpy(folderbuf, mailbox_path(shared->mailbox));
2592  mutt_buffer_pretty_mailbox(folderbuf);
2593  }
2594 
2595  OptNews = true;
2596  const char *const c_news_server =
2597  cs_subset_string(shared->sub, "news_server");
2598  CurrentNewsSrv = nntp_select_server(shared->mailbox, c_news_server, false);
2599  if (!CurrentNewsSrv)
2600  goto changefoldercleanup2;
2601 
2602  nntp_mailbox(shared->mailbox, folderbuf->data, folderbuf->dsize);
2603 
2604  if (mutt_buffer_enter_fname(cp, folderbuf, true, shared->mailbox, false,
2605  NULL, NULL, MUTT_SEL_NO_FLAGS) == -1)
2606  goto changefoldercleanup2;
2607 
2608  /* Selected directory is okay, let's save it. */
2610 
2611  if (mutt_buffer_is_empty(folderbuf))
2612  {
2614  goto changefoldercleanup2;
2615  }
2616 
2617  struct Mailbox *m = mx_mbox_find2(mutt_buffer_string(folderbuf));
2618  if (m)
2619  {
2620  change_folder_mailbox(priv->menu, m, &priv->oldcount, shared, read_only);
2621  pager_return = false;
2622  }
2623  else
2624  {
2625  change_folder_string(priv->menu, folderbuf->data, folderbuf->dsize,
2626  &priv->oldcount, shared, &pager_return, read_only);
2627  }
2628  dlg->help_data = IndexNewsHelp;
2629 
2630  changefoldercleanup2:
2631  mutt_buffer_pool_release(&folderbuf);
2632  if (priv->in_pager && pager_return)
2633  {
2634  op = OP_DISPLAY_MESSAGE;
2635  continue;
2636  }
2637  break;
2638  }
2639 #endif
2640 
2641  case OP_DISPLAY_MESSAGE:
2642  case OP_DISPLAY_HEADERS: /* don't weed the headers */
2643  {
2644  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2645  break;
2646  if (!shared->email)
2647  break;
2648  /* toggle the weeding of headers so that a user can press the key
2649  * again while reading the message. */
2650  if (op == OP_DISPLAY_HEADERS)
2651  bool_str_toggle(shared->sub, "weed", NULL);
2652 
2653  OptNeedResort = false;
2654 
2655  const short c_sort = cs_subset_sort(shared->sub, "sort");
2656  if (((c_sort & SORT_MASK) == SORT_THREADS) && shared->email->collapsed)
2657  {
2658  mutt_uncollapse_thread(shared->email);
2659  mutt_set_vnum(shared->mailbox);
2660  const bool c_uncollapse_jump =
2661  cs_subset_bool(shared->sub, "uncollapse_jump");
2662  if (c_uncollapse_jump)
2664  }
2665 
2666  const bool c_pgp_auto_decode =
2667  cs_subset_bool(shared->sub, "pgp_auto_decode");
2668  if (c_pgp_auto_decode &&
2669  (priv->tag || !(shared->email->security & PGP_TRADITIONAL_CHECKED)))
2670  {
2671  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
2672  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
2673  if (mutt_check_traditional_pgp(shared->mailbox, &el))
2675  emaillist_clear(&el);
2676  }
2677  const int index = menu_get_index(priv->menu);
2678  index_shared_data_set_email(shared, mutt_get_virt_email(shared->mailbox, index));
2679 
2680  op = mutt_display_message(priv->win_index, priv->win_ibar, priv->win_pager,
2681  priv->win_pbar, shared->mailbox, shared->email);
2682  window_set_focus(priv->win_index);
2683  if (op < 0)
2684  {
2685  OptNeedResort = false;
2686  break;
2687  }
2688 
2689  /* This is used to redirect a single operation back here afterwards. If
2690  * mutt_display_message() returns 0, then this flag and pager state will
2691  * be cleaned up after this switch statement. */
2692  priv->in_pager = true;
2693  if (shared->mailbox)
2694  {
2695  update_index(priv->menu, shared->ctx, MX_STATUS_NEW_MAIL,
2696  shared->mailbox->msg_count, shared);
2697  }
2698  continue;
2699  }
2700 
2701  case OP_EXIT:
2702  if ((!priv->in_pager) && priv->attach_msg)
2703  {
2704  priv->done = true;
2705  break;
2706  }
2707 
2708  const enum QuadOption c_quit = cs_subset_quad(shared->sub, "quit");
2709  if ((!priv->in_pager) &&
2710  (query_quadoption(c_quit, _("Exit NeoMutt without saving?")) == MUTT_YES))
2711  {
2712  if (shared->ctx)
2713  {
2714  struct Context *ctx = shared->ctx;
2715  index_shared_data_set_context(shared, NULL);
2716  mx_fastclose_mailbox(shared->mailbox);
2717  ctx_free(&ctx);
2718  }
2719  priv->done = true;
2720  }
2721  break;
2722 
2723  case OP_MAIN_BREAK_THREAD:
2724  {
2725  if (!prereq(shared->ctx, priv->menu,
2727  break;
2728  /* L10N: CHECK_ACL */
2729  if (!check_acl(shared->mailbox, MUTT_ACL_WRITE, _("Can't break thread")))
2730  break;
2731  if (!shared->email)
2732  break;
2733 
2734  const short c_sort = cs_subset_sort(shared->sub, "sort");
2735  if ((c_sort & SORT_MASK) != SORT_THREADS)
2736  mutt_error(_("Threading is not enabled"));
2737  else if (!STAILQ_EMPTY(&shared->email->env->in_reply_to) ||
2738  !STAILQ_EMPTY(&shared->email->env->references))
2739  {
2740  {
2741  mutt_break_thread(shared->email);
2742  mutt_sort_headers(shared->mailbox, shared->ctx->threads, true,
2743  &shared->ctx->vsize);
2744  menu_set_index(priv->menu, shared->email->vnum);
2745  }
2746 
2747  shared->mailbox->changed = true;
2748  mutt_message(_("Thread broken"));
2749 
2750  if (priv->in_pager)
2751  {
2752  op = OP_DISPLAY_MESSAGE;
2753  continue;
2754  }
2755  else
2757  }
2758  else
2759  {
2760  mutt_error(
2761  _("Thread can't be broken, message is not part of a thread"));
2762  }
2763  break;
2764  }
2765 
2766  case OP_MAIN_LINK_THREADS:
2767  {
2768  if (!prereq(shared->ctx, priv->menu,
2770  break;
2771  /* L10N: CHECK_ACL */
2772  if (!check_acl(shared->mailbox, MUTT_ACL_WRITE, _("Can't link threads")))
2773  break;
2774  if (!shared->email)
2775  break;
2776 
2777  const short c_sort = cs_subset_sort(shared->sub, "sort");
2778  if ((c_sort & SORT_MASK) != SORT_THREADS)
2779  mutt_error(_("Threading is not enabled"));
2780  else if (!shared->email->env->message_id)
2781  mutt_error(_("No Message-ID: header available to link thread"));
2782  else
2783  {
2784  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
2785  el_add_tagged(&el, shared->ctx, NULL, true);
2786 
2787  if (mutt_link_threads(shared->email, &el, shared->mailbox))
2788  {
2789  mutt_sort_headers(shared->mailbox, shared->ctx->threads, true,
2790  &shared->ctx->vsize);
2791  menu_set_index(priv->menu, shared->email->vnum);
2792 
2793  shared->mailbox->changed = true;
2794  mutt_message(_("Threads linked"));
2795  }
2796  else
2797  mutt_error(_("No thread linked"));
2798 
2799  emaillist_clear(&el);
2800  }
2801 
2802  if (priv->in_pager)
2803  {
2804  op = OP_DISPLAY_MESSAGE;
2805  continue;
2806  }
2807  else
2809 
2810  break;
2811  }
2812 
2813  case OP_EDIT_TYPE:
2814  {
2815  if (!prereq(shared->ctx, priv->menu,
2817  break;
2818  if (!shared->email)
2819  break;
2820  mutt_edit_content_type(shared->email, shared->email->body, NULL);
2821  /* if we were in the pager, redisplay the message */
2822  if (priv->in_pager)
2823  {
2824  op = OP_DISPLAY_MESSAGE;
2825  continue;
2826  }
2827  else
2829  break;
2830  }
2831 
2832  case OP_MAIN_NEXT_UNDELETED:
2833  {
2834  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2835  break;
2836  int index = menu_get_index(priv->menu);
2837  if (index >= (shared->mailbox->vcount - 1))
2838  {
2839  if (!priv->in_pager)
2840  mutt_message(_("You are on the last message"));
2841  break;
2842  }
2843  index = ci_next_undeleted(shared->mailbox, index);
2844  if (index != -1)
2845  menu_set_index(priv->menu, index);
2846 
2847  if (index == -1)
2848  {
2849  if (!priv->in_pager)
2850  mutt_error(_("No undeleted messages"));
2851  }
2852  else if (priv->in_pager)
2853  {
2854  op = OP_DISPLAY_MESSAGE;
2855  continue;
2856  }
2857  else
2859  break;
2860  }
2861 
2862  case OP_NEXT_ENTRY:
2863  {
2864  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2865  break;
2866  const int index = menu_get_index(priv->menu) + 1;
2867  if (index >= shared->mailbox->vcount)
2868  {
2869  if (!priv->in_pager)
2870  mutt_message(_("You are on the last message"));
2871  break;
2872  }
2873  menu_set_index(priv->menu, index);
2874  if (priv->in_pager)
2875  {
2876  op = OP_DISPLAY_MESSAGE;
2877  continue;
2878  }
2879  else
2881  break;
2882  }
2883 
2884  case OP_MAIN_PREV_UNDELETED:
2885  {
2886  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2887  break;
2888  int index = menu_get_index(priv->menu);
2889  if (index < 1)
2890  {
2891  mutt_message(_("You are on the first message"));
2892  break;
2893  }
2894  index = ci_previous_undeleted(shared->mailbox, index);
2895  if (index != -1)
2896  menu_set_index(priv->menu, index);
2897 
2898  if (index == -1)
2899  {
2900  if (!priv->in_pager)
2901  mutt_error(_("No undeleted messages"));
2902  }
2903  else if (priv->in_pager)
2904  {
2905  op = OP_DISPLAY_MESSAGE;
2906  continue;
2907  }
2908  else
2910  break;
2911  }
2912 
2913  case OP_PREV_ENTRY:
2914  {
2915  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2916  break;
2917  int index = menu_get_index(priv->menu);
2918  if (index < 1)
2919  {
2920  if (!priv->in_pager)
2921  mutt_message(_("You are on the first message"));
2922  break;
2923  }
2924  menu_set_index(priv->menu, index - 1);
2925  if (priv->in_pager)
2926  {
2927  op = OP_DISPLAY_MESSAGE;
2928  continue;
2929  }
2930  else
2932  break;
2933  }
2934 
2935  case OP_DECRYPT_COPY:
2936  case OP_DECRYPT_SAVE:
2937  if (!WithCrypto)
2938  break;
2939  /* fallthrough */
2940  case OP_COPY_MESSAGE:
2941  case OP_SAVE:
2942  case OP_DECODE_COPY:
2943  case OP_DECODE_SAVE:
2944  {
2945  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2946  break;
2947  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
2948  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
2949 
2950  const enum MessageSaveOpt save_opt =
2951  ((op == OP_SAVE) || (op == OP_DECODE_SAVE) || (op == OP_DECRYPT_SAVE)) ?
2952  SAVE_MOVE :
2953  SAVE_COPY;
2954 
2955  enum MessageTransformOpt transform_opt =
2956  ((op == OP_DECODE_SAVE) || (op == OP_DECODE_COPY)) ? TRANSFORM_DECODE :
2957  ((op == OP_DECRYPT_SAVE) || (op == OP_DECRYPT_COPY)) ? TRANSFORM_DECRYPT :
2959 
2960  const int rc = mutt_save_message(shared->mailbox, &el, save_opt, transform_opt);
2961  if ((rc == 0) && (save_opt == SAVE_MOVE))
2962  {
2964  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
2965  if (priv->tag)
2967  else if (c_resolve)
2968  {
2969  int index = menu_get_index(priv->menu);
2970  index = ci_next_undeleted(shared->mailbox, index);
2971  if (index == -1)
2972  {
2974  }
2975  else
2976  {
2977  menu_set_index(priv->menu, index);
2978  }
2979  }
2980  else
2982  }
2983  emaillist_clear(&el);
2984  break;
2985  }
2986 
2987  case OP_MAIN_NEXT_NEW:
2988  case OP_MAIN_NEXT_UNREAD:
2989  case OP_MAIN_PREV_NEW:
2990  case OP_MAIN_PREV_UNREAD:
2991  case OP_MAIN_NEXT_NEW_THEN_UNREAD:
2992  case OP_MAIN_PREV_NEW_THEN_UNREAD:
2993  {
2994  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
2995  break;
2996 
2997  int first_unread = -1;
2998  int first_new = -1;
2999 
3000  const int saved_current = menu_get_index(priv->menu);
3001  int mcur = saved_current;
3002  menu_set_index(priv->menu, -1);
3003  for (size_t i = 0; i != shared->mailbox->vcount; i++)
3004  {
3005  if ((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_NEXT_UNREAD) ||
3006  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD))
3007  {
3008  mcur++;
3009  if (mcur > (shared->mailbox->vcount - 1))
3010  {
3011  mcur = 0;
3012  }
3013  }
3014  else
3015  {
3016  mcur--;
3017  if (mcur < 0)
3018  {
3019  mcur = shared->mailbox->vcount - 1;
3020  }
3021  }
3022 
3023  struct Email *e = mutt_get_virt_email(shared->mailbox, mcur);
3024  if (!e)
3025  break;
3026  const short c_sort = cs_subset_sort(shared->sub, "sort");
3027  if (e->collapsed && ((c_sort & SORT_MASK) == SORT_THREADS))
3028  {
3029  int unread = mutt_thread_contains_unread(e);
3030  if ((unread != 0) && (first_unread == -1))
3031  first_unread = mcur;
3032  if ((unread == 1) && (first_new == -1))
3033  first_new = mcur;
3034  }
3035  else if (!e->deleted && !e->read)
3036  {
3037  if (first_unread == -1)
3038  first_unread = mcur;
3039  if (!e->old && (first_new == -1))
3040  first_new = mcur;
3041  }
3042 
3043  if (((op == OP_MAIN_NEXT_UNREAD) || (op == OP_MAIN_PREV_UNREAD)) &&
3044  (first_unread != -1))
3045  break;
3046  if (((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_PREV_NEW) ||
3047  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD) || (op == OP_MAIN_PREV_NEW_THEN_UNREAD)) &&
3048  (first_new != -1))
3049  {
3050  break;
3051  }
3052  }
3053  if (((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_PREV_NEW) ||
3054  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD) || (op == OP_MAIN_PREV_NEW_THEN_UNREAD)) &&
3055  (first_new != -1))
3056  {
3057  menu_set_index(priv->menu, first_new);
3058  }
3059  else if (((op == OP_MAIN_NEXT_UNREAD) || (op == OP_MAIN_PREV_UNREAD) ||
3060  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD) || (op == OP_MAIN_PREV_NEW_THEN_UNREAD)) &&
3061  (first_unread != -1))
3062  {
3063  menu_set_index(priv->menu, first_unread);
3064  }
3065 
3066  if (menu_get_index(priv->menu) == -1)
3067  {
3068  menu_set_index(priv->menu, saved_current);
3069  if ((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_PREV_NEW))
3070  {
3071  if (ctx_has_limit(shared->ctx))
3072  mutt_error(_("No new messages in this limited view"));
3073  else
3074  mutt_error(_("No new messages"));
3075  }
3076  else
3077  {
3078  if (ctx_has_limit(shared->ctx))
3079  mutt_error(_("No unread messages in this limited view"));
3080  else
3081  mutt_error(_("No unread messages"));
3082  }
3083  break;
3084  }
3085 
3086  const int index = menu_get_index(priv->menu);
3087  if ((op == OP_MAIN_NEXT_NEW) || (op == OP_MAIN_NEXT_UNREAD) ||
3088  (op == OP_MAIN_NEXT_NEW_THEN_UNREAD))
3089  {
3090  if (saved_current > index)
3091  {
3092  mutt_message(_("Search wrapped to top"));
3093  }
3094  }
3095  else if (saved_current < index)
3096  {
3097  mutt_message(_("Search wrapped to bottom"));
3098  }
3099 
3100  if (priv->in_pager)
3101  {
3102  op = OP_DISPLAY_MESSAGE;
3103  continue;
3104  }
3105  else
3107  break;
3108  }
3109  case OP_FLAG_MESSAGE:
3110  {
3111  if (!prereq(shared->ctx, priv->menu,
3113  break;
3114  /* L10N: CHECK_ACL */
3115  if (!check_acl(shared->mailbox, MUTT_ACL_WRITE, _("Can't flag message")))
3116  break;
3117 
3118  struct Mailbox *m = shared->mailbox;
3119  if (priv->tag)
3120  {
3121  for (size_t i = 0; i < m->msg_count; i++)
3122  {
3123  struct Email *e = m->emails[i];
3124  if (!e)
3125  break;
3126  if (message_is_tagged(e))
3127  mutt_set_flag(m, e, MUTT_FLAG, !e->flagged);
3128  }
3129 
3131  }
3132  else
3133  {
3134  if (!shared->email)
3135  break;
3136  mutt_set_flag(m, shared->email, MUTT_FLAG, !shared->email->flagged);
3137  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
3138  if (c_resolve)
3139  {
3140  int index = menu_get_index(priv->menu);
3141  index = ci_next_undeleted(shared->mailbox, index);
3142  if (index == -1)
3143  {
3145  }
3146  else
3147  {
3148  menu_set_index(priv->menu, index);
3149  }
3150  }
3151  else
3153  }
3155  break;
3156  }
3157 
3158  case OP_TOGGLE_NEW:
3159  {
3160  if (!prereq(shared->ctx, priv->menu,
3162  break;
3163  /* L10N: CHECK_ACL */
3164  if (!check_acl(shared->mailbox, MUTT_ACL_SEEN, _("Can't toggle new")))
3165  break;
3166 
3167  struct Mailbox *m = shared->mailbox;
3168  if (priv->tag)
3169  {
3170  for (size_t i = 0; i < m->msg_count; i++)
3171  {
3172  struct Email *e = m->emails[i];
3173  if (!e)
3174  break;
3175  if (!message_is_tagged(e))
3176  continue;
3177 
3178  if (e->read || e->old)
3179  mutt_set_flag(m, e, MUTT_NEW, true);
3180  else
3181  mutt_set_flag(m, e, MUTT_READ, true);
3182  }
3184  }
3185  else
3186  {
3187  if (!shared->email)
3188  break;
3189  if (shared->email->read || shared->email->old)
3190  mutt_set_flag(m, shared->email, MUTT_NEW, true);
3191  else
3192  mutt_set_flag(m, shared->email, MUTT_READ, true);
3193 
3194  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
3195  if (c_resolve)
3196  {
3197  int index = menu_get_index(priv->menu);
3198  index = ci_next_undeleted(shared->mailbox, index);
3199  if (index == -1)
3200  {
3202  }
3203  else
3204  {
3205  menu_set_index(priv->menu, index);
3206  }
3207  }
3208  else
3211  }
3212  break;
3213  }
3214 
3215  case OP_TOGGLE_WRITE:
3216  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
3217  break;
3218  if (mx_toggle_write(shared->mailbox) == 0)
3219  {
3220  if (priv->in_pager)
3221  {
3222  op = OP_DISPLAY_MESSAGE;
3223  continue;
3224  }
3225  else
3227  }
3228  break;
3229 
3230  case OP_MAIN_NEXT_THREAD:
3231  case OP_MAIN_NEXT_SUBTHREAD:
3232  case OP_MAIN_PREV_THREAD:
3233  case OP_MAIN_PREV_SUBTHREAD:
3234  {
3235  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3236  break;
3237 
3238  int index = -1;
3239  switch (op)
3240  {
3241  case OP_MAIN_NEXT_THREAD:
3242  index = mutt_next_thread(shared->email);
3243  break;
3244 
3245  case OP_MAIN_NEXT_SUBTHREAD:
3246  index = mutt_next_subthread(shared->email);
3247  break;
3248 
3249  case OP_MAIN_PREV_THREAD:
3250  index = mutt_previous_thread(shared->email);
3251  break;
3252 
3253  case OP_MAIN_PREV_SUBTHREAD:
3254  index = mutt_previous_subthread(shared->email);
3255  break;
3256  }
3257 
3258  if (index != -1)
3259  menu_set_index(priv->menu, index);
3260 
3261  if (index < 0)
3262  {
3263  if ((op == OP_MAIN_NEXT_THREAD) || (op == OP_MAIN_NEXT_SUBTHREAD))
3264  mutt_error(_("No more threads"));
3265  else
3266  mutt_error(_("You are on the first thread"));
3267  }
3268  else if (priv->in_pager)
3269  {
3270  op = OP_DISPLAY_MESSAGE;
3271  continue;
3272  }
3273  else
3275  break;
3276  }
3277 
3278  case OP_MAIN_ROOT_MESSAGE:
3279  case OP_MAIN_PARENT_MESSAGE:
3280  {
3281  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3282  break;
3283 
3284  int index = mutt_parent_message(shared->email, op == OP_MAIN_ROOT_MESSAGE);
3285  if (index != -1)
3286  menu_set_index(priv->menu, index);
3287 
3288  if (priv->in_pager)
3289  {
3290  op = OP_DISPLAY_MESSAGE;
3291  continue;
3292  }
3293  else
3295  break;
3296  }
3297 
3298  case OP_MAIN_SET_FLAG:
3299  case OP_MAIN_CLEAR_FLAG:
3300  {
3301  if (!prereq(shared->ctx, priv->menu,
3303  break;
3304  /* check_acl(MUTT_ACL_WRITE); */
3305 
3306  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3307  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3308 
3309  if (mutt_change_flag(shared->mailbox, &el, (op == OP_MAIN_SET_FLAG)) == 0)
3310  {
3312  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
3313  if (priv->tag)
3315  else if (c_resolve)
3316  {
3317  int index = menu_get_index(priv->menu);
3318  index = ci_next_undeleted(shared->mailbox, index);
3319  if (index == -1)
3320  {
3322  }
3323  else
3324  {
3325  menu_set_index(priv->menu, index);
3326  }
3327  }
3328  else
3330  }
3331  emaillist_clear(&el);
3332  break;
3333  }
3334 
3335  case OP_MAIN_COLLAPSE_THREAD:
3336  {
3337  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3338  break;
3339 
3340  const short c_sort = cs_subset_sort(shared->sub, "sort");
3341  if ((c_sort & SORT_MASK) != SORT_THREADS)
3342  {
3343  mutt_error(_("Threading is not enabled"));
3344  break;
3345  }
3346 
3347  if (!shared->email)
3348  break;
3349 
3350  if (shared->email->collapsed)
3351  {
3353  mutt_set_vnum(shared->mailbox);
3354  const bool c_uncollapse_jump =
3355  cs_subset_bool(shared->sub, "uncollapse_jump");
3356  if (c_uncollapse_jump)
3358  }
3359  else if (mutt_thread_can_collapse(shared->email))
3360  {
3361  menu_set_index(priv->menu, mutt_collapse_thread(shared->email));
3362  mutt_set_vnum(shared->mailbox);
3363  }
3364  else
3365  {
3366  mutt_error(_("Thread contains unread or flagged messages"));
3367  break;
3368  }
3369 
3371 
3372  break;
3373  }
3374 
3375  case OP_MAIN_COLLAPSE_ALL:
3376  {
3377  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX))
3378  break;
3379 
3380  const short c_sort = cs_subset_sort(shared->sub, "sort");
3381  if ((c_sort & SORT_MASK) != SORT_THREADS)
3382  {
3383  mutt_error(_("Threading is not enabled"));
3384  break;
3385  }
3386  collapse_all(shared->ctx, priv->menu, 1);
3387  break;
3388  }
3389 
3390  /* --------------------------------------------------------------------
3391  * These functions are invoked directly from the internal-pager
3392  */
3393 
3394  case OP_BOUNCE_MESSAGE:
3395  {
3396  if (!prereq(shared->ctx, priv->menu,
3398  break;
3399  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3400  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3401  ci_bounce_message(shared->mailbox, &el);
3402  emaillist_clear(&el);
3403  break;
3404  }
3405 
3406  case OP_CREATE_ALIAS:
3407  {
3408  struct AddressList *al = NULL;
3409  if (shared->email && shared->email->env)
3410  al = mutt_get_address(shared->email->env, NULL);
3411  alias_create(al, shared->sub);
3413  break;
3414  }
3415 
3416  case OP_QUERY:
3417  if (!prereq(shared->ctx, priv->menu, CHECK_ATTACH))
3418  break;
3419  query_index(shared->sub);
3420  break;
3421 
3422  case OP_PURGE_MESSAGE:
3423  case OP_DELETE:
3424  {
3425  if (!prereq(shared->ctx, priv->menu,
3427  break;
3428  /* L10N: CHECK_ACL */
3429  if (!check_acl(shared->mailbox, MUTT_ACL_DELETE, _("Can't delete message")))
3430  break;
3431 
3432  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3433  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3434 
3435  mutt_emails_set_flag(shared->mailbox, &el, MUTT_DELETE, true);
3436  mutt_emails_set_flag(shared->mailbox, &el, MUTT_PURGE, (op == OP_PURGE_MESSAGE));
3437  const bool c_delete_untag = cs_subset_bool(shared->sub, "delete_untag");
3438  if (c_delete_untag)
3439  mutt_emails_set_flag(shared->mailbox, &el, MUTT_TAG, false);
3440  emaillist_clear(&el);
3441 
3442  if (priv->tag)
3443  {
3445  }
3446  else
3447  {
3448  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
3449  if (c_resolve)
3450  {
3451  int index = menu_get_index(priv->menu);
3452  index = ci_next_undeleted(shared->mailbox, index);
3453  if (index != -1)
3454  menu_set_index(priv->menu, index);
3455 
3456  if (index == -1)
3457  {
3459  }
3460  else if (priv->in_pager)
3461  {
3462  op = OP_DISPLAY_MESSAGE;
3463  continue;
3464  }
3465  }
3466  else
3468  }
3470  break;
3471  }
3472 
3473  case OP_DELETE_THREAD:
3474  case OP_DELETE_SUBTHREAD:
3475  case OP_PURGE_THREAD:
3476  {
3477  if (!prereq(shared->ctx, priv->menu,
3479  break;
3480  /* L10N: CHECK_ACL */
3481  /* L10N: Due to the implementation details we do not know whether we
3482  delete zero, 1, 12, ... messages. So in English we use
3483  "messages". Your language might have other means to express this. */
3484  if (!check_acl(shared->mailbox, MUTT_ACL_DELETE, _("Can't delete messages")))
3485  break;
3486  if (!shared->email)
3487  break;
3488 
3489  int subthread = (op == OP_DELETE_SUBTHREAD);
3490  int rc = mutt_thread_set_flag(shared->mailbox, shared->email,
3491  MUTT_DELETE, true, subthread);
3492  if (rc == -1)
3493  break;
3494  if (op == OP_PURGE_THREAD)
3495  {
3496  rc = mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_PURGE,
3497  true, subthread);
3498  if (rc == -1)
3499  break;
3500  }
3501 
3502  const bool c_delete_untag = cs_subset_bool(shared->sub, "delete_untag");
3503  if (c_delete_untag)
3504  mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_TAG, false, subthread);
3505  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
3506  if (c_resolve)
3507  {
3508  int index = menu_get_index(priv->menu);
3509  index = ci_next_undeleted(shared->mailbox, index);
3510  if (index != -1)
3511  menu_set_index(priv->menu, index);
3512  }
3514  break;
3515  }
3516 
3517 #ifdef USE_NNTP
3518  case OP_CATCHUP:
3519  if (!prereq(shared->ctx, priv->menu,
3521  break;
3522  if (shared->mailbox && (shared->mailbox->type == MUTT_NNTP))
3523  {
3524  struct NntpMboxData *mdata = shared->mailbox->mdata;
3525  if (mutt_newsgroup_catchup(shared->mailbox, mdata->adata, mdata->group))
3527  }
3528  break;
3529 #endif
3530 
3531  case OP_DISPLAY_ADDRESS:
3532  {
3533  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3534  break;
3535  if (!shared->email)
3536  break;
3537  mutt_display_address(shared->email->env);
3538  break;
3539  }
3540 
3541  case OP_ENTER_COMMAND:
3543  window_set_focus(priv->win_index);
3544  mutt_check_rescore(shared->mailbox);
3546  break;
3547 
3548  case OP_EDIT_OR_VIEW_RAW_MESSAGE:
3549  case OP_EDIT_RAW_MESSAGE:
3550  case OP_VIEW_RAW_MESSAGE:
3551  {
3552  /* TODO split this into 3 cases? */
3553  if (!prereq(shared->ctx, priv->menu,
3555  break;
3556  bool edit;
3557  if (op == OP_EDIT_RAW_MESSAGE)
3558  {
3559  if (!prereq(shared->ctx, priv->menu, CHECK_READONLY))
3560  break;
3561  /* L10N: CHECK_ACL */
3562  if (!check_acl(shared->mailbox, MUTT_ACL_INSERT, _("Can't edit message")))
3563  break;
3564  edit = true;
3565  }
3566  else if (op == OP_EDIT_OR_VIEW_RAW_MESSAGE)
3567  edit = !shared->mailbox->readonly && (shared->mailbox->rights & MUTT_ACL_INSERT);
3568  else
3569  edit = false;
3570 
3571  if (!shared->email)
3572  break;
3573  const bool c_pgp_auto_decode =
3574  cs_subset_bool(shared->sub, "pgp_auto_decode");
3575  if (c_pgp_auto_decode &&
3576  (priv->tag || !(shared->email->security & PGP_TRADITIONAL_CHECKED)))
3577  {
3578  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3579  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3580  if (mutt_check_traditional_pgp(shared->mailbox, &el))
3582  emaillist_clear(&el);
3583  }
3584  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3585  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3586  mutt_ev_message(shared->mailbox, &el, edit ? EVM_EDIT : EVM_VIEW);
3587  emaillist_clear(&el);
3589 
3590  break;
3591  }
3592 
3593  case OP_FORWARD_MESSAGE:
3594  {
3595  if (!prereq(shared->ctx, priv->menu,
3597  break;
3598  if (!shared->email)
3599  break;
3600  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3601  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3602  const bool c_pgp_auto_decode =
3603  cs_subset_bool(shared->sub, "pgp_auto_decode");
3604  if (c_pgp_auto_decode &&
3605  (priv->tag || !(shared->email->security & PGP_TRADITIONAL_CHECKED)))
3606  {
3607  if (mutt_check_traditional_pgp(shared->mailbox, &el))
3609  }
3610  mutt_send_message(SEND_FORWARD, NULL, NULL, shared->mailbox, &el, shared->sub);
3611  emaillist_clear(&el);
3613  break;
3614  }
3615 
3616  case OP_FORGET_PASSPHRASE:
3618  break;
3619 
3620  case OP_GROUP_REPLY:
3621  case OP_GROUP_CHAT_REPLY:
3622  {
3623  SendFlags replyflags = SEND_REPLY;
3624  if (op == OP_GROUP_REPLY)
3625  replyflags |= SEND_GROUP_REPLY;
3626  else
3627  replyflags |= SEND_GROUP_CHAT_REPLY;
3628  if (!prereq(shared->ctx, priv->menu,
3630  break;
3631  if (!shared->email)
3632  break;
3633  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3634  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3635  const bool c_pgp_auto_decode =
3636  cs_subset_bool(shared->sub, "pgp_auto_decode");
3637  if (c_pgp_auto_decode &&
3638  (priv->tag || !(shared->email->security & PGP_TRADITIONAL_CHECKED)))
3639  {
3640  if (mutt_check_traditional_pgp(shared->mailbox, &el))
3642  }
3643  mutt_send_message(replyflags, NULL, NULL, shared->mailbox, &el, shared->sub);
3644  emaillist_clear(&el);
3646  break;
3647  }
3648 
3649  case OP_EDIT_LABEL:
3650  {
3651  if (!prereq(shared->ctx, priv->menu,
3653  break;
3654 
3655  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3656  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3657  int num_changed = mutt_label_message(shared->mailbox, &el);
3658  emaillist_clear(&el);
3659 
3660  if (num_changed > 0)
3661  {
3662  shared->mailbox->changed = true;
3664  /* L10N: This is displayed when the x-label on one or more
3665  messages is edited. */
3666  mutt_message(ngettext("%d label changed", "%d labels changed", num_changed),
3667  num_changed);
3668  }
3669  else
3670  {
3671  /* L10N: This is displayed when editing an x-label, but no messages
3672  were updated. Possibly due to canceling at the prompt or if the new
3673  label is the same as the old label. */
3674  mutt_message(_("No labels changed"));
3675  }
3676  break;
3677  }
3678 
3679  case OP_LIST_REPLY:
3680  {
3681  if (!prereq(shared->ctx, priv->menu,
3683  break;
3684  if (!shared->email)
3685  break;
3686  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3687  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3688  const bool c_pgp_auto_decode =
3689  cs_subset_bool(shared->sub, "pgp_auto_decode");
3690  if (c_pgp_auto_decode &&
3691  (priv->tag || !(shared->email->security & PGP_TRADITIONAL_CHECKED)))
3692  {
3693  if (mutt_check_traditional_pgp(shared->mailbox, &el))
3695  }
3697  shared->mailbox, &el, shared->sub);
3698  emaillist_clear(&el);
3700  break;
3701  }
3702 
3703  case OP_MAIL:
3704  if (!prereq(shared->ctx, priv->menu, CHECK_ATTACH))
3705  break;
3706  mutt_send_message(SEND_NO_FLAGS, NULL, NULL, shared->mailbox, NULL,
3707  shared->sub);
3709  break;
3710 
3711  case OP_MAIL_KEY:
3712  if (!(WithCrypto & APPLICATION_PGP))
3713  break;
3714  if (!prereq(shared->ctx, priv->menu, CHECK_ATTACH))
3715  break;
3716  mutt_send_message(SEND_KEY, NULL, NULL, NULL, NULL, shared->sub);
3718  break;
3719 
3720  case OP_EXTRACT_KEYS:
3721  {
3722  if (!WithCrypto)
3723  break;
3724  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3725  break;
3726  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3727  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3729  emaillist_clear(&el);
3731  break;
3732  }
3733 
3734  case OP_CHECK_TRADITIONAL:
3735  {
3736  if (!(WithCrypto & APPLICATION_PGP))
3737  break;
3738  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3739  break;
3740  if (!shared->email)
3741  break;
3742  if (priv->tag || !(shared->email->security & PGP_TRADITIONAL_CHECKED))
3743  {
3744  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3745  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3746  if (mutt_check_traditional_pgp(shared->mailbox, &el))
3748  emaillist_clear(&el);
3749  }
3750 
3751  if (priv->in_pager)
3752  {
3753  op = OP_DISPLAY_MESSAGE;
3754  continue;
3755  }
3756  break;
3757  }
3758 
3759  case OP_PIPE:
3760  {
3761  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3762  break;
3763  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3764  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3765  mutt_pipe_message(shared->mailbox, &el);
3766  emaillist_clear(&el);
3767 
3768 #ifdef USE_IMAP
3769  /* in an IMAP folder index with imap_peek=no, piping could change
3770  * new or old messages status to read. Redraw what's needed. */
3771  const bool c_imap_peek = cs_subset_bool(shared->sub, "imap_peek");
3772  if ((shared->mailbox->type == MUTT_IMAP) && !c_imap_peek)
3773  {
3775  }
3776 #endif
3777  break;
3778  }
3779 
3780  case OP_PRINT:
3781  {
3782  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3783  break;
3784  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3785  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3786  mutt_print_message(shared->mailbox, &el);
3787  emaillist_clear(&el);
3788 
3789 #ifdef USE_IMAP
3790  /* in an IMAP folder index with imap_peek=no, printing could change
3791  * new or old messages status to read. Redraw what's needed. */
3792  const bool c_imap_peek = cs_subset_bool(shared->sub, "imap_peek");
3793  if ((shared->mailbox->type == MUTT_IMAP) && !c_imap_peek)
3794  {
3796  }
3797 #endif
3798  break;
3799  }
3800 
3801  case OP_MAIN_READ_THREAD:
3802  case OP_MAIN_READ_SUBTHREAD:
3803  {
3804  if (!prereq(shared->ctx, priv->menu,
3806  break;
3807  /* L10N: CHECK_ACL */
3808  /* L10N: Due to the implementation details we do not know whether we
3809  mark zero, 1, 12, ... messages as read. So in English we use
3810  "messages". Your language might have other means to express this. */
3811  if (!check_acl(shared->mailbox, MUTT_ACL_SEEN, _("Can't mark messages as read")))
3812  break;
3813 
3814  int rc = mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_READ,
3815  true, (op != OP_MAIN_READ_THREAD));
3816  if (rc != -1)
3817  {
3818  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
3819  if (c_resolve)
3820  {
3821  int index =
3822  ((op == OP_MAIN_READ_THREAD) ? mutt_next_thread(shared->email) :
3823  mutt_next_subthread(shared->email));
3824  if (index != -1)
3825  menu_set_index(priv->menu, index);
3826 
3827  if (priv->in_pager)
3828  {
3829  op = OP_DISPLAY_MESSAGE;
3830  continue;
3831  }
3832  }
3834  }
3835  break;
3836  }
3837 
3838  case OP_MARK_MSG:
3839  {
3840  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3841  break;
3842  if (!shared->email)
3843  break;
3844  if (shared->email->env->message_id)
3845  {
3846  char buf2[128] = { 0 };
3847 
3848  /* L10N: This is the prompt for <mark-message>. Whatever they
3849  enter will be prefixed by $mark_macro_prefix and will become
3850  a macro hotkey to jump to the currently selected message. */
3851  if (!mutt_get_field(_("Enter macro stroke: "), buf2, sizeof(buf2),
3852  MUTT_COMP_NO_FLAGS, false, NULL, NULL) &&
3853  buf2[0])
3854  {
3855  const char *const c_mark_macro_prefix =
3856  cs_subset_string(shared->sub, "mark_macro_prefix");
3857  char str[256];
3858  snprintf(str, sizeof(str), "%s%s", c_mark_macro_prefix, buf2);
3859 
3860  struct Buffer *msg_id = mutt_buffer_pool_get();
3861  mutt_file_sanitize_regex(msg_id, shared->email->env->message_id);
3862  char macro[256];
3863  snprintf(macro, sizeof(macro), "<search>~i '%s'\n", mutt_buffer_string(msg_id));
3864  mutt_buffer_pool_release(&msg_id);
3865 
3866  /* L10N: "message hotkey" is the key bindings priv->menu description of a
3867  macro created by <mark-message>. */
3868  km_bind(str, MENU_MAIN, OP_MACRO, macro, _("message hotkey"));
3869 
3870  /* L10N: This is echoed after <mark-message> creates a new hotkey
3871  macro. %s is the hotkey string ($mark_macro_prefix followed
3872  by whatever they typed at the prompt.) */
3873  snprintf(buf2, sizeof(buf2), _("Message bound to %s"), str);
3874  mutt_message(buf2);
3875  mutt_debug(LL_DEBUG1, "Mark: %s => %s\n", str, macro);
3876  }
3877  }
3878  else
3879  {
3880  /* L10N: This error is printed if <mark-message> can't find a
3881  Message-ID for the currently selected message in the index. */
3882  mutt_error(_("No message ID to macro"));
3883  }
3884  break;
3885  }
3886 
3887  case OP_RECALL_MESSAGE:
3888  if (!prereq(shared->ctx, priv->menu, CHECK_ATTACH))
3889  break;
3890  mutt_send_message(SEND_POSTPONED, NULL, NULL, shared->mailbox, NULL,
3891  shared->sub);
3893  break;
3894 
3895  case OP_RESEND:
3896  if (!prereq(shared->ctx, priv->menu,
3898  break;
3899 
3900  if (priv->tag)
3901  {
3902  struct Mailbox *m = shared->mailbox;
3903  for (size_t i = 0; i < m->msg_count; i++)
3904  {
3905  struct Email *e = m->emails[i];
3906  if (!e)
3907  break;
3908  if (message_is_tagged(e))
3909  mutt_resend_message(NULL, shared->mailbox, e, shared->sub);
3910  }
3911  }
3912  else
3913  {
3914  mutt_resend_message(NULL, shared->mailbox, shared->email, shared->sub);
3915  }
3916 
3918  break;
3919 
3920 #ifdef USE_NNTP
3921  case OP_FOLLOWUP:
3922  case OP_FORWARD_TO_GROUP:
3923  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
3924  break;
3925  /* fallthrough */
3926 
3927  case OP_POST:
3928  {
3929  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_ATTACH))
3930  break;
3931  if (!shared->email)
3932  break;
3933  const enum QuadOption c_followup_to_poster =
3934  cs_subset_quad(shared->sub, "followup_to_poster");
3935  if ((op != OP_FOLLOWUP) || !shared->email->env->followup_to ||
3936  !mutt_istr_equal(shared->email->env->followup_to, "poster") ||
3937  (query_quadoption(c_followup_to_poster,
3938  _("Reply by mail as poster prefers?")) != MUTT_YES))
3939  {
3940  const enum QuadOption c_post_moderated =
3941  cs_subset_quad(shared->sub, "post_moderated");
3942  if (shared->mailbox && (shared->mailbox->type == MUTT_NNTP) &&
3943  !((struct NntpMboxData *) shared->mailbox->mdata)->allowed && (query_quadoption(c_post_moderated, _("Posting to this group not allowed, may be moderated. Continue?")) != MUTT_YES))
3944  {
3945  break;
3946  }
3947  if (op == OP_POST)
3948  mutt_send_message(SEND_NEWS, NULL, NULL, shared->mailbox, NULL,
3949  shared->sub);
3950  else
3951  {
3952  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT))
3953  break;
3954  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3955  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3956  mutt_send_message(((op == OP_FOLLOWUP) ? SEND_REPLY : SEND_FORWARD) | SEND_NEWS,
3957  NULL, NULL, shared->mailbox, &el, shared->sub);
3958  emaillist_clear(&el);
3959  }
3961  break;
3962  }
3963  }
3964 #endif
3965  /* fallthrough */
3966  case OP_REPLY:
3967  {
3968  if (!prereq(shared->ctx, priv->menu,
3970  break;
3971  if (!shared->email)
3972  break;
3973  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
3974  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
3975  const bool c_pgp_auto_decode =
3976  cs_subset_bool(shared->sub, "pgp_auto_decode");
3977  if (c_pgp_auto_decode &&
3978  (priv->tag || !(shared->email->security & PGP_TRADITIONAL_CHECKED)))
3979  {
3980  if (mutt_check_traditional_pgp(shared->mailbox, &el))
3982  }
3983  mutt_send_message(SEND_REPLY, NULL, NULL, shared->mailbox, &el, shared->sub);
3984  emaillist_clear(&el);
3986  break;
3987  }
3988 
3989  case OP_SHELL_ESCAPE:
3990  if (mutt_shell_escape())
3991  {
3993  }
3994  break;
3995 
3996  case OP_TAG_THREAD:
3997  case OP_TAG_SUBTHREAD:
3998  {
3999  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
4000  break;
4001  if (!shared->email)
4002  break;
4003 
4004  int rc = mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_TAG,
4005  !shared->email->tagged, (op != OP_TAG_THREAD));
4006  if (rc != -1)
4007  {
4008  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
4009  if (c_resolve)
4010  {
4011  int index;
4012  if (op == OP_TAG_THREAD)
4013  index = mutt_next_thread(shared->email);
4014  else
4015  index = mutt_next_subthread(shared->email);
4016 
4017  if (index != -1)
4018  menu_set_index(priv->menu, index);
4019  }
4021  }
4022  break;
4023  }
4024 
4025  case OP_UNDELETE:
4026  {
4027  if (!prereq(shared->ctx, priv->menu,
4029  break;
4030  /* L10N: CHECK_ACL */
4031  if (!check_acl(shared->mailbox, MUTT_ACL_DELETE, _("Can't undelete message")))
4032  break;
4033 
4034  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
4035  el_add_tagged(&el, shared->ctx, shared->email, priv->tag);
4036 
4037  mutt_emails_set_flag(shared->mailbox, &el, MUTT_DELETE, false);
4038  mutt_emails_set_flag(shared->mailbox, &el, MUTT_PURGE, false);
4039  emaillist_clear(&el);
4040 
4041  if (priv->tag)
4042  {
4044  }
4045  else
4046  {
4047  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
4048  const int index = menu_get_index(priv->menu) + 1;
4049  if (c_resolve && (index < shared->mailbox->vcount))
4050  {
4051  menu_set_index(priv->menu, index);
4052  }
4053  else
4055  }
4056 
4058  break;
4059  }
4060 
4061  case OP_UNDELETE_THREAD:
4062  case OP_UNDELETE_SUBTHREAD:
4063  {
4064  if (!prereq(shared->ctx, priv->menu,
4066  break;
4067  /* L10N: CHECK_ACL */
4068  /* L10N: Due to the implementation details we do not know whether we
4069  undelete zero, 1, 12, ... messages. So in English we use
4070  "messages". Your language might have other means to express this. */
4071  if (!check_acl(shared->mailbox, MUTT_ACL_DELETE, _("Can't undelete messages")))
4072  break;
4073 
4074  int rc = mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_DELETE,
4075  false, (op != OP_UNDELETE_THREAD));
4076  if (rc != -1)
4077  {
4078  rc = mutt_thread_set_flag(shared->mailbox, shared->email, MUTT_PURGE,
4079  false, (op != OP_UNDELETE_THREAD));
4080  }
4081  if (rc != -1)
4082  {
4083  const bool c_resolve = cs_subset_bool(shared->sub, "resolve");
4084  if (c_resolve)
4085  {
4086  int index;
4087  if (op == OP_UNDELETE_THREAD)
4088  index = mutt_next_thread(shared->email);
4089  else
4090  index = mutt_next_subthread(shared->email);
4091 
4092  if (index != -1)
4093  menu_set_index(priv->menu, index);
4094  }
4096  }
4097  break;
4098  }
4099 
4100  case OP_VERSION:
4102  break;
4103 
4104  case OP_MAILBOX_LIST:
4106  break;
4107 
4108  case OP_VIEW_ATTACHMENTS:
4109  {
4110  if (!prereq(shared->ctx, priv->menu, CHECK_IN_MAILBOX | CHECK_MSGCOUNT | CHECK_VISIBLE))
4111  break;
4112  if (!shared->email)
4113  break;
4114  struct Message *msg = mx_msg_open(shared->mailbox, shared->email->msgno);
4115  if (msg)
4116  {
4117  dlg_select_attachment(NeoMutt->sub, shared->mailbox, shared->email, msg->fp);
4118  if (shared->email->attach_del)
4119  {
4120  shared->mailbox->changed = true;
4121  }
4122  mx_msg_close(shared->mailbox, &msg);
4123  }
4125  break;
4126  }
4127 
4128  case OP_END_COND:
4129  break;
4130 
4131  case OP_WHAT_KEY:
4132  mutt_what_key();
4133  break;
4134 
4135 #ifdef USE_SIDEBAR
4136  case OP_SIDEBAR_FIRST:
4137  case OP_SIDEBAR_LAST:
4138  case OP_SIDEBAR_NEXT:
4139  case OP_SIDEBAR_NEXT_NEW:
4140  case OP_SIDEBAR_PAGE_DOWN:
4141  case OP_SIDEBAR_PAGE_UP:
4142  case OP_SIDEBAR_PREV:
4143  case OP_SIDEBAR_PREV_NEW:
4144  {
4145  struct MuttWindow *win_sidebar = mutt_window_find(dlg, WT_SIDEBAR);
4146  if (!win_sidebar)
4147  break;
4148  sb_change_mailbox(win_sidebar, op);
4149  break;
4150  }
4151 
4152  case OP_SIDEBAR_TOGGLE_VISIBLE:
4153  bool_str_toggle(shared->sub, "sidebar_visible", NULL);
4154  mutt_window_reflow(NULL);
4155  break;
4156 #endif
4157 
4158 #ifdef USE_AUTOCRYPT
4159  case OP_AUTOCRYPT_ACCT_MENU:
4161  break;
4162 #endif
4163 
4164  default:
4165  if (!priv->in_pager)
4167  }
4168 
4169 #ifdef USE_NOTMUCH
4170  nm_db_debug_check(shared->mailbox);
4171 #endif
4172 
4173  if (priv->in_pager)
4174  {
4176  priv->in_pager = false;
4178  }
4179 
4180  if (priv->done)
4181  break;
4182  }
4183 
4184  struct Context *ctx = shared->ctx;
4185  struct Mailbox *m = ctx_mailbox(ctx);
4186  index_shared_data_set_context(shared, ctx_old);
4187  ctx_free(&ctx);
4188 
4189  return m;
4190 }
4191 
4197 void mutt_set_header_color(struct Mailbox *m, struct Email *e)
4198 {
4199  if (!e)
4200  return;
4201 
4202  struct ColorLine *color = NULL;
4203  struct PatternCache cache = { 0 };
4204 
4205  STAILQ_FOREACH(color, mutt_color_index(), entries)
4206  {
4208  MUTT_MATCH_FULL_ADDRESS, m, e, &cache))
4209  {
4210  e->pair = color->pair;
4211  return;
4212  }
4213  }
4215 }
4216 
4223 static struct MuttWindow *create_panel_index(struct MuttWindow *parent, bool status_on_top)
4224 {
4225  struct MuttWindow *panel_index =
4228  parent->focus = panel_index;
4229 
4230  struct MuttWindow *win_index = menu_new_window(MENU_MAIN);
4231  panel_index->focus = win_index;
4232 
4233  struct MuttWindow *win_ibar =
4236 
4237  if (status_on_top)
4238  {
4239  mutt_window_add_child(panel_index, win_ibar);
4240  mutt_window_add_child(panel_index, win_index);
4241  }
4242  else
4243  {
4244  mutt_window_add_child(panel_index, win_index);
4245  mutt_window_add_child(panel_index, win_ibar);
4246  }
4247 
4248  struct IndexPrivateData *priv = index_private_data_new();
4249 
4250  panel_index->wdata = priv;
4251  panel_index->wdata_free = index_private_data_free;
4252 
4253  return panel_index;
4254 }
4255 
4261 {
4262  struct MuttWindow *dlg =
4265 
4266  const bool c_status_on_top = cs_subset_bool(NeoMutt->sub, "status_on_top");
4267 
4268  struct MuttWindow *panel_index = create_panel_index(dlg, c_status_on_top);
4269  notify_set_parent(panel_index->notify, dlg->notify);
4270  mutt_window_add_child(dlg, panel_index);
4271 
4272  struct MuttWindow *panel_pager = add_panel_pager(dlg, c_status_on_top);
4273  notify_set_parent(panel_pager->notify, dlg->notify);
4274 
4275  struct IndexSharedData *shared = index_shared_data_new();
4276  notify_set_parent(shared->notify, dlg->notify);
4277 
4278  dlg->wdata = shared;
4280 
4281  index_add_observers(dlg);
4282  return dlg;
4283 }
4284 
4290 {
4292 }
int km_dokey(enum MenuType menu)
Determine what a keypress should do.
Definition: keymap.c:661
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:354
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
Convenience wrapper for the gui headers.
The "current" mailbox.
Definition: context.h:37
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:206
void nm_query_window_forward(void)
Function to move the current search window forward in time.
Definition: notmuch.c:1709
static int ci_first_message(struct Mailbox *m)
Get index of first new message.
Definition: index.c:339
Manage keymappings.
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
Decrypt message.
Definition: commands.h:42
bool mutt_mailbox_list(void)
List the mailboxes with new mail.
Definition: mutt_mailbox.c:222
#define NONULL(x)
Definition: string2.h:37
int msg_count
Total number of messages.
Definition: mailbox.h:91
void mutt_thread_collapse(struct ThreadsContext *tctx, bool collapse)
toggle collapse
Definition: mutt_thread.c:1624
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:66
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:71
Representation of the email&#39;s header.
#define WithCrypto
Definition: lib.h:113
int mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: string.c:252
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:303
#define SEND_TO_SENDER
Compose new email to sender.
Definition: send.h:51
int msg_in_pager
Message currently shown in the pager.
Definition: context.h:43
IMAP network mailbox.
void mutt_pipe_message(struct Mailbox *m, struct EmailList *el)
Pipe a message.
Definition: commands.c:757
bool index_shared_data_is_cur_email(const struct IndexSharedData *shared, const struct Email *e)
Check whether an email is the currently selected Email.
Definition: shared_data.c:220
The envelope/body of an email.
Definition: email.h:37
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:408
void(* wdata_free)(struct MuttWindow *win, void **ptr)
Free the private data attached to the MuttWindow.
Definition: mutt_window.h:147
#define MIN(a, b)
Definition: memory.h:31
#define mutt_perror(...)
Definition: logging.h:85
#define MUTT_SHUTDOWN_HOOK
shutdown-hook: run when leaving NeoMutt
Definition: hook.h:59
GUI selectable list of items.
Definition: lib.h:56
Data passed to a notification function.
Definition: observer.h:39
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:36
struct Body * body
List of MIME parts.
Definition: email.h:91
void nm_query_window_backward(void)
Function to move the current search window backward in time.
Definition: notmuch.c:1730
struct Mailbox * ctx_mailbox(struct Context *ctx)
wrapper to get the mailbox in a Context, or NULL
Definition: context.c:440
void window_set_focus(struct MuttWindow *win)
Set the Window focus.
Definition: mutt_window.c:760
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:52
Structs that make up an email.
struct Email * email
Currently selected Email.
Definition: shared_data.h:42
struct Menu * menu
Menu controlling the index.
Definition: private_data.h:42
String processing routines to generate the mail index.
struct Mailbox * mailbox
The Mailbox this Event relates to.
Definition: mailbox.h:185
The "currently-open" mailbox.
#define mutt_uncollapse_thread(e)
Definition: mutt_thread.h:76
Autocrypt end-to-end encryption.
Convenience wrapper for the send headers.
#define mutt_collapse_thread(e)
Definition: mutt_thread.h:75
bool tag
tag-prefix has been pressed
Definition: private_data.h:36
void index_remove_observers(struct MuttWindow *dlg)
Remove Observers from the Index Dialog.
Definition: observer.c:286
Pager Bar containing status info about the Pager.
Definition: mutt_window.h:100
#define MUTT_FORMAT_FORCESUBJ
Print the subject even if unchanged.
Definition: format_flags.h:31
#define mutt_message(...)
Definition: logging.h:83
void mutt_emails_set_flag(struct Mailbox *m, struct EmailList *el, enum MessageType flag, bool bf)
Set flag on messages.
Definition: flags.c:351
uint8_t CheckFlags
Checks to perform before running a function.
Definition: index.c:143
int help_menu
Menu for key bindings, e.g. MENU_PAGER.
Definition: mutt_window.h:136
void mutt_check_rescore(struct Mailbox *m)
Do the emails need to have their scores recalculated?
Definition: score.c:64
Private state data for the Index.
Definition: private_data.h:33
void mutt_sort_headers(struct Mailbox *m, struct ThreadsContext *threads, bool init, off_t *vsize)
Sort emails by their headers.
Definition: sort.c:378
Window uses all available vertical space.
Definition: mutt_window.h:36
int mutt_get_field(const char *field, char *buf, size_t buflen, CompletionFlags complete, bool multiple, char ***files, int *numfiles)
Ask the user for a string.
Definition: curs_lib.c:306
struct MuttThread * thread
Thread of Emails.
Definition: email.h:95
void mutt_display_address(struct Envelope *env)
Display the address of a message.
Definition: commands.c:992
void mutt_curses_set_color(enum ColorId color)
Set the current colour for text.
Definition: mutt_curses.c:56
struct ConfigSubset * sub
Config set to use.
Definition: shared_data.h:38
struct NntpAccountData * adata
Definition: mdata.h:47
struct MuttWindow * add_panel_pager(struct MuttWindow *parent, bool status_on_top)
Add the Windows for the Pager panel.
Definition: pager.c:4162
bool mutt_shell_escape(void)
invoke a command in a subshell
Definition: commands.c:908
static void index_custom_redraw(struct Menu *menu)
Redraw the index - Implements Menu::custom_redraw()
Definition: index.c:1090
Progress tracks elements, according to $write_inc
Definition: progress.h:37
Messages in limited view.
Definition: mutt.h:101
struct IndexSharedData * index_shared_data_new(void)
Create new Index Data.
Definition: shared_data.c:258
struct MuttWindow * win_pbar
Window for the Pager Bar.
Definition: private_data.h:46
struct HashTable * mutt_make_id_hash(struct Mailbox *m)
Create a Hash Table for message-ids.
Definition: mutt_thread.c:1538
void mutt_flushinp(void)
Empty all the keyboard buffers.
Definition: curs_lib.c:810
int mutt_monitor_remove(struct Mailbox *m)
Remove a watch for a mailbox.
Definition: monitor.c:526
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:49
#define SEND_FORWARD
Forward email.
Definition: send.h:43
struct MuttWindow * mutt_window_find(struct MuttWindow *root, enum WindowType type)
Find a Window of a given type.
Definition: mutt_window.c:675
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:242
struct PatternList * color_pattern
Compiled pattern to speed up index color calculation.
Definition: color.h:104
void mutt_resize_screen(void)
Update NeoMutt&#39;s opinion about the window size (CURSES)
Definition: resize.c:101
char * realpath
Used for duplicate detection, context comparison, and the sidebar.
Definition: mailbox.h:84
NeoMutt Logging.
bool mutt_check_traditional_pgp(struct Mailbox *m, struct EmailList *el)
Check if a message has inline PGP content.
Definition: commands.c:1537
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
#define CHECK_ATTACH
Is the user in message-attach mode?
Definition: index.c:149
WHERE bool OptNeedResort
(pseudo) used to force a re-sort
Definition: options.h:43
#define MUTT_FORMAT_ARROWCURSOR
Reserve space for arrow_cursor.
Definition: format_flags.h:35
struct ThreadsContext * threads
Threads context.
Definition: context.h:42
#define mutt_next_subthread(e)
Definition: mutt_thread.h:84
void crypt_forget_passphrase(void)
Forget a passphrase and display a message.
Definition: crypt.c:92
bool mutt_link_threads(struct Email *parent, struct EmailList *children, struct Mailbox *m)
Forcibly link threads together.
Definition: mutt_thread.c:1583
No transformation.
Definition: commands.h:41
bool display_subject
Used for threading.
Definition: email.h:57
struct ColorLineList * mutt_color_index(void)
Return the ColorLineList for the index.
Definition: color.c:1411
#define MUTT_NM_QUERY
Notmuch query mode.
Definition: mutt.h:62
bool mutt_limit_current_thread(struct Context *ctx, struct Email *e)
Limit the email view to the current thread.
Definition: pattern.c:192
int pair
Colour pair index.
Definition: color.h:107
void mutt_enter_command(void)
enter a neomutt command
Definition: commands.c:943
String manipulation buffer.
Definition: buffer.h:33
Nondestructive flags change (IMAP)
Definition: mxapi.h:82
#define MUTT_ACL_INSERT
Add/copy into the mailbox (used when editing a message)
Definition: mailbox.h:69
int newcount
New count of Emails in the Mailbox.
Definition: private_data.h:38
int nntp_check_msgid(struct Mailbox *m, const char *msgid)
Fetch article by Message-ID.
Definition: nntp.c:2134
New mail received in Mailbox.
Definition: mxapi.h:79
int oldcount
Old count of Emails in the Mailbox.
Definition: private_data.h:37
bool ctx_has_limit(const struct Context *ctx)
Is a limit active?
Definition: context.c:429
Messages to be un-deleted.
Definition: mutt.h:95
paged view into some data
Definition: lib.h:148
Flagged messages.
Definition: mutt.h:98
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
void mutt_unget_event(int ch, int op)
Return a keystroke to the input buffer.
Definition: curs_lib.c:725
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:47
Error occurred examining Mailbox.
Definition: mailbox.h:46
enum MxStatus mx_mbox_sync(struct Mailbox *m)
Save changes to mailbox.
Definition: mx.c:915
static bool check_acl(struct Mailbox *m, AclFlags acl, const char *msg)
Check the ACLs for a function.
Definition: index.c:210
void index_add_observers(struct MuttWindow *dlg)
Add Observers to the Index Dialog.
Definition: observer.c:270
Data shared between Index, Pager and Sidebar.
Definition: shared_data.h:36
struct MuttThread * parent
Parent of this Thread.
Definition: thread.h:45
#define MUTT_SEL_NO_FLAGS
No flags are set.
Definition: browser.h:36
static const struct Mapping IndexNewsHelp[]
Help Bar for the News Index dialog.
Definition: index.c:124
Messages to be purged (bypass trash)
Definition: mutt.h:96
A division of the screen.
Definition: mutt_window.h:117
void nm_db_longrun_init(struct Mailbox *m, bool writable)
Start a long transaction.
Definition: db.c:314
int attach_msg
Are we in "attach message" mode?
Definition: private_data.h:40
void emaillist_clear(struct EmailList *el)
Drop a private list of Emails.
Definition: email.c:137
Index panel (list of emails)
Definition: keymap.h:80
WHERE struct Context * Context
Definition: mutt_globals.h:40
Copy message, making a duplicate in another mailbox.
Definition: commands.h:51
struct Mailbox * mutt_mailbox_next(struct Mailbox *m_cur, struct Buffer *s)
incoming folders completion routine
Definition: mutt_mailbox.c:314
int mutt_do_pager(struct PagerView *pview)
Display some page-able text to the user (help or attachment)
Definition: do_pager.c:82
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition: mx.c:609
An Event that happened to a Mailbox.
Definition: mailbox.h:183
uint16_t SendFlags
Flags for mutt_send_message(), e.g. SEND_REPLY.
Definition: send.h:36
void index_shared_data_free(struct MuttWindow *win, void **ptr)
Free Index Data.
Definition: shared_data.c:236
int mutt_window_mvaddstr(struct MuttWindow *win, int col, int row, const char *str)
Move the cursor and write a fixed string to a Window.
Definition: mutt_window.c:396
static int ci_next_undeleted(struct Mailbox *m, int msgno)
Find the next undeleted email.
Definition: index.c:292
int mutt_monitor_add(struct Mailbox *m)
Add a watch for a mailbox.
Definition: monitor.c:481
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1639
Flags to control mutt_expando_format()
All user-callable functions.
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:159
#define SEND_POSTPONED
Recall a postponed email.
Definition: send.h:44
void mutt_help(enum MenuType menu)
Display the help menu.
Definition: help.c:388
Container for Accounts, Notifications.
Definition: neomutt.h:36
A progress bar.
Definition: progress.h:44
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:602
Mailbox was closed.
Definition: mailbox.h:172
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:66
int index_color(struct Menu *menu, int line)
Calculate the colour for a line of the index - Implements Menu::color()
Definition: index.c:934
struct Notify * notify
Notifications: NotifyIndex, IndexSharedData.
Definition: shared_data.h:44
int vcount
The number of virtual messages.
Definition: mailbox.h:102
The body of an email.
Definition: body.h:34
Mailbox was reopened.
Definition: mxapi.h:81
void index_shared_data_set_email(struct IndexSharedData *shared, struct Email *e)
Set the current Email for the Index and friends.
Definition: shared_data.c:198
QuadOption
Possible values for a quad-option.
Definition: quad.h:35
Convenience wrapper for the config headers.
void mutt_paddstr(int n, const char *s)
Display a string on screen, padded if necessary.
Definition: curs_lib.c:1156
int mutt_window_move(struct MuttWindow *win, int col, int row)
Move the cursor in a Window.
Definition: mutt_window.c:382
GUI component for displaying/selecting items from a list.
void(* make_entry)(struct Menu *menu, char *buf, size_t buflen, int line)
Format a item for a menu.
Definition: lib.h:91
A panel containing the Index Window.
Definition: mutt_window.h:95
void imap_logout_all(void)
close all open connections
Definition: imap.c:561
Assorted sorting methods.
void mutt_print_message(struct Mailbox *m, struct EmailList *el)
Print a message.
Definition: commands.c:788
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
#define SEND_NO_FLAGS
No flags are set.
Definition: send.h:39
int event_subtype
Send: Event subtype, e.g. NT_ACCOUNT_ADD.
Definition: observer.h:43
void mutt_beep(bool force)
Irritate the user.
Definition: curs_lib.c:92
enum NotifyType event_type
Send: Event type, e.g. NT_ACCOUNT.
Definition: observer.h:42
Some miscellaneous functions.
#define MAX(a, b)
Definition: memory.h:30
int match
Substring to match, 0 for old behaviour.
Definition: color.h:102
struct MuttWindow * focus
Focussed Window.
Definition: mutt_window.h:135
enum MxStatus mx_mbox_check(struct Mailbox *m)
Check for new mail - Wrapper for MxOps::mbox_check()
Definition: mx.c:1118
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1185
#define mutt_previous_thread(e)
Definition: mutt_thread.h:83
void mutt_folder_hook(const char *path, const char *desc)
Perform a folder hook.
Definition: hook.c:529
size_t dsize
Length of data.
Definition: buffer.h:37
int mutt_window_addnstr(const char *str, int num)
Write a partial string to a Window.
Definition: mutt_window.c:501
bool tagged
Email is tagged.
Definition: email.h:44
#define CHECK_VISIBLE
Is the selected message visible in the index?
Definition: index.c:147
bool read
Email is read.
Definition: email.h:51
void mutt_progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:211
char * name
A short name for the Mailbox.
Definition: mailbox.h:85
Data to be displayed by PagerView.
Definition: lib.h:135
void mutt_startup_shutdown_hook(HookFlags type)
Execute any startup/shutdown hooks.
Definition: hook.c:863
void mutt_curses_set_cursor(enum MuttCursorState state)
Set the cursor state.
Definition: mutt_curses.c:71
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:82
char * message_id
Message ID.
Definition: envelope.h:69
struct Notify * notify
Notifications: NotifyWindow, EventWindow.
Definition: mutt_window.h:133
struct MuttThread * prev
Previous sibling Thread.
Definition: thread.h:48
#define MUTT_PAGER_BOTTOM
Start at the bottom.
Definition: lib.h:61
struct Mailbox * mailbox
Definition: context.h:49
struct HashTable * id_hash
Hash Table by msg id.
Definition: mailbox.h:127
Parse and execute user-defined hooks.
#define MUTT_FORMAT_TREE
Draw the thread tree.
Definition: format_flags.h:32
Many unsorted constants and some structs.
API for mailboxes.
#define CHECK_IN_MAILBOX
Is there a mailbox open?
Definition: index.c:145
bool old
Email is seen, but unread.
Definition: email.h:50
static void update_index(struct Menu *menu, struct Context *ctx, enum MxStatus check, int oldcount, const struct IndexSharedData *shared)
Update the index.
Definition: index.c:584
static bool prereq(struct Context *ctx, struct Menu *menu, CheckFlags checks)
Check the pre-requisites for a function.
Definition: index.c:159
void mutt_flush_macro_to_endcond(void)
Drop a macro from the input buffer.
Definition: curs_lib.c:781
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:52
struct MuttWindow * win_ibar
Definition: lib.h:67
bool readonly
Don&#39;t allow changes to the mailbox.
Definition: mailbox.h:119
bool nm_message_is_still_queried(struct Mailbox *m, struct Email *e)
Is a message still visible in the query?
Definition: notmuch.c:1745
void mutt_window_clearline(struct MuttWindow *win, int row)
Clear a row of a Window.
Definition: mutt_window.c:230
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
#define mutt_thread_next_unread(e)
Definition: mutt_thread.h:79
void mutt_make_string(char *buf, size_t buflen, int cols, const char *s, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition: hdrline.c:1410
void mutt_what_key(void)
Ask the user to press a key.
Definition: keymap.c:1687
bool message_is_tagged(struct Email *e)
Is a message in the index tagged (and within limit)
Definition: context.c:346
struct Envelope * env
Envelope information.
Definition: email.h:90
Display a normal cursor.
Definition: mutt_curses.h:81
int LastKey
contains the last key the user pressed
Definition: keymap.c:149
Convenience wrapper for the core headers.
static void update_index_threaded(struct Context *ctx, enum MxStatus check, int oldcount)
Update the index (if threaded)
Definition: index.c:459
int mx_tags_commit(struct Mailbox *m, struct Email *e, char *tags)
Save tags to the Mailbox - Wrapper for MxOps::tags_commit()
Definition: mx.c:1289
void alias_create(struct AddressList *al, const struct ConfigSubset *sub)
Create a new Alias from an Address.
Definition: alias.c:371
bool mutt_mailbox_notify(struct Mailbox *m_cur)
Notify the user if there&#39;s new mail.
Definition: mutt_mailbox.c:209
int mutt_send_message(SendFlags flags, struct Email *e_templ, const char *tempfile, struct Mailbox *m, struct EmailList *el, struct ConfigSubset *sub)
Send an email.
Definition: send.c:2122
void mx_fastclose_mailbox(struct Mailbox *m)
free up memory associated with the Mailbox
Definition: mx.c:428
char * group
Definition: mdata.h:34
#define mutt_thread_contains_unread(e)
Definition: mutt_thread.h:77
int mutt_display_message(struct MuttWindow *win_index, struct MuttWindow *win_ibar, struct MuttWindow *win_pager, struct MuttWindow *win_pbar, struct Mailbox *m, struct Email *e)
Display a message in the pager.
Definition: commands.c:203
struct Menu * menu
Needed for pattern compilation.
Definition: context.h:45
void mutt_break_thread(struct Email *e)
Break the email Thread.
Definition: thread.c:225
int bool_str_toggle(struct ConfigSubset *sub, const char *name, struct Buffer *err)
Toggle the value of a bool.
Definition: bool.c:213
#define SEND_LIST_REPLY
Reply to mailing list.
Definition: send.h:42
int nntp_check_children(struct Mailbox *m, const char *msgid)
Fetch children of article with the Message-ID.
Definition: nntp.c:2204
int mutt_pattern_func(struct Context *ctx, int op, char *prompt)
Perform some Pattern matching.
Definition: pattern.c:341
Email Aliases.
MessageTransformOpt
Message transformation option.
Definition: commands.h:39
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:112
static int mx_toggle_write(struct Mailbox *m)
Toggle the mailbox&#39;s readonly flag.
Definition: index.c:387
Progress bar.
const char * OpStrings[][2]
Definition: opcodes.c:34
void * mdata
Driver specific data.
Definition: mailbox.h:136
static int ci_previous_undeleted(struct Mailbox *m, int msgno)
Find the previous undeleted email.
Definition: index.c:315
bool notify_observer_add(struct Notify *notify, enum NotifyType type, observer_t callback, void *global_data)
Add an observer to an object.
Definition: notify.c:173
uint16_t AclFlags
ACL Rights - These show permission to...
Definition: mailbox.h:62
struct NntpAccountData * nntp_select_server(struct Mailbox *m, const char *server, bool leave_lock)
Open a connection to an NNTP server.
Definition: newsrc.c:1017
#define MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition: mailbox.h:74
struct TagList tags
For drivers that support server tagging.
Definition: email.h:109
static struct Mailbox * change_folder_notmuch(struct Menu *menu, char *buf, int buflen, int *oldcount, struct IndexSharedData *shared, bool read_only)
Change to a different Notmuch Mailbox by string.
Definition: index.c:785
&#39;Maildir&#39; Mailbox type
Definition: mailbox.h:51
void * global_data
Data from notify_observer_add()
Definition: observer.h:45
Usenet network mailbox type; talk to an NNTP server.
void nm_db_debug_check(struct Mailbox *m)
Check if the database is open.
Definition: db.c:347
bool quasi_deleted
Deleted from neomutt, but not modified on disk.
Definition: email.h:47
#define SEND_KEY
Mail a PGP public key.
Definition: send.h:46
Window has a fixed size.
Definition: mutt_window.h:45
Plain text.
Definition: color.h:58
bool mutt_select_sort(bool reverse)
Ask the user for a sort method.
Definition: commands.c:832
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:883
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
struct Context * ctx
Current Mailbox view.
Definition: shared_data.h:39
static int mailbox_index_observer(struct NotifyCallback *nc)
Listen for Mailbox changes - Implements observer_t.
Definition: index.c:641
enum PagerMode mode
Pager mode.
Definition: lib.h:151
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:272
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:58
off_t vsize
Size (in bytes) of the messages shown.
Definition: context.h:39
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition: file.c:637
View the message.
Definition: protos.h:55
int mutt_save_message(struct Mailbox *m, struct EmailList *el, enum MessageSaveOpt save_opt, enum MessageTransformOpt transform_opt)
Save an email.
Definition: commands.c:1110
void mutt_refresh(void)
Force a refresh of the screen.
Definition: curs_lib.c:102
Side panel containing Accounts or groups of data.
Definition: mutt_window.h:101
Prototypes for many functions.
Edit the message.
Definition: protos.h:56
bool visible
Is this message part of the view?
Definition: email.h:74
Notmuch virtual mailbox type.
#define mutt_mktemp(buf, buflen)
Definition: muttlib.h:71
Sort by email threads.
Definition: sort2.h:49
struct Context * ctx_new(struct Mailbox *m)
Create a new Context.
Definition: context.c:75
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:122
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:87
void index_make_entry(struct Menu *menu, char *buf, size_t buflen, int line)
Format a menu item for the index list - Implements Menu::make_entry()
Definition: index.c:853
void * mdata
Private data.
Definition: lib.h:129
WHERE bool OptAttachMsg
(pseudo) used by attach-message
Definition: options.h:31
struct AddressList * mutt_get_address(struct Envelope *env, const char **prefix)
Get an Address from an Envelope.
Definition: alias.c:331
struct MuttWindow * MessageWindow
Message Window, ":set", etc.
Definition: mutt_window.c:45
#define SLIST_FIRST(head)
Definition: queue.h:228
A local copy of an email.
Definition: mxapi.h:41
int email_max
Number of pointers in emails.
Definition: mailbox.h:100
struct MuttWindow * RootWindow
Parent of all Windows.
Definition: mutt_window.c:43
void dlg_select_attachment(struct ConfigSubset *sub, struct Mailbox *m, struct Email *e, FILE *fp)
Show the attachments in a Menu.
Definition: recvattach.c:1624
static void change_folder_mailbox(struct Menu *menu, struct Mailbox *m, int *oldcount, struct IndexSharedData *shared, bool read_only)
Change to a different Mailbox by pointer.
Definition: index.c:664
struct Mailbox * mailbox_find_name(const char *name)
Find the mailbox with a given name.
Definition: mailbox.c:140
Create/manipulate threading in emails.
void km_error_key(enum MenuType menu)
Handle an unbound key sequence.
Definition: keymap.c:1147
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
Status bar (takes a pattern)
Definition: color.h:75
static struct MuttWindow * create_panel_index(struct MuttWindow *parent, bool status_on_top)
Create the Windows for the Index panel.
Definition: index.c:4223
Messages to be deleted.
Definition: mutt.h:94
A mailbox.
Definition: mailbox.h:81
struct ConfigSubset * sub
Inherited config items.
Definition: mailbox.h:86
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1249
#define PATH_MAX
Definition: mutt.h:40
void index_private_data_free(struct MuttWindow *win, void **ptr)
Free Index Data.
Definition: private_data.c:38
int mutt_mailbox_check(struct Mailbox *m_cur, int force)
Check all all Mailboxes for new mail.
Definition: mutt_mailbox.c:137
#define MUTT_WIN_SIZE_UNLIMITED
Use as much space as possible.
Definition: mutt_window.h:50
static void update_index_unthreaded(struct Context *ctx, enum MxStatus check)
Update the index (if unthreaded)
Definition: index.c:540
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
int top
Entry that is the top of the current page.
Definition: lib.h:78
int log_queue_save(FILE *fp)
Save the contents of the queue to a temporary file.
Definition: logging.c:369
void pop_fetch_mail(void)
Fetch messages and save them in $spool_file.
Definition: pop.c:512
Manage where the email is piped to external commands.
No changes.
Definition: mxapi.h:78
struct PatternList * limit_pattern
Compiled limit pattern.
Definition: context.h:41
int nm_read_entire_thread(struct Mailbox *m, struct Email *e)
Get the entire thread of an email.
Definition: notmuch.c:1581
const struct Mapping * help_data
Data for the Help Bar.
Definition: mutt_window.h:137
bool dontwrite
Don&#39;t write the mailbox on close.
Definition: mailbox.h:115
Match patterns to emails.
Tagged messages.
Definition: mutt.h:99
char * data
Pointer to data.
Definition: buffer.h:35
#define MUTT_READONLY
Open in read-only mode.
Definition: mxapi.h:63
Private state data for the Index.
int mutt_parent_message(struct Email *e, bool find_root)
Find the parent of a message.
Definition: mutt_thread.c:1199
Nntp-specific Mailbox data.
size_t mutt_wstr_trunc(const char *src, size_t maxlen, size_t maxwid, size_t *width)
Work out how to truncate a widechar string.
Definition: curs_lib.c:1199
#define mutt_next_thread(e)
Definition: mutt_thread.h:82
void mutt_clear_pager_position(void)
Reset the pager&#39;s viewing position.
Definition: pager.c:2016
static void change_folder_string(struct Menu *menu, char *buf, size_t buflen, int *oldcount, struct IndexSharedData *shared, bool *pager_return, bool read_only)
Change to a different Mailbox by string.
Definition: index.c:811
void nm_db_longrun_done(struct Mailbox *m)
Finish a long transaction.
Definition: db.c:329
POP network mailbox.
New messages.
Definition: mutt.h:89
Messages that have been read.
Definition: mutt.h:92
Not object-related, NotifyGlobal.
Definition: notify_type.h:44
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:295
API for encryption/signing of emails.
&#39;MH&#39; Mailbox type
Definition: mailbox.h:50
bool collapsed
Is this message part of a collapsed thread?
Definition: email.h:73
bool verbose
Display status messages?
Definition: mailbox.h:118
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition: mxapi.h:59
void index_shared_data_set_context(struct IndexSharedData *shared, struct Context *ctx)
Set the Context for the Index and friends.
Definition: shared_data.c:149
int mx_tags_edit(struct Mailbox *m, const char *tags, char *buf, size_t buflen)
start the tag editor of the mailbox
Definition: mx.c:1269
int msg_tagged
How many messages are tagged?
Definition: mailbox.h:97
#define CHECK_READONLY
Is the mailbox readonly?
Definition: index.c:148
int vnum
Virtual message number.
Definition: email.h:88
void(* custom_redraw)(struct Menu *menu)
Redraw the menu.
Definition: lib.h:125
#define SEND_NEWS
Reply to a news article.
Definition: send.h:53
WHERE bool OptSearchInvalid
(pseudo) used to invalidate the search pattern
Definition: options.h:52
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:121
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
PagerFlags flags
Additional settings to tweak pager&#39;s function.
Definition: lib.h:152
void mutt_progress_init(struct Progress *progress, const char *msg, enum ProgressType type, size_t size)
Set up a progress bar.
Definition: progress.c:152
int pagelen
Number of entries per screen.
Definition: lib.h:63
char * tree
Character string to print thread tree.
Definition: email.h:94
void menu_status_line(char *buf, size_t buflen, struct IndexSharedData *shared, struct Menu *menu, int cols, const char *fmt)
Create the status line.
Definition: status.c:430
struct Notify * notify
Notifications handler.
Definition: neomutt.h:38
WHERE bool OptRedrawTree
(pseudo) redraw the thread tree
Definition: options.h:50
bool TsSupported
Terminal Setting is supported.
Definition: terminal.c:43
#define MUTT_PAGER_LOGS
Logview mode.
Definition: lib.h:60
int mutt_color(enum ColorId id)
Return the color of an object.
Definition: color.c:1393
#define STAILQ_NEXT(elm, field)
Definition: queue.h:397
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib...
Definition: email.h:39
struct Email * message
Email this Thread refers to.
Definition: thread.h:49
#define SEND_REPLY
Reply to sender.
Definition: send.h:40
NNTP-specific Mailbox data -.
Definition: mdata.h:32
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:204
int mutt_messages_in_thread(struct Mailbox *m, struct Email *e, enum MessageInThread mit)
Count the messages in a thread.
Definition: mutt_thread.c:1495
WHERE char * LastFolder
Previously selected mailbox.
Definition: mutt_globals.h:51
Routines for adding user scores to emails.
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
#define MUTT_MAILBOX_CHECK_FORCE
Definition: mutt_mailbox.h:32
int max
Number of entries in the menu.
Definition: lib.h:60
void mutt_window_add_child(struct MuttWindow *parent, struct MuttWindow *child)
Add a child to Window.
Definition: mutt_window.c:562
&#39;Notmuch&#39; (virtual) Mailbox type
Definition: mailbox.h:54
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:84
bool done
Time to leave the Index.
Definition: private_data.h:35
GUI display a user-configurable status line.
MuttRedrawFlags redraw
When to redraw the screen.
Definition: lib.h:61
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:385
An Email conversation.
Definition: thread.h:34
int mutt_pattern_exec(struct Pattern *pat, PatternExecFlags flags, struct Mailbox *m, struct Email *e, struct PatternCache *cache)
Match a pattern against an email header.
Definition: exec.c:1089
void mutt_set_header_color(struct Mailbox *m, struct Email *e)
Select a colour for a message.
Definition: index.c:4197
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:101
struct Connection * conn
Definition: adata.h:63
A panel containing the Pager Window.
Definition: mutt_window.h:99
regex_t regex
Compiled regex.
Definition: color.h:101
#define MUTT_FORMAT_INDEX
This is a main index entry.
Definition: format_flags.h:36
enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
use the NOOP or IDLE command to poll for new mail
Definition: imap.c:1100
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: curs_lib.c:513
void ci_bounce_message(struct Mailbox *m, struct EmailList *el)
Bounce an email.
Definition: commands.c:432
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1316
char * driver_tags_get_with_hidden(struct TagList *list)
Get tags with hiddens.
Definition: tags.c:158
#define SEND_GROUP_REPLY
Reply to all.
Definition: send.h:41
char * data
String.
Definition: list.h:36
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:659
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
int mutt_label_message(struct Mailbox *m, struct EmailList *el)
Let the user label a message.
Definition: mutt_header.c:124
Move message to another mailbox, removing the original.
Definition: commands.h:52
Convenience wrapper for the debug headers.
struct MuttWindow * win_index
Window for the Index.
Definition: private_data.h:43
Routines for managing attachments.
Log at debug level 1.
Definition: logging.h:40
void query_index(struct ConfigSubset *sub)
Perform an Alias Query and display the results.
Definition: dlgquery.c:653
bool flagged
Marked important?
Definition: email.h:43
void mutt_browser_select_dir(const char *f)
Remember the last directory selected.
Definition: browser.c:1141
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:716
Window uses all available horizontal space.
Definition: mutt_window.h:37
void dump_graphviz(const char *title, struct Context *ctx)
Definition: graphviz.c:945
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:593
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
bool collapsed
Are all threads collapsed?
Definition: context.h:47
void mailbox_gc_run(void)
Definition: mailbox.c:255
bool deleted
Email is deleted.
Definition: email.h:45
struct ColorLineList * mutt_color_status_line(void)
Return the ColorLineList for the status_line.
Definition: color.c:1402
void mutt_window_reflow(struct MuttWindow *win)
Resize a Window and its children.
Definition: mutt_window.c:433
Current Mailbox has changed.
Definition: mailbox.h:175
struct MuttWindow * parent
Parent Window.
Definition: mutt_window.h:130
#define mutt_error(...)
Definition: logging.h:84
struct MuttWindow * win_ibar
Window for the Index Bar (status)
Definition: private_data.h:44
static const struct Mapping IndexHelp[]
Help Bar for the Index dialog.
Definition: index.c:108
char * followup_to
List of &#39;followup-to&#39; fields.
Definition: envelope.h:77
void * compress_info
Compressed mbox module private data.
Definition: mailbox.h:124
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: mutt_globals.h:50
Connection Library.
bool in_pager
Is the Pager active?
Definition: private_data.h:41
NeoMutt is about to close.
Definition: neomutt.h:51
void mutt_timeout_hook(void)
Execute any timeout hooks.
Definition: hook.c:827
FILE * fp
pointer to the message data
Definition: mxapi.h:43
static void collapse_all(struct Context *ctx, struct Menu *menu, int toggle)
Collapse/uncollapse all threads.
Definition: index.c:237
MessageSaveOpt
Message save option.
Definition: commands.h:49
void mutt_check_stats(struct Mailbox *m)
Forcibly update mailbox stats.
Definition: commands.c:1553
int index
The absolute (unsorted) message number.
Definition: email.h:86
#define FREE(x)
Definition: memory.h:40
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1667
Mailbox has changed, NotifyMailbox, EventMailbox.
Definition: notify_type.h:47
Data shared between Index, Pager and Sidebar.
Mapping between user-readable string and a constant.
Definition: mapping.h:31
bool do_mailbox_notify
Do we need to notify the user of new mail?
Definition: private_data.h:39
#define MUTT_OPEN_NO_FLAGS
No flags are set.
Definition: mxapi.h:60
Monitor files for changes.
void crypt_extract_keys_from_messages(struct Mailbox *m, struct EmailList *el)
Extract keys from a message.
Definition: crypt.c:857
#define MUTT_ACL_SEEN
Change the &#39;seen&#39; status of a message.
Definition: mailbox.h:73
#define STAILQ_EMPTY(head)
Definition: queue.h:345
Hide the cursor.
Definition: mutt_curses.h:80
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:321
void mutt_draw_tree(struct ThreadsContext *tctx)
Draw a tree of threaded emails.
Definition: mutt_thread.c:318
Decode message.
Definition: commands.h:43
void index_pager_shutdown(struct MuttWindow *dlg)
Clear up any non-Window parts.
Definition: index.c:4289
struct NntpAccountData * CurrentNewsSrv
Current NNTP news server.
Definition: nntp.c:77
void mutt_ts_icon(char *str)
Set the icon in the terminal title bar.
Definition: terminal.c:118
enum CommandResult km_bind(char *s, enum MenuType menu, int op, char *macro, char *desc)
Bind a key to a macro.
Definition: keymap.c:481
bool notify_observer_remove(struct Notify *notify, observer_t callback, void *global_data)
Remove an observer from an object.
Definition: notify.c:212
void window_redraw(struct MuttWindow *win, bool force)
Reflow, recalc and repaint a tree of Windows.
Definition: mutt_window.c:744
void mutt_show_error(void)
Show the user an error message.
Definition: curs_lib.c:554
void notify_set_parent(struct Notify *notify, struct Notify *parent)
Set the parent notification handler.
Definition: notify.c:82
struct MuttWindow * index_pager_init(void)
Allocate the Windows for the Index/Pager.
Definition: index.c:4260
#define PGP_TRADITIONAL_CHECKED
Email has a traditional (inline) signature.
Definition: lib.h:89
Messages to be un-tagged.
Definition: mutt.h:100
bool changed
Mailbox has been modified.
Definition: mailbox.h:114
int mutt_change_flag(struct Mailbox *m, struct EmailList *el, bool bf)
Change the flag on a Message.
Definition: flags.c:434
void ctx_free(struct Context **ptr)
Free a Context.
Definition: context.c:49
Hundreds of global variables to back the user variables.
Handling of global boolean variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size()
Definition: mx.c:1539
bool mx_tags_is_supported(struct Mailbox *m)
return true if mailbox support tagging
Definition: mx.c:1306
int mutt_resend_message(FILE *fp, struct Mailbox *m, struct Email *e_cur, struct ConfigSubset *sub)
Resend an email.
Definition: send.c:1629
struct Notify * notify
Notifications: NotifyMailbox, EventMailbox.
Definition: mailbox.h:144
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:42
void mutt_ts_status(char *str)
Set the text of the terminal title bar.
Definition: terminal.c:104
#define CHECK_MSGCOUNT
Are there any messages?
Definition: index.c:146
const char * fname
Name of the file to read.
Definition: lib.h:142
struct MuttWindow * mutt_window_new(enum WindowType type, enum MuttWindowOrientation orient, enum MuttWindowSize size, int cols, int rows)
Create a new Window.
Definition: mutt_window.c:164
#define SEND_GROUP_CHAT_REPLY
Reply to all recipients preserving To/Cc.
Definition: send.h:52
struct Mailbox * mailbox
Current Mailbox.
Definition: shared_data.h:41
void mutt_update_index(struct Menu *menu, struct Context *ctx, enum MxStatus check, int oldcount, struct IndexSharedData *shared)
Update the index.
Definition: index.c:630
off_t mutt_set_vnum(struct Mailbox *m)
Set the virtual index number of all the messages in a mailbox.
Definition: mutt_thread.c:1250
bool mutt_edit_content_type(struct Email *e, struct Body *b, FILE *fp)
Edit the content type of an attachment.
Definition: commands.c:1396
Cache commonly-used patterns.
Definition: lib.h:105
int el_add_tagged(struct EmailList *el, struct Context *ctx, struct Email *e, bool use_tagged)
Get a list of the tagged Emails.
Definition: context.c:360
Index Dialog, index_pager_init()
Definition: mutt_window.h:84
struct NntpMboxData * mutt_newsgroup_catchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Catchup newsgroup.
Definition: newsrc.c:1308
Nntp-specific Account data.
struct Buffer pathbuf
Definition: mailbox.h:83
struct MuttWindow * win_index
Definition: lib.h:66
Convenience wrapper for the library headers.
bool mutt_thread_can_collapse(struct Email *e)
Check whether a thread can be collapsed.
Definition: mutt_thread.c:1652
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:79
struct ListHead references
message references (in reverse order)
Definition: envelope.h:81
A List node for strings.
Definition: list.h:34
void nntp_expand_path(char *buf, size_t buflen, struct ConnAccount *acct)
Make fully qualified url from newsgroup name.
Definition: newsrc.c:559
const char * banner
Title to display in status bar.
Definition: lib.h:153
Window wants as much space as possible.
Definition: mutt_window.h:46
GUI display a file/email/help in a viewport with paging.
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
struct Mailbox * mutt_index_menu(struct MuttWindow *dlg, struct Mailbox *m_init)
Display a list of emails.
Definition: index.c:1155
Pager is invoked via 3rd path. Non-email content is likely to be shown.
Definition: lib.h:127
void * wdata
Private data.
Definition: mutt_window.h:140
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:53
int mx_path_canon(char *buf, size_t buflen, const char *folder, enum MailboxType *type)
Canonicalise a mailbox path - Wrapper for MxOps::path_canon()
Definition: mx.c:1362
char * pattern
Limit pattern string.