NeoMutt  2020-06-26-30-g76c339
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 "alias/lib.h"
45 #include "mutt.h"
46 #include "mx.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  struct Url *ua = url_parse(str);
159  struct Url *ub = url_parse(C_Spoolfile);
160 
161  const bool is_spool =
162  ua && ub && (ua->scheme == ub->scheme) &&
163  mutt_istr_equal(ua->host, ub->host) && mutt_istr_equal(ua->path, ub->path) &&
164  (!ua->user || !ub->user || mutt_str_equal(ua->user, ub->user));
165 
166  url_free(&ua);
167  url_free(&ub);
168  return is_spool;
169 }
170 
181 int mx_access(const char *path, int flags)
182 {
183 #ifdef USE_IMAP
184  if (imap_path_probe(path, NULL) == MUTT_IMAP)
185  return imap_access(path);
186 #endif
187 
188  return access(path, flags);
189 }
190 
198 static int mx_open_mailbox_append(struct Mailbox *m, OpenMailboxFlags flags)
199 {
200  if (!m)
201  return -1;
202 
203  struct stat sb;
204 
205  m->append = true;
206  if ((m->type == MUTT_UNKNOWN) || (m->type == MUTT_MAILBOX_ERROR))
207  {
209 
210  if (m->type == MUTT_UNKNOWN)
211  {
212  if (flags & (MUTT_APPEND | MUTT_NEWFOLDER))
213  {
215  }
216  else
217  {
218  mutt_error(_("%s is not a mailbox"), mailbox_path(m));
219  return -1;
220  }
221  }
222 
223  if (m->type == MUTT_MAILBOX_ERROR)
224  {
225  if (stat(mailbox_path(m), &sb) == -1)
226  {
227  if (errno == ENOENT)
228  {
229 #ifdef USE_COMP_MBOX
230  if (mutt_comp_can_append(m))
231  m->type = MUTT_COMPRESSED;
232  else
233 #endif
234  m->type = C_MboxType;
235  flags |= MUTT_APPENDNEW;
236  }
237  else
238  {
240  return -1;
241  }
242  }
243  else
244  return -1;
245  }
246 
247  m->mx_ops = mx_get_ops(m->type);
248  }
249 
250  if (!m->mx_ops || !m->mx_ops->mbox_open_append)
251  return -1;
252 
253  int rc = m->mx_ops->mbox_open_append(m, flags);
254  m->opened++;
255  return rc;
256 }
257 
265 struct Context *mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
266 {
267  if (!m)
268  return NULL;
269 
270  struct Context *ctx = ctx_new();
271  ctx->mailbox = m;
272 
273  struct EventContext ev_ctx = { ctx };
274  notify_send(ctx->notify, NT_CONTEXT, NT_CONTEXT_OPEN, &ev_ctx);
275 
276  // If the Mailbox is closed, Context->mailbox must be set to NULL
278 
279  if ((m->type == MUTT_UNKNOWN) && (flags & (MUTT_NEWFOLDER | MUTT_APPEND)))
280  {
281  m->type = C_MboxType;
282  m->mx_ops = mx_get_ops(m->type);
283  }
284 
285  const bool newly_linked_account = !m->account;
286  if (newly_linked_account)
287  {
288  struct Account *a = mx_ac_find(m);
289  bool new_account = false;
290  if (!a)
291  {
292  a = account_new(NULL, NeoMutt->sub);
293  a->type = m->type;
294  new_account = true;
295  }
296  if (mx_ac_add(a, m) < 0)
297  {
298  ctx_free(&ctx);
299  if (new_account)
300  {
301  FREE(&a);
302  }
303  return NULL;
304  }
305  if (new_account)
306  {
308  }
309  }
310 
311  ctx->msg_not_read_yet = -1;
312  ctx->collapsed = false;
313 
314  m->verbose = !(flags & MUTT_QUIET);
315  if (flags & MUTT_READONLY)
316  m->readonly = true;
317  m->peekonly = (flags & MUTT_PEEK);
318 
319  if (flags & (MUTT_APPEND | MUTT_NEWFOLDER))
320  {
321  if (mx_open_mailbox_append(ctx->mailbox, flags) != 0)
322  {
323  goto error;
324  }
325  return ctx;
326  }
327 
328  if (m->opened > 0)
329  {
330  m->opened++;
331  return ctx;
332  }
333 
334  m->size = 0;
335  m->msg_unread = 0;
336  m->msg_flagged = 0;
337  m->rights = MUTT_ACL_ALL;
338 
339  if (m->type == MUTT_UNKNOWN)
340  {
342  m->mx_ops = mx_get_ops(m->type);
343  }
344 
345  if ((m->type == MUTT_UNKNOWN) || (m->type == MUTT_MAILBOX_ERROR) || !m->mx_ops)
346  {
347  if (m->type == MUTT_MAILBOX_ERROR)
349  else if ((m->type == MUTT_UNKNOWN) || !m->mx_ops)
350  mutt_error(_("%s is not a mailbox"), mailbox_path(m));
351  goto error;
352  }
353 
355 
356  /* if the user has a 'push' command in their .neomuttrc, or in a folder-hook,
357  * it will cause the progress messages not to be displayed because
358  * mutt_refresh() will think we are in the middle of a macro. so set a
359  * flag to indicate that we should really refresh the screen. */
360  OptForceRefresh = true;
361 
362  if (m->verbose)
363  mutt_message(_("Reading %s..."), mailbox_path(m));
364 
365  // Clear out any existing emails
366  for (int i = 0; i < m->email_max; i++)
367  {
368  email_free(&m->emails[i]);
369  }
370 
371  m->msg_count = 0;
372  m->msg_unread = 0;
373  m->msg_flagged = 0;
374  m->msg_new = 0;
375  m->msg_deleted = 0;
376  m->msg_tagged = 0;
377  m->vcount = 0;
378 
379  int rc = m->mx_ops->mbox_open(ctx->mailbox);
380  m->opened++;
381  if (rc == 0)
382  ctx_update(ctx);
383 
384  if ((rc == 0) || (rc == -2))
385  {
386  if ((flags & MUTT_NOSORT) == 0)
387  {
388  /* avoid unnecessary work since the mailbox is completely unthreaded
389  * to begin with */
390  OptSortSubthreads = false;
391  OptNeedRescore = false;
392  }
393  if (m->verbose)
395  if (rc == -2)
396  {
397  mutt_error(_("Reading from %s interrupted..."), mailbox_path(m));
398  mutt_sort_headers(ctx, true);
399  }
400  }
401  else
402  {
403  goto error;
404  }
405 
406  if (!m->peekonly)
407  m->has_new = false;
408  OptForceRefresh = false;
409 
410  return ctx;
411 
412 error:
414  if (newly_linked_account)
416  ctx_free(&ctx);
417  return NULL;
418 }
419 
425 {
426  if (!m)
427  return;
428 
429  m->opened--;
430  if (m->opened != 0)
431  return;
432 
433  /* never announce that a mailbox we've just left has new mail.
434  * TODO: really belongs in mx_mbox_close, but this is a nice hook point */
435  if (!m->peekonly)
437 
438  if (m->mx_ops)
439  m->mx_ops->mbox_close(m);
440 
442  mutt_hash_free(&m->id_hash);
444 
445  if (m->emails)
446  {
447  for (int i = 0; i < m->msg_count; i++)
448  {
449  if (!m->emails[i])
450  break;
451  email_free(&m->emails[i]);
452  }
453  }
454 }
455 
463 static int sync_mailbox(struct Mailbox *m, int *index_hint)
464 {
465  if (!m || !m->mx_ops || !m->mx_ops->mbox_sync)
466  return -1;
467 
468  if (m->verbose)
469  {
470  /* L10N: Displayed before/as a mailbox is being synced */
471  mutt_message(_("Writing %s..."), mailbox_path(m));
472  }
473 
474  int rc = m->mx_ops->mbox_sync(m, index_hint);
475  if (rc != 0)
476  {
477  mutt_debug(LL_DEBUG2, "mbox_sync returned: %d\n", rc);
478  if ((rc < 0) && m->verbose)
479  {
480  /* L10N: Displayed if a mailbox sync fails */
481  mutt_error(_("Unable to write %s"), mailbox_path(m));
482  }
483  }
484 
485  return rc;
486 }
487 
494 static int trash_append(struct Mailbox *m)
495 {
496  if (!m)
497  return -1;
498 
499  struct stat st, stc;
500  int opt_confappend, rc;
501 
502  if (!C_Trash || (m->msg_deleted == 0) || ((m->type == MUTT_MAILDIR) && C_MaildirTrash))
503  {
504  return 0;
505  }
506 
507  int delmsgcount = 0;
508  int first_del = -1;
509  for (int i = 0; i < m->msg_count; i++)
510  {
511  struct Email *e = m->emails[i];
512  if (!e)
513  break;
514 
515  if (e->deleted && !e->purge)
516  {
517  if (first_del < 0)
518  first_del = i;
519  delmsgcount++;
520  }
521  }
522 
523  if (delmsgcount == 0)
524  return 0; /* nothing to be done */
525 
526  /* avoid the "append messages" prompt */
527  opt_confappend = C_Confirmappend;
528  if (opt_confappend)
529  C_Confirmappend = false;
530  rc = mutt_save_confirm(C_Trash, &st);
531  if (opt_confappend)
532  C_Confirmappend = true;
533  if (rc != 0)
534  {
535  /* L10N: Although we know the precise number of messages, we do not show it to the user.
536  So feel free to use a "generic plural" as plural translation if your language has one. */
537  mutt_error(ngettext("message not deleted", "messages not deleted", delmsgcount));
538  return -1;
539  }
540 
541  if ((lstat(mailbox_path(m), &stc) == 0) && (stc.st_ino == st.st_ino) &&
542  (stc.st_dev == st.st_dev) && (stc.st_rdev == st.st_rdev))
543  {
544  return 0; /* we are in the trash folder: simple sync */
545  }
546 
547 #ifdef USE_IMAP
548  if ((m->type == MUTT_IMAP) && (imap_path_probe(C_Trash, NULL) == MUTT_IMAP))
549  {
550  if (imap_fast_trash(m, C_Trash) == 0)
551  return 0;
552  }
553 #endif
554 
555  struct Mailbox *m_trash = mx_path_resolve(C_Trash);
556  const bool old_append = m_trash->append;
557  struct Context *ctx_trash = mx_mbox_open(m_trash, MUTT_APPEND);
558  if (ctx_trash)
559  {
560  /* continue from initial scan above */
561  for (int i = first_del; i < m->msg_count; i++)
562  {
563  struct Email *e = m->emails[i];
564  if (!e)
565  break;
566 
567  if (e->deleted && !e->purge)
568  {
569  if (mutt_append_message(ctx_trash->mailbox, m, e, MUTT_CM_NO_FLAGS, CH_NO_FLAGS) == -1)
570  {
571  mx_mbox_close(&ctx_trash);
572  m_trash->append = old_append;
573  return -1;
574  }
575  }
576  }
577 
578  mx_mbox_close(&ctx_trash);
579  m_trash->append = old_append;
580  }
581  else
582  {
583  mutt_error(_("Can't open trash folder"));
584  mailbox_free(&m_trash);
585  return -1;
586  }
587 
588  return 0;
589 }
590 
603 int mx_mbox_close(struct Context **ptr)
604 {
605  if (!ptr || !*ptr)
606  return 0;
607 
608  struct Context *ctx = *ptr;
609  if (!ctx || !ctx->mailbox)
610  return -1;
611 
612  struct Mailbox *m = ctx->mailbox;
613 
614  if (C_MailCheckRecent && !m->peekonly)
615  m->has_new = false;
616 
617  if (m->readonly || m->dontwrite || m->append || m->peekonly)
618  {
620  ctx_free(ptr);
621  return 0;
622  }
623 
624  int i, read_msgs = 0;
625  int rc = -1;
626  enum QuadOption move_messages = MUTT_NO;
627  enum QuadOption purge = MUTT_YES;
628  struct Buffer *mbox = NULL;
629  struct Buffer *buf = mutt_buffer_pool_get();
630 
631 #ifdef USE_NNTP
632  if ((m->msg_unread != 0) && (m->type == MUTT_NNTP))
633  {
634  struct NntpMboxData *mdata = m->mdata;
635 
636  if (mdata && mdata->adata && mdata->group)
637  {
638  enum QuadOption ans =
639  query_quadoption(C_CatchupNewsgroup, _("Mark all articles read?"));
640  if (ans == MUTT_ABORT)
641  goto cleanup;
642  if (ans == MUTT_YES)
643  mutt_newsgroup_catchup(m, mdata->adata, mdata->group);
644  }
645  }
646 #endif
647 
648  for (i = 0; i < m->msg_count; i++)
649  {
650  struct Email *e = m->emails[i];
651  if (!e)
652  break;
653 
654  if (!e->deleted && e->read && !(e->flagged && C_KeepFlagged))
655  read_msgs++;
656  }
657 
658 #ifdef USE_NNTP
659  /* don't need to move articles from newsgroup */
660  if (m->type == MUTT_NNTP)
661  read_msgs = 0;
662 #endif
663 
664  if ((read_msgs != 0) && (C_Move != MUTT_NO))
665  {
666  bool is_spool;
667  mbox = mutt_buffer_pool_get();
668 
670  if (p)
671  {
672  is_spool = true;
673  mutt_buffer_strcpy(mbox, p);
674  }
675  else
676  {
677  mutt_buffer_strcpy(mbox, C_Mbox);
678  is_spool = mutt_is_spool(mailbox_path(m)) && !mutt_is_spool(mutt_b2s(mbox));
679  }
680 
681  if (is_spool && !mutt_buffer_is_empty(mbox))
682  {
684  mutt_buffer_printf(buf,
685  /* L10N: The first argument is the number of read messages to be
686  moved, the second argument is the target mailbox. */
687  ngettext("Move %d read message to %s?",
688  "Move %d read messages to %s?", read_msgs),
689  read_msgs, mutt_b2s(mbox));
690  move_messages = query_quadoption(C_Move, mutt_b2s(buf));
691  if (move_messages == MUTT_ABORT)
692  goto cleanup;
693  }
694  }
695 
696  /* There is no point in asking whether or not to purge if we are
697  * just marking messages as "trash". */
698  if ((m->msg_deleted != 0) && !((m->type == MUTT_MAILDIR) && C_MaildirTrash))
699  {
700  mutt_buffer_printf(buf,
701  ngettext("Purge %d deleted message?",
702  "Purge %d deleted messages?", m->msg_deleted),
703  m->msg_deleted);
704  purge = query_quadoption(C_Delete, mutt_b2s(buf));
705  if (purge == MUTT_ABORT)
706  goto cleanup;
707  }
708 
709  if (C_MarkOld && !m->peekonly)
710  {
711  for (i = 0; i < m->msg_count; i++)
712  {
713  struct Email *e = m->emails[i];
714  if (!e)
715  break;
716  if (!e->deleted && !e->old && !e->read)
717  mutt_set_flag(m, e, MUTT_OLD, true);
718  }
719  }
720 
721  if (move_messages)
722  {
723  if (m->verbose)
724  mutt_message(_("Moving read messages to %s..."), mutt_b2s(mbox));
725 
726 #ifdef USE_IMAP
727  /* try to use server-side copy first */
728  i = 1;
729 
730  if ((m->type == MUTT_IMAP) && (imap_path_probe(mutt_b2s(mbox), NULL) == MUTT_IMAP))
731  {
732  /* add messages for moving, and clear old tags, if any */
733  struct EmailList el = STAILQ_HEAD_INITIALIZER(el);
734  for (i = 0; i < m->msg_count; i++)
735  {
736  struct Email *e = m->emails[i];
737  if (!e)
738  break;
739 
740  if (e->read && !e->deleted && !(e->flagged && C_KeepFlagged))
741  {
742  e->tagged = true;
743  emaillist_add_email(&el, e);
744  }
745  else
746  e->tagged = false;
747  }
748 
749  i = imap_copy_messages(ctx->mailbox, &el, mutt_b2s(mbox), true);
750  emaillist_clear(&el);
751  }
752 
753  if (i == 0) /* success */
755  else if (i == -1) /* horrible error, bail */
756  goto cleanup;
757  else /* use regular append-copy mode */
758 #endif
759  {
760  struct Mailbox *m_read = mx_path_resolve(mutt_b2s(mbox));
761  struct Context *ctx_read = mx_mbox_open(m_read, MUTT_APPEND);
762  if (!ctx_read)
763  {
764  mailbox_free(&m_read);
765  goto cleanup;
766  }
767 
768  for (i = 0; i < m->msg_count; i++)
769  {
770  struct Email *e = m->emails[i];
771  if (!e)
772  break;
773  if (e->read && !e->deleted && !(e->flagged && C_KeepFlagged))
774  {
775  if (mutt_append_message(ctx_read->mailbox, ctx->mailbox, e,
777  {
778  mutt_set_flag(m, e, MUTT_DELETE, true);
779  mutt_set_flag(m, e, MUTT_PURGE, true);
780  }
781  else
782  {
783  mx_mbox_close(&ctx_read);
784  goto cleanup;
785  }
786  }
787  }
788 
789  mx_mbox_close(&ctx_read);
790  }
791  }
792  else if (!m->changed && (m->msg_deleted == 0))
793  {
794  if (m->verbose)
795  mutt_message(_("Mailbox is unchanged"));
796  if ((m->type == MUTT_MBOX) || (m->type == MUTT_MMDF))
797  mbox_reset_atime(m, NULL);
799  ctx_free(ptr);
800  rc = 0;
801  goto cleanup;
802  }
803 
804  /* copy mails to the trash before expunging */
805  const struct Mailbox *m_trash = mx_mbox_find(m->account, C_Trash);
806  if (purge && (m->msg_deleted != 0) && (m != m_trash))
807  {
808  if (trash_append(ctx->mailbox) != 0)
809  goto cleanup;
810  }
811 
812 #ifdef USE_IMAP
813  /* allow IMAP to preserve the deleted flag across sessions */
814  if (m->type == MUTT_IMAP)
815  {
816  int check = imap_sync_mailbox(ctx->mailbox, (purge != MUTT_NO), true);
817  if (check < 0)
818  {
819  rc = check;
820  goto cleanup;
821  }
822  }
823  else
824 #endif
825  {
826  if (purge == MUTT_NO)
827  {
828  for (i = 0; i < m->msg_count; i++)
829  {
830  struct Email *e = m->emails[i];
831  if (!e)
832  break;
833 
834  e->deleted = false;
835  e->purge = false;
836  }
837  m->msg_deleted = 0;
838  }
839 
840  if (m->changed || (m->msg_deleted != 0))
841  {
842  int check = sync_mailbox(ctx->mailbox, NULL);
843  if (check != 0)
844  {
845  rc = check;
846  goto cleanup;
847  }
848  }
849  }
850 
851  if (m->verbose)
852  {
853  if (move_messages)
854  {
855  mutt_message(_("%d kept, %d moved, %d deleted"),
856  m->msg_count - m->msg_deleted, read_msgs, m->msg_deleted);
857  }
858  else
859  mutt_message(_("%d kept, %d deleted"), m->msg_count - m->msg_deleted, m->msg_deleted);
860  }
861 
862  if ((m->msg_count == m->msg_deleted) &&
863  ((m->type == MUTT_MMDF) || (m->type == MUTT_MBOX)) &&
865  {
867  }
868 
869 #ifdef USE_SIDEBAR
870  if ((purge == MUTT_YES) && (m->msg_deleted != 0))
871  {
872  for (i = 0; i < m->msg_count; i++)
873  {
874  struct Email *e = m->emails[i];
875  if (!e)
876  break;
877  if (e->deleted && !e->read)
878  {
879  m->msg_unread--;
880  if (!e->old)
881  m->msg_new--;
882  }
883  if (e->deleted && e->flagged)
884  m->msg_flagged--;
885  }
886  }
887 #endif
888 
890  ctx_free(ptr);
891 
892  rc = 0;
893 
894 cleanup:
897  return rc;
898 }
899 
911 int mx_mbox_sync(struct Mailbox *m, int *index_hint)
912 {
913  if (!m)
914  return -1;
915 
916  int rc;
917  int purge = 1;
918  int msgcount, deleted;
919 
920  if (m->dontwrite)
921  {
922  char buf[256], tmp[256];
923  if (km_expand_key(buf, sizeof(buf), km_find_func(MENU_MAIN, OP_TOGGLE_WRITE)))
924  snprintf(tmp, sizeof(tmp), _(" Press '%s' to toggle write"), buf);
925  else
926  mutt_str_copy(tmp, _("Use 'toggle-write' to re-enable write"), sizeof(tmp));
927 
928  mutt_error(_("Mailbox is marked unwritable. %s"), tmp);
929  return -1;
930  }
931  else if (m->readonly)
932  {
933  mutt_error(_("Mailbox is read-only"));
934  return -1;
935  }
936 
937  if (!m->changed && (m->msg_deleted == 0))
938  {
939  if (m->verbose)
940  mutt_message(_("Mailbox is unchanged"));
941  return 0;
942  }
943 
944  if (m->msg_deleted != 0)
945  {
946  char buf[128];
947 
948  snprintf(buf, sizeof(buf),
949  ngettext("Purge %d deleted message?", "Purge %d deleted messages?", m->msg_deleted),
950  m->msg_deleted);
951  purge = query_quadoption(C_Delete, buf);
952  if (purge == MUTT_ABORT)
953  return -1;
954  if (purge == MUTT_NO)
955  {
956  if (!m->changed)
957  return 0; /* nothing to do! */
958  /* let IMAP servers hold on to D flags */
959  if (m->type != MUTT_IMAP)
960  {
961  for (int i = 0; i < m->msg_count; i++)
962  {
963  struct Email *e = m->emails[i];
964  if (!e)
965  break;
966  e->deleted = false;
967  e->purge = false;
968  }
969  m->msg_deleted = 0;
970  }
971  }
973  }
974 
975  /* really only for IMAP - imap_sync_mailbox results in a call to
976  * ctx_update_tables, so m->msg_deleted is 0 when it comes back */
977  msgcount = m->msg_count;
978  deleted = m->msg_deleted;
979 
980  const struct Mailbox *m_trash = mx_mbox_find(m->account, C_Trash);
981  if (purge && (m->msg_deleted != 0) && (m != m_trash))
982  {
983  if (trash_append(m) != 0)
984  return -1;
985  }
986 
987 #ifdef USE_IMAP
988  if (m->type == MUTT_IMAP)
989  rc = imap_sync_mailbox(m, purge, false);
990  else
991 #endif
992  rc = sync_mailbox(m, index_hint);
993  if (rc >= 0)
994  {
995 #ifdef USE_IMAP
996  if ((m->type == MUTT_IMAP) && !purge)
997  {
998  if (m->verbose)
999  mutt_message(_("Mailbox checkpointed"));
1000  }
1001  else
1002 #endif
1003  {
1004  if (m->verbose)
1005  mutt_message(_("%d kept, %d deleted"), msgcount - deleted, deleted);
1006  }
1007 
1008  mutt_sleep(0);
1009 
1010  if ((m->msg_count == m->msg_deleted) &&
1011  ((m->type == MUTT_MBOX) || (m->type == MUTT_MMDF)) &&
1013  {
1014  unlink(mailbox_path(m));
1016  return 0;
1017  }
1018 
1019  /* if we haven't deleted any messages, we don't need to resort */
1020  /* ... except for certain folder formats which need "unsorted"
1021  * sort order in order to synchronize folders.
1022  *
1023  * MH and maildir are safe. mbox-style seems to need re-sorting,
1024  * at least with the new threading code. */
1025  if (purge || ((m->type != MUTT_MAILDIR) && (m->type != MUTT_MH)))
1026  {
1027  /* IMAP does this automatically after handling EXPUNGE */
1028  if (m->type != MUTT_IMAP)
1029  {
1032  }
1033  }
1034  }
1035 
1036  return rc;
1037 }
1038 
1046 struct Message *mx_msg_open_new(struct Mailbox *m, struct Email *e, MsgOpenFlags flags)
1047 {
1048  if (!m)
1049  return NULL;
1050 
1051  struct Address *p = NULL;
1052  struct Message *msg = NULL;
1053 
1054  if (!m->mx_ops || !m->mx_ops->msg_open_new)
1055  {
1056  mutt_debug(LL_DEBUG1, "function unimplemented for mailbox type %d\n", m->type);
1057  return NULL;
1058  }
1059 
1060  msg = mutt_mem_calloc(1, sizeof(struct Message));
1061  msg->write = true;
1062 
1063  if (e)
1064  {
1065  msg->flags.flagged = e->flagged;
1066  msg->flags.replied = e->replied;
1067  msg->flags.read = e->read;
1068  msg->flags.draft = (flags & MUTT_SET_DRAFT);
1069  msg->received = e->received;
1070  }
1071 
1072  if (msg->received == 0)
1073  msg->received = mutt_date_epoch();
1074 
1075  if (m->mx_ops->msg_open_new(m, msg, e) == 0)
1076  {
1077  if (m->type == MUTT_MMDF)
1078  fputs(MMDF_SEP, msg->fp);
1079 
1080  if (((m->type == MUTT_MBOX) || (m->type == MUTT_MMDF)) && (flags & MUTT_ADD_FROM))
1081  {
1082  if (e)
1083  {
1084  p = TAILQ_FIRST(&e->env->return_path);
1085  if (!p)
1086  p = TAILQ_FIRST(&e->env->sender);
1087  if (!p)
1088  p = TAILQ_FIRST(&e->env->from);
1089  }
1090 
1091  // Force a 'C' locale for the date, so that day/month names are in English
1092  locale_t loc = newlocale(LC_TIME_MASK, "C", 0);
1093  char buf[64] = { 0 };
1094  struct tm tm = mutt_date_localtime(msg->received);
1095  strftime_l(buf, sizeof(buf), "%a %b %e %H:%M:%S %Y", &tm, loc);
1096  freelocale(loc);
1097  fprintf(msg->fp, "From %s %s\n", p ? p->mailbox : NONULL(Username), buf);
1098  }
1099  }
1100  else
1101  FREE(&msg);
1102 
1103  return msg;
1104 }
1105 
1114 int mx_mbox_check(struct Mailbox *m, int *index_hint)
1115 {
1116  if (!m || !m->mx_ops)
1117  return -1;
1118 
1119  int rc = m->mx_ops->mbox_check(m, index_hint);
1120  if ((rc == MUTT_NEW_MAIL) || (rc == MUTT_REOPENED))
1122 
1123  return rc;
1124 }
1125 
1133 struct Message *mx_msg_open(struct Mailbox *m, int msgno)
1134 {
1135  if (!m)
1136  return NULL;
1137 
1138  struct Message *msg = NULL;
1139 
1140  if (!m->mx_ops || !m->mx_ops->msg_open)
1141  {
1142  mutt_debug(LL_DEBUG1, "function not implemented for mailbox type %d\n", m->type);
1143  return NULL;
1144  }
1145 
1146  msg = mutt_mem_calloc(1, sizeof(struct Message));
1147  if (m->mx_ops->msg_open(m, msg, msgno) < 0)
1148  FREE(&msg);
1149 
1150  return msg;
1151 }
1152 
1160 int mx_msg_commit(struct Mailbox *m, struct Message *msg)
1161 {
1162  if (!m || !m->mx_ops || !m->mx_ops->msg_commit)
1163  return -1;
1164 
1165  if (!(msg->write && m->append))
1166  {
1167  mutt_debug(LL_DEBUG1, "msg->write = %d, m->append = %d\n", msg->write, m->append);
1168  return -1;
1169  }
1170 
1171  return m->mx_ops->msg_commit(m, msg);
1172 }
1173 
1181 int mx_msg_close(struct Mailbox *m, struct Message **msg)
1182 {
1183  if (!m || !msg || !*msg)
1184  return 0;
1185 
1186  int rc = 0;
1187 
1188  if (m->mx_ops && m->mx_ops->msg_close)
1189  rc = m->mx_ops->msg_close(m, *msg);
1190 
1191  if ((*msg)->path)
1192  {
1193  mutt_debug(LL_DEBUG1, "unlinking %s\n", (*msg)->path);
1194  unlink((*msg)->path);
1195  FREE(&(*msg)->path);
1196  }
1197 
1198  FREE(&(*msg)->committed_path);
1199  FREE(msg);
1200  return rc;
1201 }
1202 
1207 void mx_alloc_memory(struct Mailbox *m)
1208 {
1209  size_t s = MAX(sizeof(struct Email *), sizeof(int));
1210 
1211  if ((m->email_max + 25) * s < m->email_max * s)
1212  {
1213  mutt_error(_("Out of memory"));
1214  mutt_exit(1);
1215  }
1216 
1217  m->email_max += 25;
1218  if (m->emails)
1219  {
1220  mutt_mem_realloc(&m->emails, sizeof(struct Email *) * m->email_max);
1221  mutt_mem_realloc(&m->v2r, sizeof(int) * m->email_max);
1222  }
1223  else
1224  {
1225  m->emails = mutt_mem_calloc(m->email_max, sizeof(struct Email *));
1226  m->v2r = mutt_mem_calloc(m->email_max, sizeof(int));
1227  }
1228  for (int i = m->email_max - 25; i < m->email_max; i++)
1229  {
1230  m->emails[i] = NULL;
1231  m->v2r[i] = -1;
1232  }
1233 }
1234 
1242 int mx_check_empty(const char *path)
1243 {
1244  switch (mx_path_probe(path))
1245  {
1246  case MUTT_MBOX:
1247  case MUTT_MMDF:
1248  return mutt_file_check_empty(path);
1249  case MUTT_MH:
1250  return mh_check_empty(path);
1251  case MUTT_MAILDIR:
1252  return maildir_check_empty(path);
1253 #ifdef USE_IMAP
1254  case MUTT_IMAP:
1255  {
1256  int rc = imap_path_status(path, false);
1257  if (rc < 0)
1258  return -1;
1259  if (rc == 0)
1260  return 1;
1261  return 0;
1262  }
1263 #endif
1264  default:
1265  errno = EINVAL;
1266  return -1;
1267  }
1268  /* not reached */
1269 }
1270 
1281 int mx_tags_edit(struct Mailbox *m, const char *tags, char *buf, size_t buflen)
1282 {
1283  if (!m)
1284  return -1;
1285 
1286  if (m->mx_ops->tags_edit)
1287  return m->mx_ops->tags_edit(m, tags, buf, buflen);
1288 
1289  mutt_message(_("Folder doesn't support tagging, aborting"));
1290  return -1;
1291 }
1292 
1301 int mx_tags_commit(struct Mailbox *m, struct Email *e, char *tags)
1302 {
1303  if (!m)
1304  return -1;
1305 
1306  if (m->mx_ops->tags_commit)
1307  return m->mx_ops->tags_commit(m, e, tags);
1308 
1309  mutt_message(_("Folder doesn't support tagging, aborting"));
1310  return -1;
1311 }
1312 
1319 {
1320  return m && m->mx_ops->tags_commit && m->mx_ops->tags_edit;
1321 }
1322 
1328 enum MailboxType mx_path_probe(const char *path)
1329 {
1330  if (!path)
1331  return MUTT_UNKNOWN;
1332 
1333  enum MailboxType rc;
1334 
1335  // First, search the non-local Mailbox types (is_local == false)
1336  for (const struct MxOps **ops = mx_ops; *ops; ops++)
1337  {
1338  if ((*ops)->is_local)
1339  continue;
1340  rc = (*ops)->path_probe(path, NULL);
1341  if (rc != MUTT_UNKNOWN)
1342  return rc;
1343  }
1344 
1345  struct stat st = { 0 };
1346  if (stat(path, &st) != 0)
1347  {
1348  mutt_debug(LL_DEBUG1, "unable to stat %s: %s (errno %d)\n", path, strerror(errno), errno);
1349  return MUTT_UNKNOWN;
1350  }
1351 
1352  if (S_ISFIFO(st.st_mode))
1353  {
1354  mutt_error(_("Can't open %s: it is a pipe"), path);
1355  return MUTT_UNKNOWN;
1356  }
1357 
1358  // Next, search the local Mailbox types (is_local == true)
1359  for (const struct MxOps **ops = mx_ops; *ops; ops++)
1360  {
1361  if (!(*ops)->is_local)
1362  continue;
1363  rc = (*ops)->path_probe(path, &st);
1364  if (rc != MUTT_UNKNOWN)
1365  return rc;
1366  }
1367 
1368  return rc;
1369 }
1370 
1374 int mx_path_canon(char *buf, size_t buflen, const char *folder, enum MailboxType *type)
1375 {
1376  if (!buf)
1377  return -1;
1378 
1379  for (size_t i = 0; i < 3; i++)
1380  {
1381  /* Look for !! ! - < > or ^ followed by / or NUL */
1382  if ((buf[0] == '!') && (buf[1] == '!'))
1383  {
1384  if (((buf[2] == '/') || (buf[2] == '\0')))
1385  {
1386  mutt_str_inline_replace(buf, buflen, 2, LastFolder);
1387  }
1388  }
1389  else if ((buf[0] == '+') || (buf[0] == '='))
1390  {
1391  size_t folder_len = mutt_str_len(folder);
1392  if ((folder_len > 0) && (folder[folder_len - 1] != '/'))
1393  {
1394  buf[0] = '/';
1395  mutt_str_inline_replace(buf, buflen, 0, folder);
1396  }
1397  else
1398  {
1399  mutt_str_inline_replace(buf, buflen, 1, folder);
1400  }
1401  }
1402  else if ((buf[1] == '/') || (buf[1] == '\0'))
1403  {
1404  if (buf[0] == '!')
1405  {
1406  mutt_str_inline_replace(buf, buflen, 1, C_Spoolfile);
1407  }
1408  else if (buf[0] == '-')
1409  {
1410  mutt_str_inline_replace(buf, buflen, 1, LastFolder);
1411  }
1412  else if (buf[0] == '<')
1413  {
1414  mutt_str_inline_replace(buf, buflen, 1, C_Record);
1415  }
1416  else if (buf[0] == '>')
1417  {
1418  mutt_str_inline_replace(buf, buflen, 1, C_Mbox);
1419  }
1420  else if (buf[0] == '^')
1421  {
1422  mutt_str_inline_replace(buf, buflen, 1, CurrentFolder);
1423  }
1424  else if (buf[0] == '~')
1425  {
1426  mutt_str_inline_replace(buf, buflen, 1, HomeDir);
1427  }
1428  }
1429  else if (buf[0] == '@')
1430  {
1431  /* elm compatibility, @ expands alias to user name */
1432  struct AddressList *al = alias_lookup(buf + 1);
1433  if (!al || TAILQ_EMPTY(al))
1434  break;
1435 
1436  struct Email *e = email_new();
1437  e->env = mutt_env_new();
1438  mutt_addrlist_copy(&e->env->from, al, false);
1439  mutt_addrlist_copy(&e->env->to, al, false);
1440  mutt_default_save(buf, buflen, e);
1441  email_free(&e);
1442  break;
1443  }
1444  else
1445  {
1446  break;
1447  }
1448  }
1449 
1450  // if (!folder) //XXX - use inherited version, or pass NULL to backend?
1451  // return -1;
1452 
1453  enum MailboxType type2 = mx_path_probe(buf);
1454  if (type)
1455  *type = type2;
1456  const struct MxOps *ops = mx_get_ops(type2);
1457  if (!ops || !ops->path_canon)
1458  return -1;
1459 
1460  if (ops->path_canon(buf, buflen) < 0)
1461  {
1462  mutt_path_canon(buf, buflen, HomeDir, true);
1463  }
1464 
1465  return 0;
1466 }
1467 
1475 int mx_path_canon2(struct Mailbox *m, const char *folder)
1476 {
1477  if (!m)
1478  return -1;
1479 
1480  char buf[PATH_MAX];
1481 
1482  if (m->realpath)
1483  mutt_str_copy(buf, m->realpath, sizeof(buf));
1484  else
1485  mutt_str_copy(buf, mailbox_path(m), sizeof(buf));
1486 
1487  int rc = mx_path_canon(buf, sizeof(buf), folder, &m->type);
1488 
1489  mutt_str_replace(&m->realpath, buf);
1490 
1491  if (rc >= 0)
1492  {
1493  m->mx_ops = mx_get_ops(m->type);
1495  }
1496 
1497  return rc;
1498 }
1499 
1503 int mx_path_pretty(char *buf, size_t buflen, const char *folder)
1504 {
1505  enum MailboxType type = mx_path_probe(buf);
1506  const struct MxOps *ops = mx_get_ops(type);
1507  if (!ops)
1508  return -1;
1509 
1510  if (!ops->path_canon)
1511  return -1;
1512 
1513  if (ops->path_canon(buf, buflen) < 0)
1514  return -1;
1515 
1516  if (!ops->path_pretty)
1517  return -1;
1518 
1519  if (ops->path_pretty(buf, buflen, folder) < 0)
1520  return -1;
1521 
1522  return 0;
1523 }
1524 
1528 int mx_path_parent(char *buf, size_t buflen)
1529 {
1530  if (!buf)
1531  return -1;
1532 
1533  return 0;
1534 }
1535 
1545 {
1546  if (!m || !m->mx_ops || !m->mx_ops->msg_padding_size)
1547  return 0;
1548 
1549  return m->mx_ops->msg_padding_size(m);
1550 }
1551 
1558 struct Account *mx_ac_find(struct Mailbox *m)
1559 {
1560  if (!m || !m->mx_ops)
1561  return NULL;
1562 
1563  struct Account *np = NULL;
1564  TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
1565  {
1566  if (np->type != m->type)
1567  continue;
1568 
1569  if (m->mx_ops->ac_find(np, m->realpath))
1570  return np;
1571  }
1572 
1573  return NULL;
1574 }
1575 
1582 struct Mailbox *mx_mbox_find(struct Account *a, const char *path)
1583 {
1584  if (!a || !path)
1585  return NULL;
1586 
1587  struct MailboxNode *np = NULL;
1588  struct Url *url_p = NULL;
1589  struct Url *url_a = NULL;
1590 
1591  const bool use_url = (a->type == MUTT_IMAP);
1592  if (use_url)
1593  {
1594  url_p = url_parse(path);
1595  if (!url_p)
1596  goto done;
1597  }
1598 
1599  STAILQ_FOREACH(np, &a->mailboxes, entries)
1600  {
1601  if (!use_url)
1602  {
1603  if (mutt_str_equal(np->mailbox->realpath, path))
1604  return np->mailbox;
1605  continue;
1606  }
1607 
1608  url_free(&url_a);
1609  url_a = url_parse(np->mailbox->realpath);
1610  if (!url_a)
1611  continue;
1612 
1613  if (!mutt_istr_equal(url_a->host, url_p->host))
1614  continue;
1615  if (url_p->user && !mutt_istr_equal(url_a->user, url_p->user))
1616  continue;
1617  if (a->type == MUTT_IMAP)
1618  {
1619  if (imap_mxcmp(url_a->path, url_p->path) == 0)
1620  break;
1621  }
1622  else
1623  {
1624  if (mutt_str_equal(url_a->path, url_p->path))
1625  break;
1626  }
1627  }
1628 
1629 done:
1630  url_free(&url_p);
1631  url_free(&url_a);
1632 
1633  if (!np)
1634  return NULL;
1635  return np->mailbox;
1636 }
1637 
1644 struct Mailbox *mx_mbox_find2(const char *path)
1645 {
1646  if (!path)
1647  return NULL;
1648 
1649  char buf[PATH_MAX];
1650  mutt_str_copy(buf, path, sizeof(buf));
1651  mx_path_canon(buf, sizeof(buf), C_Folder, NULL);
1652 
1653  struct Account *np = NULL;
1654  TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
1655  {
1656  struct Mailbox *m = mx_mbox_find(np, buf);
1657  if (m)
1658  return m;
1659  }
1660 
1661  return NULL;
1662 }
1663 
1671 struct Mailbox *mx_path_resolve(const char *path)
1672 {
1673  if (!path)
1674  return NULL;
1675 
1676  struct Mailbox *m = mx_mbox_find2(path);
1677  if (m)
1678  return m;
1679 
1680  m = mailbox_new();
1681  m->flags = MB_HIDDEN;
1682  mutt_buffer_strcpy(&m->pathbuf, path);
1684 
1685  return m;
1686 }
1687 
1691 int mx_ac_add(struct Account *a, struct Mailbox *m)
1692 {
1693  if (!a || !m || !m->mx_ops || !m->mx_ops->ac_add)
1694  return -1;
1695 
1696  if (m->mx_ops->ac_add(a, m) < 0)
1697  return -1;
1698 
1699  account_mailbox_add(a, m);
1700  return 0;
1701 }
1702 
1707 int mx_ac_remove(struct Mailbox *m)
1708 {
1709  if (!m || !m->account)
1710  return -1;
1711 
1712  struct Account *a = m->account;
1714  mailbox_free(&m);
1715  if (STAILQ_EMPTY(&a->mailboxes))
1716  {
1718  }
1719  return 0;
1720 }
1721 
1725 int mx_mbox_check_stats(struct Mailbox *m, int flags)
1726 {
1727  if (!m)
1728  return -1;
1729 
1730  return m->mx_ops->mbox_check_stats(m, flags);
1731 }
1732 
1742 int mx_save_hcache(struct Mailbox *m, struct Email *e)
1743 {
1744  if (!m->mx_ops || !m->mx_ops->msg_save_hcache)
1745  return 0;
1746 
1747  return m->mx_ops->msg_save_hcache(m, e);
1748 }
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mx.h:54
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:879
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:414
WHERE char * Username
User&#39;s login name.
Definition: globals.h:53
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:194
#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:290
int(* msg_commit)(struct Mailbox *m, struct Message *msg)
Save changes to an email.
Definition: mx.h:210
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
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:68
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:231
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:1187
#define mutt_perror(...)
Definition: logging.h:85
Clear the &#39;last-tagged&#39; pointer.
Definition: mailbox.h:168
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe()
Definition: imap.c:2351
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:1114
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:718
Update internal tables.
Definition: mailbox.h:167
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:1691
int mx_path_canon2(struct Mailbox *m, const char *folder)
Canonicalise the path to realpath.
Definition: mx.c:1475
&#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:603
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:844
struct MxOps MxNotmuchOps
Notmuch Mailbox - Implements MxOps.
Definition: notmuch.c:2583
The "currently-open" mailbox.
struct MxOps MxMhOps
MH Mailbox - Implements MxOps.
Definition: mh.c:810
int(* mbox_check_stats)(struct Mailbox *m, int flags)
Check the Mailbox statistics.
Definition: mx.h:164
struct tm mutt_date_localtime(time_t t)
Converts calendar time to a broken-down time structure expressed in user timezone.
Definition: date.c:641
int(* mbox_sync)(struct Mailbox *m, int *index_hint)
Save changes to the Mailbox.
Definition: mx.h:173
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:1503
struct HashTable * label_hash
Hash Table for x-labels.
Definition: mailbox.h:129
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:1528
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:1112
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:447
#define mutt_message(...)
Definition: logging.h:83
WHERE bool C_Confirmappend
Config: Confirm before appending emails to a mailbox.
Definition: globals.h:204
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:40
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:494
bool peekonly
Just taking a glance, revert atime.
Definition: mailbox.h:117
struct NntpAccountData * adata
Definition: lib.h:155
enum UrlScheme scheme
Scheme, e.g. U_SMTPS.
Definition: url.h:68
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:737
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:176
struct MxOps MxMaildirOps
Maildir Mailbox - Implements MxOps.
Definition: maildir.c:709
String manipulation buffer.
Definition: buffer.h:33
WHERE bool C_SaveEmpty
Config: (mbox,mmdf) Preserve empty mailboxes.
Definition: globals.h:241
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1207
#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:1582
struct Context * mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:265
WHERE char * LastFolder
Previously selected mailbox.
Definition: globals.h:56
char * mailbox
Mailbox and host address.
Definition: address.h:37
Messages to be purged (bypass trash)
Definition: mutt.h:100
int(* ac_add)(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account.
Definition: mx.h:127
Index panel (list of emails)
Definition: keymap.h:78
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:127
Email list was changed.
Definition: mailbox.h:165
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
#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:1742
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1644
WHERE bool OptNeedRescore
(pseudo) set when the &#39;score&#39; command is used
Definition: options.h:42
struct Account * mx_ac_find(struct Mailbox *m)
Find the Account owning a Mailbox.
Definition: mx.c:1558
void mutt_default_save(char *path, size_t pathlen, struct Email *e)
Find the default save path for an email.
Definition: hook.c:653
All user-callable functions.
#define MUTT_CM_NO_FLAGS
No flags are set.
Definition: copy.h:34
Container for Accounts, Notifications.
Definition: neomutt.h:36
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:1439
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:1474
char * HomeDir
User&#39;s home directory.
Definition: globals.h:50
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:201
Assorted sorting methods.
int(* mbox_open)(struct Mailbox *m)
Open a Mailbox.
Definition: mx.h:136
int(* mbox_open_append)(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending.
Definition: mx.h:145
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:274
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1181
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:1536
int mx_check_empty(const char *path)
Is the mailbox empty.
Definition: mx.c:1242
struct Mailbox * mailbox
Definition: context.h:50
struct HashTable * id_hash
Hash Table by msg id.
Definition: mailbox.h:127
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:1304
struct AddressList from
Email&#39;s &#39;From&#39; list.
Definition: envelope.h:57
struct AddressList * alias_lookup(const char *name)
Find an Alias.
Definition: alias.c:277
bool readonly
Don&#39;t allow changes to the mailbox.
Definition: mailbox.h:119
The Context has been opened.
Definition: context.h:61
WHERE char * C_Mbox
Config: Folder that receives read emails (see Move)
Definition: globals.h:117
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:1301
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:424
struct MxOps MxImapOps
IMAP Mailbox - Implements MxOps.
Definition: imap.c:2431
char * group
Definition: lib.h:142
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: globals.h:55
struct MxOps MxPopOps
POP Mailbox - Implements MxOps.
Definition: pop.c:1277
int opened
Number of times mailbox is opened.
Definition: mailbox.h:132
Email Aliases.
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:1046
Compressed mbox local mailbox type.
int(* msg_padding_size)(struct Mailbox *m)
Bytes of padding between messages.
Definition: mx.h:226
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
struct HashTable * subj_hash
Hash Table by subject.
Definition: mailbox.h:128
#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:699
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:891
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:899
Old messages.
Definition: mutt.h:94
Email list needs resorting.
Definition: mailbox.h:166
#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:247
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:119
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:1603
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:456
Messages to be deleted.
Definition: mutt.h:98
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:552
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:232
int km_expand_key(char *s, size_t len, struct Keymap *map)
Get the key string bound to a Keymap.
Definition: keymap.c:871
struct MxOps MxMmdfOps
MMDF Mailbox - Implements MxOps.
Definition: mbox.c:1882
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:235
&#39;mmdf&#39; Mailbox type
Definition: mailbox.h:49
int(* msg_open)(struct Mailbox *m, struct Message *msg, int msgno)
Open an email message in a Mailbox.
Definition: mx.h:191
POP network mailbox.
int(* mbox_check)(struct Mailbox *m, int *index_hint)
Check for new mail.
Definition: mx.h:154
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
bool verbose
Display status messages?
Definition: mailbox.h:118
const struct MxOps * mx_ops
MXAPI callback functions.
Definition: mailbox.h:111
char * host
Host.
Definition: url.h:71
#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:1281
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:450
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:1561
void mutt_exit(int code)
Leave NeoMutt NOW.
Definition: main.c:138
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:360
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:639
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:1328
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, NotifyContext, EventContext.
Definition: notify_type.h:37
Duplicate the structure of an entire email.
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:327
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:463
WHERE char * C_Spoolfile
Config: Inbox.
Definition: globals.h:140
bool flagged
Marked important?
Definition: email.h:43
void emaillist_clear(struct EmailList *el)
Drop a private list of Emails.
Definition: email.c:123
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:724
int mx_mbox_sync(struct Mailbox *m, int *index_hint)
Save changes to mailbox.
Definition: mx.c:911
int(* mbox_close)(struct Mailbox *m)
Close a Mailbox.
Definition: mx.h:181
static int mx_open_mailbox_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox for appending.
Definition: mx.c:198
int msg_new
Number of new messages.
Definition: mailbox.h:95
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:1725
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:284
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:1160
int mutt_file_check_empty(const char *path)
Is the mailbox empty.
Definition: file.c:1405
int mutt_append_message(struct Mailbox *dest, struct Mailbox *src, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags)
Append a message.
Definition: copy.c:888
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:219
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1671
Mapping between user-readable string and a constant.
Definition: mapping.h:31
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition: mx.c:181
#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:1853
#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:54
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:299
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
int mx_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Wrapper for MxOps::msg_padding_size()
Definition: mx.c:1544
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:1021
bool mx_tags_is_supported(struct Mailbox *m)
return true if mailbox support tagging
Definition: mx.c:1318
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:68
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:1707
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.
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:1374
int imap_fast_trash(struct Mailbox *m, char *dest)
Use server COPY command to copy deleted messages to trash.
Definition: imap.c:1362
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:174
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:1133
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
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:234
int(* tags_commit)(struct Mailbox *m, struct Email *e, char *buf)
Save the tags to a message.
Definition: mx.h:257
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:1601