NeoMutt  2020-03-20-65-g141838
Teaching an old dog new tricks
DOXYGEN
mx.c
Go to the documentation of this file.
1 
32 #include "config.h"
33 #include <errno.h>
34 #include <limits.h>
35 #include <locale.h>
36 #include <stdbool.h>
37 #include <string.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
40 #include "mutt/lib.h"
41 #include "address/lib.h"
42 #include "email/lib.h"
43 #include "core/lib.h"
44 #include "mutt.h"
45 #include "mx.h"
46 #include "alias.h"
47 #include "context.h"
48 #include "copy.h"
49 #include "globals.h"
50 #include "hook.h"
51 #include "init.h"
52 #include "keymap.h"
53 #include "mutt_header.h"
54 #include "mutt_logging.h"
55 #include "mutt_mailbox.h"
56 #include "muttlib.h"
57 #include "opcodes.h"
58 #include "options.h"
59 #include "protos.h"
60 #include "sort.h"
61 #include "maildir/lib.h"
62 #include "mbox/lib.h"
63 #ifdef USE_COMP_MBOX
64 #include "compmbox/lib.h"
65 #endif
66 #ifdef USE_IMAP
67 #include "imap/lib.h"
68 #endif
69 #ifdef USE_POP
70 #include "pop/lib.h"
71 #endif
72 #ifdef USE_NNTP
73 #include "nntp/lib.h"
74 #endif
75 #ifdef USE_NOTMUCH
76 #include "notmuch/lib.h"
77 #endif
78 #ifdef ENABLE_NLS
79 #include <libintl.h>
80 #endif
81 
82 /* These Config Variables are only used in mx.c */
83 unsigned char C_CatchupNewsgroup;
85 unsigned char C_MboxType;
86 unsigned char C_Move;
87 char *C_Trash;
88 
89 // clang-format off
90 static struct Mapping MboxTypeMap[] = {
91  { "mbox", MUTT_MBOX, },
92  { "MMDF", MUTT_MMDF, },
93  { "MH", MUTT_MH, },
94  { "Maildir", MUTT_MAILDIR, },
95  { NULL, 0, },
96 };
97 // clang-format on
98 
99 struct EnumDef MboxTypeDef = {
100  "mbox_type",
101  4,
102  (struct Mapping *) &MboxTypeMap,
103 };
104 
108 const struct MxOps *mx_ops[] = {
109 /* These mailboxes can be recognised by their Url scheme */
110 #ifdef USE_IMAP
111  &MxImapOps,
112 #endif
113 #ifdef USE_NOTMUCH
114  &MxNotmuchOps,
115 #endif
116 #ifdef USE_POP
117  &MxPopOps,
118 #endif
119 #ifdef USE_NNTP
120  &MxNntpOps,
121 #endif
122 
123  /* Local mailboxes */
124  &MxMaildirOps,
125  &MxMboxOps,
126  &MxMhOps,
127  &MxMmdfOps,
128 
129 /* If everything else fails... */
130 #ifdef USE_COMP_MBOX
131  &MxCompOps,
132 #endif
133  NULL,
134 };
135 
142 const struct MxOps *mx_get_ops(enum MailboxType type)
143 {
144  for (const struct MxOps **ops = mx_ops; *ops; ops++)
145  if ((*ops)->type == type)
146  return *ops;
147 
148  return NULL;
149 }
150 
156 static bool mutt_is_spool(const char *str)
157 {
158  return mutt_str_strcmp(C_Spoolfile, str) == 0;
159 }
160 
171 int mx_access(const char *path, int flags)
172 {
173 #ifdef USE_IMAP
174  if (imap_path_probe(path, NULL) == MUTT_IMAP)
175  return imap_access(path);
176 #endif
177 
178  return access(path, flags);
179 }
180 
188 static int mx_open_mailbox_append(struct Mailbox *m, OpenMailboxFlags flags)
189 {
190  if (!m)
191  return -1;
192 
193  struct stat sb;
194 
195  m->append = true;
196  if ((m->type == MUTT_UNKNOWN) || (m->type == MUTT_MAILBOX_ERROR))
197  {
199 
200  if (m->type == MUTT_UNKNOWN)
201  {
202  if (flags & (MUTT_APPEND | MUTT_NEWFOLDER))
203  {
205  }
206  else
207  {
208  mutt_error(_("%s is not a mailbox"), mailbox_path(m));
209  return -1;
210  }
211  }
212 
213  if (m->type == MUTT_MAILBOX_ERROR)
214  {
215  if (stat(mailbox_path(m), &sb) == -1)
216  {
217  if (errno == ENOENT)
218  {
219 #ifdef USE_COMP_MBOX
220  if (mutt_comp_can_append(m))
221  m->type = MUTT_COMPRESSED;
222  else
223 #endif
224  m->type = C_MboxType;
225  flags |= MUTT_APPENDNEW;
226  }
227  else
228  {
230  return -1;
231  }
232  }
233  else
234  return -1;
235  }
236 
237  m->mx_ops = mx_get_ops(m->type);
238  }
239 
240  if (!m->mx_ops || !m->mx_ops->mbox_open_append)
241  return -1;
242 
243  int rc = m->mx_ops->mbox_open_append(m, flags);
244  m->opened++;
245  return rc;
246 }
247 
255 struct Context *mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
256 {
257  if (!m)
258  return NULL;
259 
260  struct Context *ctx = ctx_new();
261  ctx->mailbox = m;
262 
263  struct EventContext ev_ctx = { ctx };
264  notify_send(ctx->notify, NT_CONTEXT, NT_CONTEXT_OPEN, &ev_ctx);
265 
266  // If the Mailbox is closed, Context->mailbox must be set to NULL
268 
269  if ((m->type == MUTT_UNKNOWN) && (flags & (MUTT_NEWFOLDER | MUTT_APPEND)))
270  {
271  m->type = C_MboxType;
272  m->mx_ops = mx_get_ops(m->type);
273  }
274 
275  const bool newly_linked_account = !m->account;
276  if (newly_linked_account)
277  {
278  struct Account *a = mx_ac_find(m);
279  bool new_account = false;
280  if (!a)
281  {
282  a = account_new(NULL, NeoMutt->sub);
283  a->type = m->type;
284  new_account = true;
285  }
286  if (mx_ac_add(a, m) < 0)
287  {
288  ctx_free(&ctx);
289  if (new_account)
290  {
291  FREE(&a);
292  }
293  return NULL;
294  }
295  if (new_account)
296  {
298  }
299  }
300 
301  ctx->msg_not_read_yet = -1;
302  ctx->collapsed = false;
303 
304  m->quiet = (flags & MUTT_QUIET);
305  if (flags & MUTT_READONLY)
306  m->readonly = true;
307  m->peekonly = (flags & MUTT_PEEK);
308 
309  if (flags & (MUTT_APPEND | MUTT_NEWFOLDER))
310  {
311  if (mx_open_mailbox_append(ctx->mailbox, flags) != 0)
312  {
313  goto error;
314  }
315  return ctx;
316  }
317 
318  if (m->opened > 0)
319  {
320  m->opened++;
321  return ctx;
322  }
323 
324  m->size = 0;
325  m->msg_unread = 0;
326  m->msg_flagged = 0;
327  m->rights = MUTT_ACL_ALL;
328 
329  if (m->type == MUTT_UNKNOWN)
330  {
332  m->mx_ops = mx_get_ops(m->type);
333  }
334 
335  if ((m->type == MUTT_UNKNOWN) || (m->type == MUTT_MAILBOX_ERROR) || !m->mx_ops)
336  {
337  if (m->type == MUTT_MAILBOX_ERROR)
339  else if ((m->type == MUTT_UNKNOWN) || !m->mx_ops)
340  mutt_error(_("%s is not a mailbox"), mailbox_path(m));
341  goto error;
342  }
343 
345 
346  /* if the user has a 'push' command in their .neomuttrc, or in a folder-hook,
347  * it will cause the progress messages not to be displayed because
348  * mutt_refresh() will think we are in the middle of a macro. so set a
349  * flag to indicate that we should really refresh the screen. */
350  OptForceRefresh = true;
351 
352  if (!m->quiet)
353  mutt_message(_("Reading %s..."), mailbox_path(m));
354 
355  // Clear out any existing emails
356  for (int i = 0; i < m->email_max; i++)
357  {
358  email_free(&m->emails[i]);
359  }
360 
361  m->msg_count = 0;
362  m->msg_unread = 0;
363  m->msg_flagged = 0;
364  m->msg_new = 0;
365  m->msg_deleted = 0;
366  m->msg_tagged = 0;
367  m->vcount = 0;
368 
369  int rc = m->mx_ops->mbox_open(ctx->mailbox);
370  m->opened++;
371  if (rc == 0)
372  ctx_update(ctx);
373 
374  if ((rc == 0) || (rc == -2))
375  {
376  if ((flags & MUTT_NOSORT) == 0)
377  {
378  /* avoid unnecessary work since the mailbox is completely unthreaded
379  * to begin with */
380  OptSortSubthreads = false;
381  OptNeedRescore = false;
382  }
383  if (!m->quiet)
385  if (rc == -2)
386  {
387  mutt_error(_("Reading from %s interrupted..."), mailbox_path(m));
388  mutt_sort_headers(ctx, true);
389  }
390  }
391  else
392  {
393  goto error;
394  }
395 
396  if (!m->peekonly)
397  m->has_new = false;
398  OptForceRefresh = false;
399 
400  return ctx;
401 
402 error:
404  if (newly_linked_account)
406  ctx_free(&ctx);
407  return NULL;
408 }
409 
415 {
416  if (!m)
417  return;
418 
419  m->opened--;
420  if (m->opened != 0)
421  return;
422 
423  /* never announce that a mailbox we've just left has new mail.
424  * TODO: really belongs in mx_mbox_close, but this is a nice hook point */
425  if (!m->peekonly)
427 
428  if (m->mx_ops)
429  m->mx_ops->mbox_close(m);
430 
432  mutt_hash_free(&m->id_hash);
434 
435  if (m->emails)
436  {
437  for (int i = 0; i < m->msg_count; i++)
438  {
439  if (!m->emails[i])
440  break;
441  email_free(&m->emails[i]);
442  }
443  }
444 }
445 
453 static int sync_mailbox(struct Mailbox *m, int *index_hint)
454 {
455  if (!m || !m->mx_ops || !m->mx_ops->mbox_sync)
456  return -1;
457 
458  if (!m->quiet)
459  {
460  /* L10N: Displayed before/as a mailbox is being synced */
461  mutt_message(_("Writing %s..."), mailbox_path(m));
462  }
463 
464  int rc = m->mx_ops->mbox_sync(m, index_hint);
465  if (rc != 0)
466  {
467  mutt_debug(LL_DEBUG2, "mbox_sync returned: %d\n", rc);
468  if ((rc < 0) && !m->quiet)
469  {
470  /* L10N: Displayed if a mailbox sync fails */
471  mutt_error(_("Unable to write %s"), mailbox_path(m));
472  }
473  }
474 
475  return rc;
476 }
477 
484 static int trash_append(struct Mailbox *m)
485 {
486  if (!m)
487  return -1;
488 
489  struct stat st, stc;
490  int opt_confappend, rc;
491 
492  if (!C_Trash || (m->msg_deleted == 0) || ((m->type == MUTT_MAILDIR) && C_MaildirTrash))
493  {
494  return 0;
495  }
496 
497  int delmsgcount = 0;
498  int first_del = -1;
499  for (int i = 0; i < m->msg_count; i++)
500  {
501  struct Email *e = m->emails[i];
502  if (!e)
503  break;
504 
505  if (e->deleted && !e->purge)
506  {
507  if (first_del < 0)
508  first_del = i;
509  delmsgcount++;
510  }
511  }
512 
513  if (delmsgcount == 0)
514  return 0; /* nothing to be done */
515 
516  /* avoid the "append messages" prompt */
517  opt_confappend = C_Confirmappend;
518  if (opt_confappend)
519  C_Confirmappend = false;
520  rc = mutt_save_confirm(C_Trash, &st);
521  if (opt_confappend)
522  C_Confirmappend = true;
523  if (rc != 0)
524  {
525  /* L10N: Although we know the precise number of messages, we do not show it to the user.
526  So feel free to use a "generic plural" as plural translation if your language has one. */
527  mutt_error(ngettext("message not deleted", "messages not deleted", delmsgcount));
528  return -1;
529  }
530 
531  if ((lstat(mailbox_path(m), &stc) == 0) && (stc.st_ino == st.st_ino) &&
532  (stc.st_dev == st.st_dev) && (stc.st_rdev == st.st_rdev))
533  {
534  return 0; /* we are in the trash folder: simple sync */
535  }
536 
537 #ifdef USE_IMAP
538  if ((m->type == MUTT_IMAP) && (imap_path_probe(C_Trash, NULL) == MUTT_IMAP))
539  {
540  if (imap_fast_trash(m, C_Trash) == 0)
541  return 0;
542  }
543 #endif
544 
545  struct Mailbox *m_trash = mx_path_resolve(C_Trash);
546  const bool old_append = m_trash->append;
547  struct Context *ctx_trash = mx_mbox_open(m_trash, MUTT_APPEND);
548  if (ctx_trash)
549  {
550  /* continue from initial scan above */
551  for (int i = first_del; i < m->msg_count; i++)
552  {
553  struct Email *e = m->emails[i];
554  if (!e)
555  break;
556 
557  if (e->deleted && !e->purge)
558  {
559  if (mutt_append_message(ctx_trash->mailbox, m, e, MUTT_CM_NO_FLAGS, CH_NO_FLAGS) == -1)
560  {
561  mx_mbox_close(&ctx_trash);
562  m_trash->append = old_append;
563  return -1;
564  }
565  }
566  }
567 
568  mx_mbox_close(&ctx_trash);
569  m_trash->append = old_append;
570  }
571  else
572  {
573  mutt_error(_("Can't open trash folder"));
574  mailbox_free(&m_trash);
575  return -1;
576  }
577 
578  return 0;
579 }
580 
593 int mx_mbox_close(struct Context **ptr)
594 {
595  if (!ptr || !*ptr)
596  return 0;
597 
598  struct Context *ctx = *ptr;
599  if (!ctx || !ctx->mailbox)
600  return -1;
601 
602  struct Mailbox *m = ctx->mailbox;
603 
604  if (C_MailCheckRecent && !m->peekonly)
605  m->has_new = false;
606 
607  if (m->readonly || m->dontwrite || m->append || m->peekonly)
608  {
610  ctx_free(ptr);
611  return 0;
612  }
613 
614  int i, read_msgs = 0;
615  int rc = -1;
616  enum QuadOption move_messages = MUTT_NO;
617  enum QuadOption purge = MUTT_YES;
618  struct Buffer *mbox = NULL;
619  struct Buffer *buf = mutt_buffer_pool_get();
620 
621 #ifdef USE_NNTP
622  if ((m->msg_unread != 0) && (m->type == MUTT_NNTP))
623  {
624  struct NntpMboxData *mdata = m->mdata;
625 
626  if (mdata && mdata->adata && mdata->group)
627  {
628  enum QuadOption ans =
629  query_quadoption(C_CatchupNewsgroup, _("Mark all articles read?"));
630  if (ans == MUTT_ABORT)
631  goto cleanup;
632  if (ans == MUTT_YES)
633  mutt_newsgroup_catchup(m, mdata->adata, mdata->group);
634  }
635  }
636 #endif
637 
638  for (i = 0; i < m->msg_count; i++)
639  {
640  struct Email *e = m->emails[i];
641  if (!e)
642  break;
643 
644  if (!e->deleted && e->read && !(e->flagged && C_KeepFlagged))
645  read_msgs++;
646  }
647 
648 #ifdef USE_NNTP
649  /* don't need to move articles from newsgroup */
650  if (m->type == MUTT_NNTP)
651  read_msgs = 0;
652 #endif
653 
654  if ((read_msgs != 0) && (C_Move != MUTT_NO))
655  {
656  bool is_spool;
657  mbox = mutt_buffer_pool_get();
658 
660  if (p)
661  {
662  is_spool = true;
663  mutt_buffer_strcpy(mbox, p);
664  }
665  else
666  {
667  mutt_buffer_strcpy(mbox, C_Mbox);
668  is_spool = mutt_is_spool(mailbox_path(m)) && !mutt_is_spool(mutt_b2s(mbox));
669  }
670 
671  if (is_spool && !mutt_buffer_is_empty(mbox))
672  {
674  mutt_buffer_printf(buf,
675  /* L10N: The first argument is the number of read messages to be
676  moved, the second argument is the target mailbox. */
677  ngettext("Move %d read message to %s?",
678  "Move %d read messages to %s?", read_msgs),
679  read_msgs, mutt_b2s(mbox));
680  move_messages = query_quadoption(C_Move, mutt_b2s(buf));
681  if (move_messages == MUTT_ABORT)
682  goto cleanup;
683  }
684  }
685 
686  /* There is no point in asking whether or not to purge if we are
687  * just marking messages as "trash". */
688  if ((m->msg_deleted != 0) && !((m->type == MUTT_MAILDIR) && C_MaildirTrash))
689  {
690  mutt_buffer_printf(buf,
691  ngettext("Purge %d deleted message?",
692  "Purge %d deleted messages?", m->msg_deleted),
693  m->msg_deleted);
694  purge = query_quadoption(C_Delete, mutt_b2s(buf));
695  if (purge == MUTT_ABORT)
696  goto cleanup;
697  }
698 
699  if (C_MarkOld && !m->peekonly)
700  {
701  for (i = 0; i < m->msg_count; i++)
702  {
703  struct Email *e = m->emails[i];
704  if (!e)
705  break;
706  if (!e->deleted && !e->old && !e->read)
707  mutt_set_flag(m, e, MUTT_OLD, true);
708  }
709  }
710 
711  if (move_messages)
712  {
713  if (!m->quiet)
714  mutt_message(_("Moving read messages to %s..."), mutt_b2s(mbox));
715 
716 #ifdef USE_IMAP
717  /* try to use server-side copy first */
718  i = 1;
719 
720  if ((m->type == MUTT_IMAP) && (imap_path_probe(mutt_b2s(mbox), NULL) == MUTT_IMAP))
721  {
722  /* add messages for moving, and clear old tags, if any */
723  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
724  for (i = 0; i < m->msg_count; i++)
725  {
726  struct Email *e = m->emails[i];
727  if (!e)
728  break;
729 
730  if (e->read && !e->deleted && !(e->flagged && C_KeepFlagged))
731  {
732  e->tagged = true;
733  emaillist_add_email(&el, e);
734  }
735  else
736  e->tagged = false;
737  }
738 
739  i = imap_copy_messages(ctx->mailbox, &el, mutt_b2s(mbox), true);
740  emaillist_clear(&el);
741  }
742 
743  if (i == 0) /* success */
745  else if (i == -1) /* horrible error, bail */
746  goto cleanup;
747  else /* use regular append-copy mode */
748 #endif
749  {
750  struct Mailbox *m_read = mx_path_resolve(mutt_b2s(mbox));
751  struct Context *ctx_read = mx_mbox_open(m_read, MUTT_APPEND);
752  if (!ctx_read)
753  {
754  mailbox_free(&m_read);
755  goto cleanup;
756  }
757 
758  for (i = 0; i < m->msg_count; i++)
759  {
760  struct Email *e = m->emails[i];
761  if (!e)
762  break;
763  if (e->read && !e->deleted && !(e->flagged && C_KeepFlagged))
764  {
765  if (mutt_append_message(ctx_read->mailbox, ctx->mailbox, e,
767  {
768  mutt_set_flag(m, e, MUTT_DELETE, true);
769  mutt_set_flag(m, e, MUTT_PURGE, true);
770  }
771  else
772  {
773  mx_mbox_close(&ctx_read);
774  goto cleanup;
775  }
776  }
777  }
778 
779  mx_mbox_close(&ctx_read);
780  }
781  }
782  else if (!m->changed && (m->msg_deleted == 0))
783  {
784  if (!m->quiet)
785  mutt_message(_("Mailbox is unchanged"));
786  if ((m->type == MUTT_MBOX) || (m->type == MUTT_MMDF))
787  mbox_reset_atime(m, NULL);
789  ctx_free(ptr);
790  rc = 0;
791  goto cleanup;
792  }
793 
794  /* copy mails to the trash before expunging */
795  const struct Mailbox *m_trash = mx_mbox_find(m->account, C_Trash);
796  if (purge && (m->msg_deleted != 0) && (m != m_trash))
797  {
798  if (trash_append(ctx->mailbox) != 0)
799  goto cleanup;
800  }
801 
802 #ifdef USE_IMAP
803  /* allow IMAP to preserve the deleted flag across sessions */
804  if (m->type == MUTT_IMAP)
805  {
806  int check = imap_sync_mailbox(ctx->mailbox, (purge != MUTT_NO), true);
807  if (check < 0)
808  {
809  rc = check;
810  goto cleanup;
811  }
812  }
813  else
814 #endif
815  {
816  if (purge == MUTT_NO)
817  {
818  for (i = 0; i < m->msg_count; i++)
819  {
820  struct Email *e = m->emails[i];
821  if (!e)
822  break;
823 
824  e->deleted = false;
825  e->purge = false;
826  }
827  m->msg_deleted = 0;
828  }
829 
830  if (m->changed || (m->msg_deleted != 0))
831  {
832  int check = sync_mailbox(ctx->mailbox, NULL);
833  if (check != 0)
834  {
835  rc = check;
836  goto cleanup;
837  }
838  }
839  }
840 
841  if (!m->quiet)
842  {
843  if (move_messages)
844  {
845  mutt_message(_("%d kept, %d moved, %d deleted"),
846  m->msg_count - m->msg_deleted, read_msgs, m->msg_deleted);
847  }
848  else
849  mutt_message(_("%d kept, %d deleted"), m->msg_count - m->msg_deleted, m->msg_deleted);
850  }
851 
852  if ((m->msg_count == m->msg_deleted) &&
853  ((m->type == MUTT_MMDF) || (m->type == MUTT_MBOX)) &&
855  {
857  }
858 
859 #ifdef USE_SIDEBAR
860  if ((purge == MUTT_YES) && (m->msg_deleted != 0))
861  {
862  for (i = 0; i < m->msg_count; i++)
863  {
864  struct Email *e = m->emails[i];
865  if (!e)
866  break;
867  if (e->deleted && !e->read)
868  {
869  m->msg_unread--;
870  if (!e->old)
871  m->msg_new--;
872  }
873  if (e->deleted && e->flagged)
874  m->msg_flagged--;
875  }
876  }
877 #endif
878 
880  ctx_free(ptr);
881 
882  rc = 0;
883 
884 cleanup:
887  return rc;
888 }
889 
901 int mx_mbox_sync(struct Mailbox *m, int *index_hint)
902 {
903  if (!m)
904  return -1;
905 
906  int rc;
907  int purge = 1;
908  int msgcount, deleted;
909 
910  if (m->dontwrite)
911  {
912  char buf[256], tmp[256];
913  if (km_expand_key(buf, sizeof(buf), km_find_func(MENU_MAIN, OP_TOGGLE_WRITE)))
914  snprintf(tmp, sizeof(tmp), _(" Press '%s' to toggle write"), buf);
915  else
916  mutt_str_strfcpy(tmp, _("Use 'toggle-write' to re-enable write"), sizeof(tmp));
917 
918  mutt_error(_("Mailbox is marked unwritable. %s"), tmp);
919  return -1;
920  }
921  else if (m->readonly)
922  {
923  mutt_error(_("Mailbox is read-only"));
924  return -1;
925  }
926 
927  if (!m->changed && (m->msg_deleted == 0))
928  {
929  if (!m->quiet)
930  mutt_message(_("Mailbox is unchanged"));
931  return 0;
932  }
933 
934  if (m->msg_deleted != 0)
935  {
936  char buf[128];
937 
938  snprintf(buf, sizeof(buf),
939  ngettext("Purge %d deleted message?", "Purge %d deleted messages?", m->msg_deleted),
940  m->msg_deleted);
941  purge = query_quadoption(C_Delete, buf);
942  if (purge == MUTT_ABORT)
943  return -1;
944  if (purge == MUTT_NO)
945  {
946  if (!m->changed)
947  return 0; /* nothing to do! */
948  /* let IMAP servers hold on to D flags */
949  if (m->type != MUTT_IMAP)
950  {
951  for (int i = 0; i < m->msg_count; i++)
952  {
953  struct Email *e = m->emails[i];
954  if (!e)
955  break;
956  e->deleted = false;
957  e->purge = false;
958  }
959  m->msg_deleted = 0;
960  }
961  }
963  }
964 
965  /* really only for IMAP - imap_sync_mailbox results in a call to
966  * ctx_update_tables, so m->msg_deleted is 0 when it comes back */
967  msgcount = m->msg_count;
968  deleted = m->msg_deleted;
969 
970  const struct Mailbox *m_trash = mx_mbox_find(m->account, C_Trash);
971  if (purge && (m->msg_deleted != 0) && (m != m_trash))
972  {
973  if (trash_append(m) != 0)
974  return -1;
975  }
976 
977 #ifdef USE_IMAP
978  if (m->type == MUTT_IMAP)
979  rc = imap_sync_mailbox(m, purge, false);
980  else
981 #endif
982  rc = sync_mailbox(m, index_hint);
983  if (rc >= 0)
984  {
985 #ifdef USE_IMAP
986  if ((m->type == MUTT_IMAP) && !purge)
987  {
988  if (!m->quiet)
989  mutt_message(_("Mailbox checkpointed"));
990  }
991  else
992 #endif
993  {
994  if (!m->quiet)
995  mutt_message(_("%d kept, %d deleted"), msgcount - deleted, deleted);
996  }
997 
998  mutt_sleep(0);
999 
1000  if ((m->msg_count == m->msg_deleted) &&
1001  ((m->type == MUTT_MBOX) || (m->type == MUTT_MMDF)) &&
1003  {
1004  unlink(mailbox_path(m));
1006  return 0;
1007  }
1008 
1009  /* if we haven't deleted any messages, we don't need to resort */
1010  /* ... except for certain folder formats which need "unsorted"
1011  * sort order in order to synchronize folders.
1012  *
1013  * MH and maildir are safe. mbox-style seems to need re-sorting,
1014  * at least with the new threading code. */
1015  if (purge || ((m->type != MUTT_MAILDIR) && (m->type != MUTT_MH)))
1016  {
1017  /* IMAP does this automatically after handling EXPUNGE */
1018  if (m->type != MUTT_IMAP)
1019  {
1022  }
1023  }
1024  }
1025 
1026  return rc;
1027 }
1028 
1036 struct Message *mx_msg_open_new(struct Mailbox *m, struct Email *e, MsgOpenFlags flags)
1037 {
1038  if (!m)
1039  return NULL;
1040 
1041  struct Address *p = NULL;
1042  struct Message *msg = NULL;
1043 
1044  if (!m->mx_ops || !m->mx_ops->msg_open_new)
1045  {
1046  mutt_debug(LL_DEBUG1, "function unimplemented for mailbox type %d\n", m->type);
1047  return NULL;
1048  }
1049 
1050  msg = mutt_mem_calloc(1, sizeof(struct Message));
1051  msg->write = true;
1052 
1053  if (e)
1054  {
1055  msg->flags.flagged = e->flagged;
1056  msg->flags.replied = e->replied;
1057  msg->flags.read = e->read;
1058  msg->flags.draft = (flags & MUTT_SET_DRAFT);
1059  msg->received = e->received;
1060  }
1061 
1062  if (msg->received == 0)
1063  msg->received = mutt_date_epoch();
1064 
1065  if (m->mx_ops->msg_open_new(m, msg, e) == 0)
1066  {
1067  if (m->type == MUTT_MMDF)
1068  fputs(MMDF_SEP, msg->fp);
1069 
1070  if (((m->type == MUTT_MBOX) || (m->type == MUTT_MMDF)) && (flags & MUTT_ADD_FROM))
1071  {
1072  if (e)
1073  {
1074  p = TAILQ_FIRST(&e->env->return_path);
1075  if (!p)
1076  p = TAILQ_FIRST(&e->env->sender);
1077  if (!p)
1078  p = TAILQ_FIRST(&e->env->from);
1079  }
1080 
1081  // Force a 'C' locale for the date, so that day/month names are in English
1082  locale_t loc = newlocale(LC_TIME_MASK, "C", 0);
1083  char buf[64] = { 0 };
1084  struct tm tm = mutt_date_localtime(msg->received);
1085  strftime_l(buf, sizeof(buf), "%a %b %e %H:%M:%S %Y", &tm, loc);
1086  freelocale(loc);
1087  fprintf(msg->fp, "From %s %s\n", p ? p->mailbox : NONULL(Username), buf);
1088  }
1089  }
1090  else
1091  FREE(&msg);
1092 
1093  return msg;
1094 }
1095 
1104 int mx_mbox_check(struct Mailbox *m, int *index_hint)
1105 {
1106  if (!m || !m->mx_ops)
1107  return -1;
1108 
1109  int rc = m->mx_ops->mbox_check(m, index_hint);
1110  if ((rc == MUTT_NEW_MAIL) || (rc == MUTT_REOPENED))
1112 
1113  return rc;
1114 }
1115 
1123 struct Message *mx_msg_open(struct Mailbox *m, int msgno)
1124 {
1125  if (!m)
1126  return NULL;
1127 
1128  struct Message *msg = NULL;
1129 
1130  if (!m->mx_ops || !m->mx_ops->msg_open)
1131  {
1132  mutt_debug(LL_DEBUG1, "function not implemented for mailbox type %d\n", m->type);
1133  return NULL;
1134  }
1135 
1136  msg = mutt_mem_calloc(1, sizeof(struct Message));
1137  if (m->mx_ops->msg_open(m, msg, msgno) < 0)
1138  FREE(&msg);
1139 
1140  return msg;
1141 }
1142 
1150 int mx_msg_commit(struct Mailbox *m, struct Message *msg)
1151 {
1152  if (!m || !m->mx_ops || !m->mx_ops->msg_commit)
1153  return -1;
1154 
1155  if (!(msg->write && m->append))
1156  {
1157  mutt_debug(LL_DEBUG1, "msg->write = %d, m->append = %d\n", msg->write, m->append);
1158  return -1;
1159  }
1160 
1161  return m->mx_ops->msg_commit(m, msg);
1162 }
1163 
1171 int mx_msg_close(struct Mailbox *m, struct Message **msg)
1172 {
1173  if (!m || !msg || !*msg)
1174  return 0;
1175 
1176  int rc = 0;
1177 
1178  if (m->mx_ops && m->mx_ops->msg_close)
1179  rc = m->mx_ops->msg_close(m, *msg);
1180 
1181  if ((*msg)->path)
1182  {
1183  mutt_debug(LL_DEBUG1, "unlinking %s\n", (*msg)->path);
1184  unlink((*msg)->path);
1185  FREE(&(*msg)->path);
1186  }
1187 
1188  FREE(&(*msg)->committed_path);
1189  FREE(msg);
1190  return rc;
1191 }
1192 
1197 void mx_alloc_memory(struct Mailbox *m)
1198 {
1199  size_t s = MAX(sizeof(struct Email *), sizeof(int));
1200 
1201  if ((m->email_max + 25) * s < m->email_max * s)
1202  {
1203  mutt_error(_("Out of memory"));
1204  mutt_exit(1);
1205  }
1206 
1207  m->email_max += 25;
1208  if (m->emails)
1209  {
1210  mutt_mem_realloc(&m->emails, sizeof(struct Email *) * m->email_max);
1211  mutt_mem_realloc(&m->v2r, sizeof(int) * m->email_max);
1212  }
1213  else
1214  {
1215  m->emails = mutt_mem_calloc(m->email_max, sizeof(struct Email *));
1216  m->v2r = mutt_mem_calloc(m->email_max, sizeof(int));
1217  }
1218  for (int i = m->email_max - 25; i < m->email_max; i++)
1219  {
1220  m->emails[i] = NULL;
1221  m->v2r[i] = -1;
1222  }
1223 }
1224 
1232 int mx_check_empty(const char *path)
1233 {
1234  switch (mx_path_probe(path))
1235  {
1236  case MUTT_MBOX:
1237  case MUTT_MMDF:
1238  return mutt_file_check_empty(path);
1239  case MUTT_MH:
1240  return mh_check_empty(path);
1241  case MUTT_MAILDIR:
1242  return maildir_check_empty(path);
1243 #ifdef USE_IMAP
1244  case MUTT_IMAP:
1245  {
1246  int rc = imap_path_status(path, false);
1247  if (rc < 0)
1248  return -1;
1249  if (rc == 0)
1250  return 1;
1251  return 0;
1252  }
1253 #endif
1254  default:
1255  errno = EINVAL;
1256  return -1;
1257  }
1258  /* not reached */
1259 }
1260 
1271 int mx_tags_edit(struct Mailbox *m, const char *tags, char *buf, size_t buflen)
1272 {
1273  if (!m)
1274  return -1;
1275 
1276  if (m->mx_ops->tags_edit)
1277  return m->mx_ops->tags_edit(m, tags, buf, buflen);
1278 
1279  mutt_message(_("Folder doesn't support tagging, aborting"));
1280  return -1;
1281 }
1282 
1291 int mx_tags_commit(struct Mailbox *m, struct Email *e, char *tags)
1292 {
1293  if (!m)
1294  return -1;
1295 
1296  if (m->mx_ops->tags_commit)
1297  return m->mx_ops->tags_commit(m, e, tags);
1298 
1299  mutt_message(_("Folder doesn't support tagging, aborting"));
1300  return -1;
1301 }
1302 
1309 {
1310  return m && m->mx_ops->tags_commit && m->mx_ops->tags_edit;
1311 }
1312 
1318 enum MailboxType mx_path_probe(const char *path)
1319 {
1320  if (!path)
1321  return MUTT_UNKNOWN;
1322 
1323  enum MailboxType rc;
1324 
1325  // First, search the non-local Mailbox types (is_local == false)
1326  for (const struct MxOps **ops = mx_ops; *ops; ops++)
1327  {
1328  if ((*ops)->is_local)
1329  continue;
1330  rc = (*ops)->path_probe(path, NULL);
1331  if (rc != MUTT_UNKNOWN)
1332  return rc;
1333  }
1334 
1335  struct stat st = { 0 };
1336  if (stat(path, &st) != 0)
1337  {
1338  mutt_debug(LL_DEBUG1, "unable to stat %s: %s (errno %d)\n", path, strerror(errno), errno);
1339  return MUTT_UNKNOWN;
1340  }
1341 
1342  // Next, search the local Mailbox types (is_local == true)
1343  for (const struct MxOps **ops = mx_ops; *ops; ops++)
1344  {
1345  if (!(*ops)->is_local)
1346  continue;
1347  rc = (*ops)->path_probe(path, &st);
1348  if (rc != MUTT_UNKNOWN)
1349  return rc;
1350  }
1351 
1352  return rc;
1353 }
1354 
1358 int mx_path_canon(char *buf, size_t buflen, const char *folder, enum MailboxType *type)
1359 {
1360  if (!buf)
1361  return -1;
1362 
1363  for (size_t i = 0; i < 3; i++)
1364  {
1365  /* Look for !! ! - < > or ^ followed by / or NUL */
1366  if ((buf[0] == '!') && (buf[1] == '!'))
1367  {
1368  if (((buf[2] == '/') || (buf[2] == '\0')))
1369  {
1370  mutt_str_inline_replace(buf, buflen, 2, LastFolder);
1371  }
1372  }
1373  else if ((buf[0] == '+') || (buf[0] == '='))
1374  {
1375  size_t folder_len = mutt_str_strlen(folder);
1376  if ((folder_len > 0) && (folder[folder_len - 1] != '/'))
1377  {
1378  buf[0] = '/';
1379  mutt_str_inline_replace(buf, buflen, 0, folder);
1380  }
1381  else
1382  {
1383  mutt_str_inline_replace(buf, buflen, 1, folder);
1384  }
1385  }
1386  else if ((buf[1] == '/') || (buf[1] == '\0'))
1387  {
1388  if (buf[0] == '!')
1389  {
1390  mutt_str_inline_replace(buf, buflen, 1, C_Spoolfile);
1391  }
1392  else if (buf[0] == '-')
1393  {
1394  mutt_str_inline_replace(buf, buflen, 1, LastFolder);
1395  }
1396  else if (buf[0] == '<')
1397  {
1398  mutt_str_inline_replace(buf, buflen, 1, C_Record);
1399  }
1400  else if (buf[0] == '>')
1401  {
1402  mutt_str_inline_replace(buf, buflen, 1, C_Mbox);
1403  }
1404  else if (buf[0] == '^')
1405  {
1406  mutt_str_inline_replace(buf, buflen, 1, CurrentFolder);
1407  }
1408  else if (buf[0] == '~')
1409  {
1410  mutt_str_inline_replace(buf, buflen, 1, HomeDir);
1411  }
1412  }
1413  else if (buf[0] == '@')
1414  {
1415  /* elm compatibility, @ expands alias to user name */
1416  struct AddressList *al = mutt_alias_lookup(buf + 1);
1417  if (!al || TAILQ_EMPTY(al))
1418  break;
1419 
1420  struct Email *e = email_new();
1421  e->env = mutt_env_new();
1422  mutt_addrlist_copy(&e->env->from, al, false);
1423  mutt_addrlist_copy(&e->env->to, al, false);
1424  mutt_default_save(buf, buflen, e);
1425  email_free(&e);
1426  break;
1427  }
1428  else
1429  {
1430  break;
1431  }
1432  }
1433 
1434  // if (!folder) //XXX - use inherited version, or pass NULL to backend?
1435  // return -1;
1436 
1437  enum MailboxType type2 = mx_path_probe(buf);
1438  if (type)
1439  *type = type2;
1440  const struct MxOps *ops = mx_get_ops(type2);
1441  if (!ops || !ops->path_canon)
1442  return -1;
1443 
1444  if (ops->path_canon(buf, buflen) < 0)
1445  {
1446  mutt_path_canon(buf, buflen, HomeDir, true);
1447  }
1448 
1449  return 0;
1450 }
1451 
1459 int mx_path_canon2(struct Mailbox *m, const char *folder)
1460 {
1461  if (!m)
1462  return -1;
1463 
1464  char buf[PATH_MAX];
1465 
1466  if (m->realpath)
1467  mutt_str_strfcpy(buf, m->realpath, sizeof(buf));
1468  else
1469  mutt_str_strfcpy(buf, mailbox_path(m), sizeof(buf));
1470 
1471  int rc = mx_path_canon(buf, sizeof(buf), folder, &m->type);
1472 
1473  mutt_str_replace(&m->realpath, buf);
1474 
1475  if (rc >= 0)
1476  {
1477  m->mx_ops = mx_get_ops(m->type);
1479  }
1480 
1481  return rc;
1482 }
1483 
1487 int mx_path_pretty(char *buf, size_t buflen, const char *folder)
1488 {
1489  enum MailboxType type = mx_path_probe(buf);
1490  const struct MxOps *ops = mx_get_ops(type);
1491  if (!ops)
1492  return -1;
1493 
1494  if (!ops->path_canon)
1495  return -1;
1496 
1497  if (ops->path_canon(buf, buflen) < 0)
1498  return -1;
1499 
1500  if (!ops->path_pretty)
1501  return -1;
1502 
1503  if (ops->path_pretty(buf, buflen, folder) < 0)
1504  return -1;
1505 
1506  return 0;
1507 }
1508 
1512 int mx_path_parent(char *buf, size_t buflen)
1513 {
1514  if (!buf)
1515  return -1;
1516 
1517  return 0;
1518 }
1519 
1529 {
1530  if (!m || !m->mx_ops || !m->mx_ops->msg_padding_size)
1531  return 0;
1532 
1533  return m->mx_ops->msg_padding_size(m);
1534 }
1535 
1542 struct Account *mx_ac_find(struct Mailbox *m)
1543 {
1544  if (!m || !m->mx_ops)
1545  return NULL;
1546 
1547  struct Account *np = NULL;
1548  TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
1549  {
1550  if (np->type != m->type)
1551  continue;
1552 
1553  if (m->mx_ops->ac_find(np, m->realpath))
1554  return np;
1555  }
1556 
1557  return NULL;
1558 }
1559 
1566 struct Mailbox *mx_mbox_find(struct Account *a, const char *path)
1567 {
1568  if (!a || !path)
1569  return NULL;
1570 
1571  struct MailboxNode *np = NULL;
1572  struct Url *url_p = NULL;
1573  struct Url *url_a = NULL;
1574 
1575  const bool use_url = (a->type == MUTT_IMAP);
1576  if (use_url)
1577  {
1578  url_p = url_parse(path);
1579  if (!url_p)
1580  goto done;
1581  }
1582 
1583  STAILQ_FOREACH(np, &a->mailboxes, entries)
1584  {
1585  if (!use_url)
1586  {
1587  if (mutt_str_strcmp(np->mailbox->realpath, path) == 0)
1588  return np->mailbox;
1589  continue;
1590  }
1591 
1592  url_free(&url_a);
1593  url_a = url_parse(np->mailbox->realpath);
1594  if (!url_a)
1595  continue;
1596 
1597  if (mutt_str_strcasecmp(url_a->host, url_p->host) != 0)
1598  continue;
1599  if (url_p->user && (mutt_str_strcasecmp(url_a->user, url_p->user) != 0))
1600  continue;
1601  if (a->type == MUTT_IMAP)
1602  {
1603  if (imap_mxcmp(url_a->path, url_p->path) == 0)
1604  break;
1605  }
1606  else
1607  {
1608  if (mutt_str_strcmp(url_a->path, url_p->path) == 0)
1609  break;
1610  }
1611  }
1612 
1613 done:
1614  url_free(&url_p);
1615  url_free(&url_a);
1616 
1617  if (!np)
1618  return NULL;
1619  return np->mailbox;
1620 }
1621 
1628 struct Mailbox *mx_mbox_find2(const char *path)
1629 {
1630  if (!path)
1631  return NULL;
1632 
1633  char buf[PATH_MAX];
1634  mutt_str_strfcpy(buf, path, sizeof(buf));
1635  mx_path_canon(buf, sizeof(buf), C_Folder, NULL);
1636 
1637  struct Account *np = NULL;
1638  TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
1639  {
1640  struct Mailbox *m = mx_mbox_find(np, buf);
1641  if (m)
1642  return m;
1643  }
1644 
1645  return NULL;
1646 }
1647 
1655 struct Mailbox *mx_path_resolve(const char *path)
1656 {
1657  if (!path)
1658  return NULL;
1659 
1660  struct Mailbox *m = mx_mbox_find2(path);
1661  if (m)
1662  return m;
1663 
1664  m = mailbox_new();
1665  m->flags = MB_HIDDEN;
1666  mutt_buffer_strcpy(&m->pathbuf, path);
1668 
1669  return m;
1670 }
1671 
1675 int mx_ac_add(struct Account *a, struct Mailbox *m)
1676 {
1677  if (!a || !m || !m->mx_ops || !m->mx_ops->ac_add)
1678  return -1;
1679 
1680  if (m->mx_ops->ac_add(a, m) < 0)
1681  return -1;
1682 
1683  account_mailbox_add(a, m);
1684  return 0;
1685 }
1686 
1691 int mx_ac_remove(struct Mailbox *m)
1692 {
1693  if (!m || !m->account)
1694  return -1;
1695 
1696  struct Account *a = m->account;
1698  mailbox_free(&m);
1699  if (STAILQ_EMPTY(&a->mailboxes))
1700  {
1702  }
1703  return 0;
1704 }
1705 
1709 int mx_mbox_check_stats(struct Mailbox *m, int flags)
1710 {
1711  if (!m)
1712  return -1;
1713 
1714  return m->mx_ops->mbox_check_stats(m, flags);
1715 }
1716 
1726 int mx_save_hcache(struct Mailbox *m, struct Email *e)
1727 {
1728  if (!m->mx_ops || !m->mx_ops->msg_save_hcache)
1729  return 0;
1730 
1731  return m->mx_ops->msg_save_hcache(m, e);
1732 }
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mx.h:54
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:411
WHERE char * Username
User&#39;s login name.
Definition: globals.h:52
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:192
#define MUTT_APPENDNEW
Set in mx_open_mailbox_append if the mailbox doesn&#39;t exist.
Definition: mx.h:61
An enumeration.
Definition: enum.h:31
int ctx_mailbox_observer(struct NotifyCallback *nc)
Watch for changes affecting the Context - Implements observer_t.
Definition: context.c:294
int(* msg_commit)(struct Mailbox *m, struct Message *msg)
Save changes to an email.
Definition: mx.h:200
const struct MxOps * mx_get_ops(enum MailboxType type)
Get mailbox operations.
Definition: mx.c:142
Manage keymappings.
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
#define MUTT_ACL_ALL
Definition: mailbox.h:76
#define NONULL(x)
Definition: string2.h:37
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
int msg_count
Total number of messages.
Definition: mailbox.h:91
struct AddressList * mutt_alias_lookup(const char *s)
Find an Alias.
Definition: alias.c:285
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:70
off_t size
Size of the Mailbox.
Definition: mailbox.h:87
Representation of the email&#39;s header.
IMAP network mailbox.
WHERE bool C_MailCheckRecent
Config: Notify the user about new mail since the last time the mailbox was opened.
Definition: globals.h:236
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
#define TAILQ_FIRST(head)
Definition: queue.h:716
int imap_path_status(const char *path, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1319
#define mutt_perror(...)
Definition: logging.h:85
Clear the &#39;last-tagged&#39; pointer.
Definition: mailbox.h:174
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe()
Definition: imap.c:2516
Config/command parsing.
int mx_mbox_check(struct Mailbox *m, int *index_hint)
Check for new mail - Wrapper for MxOps::mbox_check()
Definition: mx.c:1104
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:718
Update internal tables.
Definition: mailbox.h:173
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:96
int msg_unread
Number of unread messages.
Definition: mailbox.h:92
int mx_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Wrapper for MxOps::ac_add()
Definition: mx.c:1675
int mx_path_canon2(struct Mailbox *m, const char *folder)
Canonicalise the path to realpath.
Definition: mx.c:1459
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:52
int mx_mbox_close(struct Context **ptr)
Save changes and close mailbox.
Definition: mx.c:593
Structs that make up an email.
void mbox_reset_atime(struct Mailbox *m, struct stat *st)
Reset the access time on the mailbox file.
Definition: mbox.c:843
struct MxOps MxNotmuchOps
Notmuch Mailbox - Implements MxOps.
The "currently-open" mailbox.
struct MxOps MxMhOps
MH Mailbox - Implements MxOps.
Definition: mh.c:814
int(* mbox_check_stats)(struct Mailbox *m, int flags)
Check the Mailbox statistics.
Definition: mx.h:159
struct tm mutt_date_localtime(time_t t)
Converts calendar time to a broken-down time structure expressed in user timezone.
Definition: date.c:737
int(* mbox_sync)(struct Mailbox *m, int *index_hint)
Save changes to the Mailbox.
Definition: mx.h:167
bool replied
Definition: mx.h:93
int mx_path_pretty(char *buf, size_t buflen, const char *folder)
Abbreviate a mailbox path - Wrapper for MxOps::path_pretty()
Definition: mx.c:1487
struct MxOps MxCompOps
Compressed Mailbox - Implements MxOps.
Definition: compress.c:936
int mx_path_parent(char *buf, size_t buflen)
Find the parent of a mailbox path - Wrapper for MxOps::path_parent()
Definition: mx.c:1512
User aborted the question (with Ctrl-G)
Definition: quad.h:38
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: init.c:1099
#define mutt_message(...)
Definition: logging.h:83
WHERE bool C_Confirmappend
Config: Confirm before appending emails to a mailbox.
Definition: globals.h:209
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:93
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:66
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:39
int emaillist_add_email(struct EmailList *el, struct Email *e)
Add an Email to a list.
Definition: email.c:144
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:40
static int trash_append(struct Mailbox *m)
move deleted mails to the trash folder
Definition: mx.c:484
bool peekonly
Just taking a glance, revert atime.
Definition: mailbox.h:117
struct NntpAccountData * adata
Definition: lib.h:155
unsigned char C_Move
Config: Move emails from C_Spoolfile to C_Mbox when read.
Definition: mx.c:86
char * realpath
Used for duplicate detection, context comparison, and the sidebar.
Definition: mailbox.h:84
NeoMutt Logging.
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:728
struct MailboxList mailboxes
List of Mailboxes.
Definition: account.h:41
A group of associated Mailboxes.
Definition: account.h:36
WHERE unsigned char C_Delete
Config: Really delete messages, when the mailbox is closed.
Definition: globals.h:181
struct MxOps MxMaildirOps
Maildir Mailbox - Implements MxOps.
Definition: maildir.c:707
String manipulation buffer.
Definition: buffer.h:33
WHERE bool C_SaveEmpty
Config: (mbox,mmdf) Preserve empty mailboxes.
Definition: globals.h:246
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1197
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:47
Error occurred examining Mailbox.
Definition: mailbox.h:46
An email address.
Definition: address.h:34
struct MxOps MxNntpOps
NNTP Mailbox - Implements MxOps.
Definition: nntp.c:2883
struct Mailbox * mx_mbox_find(struct Account *a, const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1566
struct Context * mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:255
WHERE char * LastFolder
Previously selected mailbox.
Definition: globals.h:55
char * mailbox
Mailbox and host address.
Definition: address.h:37
Messages to be purged (bypass trash)
Definition: mutt.h:98
int(* ac_add)(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account.
Definition: mx.h:126
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:689
Index panel (list of emails)
Definition: keymap.h:77
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition: mx.h:51
WHERE char * C_Record
Config: Folder to save &#39;sent&#39; messages.
Definition: globals.h:132
Email list was changed.
Definition: mailbox.h:171
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:121
#define MUTT_READONLY
Open in read-only mode.
Definition: mx.h:55
enum MailboxType type
Type of Mailboxes this Account contains.
Definition: account.h:38
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
int mx_save_hcache(struct Mailbox *m, struct Email *e)
Save message to the header cache - Wrapper for MxOps::msg_save_hcache()
Definition: mx.c:1726
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1628
WHERE bool OptNeedRescore
(pseudo) set when the &#39;score&#39; command is used
Definition: options.h:43
struct Account * mx_ac_find(struct Mailbox *m)
Find the Account owning a Mailbox.
Definition: mx.c:1542
void mutt_default_save(char *path, size_t pathlen, struct Email *e)
Find the default save path for an email.
Definition: hook.c:662
All user-callable functions.
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:34
Container for Accounts, Notifications.
Definition: neomutt.h:35
Representation of a single alias to an email address.
struct Context * ctx_new(void)
Create a new Context.
Definition: context.c:71
int mutt_save_confirm(const char *s, struct stat *st)
Ask the user to save.
Definition: muttlib.c:1438
int vcount
The number of virtual messages.
Definition: mailbox.h:102
#define MUTT_SET_DRAFT
set the message draft flag
Definition: mx.h:67
Hundreds of global variables to back the user variables.
int imap_sync_mailbox(struct Mailbox *m, bool expunge, bool close)
Sync all the changes to the server.
Definition: imap.c:1647
char * HomeDir
User&#39;s home directory.
Definition: globals.h:49
Email Address Handling.
int(* msg_open_new)(struct Mailbox *m, struct Message *msg, struct Email *e)
Open a new message in a Mailbox.
Definition: mx.h:192
Assorted sorting methods.
int(* mbox_open)(struct Mailbox *m)
Open a Mailbox.
Definition: mx.h:134
int(* mbox_open_append)(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending.
Definition: mx.h:142
Some miscellaneous functions.
#define MAX(a, b)
Definition: memory.h:30
bool has_new
Mailbox has new mail.
Definition: mailbox.h:88
char * C_Trash
Config: Folder to put deleted emails.
Definition: mx.c:87
int(* path_canon)(char *buf, size_t buflen)
Canonicalise a Mailbox path.
Definition: mx.h:257
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1171
bool tagged
Email is tagged.
Definition: email.h:44
bool read
Email is read.
Definition: email.h:51
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1535
int mx_check_empty(const char *path)
Is the mailbox empty.
Definition: mx.c:1232
struct Mailbox * mailbox
Definition: context.h:51
Parse and execute user-defined hooks.
Many unsorted constants and some structs.
Log at debug level 2.
Definition: logging.h:41
API for mailboxes.
bool old
Email is seen, but unread.
Definition: email.h:50
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:60
#define MUTT_PEEK
Revert atime back after taking a look (if applicable)
Definition: mx.h:60
void mutt_file_unlink_empty(const char *path)
Delete a file if it&#39;s empty.
Definition: file.c:1305
struct AddressList from
Email&#39;s &#39;From&#39; list.
Definition: envelope.h:57
bool readonly
Don&#39;t allow changes to the mailbox.
Definition: mailbox.h:119
The Context has been opened.
Definition: context.h:68
WHERE char * C_Mbox
Config: Folder that receives read emails (see Move)
Definition: globals.h:119
struct Envelope * env
Envelope information.
Definition: email.h:89
Convenience wrapper for the core headers.
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:1291
struct Notify * notify
Notifications handler.
Definition: context.h:52
void mx_fastclose_mailbox(struct Mailbox *m)
free up memory associated with the Mailbox
Definition: mx.c:414
struct MxOps MxImapOps
IMAP Mailbox - Implements MxOps.
Definition: imap.c:2596
char * group
Definition: lib.h:142
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: globals.h:54
struct MxOps MxPopOps
POP Mailbox - Implements MxOps.
Definition: pop.c:1278
int opened
Number of times mailbox is opened.
Definition: mailbox.h:132
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:113
struct Message * mx_msg_open_new(struct Mailbox *m, struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1036
Compressed mbox local mailbox type.
int(* msg_padding_size)(struct Mailbox *m)
Bytes of padding between messages.
Definition: mx.h:214
void * mdata
Driver specific data.
Definition: mailbox.h:136
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition: envelope.c:42
struct Account * account_new(const char *name, struct ConfigSubset *sub)
Create a new Account.
Definition: account.c:43
#define MUTT_MBOX_HOOK
mbox-hook: move messages after reading them
Definition: hook.h:46
&#39;Maildir&#39; Mailbox type
Definition: mailbox.h:51
Usenet network mailbox type; talk to an NNTP server.
int flags
e.g. MB_NORMAL
Definition: mailbox.h:134
enum MailboxType type
Mailbox type, e.g. MUTT_IMAP.
Definition: mx.h:107
int imap_mxcmp(const char *mx1, const char *mx2)
Compare mailbox names, giving priority to INBOX.
Definition: util.c:695
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
int msg_not_read_yet
Which msg "new" in pager, -1 if none.
Definition: context.h:45
struct Keymap * km_find_func(enum MenuType menu, int func)
Find a function&#39;s mapping in a Menu.
Definition: keymap.c:898
Old messages.
Definition: mutt.h:92
Email list needs resorting.
Definition: mailbox.h:172
#define mutt_b2s(buf)
Definition: buffer.h:41
unsigned char C_CatchupNewsgroup
Config: (nntp) Mark all articles as read when leaving a newsgroup.
Definition: mx.c:83
WHERE bool OptForceRefresh
(pseudo) refresh even during macros
Definition: options.h:37
Prototypes for many functions.
#define MB_HIDDEN
Definition: mailbox.h:38
Notmuch virtual mailbox type.
void mutt_sort_headers(struct Context *ctx, bool init)
Sort emails by their headers.
Definition: sort.c:364
int(* tags_edit)(struct Mailbox *m, const char *tags, char *buf, size_t buflen)
Prompt and validate new messages tags.
Definition: mx.h:233
bool flagged
Definition: mx.h:92
A local copy of an email.
Definition: mx.h:83
int email_max
Number of pointers in emails.
Definition: mailbox.h:100
WHERE char * C_Folder
Config: Base folder for a set of mailboxes.
Definition: globals.h:121
int imap_copy_messages(struct Mailbox *m, struct EmailList *el, const char *dest, bool delete_original)
Server COPY messages to another folder.
Definition: message.c:1587
void ctx_update(struct Context *ctx)
Update the Context&#39;s message counts.
Definition: context.c:105
struct Message::@0 flags
int imap_access(const char *path)
Check permissions on an IMAP mailbox with a new connection.
Definition: imap.c:604
Messages to be deleted.
Definition: mutt.h:96
A mailbox.
Definition: mailbox.h:81
#define PATH_MAX
Definition: mutt.h:44
char * mutt_find_hook(HookFlags type, const char *pat)
Find a matching hook.
Definition: hook.c:558
char * user
Username.
Definition: url.h:69
#define MUTT_NOSORT
Do not sort the mailbox after opening it.
Definition: mx.h:53
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:39
#define CH_NO_FLAGS
No flags are set.
Definition: copy.h:50
WHERE bool C_MaildirTrash
Config: Use the maildir &#39;trashed&#39; flag, rather than deleting.
Definition: globals.h:237
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: keymap.c:870
struct MxOps MxMmdfOps
MMDF Mailbox - Implements MxOps.
Definition: mbox.c:1877
bool dontwrite
Don&#39;t write the mailbox on close.
Definition: mailbox.h:115
int(* msg_save_hcache)(struct Mailbox *m, struct Email *e)
Save message to the header cache.
Definition: mx.h:222
&#39;mmdf&#39; Mailbox type
Definition: mailbox.h:49
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:773
int(* msg_open)(struct Mailbox *m, struct Message *msg, int msgno)
Open an email message in a Mailbox.
Definition: mx.h:183
POP network mailbox.
int(* mbox_check)(struct Mailbox *m, int *index_hint)
Check for new mail.
Definition: mx.h:150
bool account_mailbox_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account.
Definition: account.c:67
Compressed file Mailbox type.
Definition: mailbox.h:56
bool append
Mailbox is opened in append mode.
Definition: mailbox.h:113
&#39;MH&#39; Mailbox type
Definition: mailbox.h:50
const struct MxOps * mx_ops
MXAPI callback functions.
Definition: mailbox.h:111
char * host
Host.
Definition: url.h:71
bool quiet
Inhibit status messages?
Definition: mailbox.h:118
#define CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition: copy.h:61
bool purge
Skip trash folder when deleting.
Definition: email.h:46
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:1271
unsigned char C_MboxType
Config: Default type for creating new mailboxes.
Definition: mx.c:85
int msg_tagged
How many messages are tagged?
Definition: mailbox.h:97
bool draft
Definition: mx.h:94
#define MUTT_QUIET
Do not print any messages.
Definition: mx.h:56
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:121
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
uint8_t MsgOpenFlags
Flags for mx_msg_open_new(), e.g. MUTT_ADD_FROM.
Definition: mx.h:64
NNTP-specific Mailbox data -.
Definition: lib.h:140
void mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:453
Maildir local mailbox type.
#define MMDF_SEP
Definition: lib.h:60
bool notify_observer_add(struct Notify *notify, observer_t callback, void *global_data)
Add an observer to an object.
Definition: notify.c:153
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
char * path
Path.
Definition: url.h:73
&#39;mbox&#39; Mailbox type
Definition: mailbox.h:48
bool write
nonzero if message is open for writing
Definition: mx.h:88
int maildir_check_empty(const char *path)
Is the mailbox empty.
Definition: shared.c:1562
void mutt_exit(int code)
Leave NeoMutt NOW.
Definition: main.c:137
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:101
void mutt_make_label_hash(struct Mailbox *m)
Create a Hash Table to store the labels.
Definition: mutt_header.c:359
struct Mailbox * mailbox_new(void)
Create a new Mailbox.
Definition: mailbox.c:42
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1318
bool C_MarkOld
Config: Mark new emails as old when leaving the mailbox.
Definition: email_globals.c:36
bool mutt_path_canon(char *buf, size_t buflen, const char *homedir, bool is_dir)
Create the canonical version of a path.
Definition: path.c:285
Context has changed.
Definition: notify_type.h:35
Duplicate the structure of an entire email.
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:328
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
struct AddressList return_path
Return path for the Email.
Definition: envelope.h:56
struct Account * account
Account that owns this Mailbox.
Definition: mailbox.h:131
Log at debug level 1.
Definition: logging.h:40
static int sync_mailbox(struct Mailbox *m, int *index_hint)
save changes to disk
Definition: mx.c:453
WHERE char * C_Spoolfile
Config: Inbox.
Definition: globals.h:145
bool flagged
Marked important?
Definition: email.h:43
void emaillist_clear(struct EmailList *el)
Drop a private list of Emails.
Definition: email.c:123
int mx_mbox_sync(struct Mailbox *m, int *index_hint)
Save changes to mailbox.
Definition: mx.c:901
int(* mbox_close)(struct Mailbox *m)
Close a Mailbox.
Definition: mx.h:174
static int mx_open_mailbox_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox for appending.
Definition: mx.c:188
int msg_new
Number of new messages.
Definition: mailbox.h:95
struct Hash * subj_hash
Hash table by subject.
Definition: mailbox.h:128
int mx_mbox_check_stats(struct Mailbox *m, int flags)
Check the statistics for a mailbox - Wrapper for MxOps::mbox_check_stats()
Definition: mx.c:1709
bool collapsed
Are all threads collapsed?
Definition: context.h:49
bool deleted
Email is deleted.
Definition: email.h:45
int(* path_pretty)(char *buf, size_t buflen, const char *folder)
Abbreviate a Mailbox path.
Definition: mx.h:266
bool account_mailbox_remove(struct Account *a, struct Mailbox *m)
Remove a Mailbox from an Account.
Definition: account.c:95
#define mutt_error(...)
Definition: logging.h:84
bool replied
Email has been replied to.
Definition: email.h:54
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition: mx.c:1150
int mutt_str_strcasecmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:651
int mutt_file_check_empty(const char *path)
Is the mailbox empty.
Definition: file.c:1406
int mutt_append_message(struct Mailbox *dest, struct Mailbox *src, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags)
Append a message.
Definition: copy.c:882
FILE * fp
pointer to the message data
Definition: mx.h:85
#define FREE(x)
Definition: memory.h:40
int(* msg_close)(struct Mailbox *m, struct Message *msg)
Close an email.
Definition: mx.h:208
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1655
Mapping between user-readable string and a constant.
Definition: mapping.h:29
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition: mx.c:171
#define STAILQ_EMPTY(head)
Definition: queue.h:345
time_t received
the time at which this message was received
Definition: mx.h:96
struct MxOps MxMboxOps
Mbox Mailbox - Implements MxOps.
Definition: mbox.c:1848
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:321
bool neomutt_account_remove(struct NeoMutt *n, struct Account *a)
Remove an Account from the global list.
Definition: neomutt.c:105
struct AddressList to
Email&#39;s &#39;To&#39; list.
Definition: envelope.h:58
struct AddressList sender
Email&#39;s sender.
Definition: envelope.h:61
WHERE bool OptSortSubthreads
(pseudo) used when $sort_aux changes
Definition: options.h:55
struct Email * email_new(void)
Create a new Email.
Definition: email.c:68
List of Mailboxes.
Definition: mailbox.h:145
bool changed
Mailbox has been modified.
Definition: mailbox.h:114
void ctx_free(struct Context **ptr)
Free a Context.
Definition: context.c:48
Handling of global boolean variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
void mutt_mailbox_set_notified(struct Mailbox *m)
Note when the user was last notified of new mail.
Definition: mutt_mailbox.c:297
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:38
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size()
Definition: mx.c:1528
New mail received in Mailbox.
Definition: mx.h:74
bool mutt_str_inline_replace(char *buf, size_t buflen, size_t xlen, const char *rstr)
Replace the beginning of a string.
Definition: string.c:1093
bool mx_tags_is_supported(struct Mailbox *m)
return true if mailbox support tagging
Definition: mx.c:1308
struct Notify * notify
Notifications handler.
Definition: mailbox.h:139
bool C_KeepFlagged
Config: Don&#39;t move flagged messages from C_Spoolfile to C_Mbox.
Definition: mx.c:84
An Event that happened to an Context.
Definition: context.h:58
struct Account *(* ac_find)(struct Account *a, const char *path)
Find an Account that matches a Mailbox path.
Definition: mx.h:118
#define TAILQ_EMPTY(head)
Definition: queue.h:714
#define MUTT_NEWFOLDER
Create a new folder - same as MUTT_APPEND,.
Definition: mx.h:57
bool neomutt_account_add(struct NeoMutt *n, struct Account *a)
Add an Account to the global list.
Definition: neomutt.c:84
int mx_ac_remove(struct Mailbox *m)
Remove a Mailbox from an Account and delete Account if empty.
Definition: mx.c:1691
struct NntpMboxData * mutt_newsgroup_catchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Catchup newsgroup.
Definition: newsrc.c:1299
struct Buffer pathbuf
Definition: mailbox.h:83
Convenience wrapper for the library headers.
void mutt_hash_free(struct Hash **ptr)
Free a hash table.
Definition: hash.c:471
bool read
Definition: mx.h:91
#define MUTT_ADD_FROM
add a From_ line
Definition: mx.h:66
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:41
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
Mailbox was reopened.
Definition: mx.h:76
&#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:1358
int imap_fast_trash(struct Mailbox *m, char *dest)
Use server COPY command to copy deleted messages to trash.
Definition: imap.c:1535
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:169
struct Hash * label_hash
Hash table for x-labels.
Definition: mailbox.h:129
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:638
static bool mutt_is_spool(const char *str)
Is this the spoolfile?
Definition: mx.c:156
struct Message * mx_msg_open(struct Mailbox *m, int msgno)
return a stream pointer for a message
Definition: mx.c:1123
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
struct Hash * id_hash
Hash table by msg id.
Definition: mailbox.h:127
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:82
char * path
path to temp file
Definition: mx.h:86
bool mutt_comp_can_append(struct Mailbox *m)
Can we append to this path?
Definition: compress.c:339
The Mailbox API.
Definition: mx.h:105
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:147
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:232
int(* tags_commit)(struct Mailbox *m, struct Email *e, char *buf)
Save the tags to a message.
Definition: mx.h:242
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition: notify.c:137
int mh_check_empty(const char *path)
Is mailbox empty.
Definition: shared.c:1602