NeoMutt  2019-12-07-60-g0cfa53
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 <stdbool.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include "mutt/mutt.h"
40 #include "address/lib.h"
41 #include "email/lib.h"
42 #include "core/lib.h"
43 #include "mutt.h"
44 #include "mx.h"
45 #include "alias.h"
46 #include "context.h"
47 #include "copy.h"
48 #include "globals.h"
49 #include "hook.h"
50 #include "keymap.h"
51 #include "maildir/lib.h"
52 #include "mbox/mbox.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 #ifdef USE_COMPRESSED
62 #include "compress.h"
63 #endif
64 #ifdef USE_IMAP
65 #include "imap/imap.h"
66 #endif
67 #ifdef USE_POP
68 #include "pop/pop.h"
69 #endif
70 #ifdef USE_NNTP
71 #include "nntp/nntp.h"
72 #endif
73 #ifdef USE_NOTMUCH
74 #include "notmuch/mutt_notmuch.h"
75 #endif
76 #ifdef ENABLE_NLS
77 #include <libintl.h>
78 #endif
79 
80 /* These Config Variables are only used in mx.c */
81 unsigned char C_CatchupNewsgroup;
83 unsigned char C_MboxType;
84 unsigned char C_Move;
85 char *C_Trash;
86 
87 // clang-format off
88 static struct Mapping MagicMap[] = {
89  { "mbox", MUTT_MBOX, },
90  { "MMDF", MUTT_MMDF, },
91  { "MH", MUTT_MH, },
92  { "Maildir", MUTT_MAILDIR, },
93  { NULL, 0, },
94 };
95 // clang-format on
96 
97 struct EnumDef MagicDef = {
98  "mbox_type",
99  4,
100  (struct Mapping *) &MagicMap,
101 };
102 
106 static const struct MxOps *mx_ops[] = {
107 /* These mailboxes can be recognised by their Url scheme */
108 #ifdef USE_IMAP
109  &MxImapOps,
110 #endif
111 #ifdef USE_NOTMUCH
112  &MxNotmuchOps,
113 #endif
114 #ifdef USE_POP
115  &MxPopOps,
116 #endif
117 #ifdef USE_NNTP
118  &MxNntpOps,
119 #endif
120 
121  /* Local mailboxes */
122  &MxMaildirOps,
123  &MxMboxOps,
124  &MxMhOps,
125  &MxMmdfOps,
126 
127 /* If everything else fails... */
128 #ifdef USE_COMPRESSED
129  &MxCompOps,
130 #endif
131  NULL,
132 };
133 
140 const struct MxOps *mx_get_ops(enum MailboxType magic)
141 {
142  for (const struct MxOps **ops = mx_ops; *ops; ops++)
143  if ((*ops)->magic == magic)
144  return *ops;
145 
146  return NULL;
147 }
148 
154 static bool mutt_is_spool(const char *str)
155 {
156  return mutt_str_strcmp(C_Spoolfile, str) == 0;
157 }
158 
169 int mx_access(const char *path, int flags)
170 {
171 #ifdef USE_IMAP
172  if (imap_path_probe(path, NULL) == MUTT_IMAP)
173  return imap_access(path);
174 #endif
175 
176  return access(path, flags);
177 }
178 
186 static int mx_open_mailbox_append(struct Mailbox *m, OpenMailboxFlags flags)
187 {
188  if (!m)
189  return -1;
190 
191  struct stat sb;
192 
193  m->append = true;
194  if ((m->magic == MUTT_UNKNOWN) || (m->magic == MUTT_MAILBOX_ERROR))
195  {
196  m->magic = mx_path_probe(mailbox_path(m), NULL);
197 
198  if (m->magic == MUTT_UNKNOWN)
199  {
200  if (flags & (MUTT_APPEND | MUTT_NEWFOLDER))
201  {
203  }
204  else
205  {
206  mutt_error(_("%s is not a mailbox"), mailbox_path(m));
207  return -1;
208  }
209  }
210 
211  if (m->magic == MUTT_MAILBOX_ERROR)
212  {
213  if (stat(mailbox_path(m), &sb) == -1)
214  {
215  if (errno == ENOENT)
216  {
217 #ifdef USE_COMPRESSED
218  if (mutt_comp_can_append(m))
219  m->magic = MUTT_COMPRESSED;
220  else
221 #endif
222  m->magic = C_MboxType;
223  flags |= MUTT_APPENDNEW;
224  }
225  else
226  {
228  return -1;
229  }
230  }
231  else
232  return -1;
233  }
234 
235  m->mx_ops = mx_get_ops(m->magic);
236  }
237 
238  if (!m->mx_ops || !m->mx_ops->mbox_open_append)
239  return -1;
240 
241  int rc = m->mx_ops->mbox_open_append(m, flags);
242  m->opened++;
243  return rc;
244 }
245 
253 struct Context *mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
254 {
255  if (!m)
256  return NULL;
257 
258  struct Context *ctx = ctx_new();
259  ctx->mailbox = m;
260 
261  struct EventContext ev_ctx = { ctx };
262  notify_send(ctx->notify, NT_CONTEXT, NT_CONTEXT_OPEN, &ev_ctx);
263 
264  // If the Mailbox is closed, Context->mailbox must be set to NULL
266 
267  if ((m->magic == MUTT_UNKNOWN) && (flags & (MUTT_NEWFOLDER | MUTT_APPEND)))
268  {
269  m->magic = C_MboxType;
270  m->mx_ops = mx_get_ops(m->magic);
271  }
272 
273  const bool newly_linked_account = !m->account;
274  if (newly_linked_account)
275  {
276  struct Account *a = mx_ac_find(m);
277  bool new_account = false;
278  if (!a)
279  {
280  a = account_new(NULL, NeoMutt->sub);
281  a->magic = m->magic;
282  new_account = true;
283  }
284  if (mx_ac_add(a, m) < 0)
285  {
286  ctx_free(&ctx);
287  if (new_account)
288  {
289  FREE(&a);
290  }
291  return NULL;
292  }
293  if (new_account)
294  {
296  }
297  }
298 
299  ctx->msg_not_read_yet = -1;
300  ctx->collapsed = false;
301 
302  m->quiet = (flags & MUTT_QUIET);
303  if (flags & MUTT_READONLY)
304  m->readonly = true;
305  m->peekonly = (flags & MUTT_PEEK);
306 
307  if (flags & (MUTT_APPEND | MUTT_NEWFOLDER))
308  {
309  if (mx_open_mailbox_append(ctx->mailbox, flags) != 0)
310  {
311  goto error;
312  }
313  return ctx;
314  }
315 
316  if (m->opened > 0)
317  {
318  m->opened++;
319  return ctx;
320  }
321 
322  m->size = 0;
323  m->msg_unread = 0;
324  m->msg_flagged = 0;
325  m->rights = MUTT_ACL_ALL;
326 
327  if (m->magic == MUTT_UNKNOWN)
328  {
329  m->magic = mx_path_probe(mailbox_path(m), NULL);
330  m->mx_ops = mx_get_ops(m->magic);
331  }
332 
333  if ((m->magic == MUTT_UNKNOWN) || (m->magic == MUTT_MAILBOX_ERROR) || !m->mx_ops)
334  {
335  if (m->magic == MUTT_MAILBOX_ERROR)
337  else if ((m->magic == MUTT_UNKNOWN) || !m->mx_ops)
338  mutt_error(_("%s is not a mailbox"), mailbox_path(m));
339  goto error;
340  }
341 
343 
344  /* if the user has a 'push' command in their .neomuttrc, or in a folder-hook,
345  * it will cause the progress messages not to be displayed because
346  * mutt_refresh() will think we are in the middle of a macro. so set a
347  * flag to indicate that we should really refresh the screen. */
348  OptForceRefresh = true;
349 
350  if (!m->quiet)
351  mutt_message(_("Reading %s..."), mailbox_path(m));
352 
353  // Clear out any existing emails
354  for (int i = 0; i < m->email_max; i++)
355  {
356  email_free(&m->emails[i]);
357  }
358 
359  m->msg_count = 0;
360  m->msg_unread = 0;
361  m->msg_flagged = 0;
362  m->msg_new = 0;
363  m->msg_deleted = 0;
364  m->msg_tagged = 0;
365  m->vcount = 0;
366 
367  int rc = m->mx_ops->mbox_open(ctx->mailbox);
368  m->opened++;
369  if (rc == 0)
370  ctx_update(ctx);
371 
372  if ((rc == 0) || (rc == -2))
373  {
374  if ((flags & MUTT_NOSORT) == 0)
375  {
376  /* avoid unnecessary work since the mailbox is completely unthreaded
377  * to begin with */
378  OptSortSubthreads = false;
379  OptNeedRescore = false;
380  }
381  if (!m->quiet)
383  if (rc == -2)
384  {
385  mutt_error(_("Reading from %s interrupted..."), mailbox_path(m));
386  mutt_sort_headers(ctx, true);
387  }
388  }
389  else
390  {
391  goto error;
392  }
393 
394  if (!m->peekonly)
395  m->has_new = false;
396  OptForceRefresh = false;
397 
398  return ctx;
399 
400 error:
402  if (newly_linked_account)
404  ctx_free(&ctx);
405  return NULL;
406 }
407 
413 {
414  if (!m)
415  return;
416 
417  m->opened--;
418  if (m->opened != 0)
419  return;
420 
421  /* never announce that a mailbox we've just left has new mail.
422  * TODO: really belongs in mx_mbox_close, but this is a nice hook point */
423  if (!m->peekonly)
425 
426  if (m->mx_ops)
427  m->mx_ops->mbox_close(m);
428 
430  mutt_hash_free(&m->id_hash);
432 
433  if (m->emails)
434  {
435  for (int i = 0; i < m->msg_count; i++)
436  {
437  if (!m->emails[i])
438  break;
439  email_free(&m->emails[i]);
440  }
441  }
442 }
443 
451 static int sync_mailbox(struct Mailbox *m, int *index_hint)
452 {
453  if (!m || !m->mx_ops || !m->mx_ops->mbox_sync)
454  return -1;
455 
456  if (!m->quiet)
457  {
458  /* L10N: Displayed before/as a mailbox is being synced */
459  mutt_message(_("Writing %s..."), mailbox_path(m));
460  }
461 
462  int rc = m->mx_ops->mbox_sync(m, index_hint);
463  if ((rc != 0) && !m->quiet)
464  {
465  /* L10N: Displayed if a mailbox sync fails */
466  mutt_error(_("Unable to write %s"), mailbox_path(m));
467  }
468 
469  return rc;
470 }
471 
478 static int trash_append(struct Mailbox *m)
479 {
480  if (!m)
481  return -1;
482 
483  struct stat st, stc;
484  int opt_confappend, rc;
485 
486  if (!C_Trash || (m->msg_deleted == 0) || ((m->magic == MUTT_MAILDIR) && C_MaildirTrash))
487  {
488  return 0;
489  }
490 
491  int delmsgcount = 0;
492  int first_del = -1;
493  for (int i = 0; i < m->msg_count; i++)
494  {
495  struct Email *e = m->emails[i];
496  if (!e)
497  break;
498 
499  if (e->deleted && !e->purge)
500  {
501  if (first_del < 0)
502  first_del = i;
503  delmsgcount++;
504  }
505  }
506 
507  if (delmsgcount == 0)
508  return 0; /* nothing to be done */
509 
510  /* avoid the "append messages" prompt */
511  opt_confappend = C_Confirmappend;
512  if (opt_confappend)
513  C_Confirmappend = false;
514  rc = mutt_save_confirm(C_Trash, &st);
515  if (opt_confappend)
516  C_Confirmappend = true;
517  if (rc != 0)
518  {
519  /* L10N: Although we know the precise number of messages, we do not show it to the user.
520  So feel free to use a "generic plural" as plural translation if your language has one. */
521  mutt_error(ngettext("message not deleted", "messages not deleted", delmsgcount));
522  return -1;
523  }
524 
525  if ((lstat(mailbox_path(m), &stc) == 0) && (stc.st_ino == st.st_ino) &&
526  (stc.st_dev == st.st_dev) && (stc.st_rdev == st.st_rdev))
527  {
528  return 0; /* we are in the trash folder: simple sync */
529  }
530 
531 #ifdef USE_IMAP
532  if ((m->magic == MUTT_IMAP) && (imap_path_probe(C_Trash, NULL) == MUTT_IMAP))
533  {
534  if (imap_fast_trash(m, C_Trash) == 0)
535  return 0;
536  }
537 #endif
538 
539  struct Mailbox *m_trash = mx_path_resolve(C_Trash);
540  const bool old_append = m_trash->append;
541  struct Context *ctx_trash = mx_mbox_open(m_trash, MUTT_APPEND);
542  if (ctx_trash)
543  {
544  /* continue from initial scan above */
545  for (int i = first_del; i < m->msg_count; i++)
546  {
547  struct Email *e = m->emails[i];
548  if (!e)
549  break;
550 
551  if (e->deleted && !e->purge)
552  {
553  if (mutt_append_message(ctx_trash->mailbox, m, e, MUTT_CM_NO_FLAGS, CH_NO_FLAGS) == -1)
554  {
555  m_trash->append = old_append;
556  mx_mbox_close(&ctx_trash);
557  return -1;
558  }
559  }
560  }
561 
562  m_trash->append = old_append;
563  mx_mbox_close(&ctx_trash);
564  }
565  else
566  {
567  mutt_error(_("Can't open trash folder"));
568  mailbox_free(&m_trash);
569  return -1;
570  }
571 
572  return 0;
573 }
574 
583 int mx_mbox_close(struct Context **ptr)
584 {
585  if (!ptr || !*ptr)
586  return 0;
587 
588  struct Context *ctx = *ptr;
589  if (!ctx || !ctx->mailbox)
590  return -1;
591 
592  struct Mailbox *m = ctx->mailbox;
593 
594  if (C_MailCheckRecent && !m->peekonly)
595  m->has_new = false;
596 
597  if (m->readonly || m->dontwrite || m->append || m->peekonly)
598  {
600  ctx_free(ptr);
601  return 0;
602  }
603 
604  int i, read_msgs = 0;
605  int rc = -1;
606  enum QuadOption move_messages = MUTT_NO;
607  enum QuadOption purge = MUTT_YES;
608  struct Buffer *mbox = NULL;
609  struct Buffer *buf = mutt_buffer_pool_get();
610 
611 #ifdef USE_NNTP
612  if ((m->msg_unread != 0) && (m->magic == MUTT_NNTP))
613  {
614  struct NntpMboxData *mdata = m->mdata;
615 
616  if (mdata && mdata->adata && mdata->group)
617  {
618  enum QuadOption ans =
619  query_quadoption(C_CatchupNewsgroup, _("Mark all articles read?"));
620  if (ans == MUTT_ABORT)
621  goto cleanup;
622  if (ans == MUTT_YES)
623  mutt_newsgroup_catchup(m, mdata->adata, mdata->group);
624  }
625  }
626 #endif
627 
628  for (i = 0; i < m->msg_count; i++)
629  {
630  struct Email *e = m->emails[i];
631  if (!e)
632  break;
633 
634  if (!e->deleted && e->read && !(e->flagged && C_KeepFlagged))
635  read_msgs++;
636  }
637 
638 #ifdef USE_NNTP
639  /* don't need to move articles from newsgroup */
640  if (m->magic == MUTT_NNTP)
641  read_msgs = 0;
642 #endif
643 
644  if ((read_msgs != 0) && (C_Move != MUTT_NO))
645  {
646  bool is_spool;
647  mbox = mutt_buffer_pool_get();
648 
650  if (p)
651  {
652  is_spool = true;
653  mutt_buffer_strcpy(mbox, p);
654  }
655  else
656  {
657  mutt_buffer_strcpy(mbox, C_Mbox);
658  is_spool = mutt_is_spool(mailbox_path(m)) && !mutt_is_spool(mutt_b2s(mbox));
659  }
660 
661  if (is_spool && !mutt_buffer_is_empty(mbox))
662  {
664  mutt_buffer_printf(buf,
665  /* L10N: The first argument is the number of read messages to be
666  moved, the second argument is the target mailbox. */
667  ngettext("Move %d read message to %s?",
668  "Move %d read messages to %s?", read_msgs),
669  read_msgs, mutt_b2s(mbox));
670  move_messages = query_quadoption(C_Move, mutt_b2s(buf));
671  if (move_messages == MUTT_ABORT)
672  goto cleanup;
673  }
674  }
675 
676  /* There is no point in asking whether or not to purge if we are
677  * just marking messages as "trash". */
678  if ((m->msg_deleted != 0) && !((m->magic == MUTT_MAILDIR) && C_MaildirTrash))
679  {
680  mutt_buffer_printf(buf,
681  ngettext("Purge %d deleted message?",
682  "Purge %d deleted messages?", m->msg_deleted),
683  m->msg_deleted);
684  purge = query_quadoption(C_Delete, mutt_b2s(buf));
685  if (purge == MUTT_ABORT)
686  goto cleanup;
687  }
688 
689  if (C_MarkOld && !m->peekonly)
690  {
691  for (i = 0; i < m->msg_count; i++)
692  {
693  struct Email *e = m->emails[i];
694  if (!e)
695  break;
696  if (!e->deleted && !e->old && !e->read)
697  mutt_set_flag(m, e, MUTT_OLD, true);
698  }
699  }
700 
701  if (move_messages)
702  {
703  if (!m->quiet)
704  mutt_message(_("Moving read messages to %s..."), mutt_b2s(mbox));
705 
706 #ifdef USE_IMAP
707  /* try to use server-side copy first */
708  i = 1;
709 
710  if ((m->magic == MUTT_IMAP) && (imap_path_probe(mutt_b2s(mbox), NULL) == MUTT_IMAP))
711  {
712  /* tag messages for moving, and clear old tags, if any */
713  for (i = 0; i < m->msg_count; i++)
714  {
715  struct Email *e = m->emails[i];
716  if (!e)
717  break;
718 
719  if (e->read && !e->deleted && !(e->flagged && C_KeepFlagged))
720  e->tagged = true;
721  else
722  e->tagged = false;
723  }
724 
725  i = imap_copy_messages(ctx->mailbox, NULL, mutt_b2s(mbox), true);
726  }
727 
728  if (i == 0) /* success */
730  else if (i == -1) /* horrible error, bail */
731  goto cleanup;
732  else /* use regular append-copy mode */
733 #endif
734  {
735  struct Mailbox *m_read = mx_path_resolve(mutt_b2s(mbox));
736  struct Context *ctx_read = mx_mbox_open(m_read, MUTT_APPEND);
737  if (!ctx_read)
738  {
739  mailbox_free(&m_read);
740  goto cleanup;
741  }
742 
743  for (i = 0; i < m->msg_count; i++)
744  {
745  struct Email *e = m->emails[i];
746  if (!e)
747  break;
748  if (e->read && !e->deleted && !(e->flagged && C_KeepFlagged))
749  {
750  if (mutt_append_message(ctx_read->mailbox, ctx->mailbox, e,
752  {
753  mutt_set_flag(m, e, MUTT_DELETE, true);
754  mutt_set_flag(m, e, MUTT_PURGE, true);
755  }
756  else
757  {
758  mx_mbox_close(&ctx_read);
759  goto cleanup;
760  }
761  }
762  }
763 
764  mx_mbox_close(&ctx_read);
765  }
766  }
767  else if (!m->changed && (m->msg_deleted == 0))
768  {
769  if (!m->quiet)
770  mutt_message(_("Mailbox is unchanged"));
771  if ((m->magic == MUTT_MBOX) || (m->magic == MUTT_MMDF))
772  mbox_reset_atime(m, NULL);
774  ctx_free(ptr);
775  rc = 0;
776  goto cleanup;
777  }
778 
779  /* copy mails to the trash before expunging */
780  if (purge && (m->msg_deleted != 0) && (mutt_str_strcmp(mailbox_path(m), C_Trash) != 0))
781  {
782  if (trash_append(ctx->mailbox) != 0)
783  goto cleanup;
784  }
785 
786 #ifdef USE_IMAP
787  /* allow IMAP to preserve the deleted flag across sessions */
788  if (m->magic == MUTT_IMAP)
789  {
790  int check = imap_sync_mailbox(ctx->mailbox, (purge != MUTT_NO), true);
791  if (check != 0)
792  goto cleanup;
793  }
794  else
795 #endif
796  {
797  if (purge == MUTT_NO)
798  {
799  for (i = 0; i < m->msg_count; i++)
800  {
801  struct Email *e = m->emails[i];
802  if (!e)
803  break;
804 
805  e->deleted = false;
806  e->purge = false;
807  }
808  m->msg_deleted = 0;
809  }
810 
811  if (m->changed || (m->msg_deleted != 0))
812  {
813  int check = sync_mailbox(ctx->mailbox, NULL);
814  if (check != 0)
815  goto cleanup;
816  }
817  }
818 
819  if (!m->quiet)
820  {
821  if (move_messages)
822  {
823  mutt_message(_("%d kept, %d moved, %d deleted"),
824  m->msg_count - m->msg_deleted, read_msgs, m->msg_deleted);
825  }
826  else
827  mutt_message(_("%d kept, %d deleted"), m->msg_count - m->msg_deleted, m->msg_deleted);
828  }
829 
830  if ((m->msg_count == m->msg_deleted) &&
831  ((m->magic == MUTT_MMDF) || (m->magic == MUTT_MBOX)) &&
833  {
835  }
836 
837 #ifdef USE_SIDEBAR
838  if ((purge == MUTT_YES) && (m->msg_deleted != 0))
839  {
840  for (i = 0; i < m->msg_count; i++)
841  {
842  struct Email *e = m->emails[i];
843  if (!e)
844  break;
845  if (e->deleted && !e->read)
846  {
847  m->msg_unread--;
848  if (!e->old)
849  m->msg_new--;
850  }
851  if (e->deleted && e->flagged)
852  m->msg_flagged--;
853  }
854  }
855 #endif
856 
858  ctx_free(ptr);
859 
860  rc = 0;
861 
862 cleanup:
865  return rc;
866 }
867 
875 int mx_mbox_sync(struct Mailbox *m, int *index_hint)
876 {
877  if (!m)
878  return -1;
879 
880  int rc;
881  int purge = 1;
882  int msgcount, deleted;
883 
884  if (m->dontwrite)
885  {
886  char buf[256], tmp[256];
887  if (km_expand_key(buf, sizeof(buf), km_find_func(MENU_MAIN, OP_TOGGLE_WRITE)))
888  snprintf(tmp, sizeof(tmp), _(" Press '%s' to toggle write"), buf);
889  else
890  mutt_str_strfcpy(tmp, _("Use 'toggle-write' to re-enable write"), sizeof(tmp));
891 
892  mutt_error(_("Mailbox is marked unwritable. %s"), tmp);
893  return -1;
894  }
895  else if (m->readonly)
896  {
897  mutt_error(_("Mailbox is read-only"));
898  return -1;
899  }
900 
901  if (!m->changed && (m->msg_deleted == 0))
902  {
903  if (!m->quiet)
904  mutt_message(_("Mailbox is unchanged"));
905  return 0;
906  }
907 
908  if (m->msg_deleted != 0)
909  {
910  char buf[128];
911 
912  snprintf(buf, sizeof(buf),
913  ngettext("Purge %d deleted message?", "Purge %d deleted messages?", m->msg_deleted),
914  m->msg_deleted);
915  purge = query_quadoption(C_Delete, buf);
916  if (purge == MUTT_ABORT)
917  return -1;
918  if (purge == MUTT_NO)
919  {
920  if (!m->changed)
921  return 0; /* nothing to do! */
922  /* let IMAP servers hold on to D flags */
923  if (m->magic != MUTT_IMAP)
924  {
925  for (int i = 0; i < m->msg_count; i++)
926  {
927  struct Email *e = m->emails[i];
928  if (!e)
929  break;
930  e->deleted = false;
931  e->purge = false;
932  }
933  m->msg_deleted = 0;
934  }
935  }
937  }
938 
939  /* really only for IMAP - imap_sync_mailbox results in a call to
940  * ctx_update_tables, so m->msg_deleted is 0 when it comes back */
941  msgcount = m->msg_count;
942  deleted = m->msg_deleted;
943 
944  if (purge && (m->msg_deleted != 0) && (mutt_str_strcmp(mailbox_path(m), C_Trash) != 0))
945  {
946  if (trash_append(m) != 0)
947  return -1;
948  }
949 
950 #ifdef USE_IMAP
951  if (m->magic == MUTT_IMAP)
952  rc = imap_sync_mailbox(m, purge, false);
953  else
954 #endif
955  rc = sync_mailbox(m, index_hint);
956  if (rc == 0)
957  {
958 #ifdef USE_IMAP
959  if ((m->magic == MUTT_IMAP) && !purge)
960  {
961  if (!m->quiet)
962  mutt_message(_("Mailbox checkpointed"));
963  }
964  else
965 #endif
966  {
967  if (!m->quiet)
968  mutt_message(_("%d kept, %d deleted"), msgcount - deleted, deleted);
969  }
970 
971  mutt_sleep(0);
972 
973  if ((m->msg_count == m->msg_deleted) &&
974  ((m->magic == MUTT_MBOX) || (m->magic == MUTT_MMDF)) &&
976  {
977  unlink(mailbox_path(m));
979  return 0;
980  }
981 
982  /* if we haven't deleted any messages, we don't need to resort */
983  /* ... except for certain folder formats which need "unsorted"
984  * sort order in order to synchronize folders.
985  *
986  * MH and maildir are safe. mbox-style seems to need re-sorting,
987  * at least with the new threading code. */
988  if (purge || ((m->magic != MUTT_MAILDIR) && (m->magic != MUTT_MH)))
989  {
990  /* IMAP does this automatically after handling EXPUNGE */
991  if (m->magic != MUTT_IMAP)
992  {
995  }
996  }
997  }
998 
999  return rc;
1000 }
1001 
1009 struct Message *mx_msg_open_new(struct Mailbox *m, struct Email *e, MsgOpenFlags flags)
1010 {
1011  if (!m)
1012  return NULL;
1013 
1014  struct Address *p = NULL;
1015  struct Message *msg = NULL;
1016 
1017  if (!m->mx_ops || !m->mx_ops->msg_open_new)
1018  {
1019  mutt_debug(LL_DEBUG1, "function unimplemented for mailbox type %d\n", m->magic);
1020  return NULL;
1021  }
1022 
1023  msg = mutt_mem_calloc(1, sizeof(struct Message));
1024  msg->write = true;
1025 
1026  if (e)
1027  {
1028  msg->flags.flagged = e->flagged;
1029  msg->flags.replied = e->replied;
1030  msg->flags.read = e->read;
1031  msg->flags.draft = (flags & MUTT_SET_DRAFT);
1032  msg->received = e->received;
1033  }
1034 
1035  if (msg->received == 0)
1036  msg->received = mutt_date_epoch();
1037 
1038  if (m->mx_ops->msg_open_new(m, msg, e) == 0)
1039  {
1040  if (m->magic == MUTT_MMDF)
1041  fputs(MMDF_SEP, msg->fp);
1042 
1043  if (((m->magic == MUTT_MBOX) || (m->magic == MUTT_MMDF)) && (flags & MUTT_ADD_FROM))
1044  {
1045  if (e)
1046  {
1047  p = TAILQ_FIRST(&e->env->return_path);
1048  if (!p)
1049  p = TAILQ_FIRST(&e->env->sender);
1050  if (!p)
1051  p = TAILQ_FIRST(&e->env->from);
1052  }
1053 
1054  char buf[64] = { 0 };
1055  mutt_date_localtime_format(buf, sizeof(buf), "%a %b %e %H:%M:%S %Y\n", msg->received);
1056  fprintf(msg->fp, "From %s %s", p ? p->mailbox : NONULL(Username), buf);
1057  }
1058  }
1059  else
1060  FREE(&msg);
1061 
1062  return msg;
1063 }
1064 
1073 int mx_mbox_check(struct Mailbox *m, int *index_hint)
1074 {
1075  if (!m || !m->mx_ops)
1076  return -1;
1077 
1078  int rc = m->mx_ops->mbox_check(m, index_hint);
1079  if ((rc == MUTT_NEW_MAIL) || (rc == MUTT_REOPENED))
1081 
1082  return rc;
1083 }
1084 
1092 struct Message *mx_msg_open(struct Mailbox *m, int msgno)
1093 {
1094  if (!m)
1095  return NULL;
1096 
1097  struct Message *msg = NULL;
1098 
1099  if (!m->mx_ops || !m->mx_ops->msg_open)
1100  {
1101  mutt_debug(LL_DEBUG1, "function not implemented for mailbox type %d\n", m->magic);
1102  return NULL;
1103  }
1104 
1105  msg = mutt_mem_calloc(1, sizeof(struct Message));
1106  if (m->mx_ops->msg_open(m, msg, msgno) < 0)
1107  FREE(&msg);
1108 
1109  return msg;
1110 }
1111 
1119 int mx_msg_commit(struct Mailbox *m, struct Message *msg)
1120 {
1121  if (!m || !m->mx_ops || !m->mx_ops->msg_commit)
1122  return -1;
1123 
1124  if (!(msg->write && m->append))
1125  {
1126  mutt_debug(LL_DEBUG1, "msg->write = %d, m->append = %d\n", msg->write, m->append);
1127  return -1;
1128  }
1129 
1130  return m->mx_ops->msg_commit(m, msg);
1131 }
1132 
1140 int mx_msg_close(struct Mailbox *m, struct Message **msg)
1141 {
1142  if (!m || !msg || !*msg)
1143  return 0;
1144 
1145  int rc = 0;
1146 
1147  if (m->mx_ops && m->mx_ops->msg_close)
1148  rc = m->mx_ops->msg_close(m, *msg);
1149 
1150  if ((*msg)->path)
1151  {
1152  mutt_debug(LL_DEBUG1, "unlinking %s\n", (*msg)->path);
1153  unlink((*msg)->path);
1154  FREE(&(*msg)->path);
1155  }
1156 
1157  FREE(&(*msg)->committed_path);
1158  FREE(msg);
1159  return rc;
1160 }
1161 
1166 void mx_alloc_memory(struct Mailbox *m)
1167 {
1168  size_t s = MAX(sizeof(struct Email *), sizeof(int));
1169 
1170  if ((m->email_max + 25) * s < m->email_max * s)
1171  {
1172  mutt_error(_("Out of memory"));
1173  mutt_exit(1);
1174  }
1175 
1176  m->email_max += 25;
1177  if (m->emails)
1178  {
1179  mutt_mem_realloc(&m->emails, sizeof(struct Email *) * m->email_max);
1180  mutt_mem_realloc(&m->v2r, sizeof(int) * m->email_max);
1181  }
1182  else
1183  {
1184  m->emails = mutt_mem_calloc(m->email_max, sizeof(struct Email *));
1185  m->v2r = mutt_mem_calloc(m->email_max, sizeof(int));
1186  }
1187  for (int i = m->email_max - 25; i < m->email_max; i++)
1188  {
1189  m->emails[i] = NULL;
1190  m->v2r[i] = -1;
1191  }
1192 }
1193 
1201 int mx_check_empty(const char *path)
1202 {
1203  switch (mx_path_probe(path, NULL))
1204  {
1205  case MUTT_MBOX:
1206  case MUTT_MMDF:
1207  return mutt_file_check_empty(path);
1208  case MUTT_MH:
1209  return mh_check_empty(path);
1210  case MUTT_MAILDIR:
1211  return maildir_check_empty(path);
1212 #ifdef USE_IMAP
1213  case MUTT_IMAP:
1214  {
1215  int rc = imap_path_status(path, false);
1216  if (rc < 0)
1217  return -1;
1218  if (rc == 0)
1219  return 1;
1220  return 0;
1221  }
1222 #endif
1223  default:
1224  errno = EINVAL;
1225  return -1;
1226  }
1227  /* not reached */
1228 }
1229 
1240 int mx_tags_edit(struct Mailbox *m, const char *tags, char *buf, size_t buflen)
1241 {
1242  if (!m)
1243  return -1;
1244 
1245  if (m->mx_ops->tags_edit)
1246  return m->mx_ops->tags_edit(m, tags, buf, buflen);
1247 
1248  mutt_message(_("Folder doesn't support tagging, aborting"));
1249  return -1;
1250 }
1251 
1260 int mx_tags_commit(struct Mailbox *m, struct Email *e, char *tags)
1261 {
1262  if (!m)
1263  return -1;
1264 
1265  if (m->mx_ops->tags_commit)
1266  return m->mx_ops->tags_commit(m, e, tags);
1267 
1268  mutt_message(_("Folder doesn't support tagging, aborting"));
1269  return -1;
1270 }
1271 
1278 {
1279  return m && m->mx_ops->tags_commit && m->mx_ops->tags_edit;
1280 }
1281 
1288 enum MailboxType mx_path_probe(const char *path, struct stat *st)
1289 {
1290  if (!path)
1291  return MUTT_UNKNOWN;
1292 
1293  static const struct MxOps *no_stat[] = {
1294 #ifdef USE_IMAP
1295  &MxImapOps,
1296 #endif
1297 #ifdef USE_NOTMUCH
1298  &MxNotmuchOps,
1299 #endif
1300 #ifdef USE_POP
1301  &MxPopOps,
1302 #endif
1303 #ifdef USE_NNTP
1304  &MxNntpOps,
1305 #endif
1306  };
1307 
1308  static const struct MxOps *with_stat[] = {
1310 #ifdef USE_COMPRESSED
1311  &MxCompOps,
1312 #endif
1313  };
1314 
1315  enum MailboxType rc;
1316 
1317  for (size_t i = 0; i < mutt_array_size(no_stat); i++)
1318  {
1319  rc = no_stat[i]->path_probe(path, NULL);
1320  if (rc != MUTT_UNKNOWN)
1321  return rc;
1322  }
1323 
1324  struct stat st2 = { 0 };
1325  if (!st)
1326  st = &st2;
1327 
1328  if (stat(path, st) != 0)
1329  {
1330  mutt_debug(LL_DEBUG1, "unable to stat %s: %s (errno %d)\n", path, strerror(errno), errno);
1331  return MUTT_UNKNOWN;
1332  }
1333 
1334  for (size_t i = 0; i < mutt_array_size(with_stat); i++)
1335  {
1336  rc = with_stat[i]->path_probe(path, st);
1337  if (rc != MUTT_UNKNOWN)
1338  return rc;
1339  }
1340 
1341  return rc;
1342 }
1343 
1347 int mx_path_canon(char *buf, size_t buflen, const char *folder, enum MailboxType *magic)
1348 {
1349  if (!buf)
1350  return -1;
1351 
1352  for (size_t i = 0; i < 3; i++)
1353  {
1354  /* Look for !! ! - < > or ^ followed by / or NUL */
1355  if ((buf[0] == '!') && (buf[1] == '!'))
1356  {
1357  if (((buf[2] == '/') || (buf[2] == '\0')))
1358  {
1359  mutt_str_inline_replace(buf, buflen, 2, LastFolder);
1360  }
1361  }
1362  else if ((buf[0] == '+') || (buf[0] == '='))
1363  {
1364  size_t folder_len = mutt_str_strlen(folder);
1365  if ((folder_len > 0) && (folder[folder_len - 1] != '/'))
1366  {
1367  buf[0] = '/';
1368  mutt_str_inline_replace(buf, buflen, 0, folder);
1369  }
1370  else
1371  {
1372  mutt_str_inline_replace(buf, buflen, 1, folder);
1373  }
1374  }
1375  else if ((buf[1] == '/') || (buf[1] == '\0'))
1376  {
1377  if (buf[0] == '!')
1378  {
1379  mutt_str_inline_replace(buf, buflen, 1, C_Spoolfile);
1380  }
1381  else if (buf[0] == '-')
1382  {
1383  mutt_str_inline_replace(buf, buflen, 1, LastFolder);
1384  }
1385  else if (buf[0] == '<')
1386  {
1387  mutt_str_inline_replace(buf, buflen, 1, C_Record);
1388  }
1389  else if (buf[0] == '>')
1390  {
1391  mutt_str_inline_replace(buf, buflen, 1, C_Mbox);
1392  }
1393  else if (buf[0] == '^')
1394  {
1395  mutt_str_inline_replace(buf, buflen, 1, CurrentFolder);
1396  }
1397  else if (buf[0] == '~')
1398  {
1399  mutt_str_inline_replace(buf, buflen, 1, HomeDir);
1400  }
1401  }
1402  else if (buf[0] == '@')
1403  {
1404  /* elm compatibility, @ expands alias to user name */
1405  struct AddressList *al = mutt_alias_lookup(buf + 1);
1406  if (TAILQ_EMPTY(al))
1407  break;
1408 
1409  struct Email *e = email_new();
1410  e->env = mutt_env_new();
1411  mutt_addrlist_copy(&e->env->from, al, false);
1412  mutt_addrlist_copy(&e->env->to, al, false);
1413  mutt_default_save(buf, buflen, e);
1414  email_free(&e);
1415  break;
1416  }
1417  else
1418  {
1419  break;
1420  }
1421  }
1422 
1423  // if (!folder) //XXX - use inherited version, or pass NULL to backend?
1424  // return -1;
1425 
1426  enum MailboxType magic2 = mx_path_probe(buf, NULL);
1427  if (magic)
1428  *magic = magic2;
1429  const struct MxOps *ops = mx_get_ops(magic2);
1430  if (!ops || !ops->path_canon)
1431  return -1;
1432 
1433  if (ops->path_canon(buf, buflen) < 0)
1434  {
1435  mutt_path_canon(buf, buflen, HomeDir);
1436  }
1437 
1438  return 0;
1439 }
1440 
1448 int mx_path_canon2(struct Mailbox *m, const char *folder)
1449 {
1450  if (!m)
1451  return -1;
1452 
1453  char buf[PATH_MAX];
1454 
1455  if (m->realpath)
1456  mutt_str_strfcpy(buf, m->realpath, sizeof(buf));
1457  else
1458  mutt_str_strfcpy(buf, mailbox_path(m), sizeof(buf));
1459 
1460  int rc = mx_path_canon(buf, sizeof(buf), folder, &m->magic);
1461 
1462  mutt_str_replace(&m->realpath, buf);
1463 
1464  if (rc >= 0)
1465  {
1466  m->mx_ops = mx_get_ops(m->magic);
1468  }
1469 
1470  return rc;
1471 }
1472 
1476 int mx_path_pretty(char *buf, size_t buflen, const char *folder)
1477 {
1478  enum MailboxType magic = mx_path_probe(buf, NULL);
1479  const struct MxOps *ops = mx_get_ops(magic);
1480  if (!ops)
1481  return -1;
1482 
1483  if (!ops->path_canon)
1484  return -1;
1485 
1486  if (ops->path_canon(buf, buflen) < 0)
1487  return -1;
1488 
1489  if (!ops->path_pretty)
1490  return -1;
1491 
1492  if (ops->path_pretty(buf, buflen, folder) < 0)
1493  return -1;
1494 
1495  return 0;
1496 }
1497 
1501 int mx_path_parent(char *buf, size_t buflen)
1502 {
1503  if (!buf)
1504  return -1;
1505 
1506  return 0;
1507 }
1508 
1518 {
1519  if (!m || !m->mx_ops || !m->mx_ops->msg_padding_size)
1520  return 0;
1521 
1522  return m->mx_ops->msg_padding_size(m);
1523 }
1524 
1531 struct Account *mx_ac_find(struct Mailbox *m)
1532 {
1533  if (!m || !m->mx_ops)
1534  return NULL;
1535 
1536  struct Account *np = NULL;
1537  TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
1538  {
1539  if (np->magic != m->magic)
1540  continue;
1541 
1542  if (m->mx_ops->ac_find(np, m->realpath))
1543  return np;
1544  }
1545 
1546  return NULL;
1547 }
1548 
1555 struct Mailbox *mx_mbox_find(struct Account *a, const char *path)
1556 {
1557  if (!a || !path)
1558  return NULL;
1559 
1560  struct MailboxNode *np = NULL;
1561  STAILQ_FOREACH(np, &a->mailboxes, entries)
1562  {
1563  if (mutt_str_strcmp(np->mailbox->realpath, path) == 0)
1564  return np->mailbox;
1565  }
1566 
1567  return NULL;
1568 }
1569 
1576 struct Mailbox *mx_mbox_find2(const char *path)
1577 {
1578  if (!path)
1579  return NULL;
1580 
1581  char buf[PATH_MAX];
1582  mutt_str_strfcpy(buf, path, sizeof(buf));
1583  mx_path_canon(buf, sizeof(buf), C_Folder, NULL);
1584 
1585  struct Account *np = NULL;
1586  TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
1587  {
1588  struct Mailbox *m = mx_mbox_find(np, buf);
1589  if (m)
1590  return m;
1591  }
1592 
1593  return NULL;
1594 }
1595 
1603 struct Mailbox *mx_path_resolve(const char *path)
1604 {
1605  if (!path)
1606  return NULL;
1607 
1608  struct Mailbox *m = mx_mbox_find2(path);
1609  if (m)
1610  return m;
1611 
1612  m = mailbox_new();
1613  m->flags = MB_HIDDEN;
1614  mutt_buffer_strcpy(&m->pathbuf, path);
1616 
1617  return m;
1618 }
1619 
1623 int mx_ac_add(struct Account *a, struct Mailbox *m)
1624 {
1625  if (!a || !m || !m->mx_ops || !m->mx_ops->ac_add)
1626  return -1;
1627 
1628  if (m->mx_ops->ac_add(a, m) < 0)
1629  return -1;
1630 
1631  account_mailbox_add(a, m);
1632  return 0;
1633 }
1634 
1639 int mx_ac_remove(struct Mailbox *m)
1640 {
1641  if (!m || !m->account)
1642  return -1;
1643 
1644  struct Account *a = m->account;
1646  mailbox_free(&m);
1647  if (STAILQ_EMPTY(&a->mailboxes))
1648  {
1650  }
1651  return 0;
1652 }
1653 
1657 int mx_mbox_check_stats(struct Mailbox *m, int flags)
1658 {
1659  if (!m)
1660  return -1;
1661 
1662  return m->mx_ops->mbox_check_stats(m, flags);
1663 }
1664 
1674 int mx_save_hcache(struct Mailbox *m, struct Email *e)
1675 {
1676  if (!m->mx_ops || !m->mx_ops->msg_save_hcache)
1677  return 0;
1678 
1679  return m->mx_ops->msg_save_hcache(m, e);
1680 }
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mx.h:52
struct Email ** emails
Array of Emails.
Definition: mailbox.h:98
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:36
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:191
#define MUTT_APPENDNEW
Set in mx_open_mailbox_append if the mailbox doesn&#39;t exist.
Definition: mx.h:59
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:295
Notmuch virtual mailbox type.
int(* msg_commit)(struct Mailbox *m, struct Message *msg)
Save changes to an email.
Definition: mx.h:197
Manage keymappings.
#define MUTT_ACL_ALL
Definition: mailbox.h:75
#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:90
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:86
Representation of the email&#39;s header.
size_t mutt_date_localtime_format(char *buf, size_t buflen, const char *format, time_t t)
Format localtime.
Definition: date.c:774
WHERE bool C_MailCheckRecent
Config: Notify the user about new mail since the last time the mailbox was opened.
Definition: globals.h:239
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:1310
#define mutt_perror(...)
Definition: logging.h:85
Clear the &#39;last-tagged&#39; pointer.
Definition: mailbox.h:173
struct NntpMboxData * mutt_newsgroup_catchup(struct Mailbox *m, struct NntpAccountData *adata, char *group)
Catchup newsgroup.
Definition: newsrc.c:1268
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe()
Definition: imap.c:2474
int mx_mbox_check(struct Mailbox *m, int *index_hint)
Check for new mail - Wrapper for MxOps::mbox_check()
Definition: mx.c:1073
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:718
Update internal tables.
Definition: mailbox.h:172
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:95
int msg_unread
Number of unread messages.
Definition: mailbox.h:91
int mx_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Wrapper for MxOps::ac_add()
Definition: mx.c:1623
int mx_path_canon2(struct Mailbox *m, const char *folder)
Canonicalise the path to realpath.
Definition: mx.c:1448
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:51
int mx_mbox_close(struct Context **ptr)
Save changes and close mailbox.
Definition: mx.c:583
Structs that make up an email.
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:156
Mbox local mailbox type.
int(* mbox_sync)(struct Mailbox *m, int *index_hint)
Save changes to the Mailbox.
Definition: mx.h:164
bool replied
Definition: mx.h:91
int mx_path_pretty(char *buf, size_t buflen, const char *folder)
Abbreviate a mailbox path - Wrapper for MxOps::path_pretty()
Definition: mx.c:1476
int mx_path_parent(char *buf, size_t buflen)
Find the parent of a mailbox path - Wrapper for MxOps::path_parent()
Definition: mx.c:1501
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:3360
#define mutt_message(...)
Definition: logging.h:83
WHERE bool C_Confirmappend
Config: Confirm before appending emails to a mailbox.
Definition: globals.h:207
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:92
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:39
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:478
bool peekonly
Just taking a glance, revert atime.
Definition: mailbox.h:116
enum MailboxType(* path_probe)(const char *path, const struct stat *st)
Does this Mailbox type recognise this path?
Definition: mx.h:246
int mx_path_canon(char *buf, size_t buflen, const char *folder, enum MailboxType *magic)
Canonicalise a mailbox path - Wrapper for MxOps::path_canon()
Definition: mx.c:1347
struct NntpAccountData * adata
Definition: nntp.h:153
unsigned char C_Move
Config: Move emails from C_Spoolfile to C_Mbox when read.
Definition: mx.c:84
char * realpath
Used for duplicate detection, context comparison, and the sidebar.
Definition: mailbox.h:83
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
Compressed mbox local mailbox type.
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:180
struct MxOps MxMaildirOps
Maildir Mailbox - Implements MxOps.
Definition: maildir.c:704
String manipulation buffer.
Definition: buffer.h:33
WHERE bool C_SaveEmpty
Config: (mbox,mmdf) Preserve empty mailboxes.
Definition: globals.h:249
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1166
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:46
Error occurred examining Mailbox.
Definition: mailbox.h:45
An email address.
Definition: address.h:34
struct Mailbox * mx_mbox_find(struct Account *a, const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1555
struct MxOps MxPopOps
POP Mailbox - Implements MxOps.
Definition: pop.c:1284
struct Context * mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:253
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:104
int(* ac_add)(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account.
Definition: mx.h:123
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:666
Index panel (list of emails)
Definition: keymap.h:77
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition: mx.h:49
struct MxOps MxMboxOps
Mbox Mailbox - Implements MxOps.
Definition: mbox.c:1848
WHERE char * C_Record
Config: Folder to save &#39;sent&#39; messages.
Definition: globals.h:131
Email list was changed.
Definition: mailbox.h:170
#define MUTT_READONLY
Open in read-only mode.
Definition: mx.h:53
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:1674
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1576
WHERE bool OptNeedRescore
(pseudo) set when the &#39;score&#39; command is used
Definition: options.h:40
struct Account * mx_ac_find(struct Mailbox *m)
Find the Account owning a Mailbox.
Definition: mx.c:1531
void mutt_default_save(char *path, size_t pathlen, struct Email *e)
Find the default save path for an email.
Definition: hook.c:656
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:72
int mutt_save_confirm(const char *s, struct stat *st)
Ask the user to save.
Definition: muttlib.c:1448
bool notify_observer_add(struct Notify *notify, observer_t callback, void *global_data)
Add an observer to an object.
Definition: notify.c:154
int vcount
The number of virtual messages.
Definition: mailbox.h:101
#define MUTT_SET_DRAFT
set the message draft flag
Definition: mx.h:65
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:1634
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:189
Assorted sorting methods.
int(* mbox_open)(struct Mailbox *m)
Open a Mailbox.
Definition: mx.h:131
int(* mbox_open_append)(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending.
Definition: mx.h:139
Some miscellaneous functions.
#define MAX(a, b)
Definition: memory.h:30
bool has_new
Mailbox has new mail.
Definition: mailbox.h:87
char * C_Trash
Config: Folder to put deleted emails.
Definition: mx.c:85
#define mutt_array_size(x)
Definition: memory.h:33
int(* path_canon)(char *buf, size_t buflen)
Canonicalise a Mailbox path.
Definition: mx.h:254
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1140
bool tagged
Email is tagged.
Definition: email.h:44
struct MxOps MxCompOps
Compressed Mailbox - Implements MxOps.
Definition: compress.c:936
bool read
Email is read.
Definition: email.h:51
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1545
int mx_check_empty(const char *path)
Is the mailbox empty.
Definition: mx.c:1201
struct Mailbox * mailbox
Definition: context.h:50
Parse and execute user-defined hooks.
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:58
void mutt_file_unlink_empty(const char *path)
Delete a file if it&#39;s empty.
Definition: file.c:1302
struct AddressList from
Email&#39;s &#39;From&#39; list.
Definition: envelope.h:57
enum MailboxType magic
Mailbox type.
Definition: mailbox.h:104
bool readonly
Don&#39;t allow changes to the mailbox.
Definition: mailbox.h:118
The Context has been opened.
Definition: context.h:67
WHERE char * C_Mbox
Config: Folder that receives read emails (see Move)
Definition: globals.h:117
IMAP network mailbox.
struct Envelope * env
Envelope information.
Definition: email.h:89
enum MailboxType magic
Mailbox type, e.g. MUTT_IMAP.
Definition: mx.h:105
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:1260
POP network mailbox.
WHERE char * HomeDir
User&#39;s home directory.
Definition: globals.h:49
struct Notify * notify
Notifications handler.
Definition: context.h:51
void mx_fastclose_mailbox(struct Mailbox *m)
free up memory associated with the Mailbox
Definition: mx.c:412
struct MxOps MxImapOps
IMAP Mailbox - Implements MxOps.
Definition: imap.c:2554
char * group
Definition: nntp.h:140
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: globals.h:54
int opened
Number of times mailbox is opened.
Definition: mailbox.h:131
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:1009
int(* msg_padding_size)(struct Mailbox *m)
Bytes of padding between messages.
Definition: mx.h:211
void * mdata
Driver specific data.
Definition: mailbox.h:135
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:50
int flags
e.g. MB_NORMAL
Definition: mailbox.h:133
struct MxOps MxNntpOps
NNTP Mailbox - Implements MxOps.
Definition: nntp.c:2883
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:44
struct Keymap * km_find_func(enum MenuType menu, int func)
Find a function&#39;s mapping in a Menu.
Definition: keymap.c:854
Old messages.
Definition: mutt.h:98
Email list needs resorting.
Definition: mailbox.h:171
#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:81
WHERE bool OptForceRefresh
(pseudo) refresh even during macros
Definition: options.h:35
Prototypes for many functions.
bool mutt_path_canon(char *buf, size_t buflen, const char *homedir)
Create the canonical version of a path.
Definition: path.c:221
#define MB_HIDDEN
Definition: mailbox.h:37
void mutt_sort_headers(struct Context *ctx, bool init)
Sort emails by their headers.
Definition: sort.c:362
int(* tags_edit)(struct Mailbox *m, const char *tags, char *buf, size_t buflen)
Prompt and validate new messages tags.
Definition: mx.h:230
bool flagged
Definition: mx.h:90
A local copy of an email.
Definition: mx.h:81
int email_max
Number of pointers in emails.
Definition: mailbox.h:99
WHERE char * C_Folder
Config: Base folder for a set of mailboxes.
Definition: globals.h:119
void ctx_update(struct Context *ctx)
Update the Context&#39;s message counts.
Definition: context.c:106
struct Message::@0 flags
int imap_access(const char *path)
Check permissions on an IMAP mailbox with a new connection.
Definition: imap.c:601
Messages to be deleted.
Definition: mutt.h:102
A mailbox.
Definition: mailbox.h:80
#define PATH_MAX
Definition: mutt.h:50
char * mutt_find_hook(HookFlags type, const char *pat)
Find a matching hook.
Definition: hook.c:552
enum MailboxType magic
Type of Mailboxes this Account contains.
Definition: account.h:38
#define MUTT_NOSORT
Do not sort the mailbox after opening it.
Definition: mx.h:51
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:240
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: keymap.c:826
bool dontwrite
Don&#39;t write the mailbox on close.
Definition: mailbox.h:114
int(* msg_save_hcache)(struct Mailbox *m, struct Email *e)
Save message to the header cache.
Definition: mx.h:219
&#39;mmdf&#39; Mailbox type
Definition: mailbox.h:48
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:750
int(* msg_open)(struct Mailbox *m, struct Message *msg, int msgno)
Open an email message in a Mailbox.
Definition: mx.h:180
int(* mbox_check)(struct Mailbox *m, int *index_hint)
Check for new mail.
Definition: mx.h:147
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:55
bool append
Mailbox is opened in append mode.
Definition: mailbox.h:112
&#39;MH&#39; Mailbox type
Definition: mailbox.h:49
const struct MxOps * mx_ops
MXAPI callback functions.
Definition: mailbox.h:110
bool quiet
Inhibit status messages?
Definition: mailbox.h:117
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
#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:1240
unsigned char C_MboxType
Config: Default type for creating new mailboxes.
Definition: mx.c:83
int msg_tagged
How many messages are tagged?
Definition: mailbox.h:96
bool draft
Definition: mx.h:92
#define MUTT_QUIET
Do not print any messages.
Definition: mx.h:54
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:120
#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:62
NNTP-specific Mailbox data -.
Definition: nntp.h:138
void mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:453
Maildir local mailbox type.
bool mutt_comp_can_append(struct Mailbox *m)
Can we append to this path?
Definition: compress.c:339
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
&#39;mbox&#39; Mailbox type
Definition: mailbox.h:47
struct MxOps MxMmdfOps
MMDF Mailbox - Implements MxOps.
Definition: mbox.c:1876
bool write
nonzero if message is open for writing
Definition: mx.h:86
int maildir_check_empty(const char *path)
Is the mailbox empty.
Definition: shared.c:1564
void mutt_exit(int code)
Leave NeoMutt NOW.
Definition: main.c:207
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition: notify.c:138
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:100
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
bool C_MarkOld
Config: Mark new emails as old when leaving the mailbox.
Definition: email_globals.c:36
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:342
MailboxType
Supported mailbox formats.
Definition: mailbox.h:42
struct AddressList return_path
Return path for the Email.
Definition: envelope.h:56
struct Account * account
Account that owns this Mailbox.
Definition: mailbox.h:130
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:451
WHERE char * C_Spoolfile
Config: Inbox.
Definition: globals.h:144
bool flagged
Marked important?
Definition: email.h:43
int mx_mbox_sync(struct Mailbox *m, int *index_hint)
Save changes to mailbox.
Definition: mx.c:875
int(* mbox_close)(struct Mailbox *m)
Close a Mailbox.
Definition: mx.h:171
static int mx_open_mailbox_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox for appending.
Definition: mx.c:186
int msg_new
Number of new messages.
Definition: mailbox.h:94
struct Hash * subj_hash
Hash table by subject.
Definition: mailbox.h:127
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:1657
const struct MxOps * mx_get_ops(enum MailboxType magic)
Get mailbox operations.
Definition: mx.c:140
bool collapsed
Are all threads collapsed?
Definition: context.h:48
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:263
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:1119
void mbox_reset_atime(struct Mailbox *m, struct stat *st)
Reset the access time on the mailbox file.
Definition: mbox.c:843
int mutt_file_check_empty(const char *path)
Is the mailbox empty.
Definition: file.c:1403
int mutt_append_message(struct Mailbox *dest, struct Mailbox *src, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags)
Append a message.
Definition: copy.c:880
FILE * fp
pointer to the message data
Definition: mx.h:83
#define FREE(x)
Definition: memory.h:40
int(* msg_close)(struct Mailbox *m, struct Message *msg)
Close an email.
Definition: mx.h:205
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1603
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:169
#define STAILQ_EMPTY(head)
Definition: queue.h:345
time_t received
the time at which this message was received
Definition: mx.h:94
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:52
struct Email * email_new(void)
Create a new Email.
Definition: email.c:68
List of Mailboxes.
Definition: mailbox.h:144
bool changed
Mailbox has been modified.
Definition: mailbox.h:113
void ctx_free(struct Context **ptr)
Free a Context.
Definition: context.c:49
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:270
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:1517
New mail received in Mailbox.
Definition: mx.h:72
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:1070
bool mx_tags_is_supported(struct Mailbox *m)
return true if mailbox support tagging
Definition: mx.c:1277
struct Notify * notify
Notifications handler.
Definition: mailbox.h:138
bool C_KeepFlagged
Config: Don&#39;t move flagged messages from C_Spoolfile to C_Mbox.
Definition: mx.c:82
An Event that happened to an Context.
Definition: context.h:57
struct Account *(* ac_find)(struct Account *a, const char *path)
Find an Account that matches a Mailbox path.
Definition: mx.h:115
#define TAILQ_EMPTY(head)
Definition: queue.h:714
#define MUTT_NEWFOLDER
Create a new folder - same as MUTT_APPEND,.
Definition: mx.h:55
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:1639
struct Buffer pathbuf
Definition: mailbox.h:82
void mutt_hash_free(struct Hash **ptr)
Free a hash table.
Definition: hash.c:471
bool read
Definition: mx.h:89
#define MUTT_ADD_FROM
add a From_ line
Definition: mx.h:64
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
enum MailboxType mx_path_probe(const char *path, struct stat *st)
Find a mailbox that understands a path.
Definition: mx.c:1288
Mailbox was reopened.
Definition: mx.h:74
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:52
int imap_fast_trash(struct Mailbox *m, char *dest)
Use server COPY command to copy deleted messages to trash.
Definition: imap.c:1526
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:171
Convenience wrapper for the library headers.
struct Hash * label_hash
Hash table for x-labels.
Definition: mailbox.h:128
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:615
static bool mutt_is_spool(const char *str)
Is this the spoolfile?
Definition: mx.c:154
struct Message * mx_msg_open(struct Mailbox *m, int msgno)
return a stream pointer for a message
Definition: mx.c:1092
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
struct Hash * id_hash
Hash table by msg id.
Definition: mailbox.h:126
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:84
Usenet network mailbox type; talk to an NNTP server.
The Mailbox API.
Definition: mx.h:103
struct MxOps MxNotmuchOps
Notmuch Mailbox - Implements MxOps.
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:146
#define MMDF_SEP
Definition: mbox.h:60
int(* tags_commit)(struct Mailbox *m, struct Email *e, char *buf)
Save the tags to a message.
Definition: mx.h:239
int mh_check_empty(const char *path)
Is mailbox empty.
Definition: shared.c:1604