NeoMutt  2021-02-05-89-gabe350
Teaching an old dog new tricks
DOXYGEN
mbox.c
Go to the documentation of this file.
1 
33 #include "config.h"
34 #include <fcntl.h>
35 #include <inttypes.h> // IWYU pragma: keep
36 #include <limits.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <sys/stat.h>
41 #include <time.h>
42 #include <unistd.h>
43 #include <utime.h>
44 #include "mutt/lib.h"
45 #include "address/lib.h"
46 #include "config/lib.h"
47 #include "email/lib.h"
48 #include "core/lib.h"
49 #include "mutt.h"
50 #include "lib.h"
51 #include "copy.h"
52 #include "mutt_globals.h"
53 #include "mutt_header.h"
54 #include "muttlib.h"
55 #include "mx.h"
56 #include "progress.h"
57 #include "protos.h"
58 #include "sort.h"
59 
63 struct MUpdate
64 {
65  bool valid;
66  LOFF_T hdr;
67  LOFF_T body;
68  long lines;
69  LOFF_T length;
70 };
71 
75 static void mbox_adata_free(void **ptr)
76 {
77  if (!ptr || !*ptr)
78  return;
79 
80  struct MboxAccountData *m = *ptr;
81 
82  mutt_file_fclose(&m->fp);
83  FREE(ptr);
84 }
85 
90 static struct MboxAccountData *mbox_adata_new(void)
91 {
92  return mutt_mem_calloc(1, sizeof(struct MboxAccountData));
93 }
94 
100 static struct MboxAccountData *mbox_adata_get(struct Mailbox *m)
101 {
102  if (!m)
103  return NULL;
104  if ((m->type != MUTT_MBOX) && (m->type != MUTT_MMDF))
105  return NULL;
106  struct Account *a = m->account;
107  if (!a)
108  return NULL;
109  return a->adata;
110 }
111 
118 static int init_mailbox(struct Mailbox *m)
119 {
120  if (!m || !m->account)
121  return -1;
122  if ((m->type != MUTT_MBOX) && (m->type != MUTT_MMDF))
123  return -1;
124  if (m->account->adata)
125  return 0;
126 
127  m->account->adata = mbox_adata_new();
129  return 0;
130 }
131 
140 static int mbox_lock_mailbox(struct Mailbox *m, bool excl, bool retry)
141 {
142  struct MboxAccountData *adata = mbox_adata_get(m);
143  if (!adata)
144  return -1;
145 
146  int rc = mutt_file_lock(fileno(adata->fp), excl, retry);
147  if (rc == 0)
148  adata->locked = true;
149  else if (retry && !excl)
150  {
151  m->readonly = true;
152  return 0;
153  }
154 
155  return rc;
156 }
157 
162 static void mbox_unlock_mailbox(struct Mailbox *m)
163 {
164  struct MboxAccountData *adata = mbox_adata_get(m);
165  if (!adata)
166  return;
167 
168  if (adata->locked)
169  {
170  fflush(adata->fp);
171 
172  mutt_file_unlock(fileno(adata->fp));
173  adata->locked = false;
174  }
175 }
176 
183 {
184  if (!m)
185  return MX_OPEN_ERROR;
186 
187  struct MboxAccountData *adata = mbox_adata_get(m);
188  if (!adata)
189  return MX_OPEN_ERROR;
190 
191  char buf[8192];
192  char return_path[1024];
193  int count = 0;
194  int lines;
195  time_t t;
196  LOFF_T loc, tmploc;
197  struct Email *e = NULL;
198  struct stat sb;
199  struct Progress progress;
200 
201  if (stat(mailbox_path(m), &sb) == -1)
202  {
204  return MX_OPEN_ERROR;
205  }
208  m->size = sb.st_size;
209 
210  buf[sizeof(buf) - 1] = '\0';
211 
212  if (m->verbose)
213  {
214  char msg[PATH_MAX];
215  snprintf(msg, sizeof(msg), _("Reading %s..."), mailbox_path(m));
216  mutt_progress_init(&progress, msg, MUTT_PROGRESS_READ, 0);
217  }
218 
219  while (true)
220  {
221  if (!fgets(buf, sizeof(buf) - 1, adata->fp))
222  break;
223 
224  if (SigInt == 1)
225  break;
226 
227  if (mutt_str_equal(buf, MMDF_SEP))
228  {
229  loc = ftello(adata->fp);
230  if (loc < 0)
231  return MX_OPEN_ERROR;
232 
233  count++;
234  if (m->verbose)
235  mutt_progress_update(&progress, count, (int) (loc / (m->size / 100 + 1)));
236 
237  if (m->msg_count == m->email_max)
238  mx_alloc_memory(m);
239  e = email_new();
240  m->emails[m->msg_count] = e;
241  e->offset = loc;
242  e->index = m->msg_count;
243 
244  if (!fgets(buf, sizeof(buf) - 1, adata->fp))
245  {
246  /* TODO: memory leak??? */
247  mutt_debug(LL_DEBUG1, "unexpected EOF\n");
248  break;
249  }
250 
251  return_path[0] = '\0';
252 
253  if (!is_from(buf, return_path, sizeof(return_path), &t))
254  {
255  if (fseeko(adata->fp, loc, SEEK_SET) != 0)
256  {
257  mutt_debug(LL_DEBUG1, "#1 fseek() failed\n");
258  mutt_error(_("Mailbox is corrupt"));
259  return MX_OPEN_ERROR;
260  }
261  }
262  else
263  e->received = t - mutt_date_local_tz(t);
264 
265  e->env = mutt_rfc822_read_header(adata->fp, e, false, false);
266 
267  loc = ftello(adata->fp);
268  if (loc < 0)
269  return MX_OPEN_ERROR;
270 
271  if ((e->body->length > 0) && (e->lines > 0))
272  {
273  tmploc = loc + e->body->length;
274 
275  if ((tmploc > 0) && (tmploc < m->size))
276  {
277  if ((fseeko(adata->fp, tmploc, SEEK_SET) != 0) ||
278  !fgets(buf, sizeof(buf) - 1, adata->fp) || !mutt_str_equal(MMDF_SEP, buf))
279  {
280  if (fseeko(adata->fp, loc, SEEK_SET) != 0)
281  mutt_debug(LL_DEBUG1, "#2 fseek() failed\n");
282  e->body->length = -1;
283  }
284  }
285  else
286  e->body->length = -1;
287  }
288  else
289  e->body->length = -1;
290 
291  if (e->body->length < 0)
292  {
293  lines = -1;
294  do
295  {
296  loc = ftello(adata->fp);
297  if (loc < 0)
298  return MX_OPEN_ERROR;
299  if (!fgets(buf, sizeof(buf) - 1, adata->fp))
300  break;
301  lines++;
302  } while (!mutt_str_equal(buf, MMDF_SEP));
303 
304  e->lines = lines;
305  e->body->length = loc - e->body->offset;
306  }
307 
308  if (TAILQ_EMPTY(&e->env->return_path) && return_path[0])
309  mutt_addrlist_parse(&e->env->return_path, return_path);
310 
311  if (TAILQ_EMPTY(&e->env->from))
312  mutt_addrlist_copy(&e->env->from, &e->env->return_path, false);
313 
314  m->msg_count++;
315  }
316  else
317  {
318  mutt_debug(LL_DEBUG1, "corrupt mailbox\n");
319  mutt_error(_("Mailbox is corrupt"));
320  return MX_OPEN_ERROR;
321  }
322  }
323 
324  if (SigInt == 1)
325  {
326  SigInt = 0;
327  return MX_OPEN_ABORT; /* action aborted */
328  }
329 
330  return MX_OPEN_OK;
331 }
332 
345 {
346  if (!m)
347  return MX_OPEN_ERROR;
348 
349  struct MboxAccountData *adata = mbox_adata_get(m);
350  if (!adata)
351  return MX_OPEN_ERROR;
352 
353  struct stat sb;
354  char buf[8192], return_path[256];
355  struct Email *e_cur = NULL;
356  time_t t;
357  int count = 0, lines = 0;
358  LOFF_T loc;
359  struct Progress progress;
360 
361  /* Save information about the folder at the time we opened it. */
362  if (stat(mailbox_path(m), &sb) == -1)
363  {
365  return MX_OPEN_ERROR;
366  }
367 
368  m->size = sb.st_size;
371 
372  if (!m->readonly)
373  m->readonly = access(mailbox_path(m), W_OK) ? true : false;
374 
375  if (m->verbose)
376  {
377  char msg[PATH_MAX];
378  snprintf(msg, sizeof(msg), _("Reading %s..."), mailbox_path(m));
379  mutt_progress_init(&progress, msg, MUTT_PROGRESS_READ, 0);
380  }
381 
382  loc = ftello(adata->fp);
383  while ((fgets(buf, sizeof(buf), adata->fp)) && (SigInt != 1))
384  {
385  if (is_from(buf, return_path, sizeof(return_path), &t))
386  {
387  /* Save the Content-Length of the previous message */
388  if (count > 0)
389  {
390  struct Email *e = m->emails[m->msg_count - 1];
391  if (e->body->length < 0)
392  {
393  e->body->length = loc - e->body->offset - 1;
394  if (e->body->length < 0)
395  e->body->length = 0;
396  }
397  if (!e->lines)
398  e->lines = lines ? lines - 1 : 0;
399  }
400 
401  count++;
402 
403  if (m->verbose)
404  {
405  mutt_progress_update(&progress, count,
406  (int) (ftello(adata->fp) / (m->size / 100 + 1)));
407  }
408 
409  if (m->msg_count == m->email_max)
410  mx_alloc_memory(m);
411 
412  m->emails[m->msg_count] = email_new();
413  e_cur = m->emails[m->msg_count];
414  e_cur->received = t - mutt_date_local_tz(t);
415  e_cur->offset = loc;
416  e_cur->index = m->msg_count;
417 
418  e_cur->env = mutt_rfc822_read_header(adata->fp, e_cur, false, false);
419 
420  /* if we know how long this message is, either just skip over the body,
421  * or if we don't know how many lines there are, count them now (this will
422  * save time by not having to search for the next message marker). */
423  if (e_cur->body->length > 0)
424  {
425  LOFF_T tmploc;
426 
427  loc = ftello(adata->fp);
428 
429  /* The test below avoids a potential integer overflow if the
430  * content-length is huge (thus necessarily invalid). */
431  tmploc = (e_cur->body->length < m->size) ? (loc + e_cur->body->length + 1) : -1;
432 
433  if ((tmploc > 0) && (tmploc < m->size))
434  {
435  /* check to see if the content-length looks valid. we expect to
436  * to see a valid message separator at this point in the stream */
437  if ((fseeko(adata->fp, tmploc, SEEK_SET) != 0) ||
438  !fgets(buf, sizeof(buf), adata->fp) || !mutt_str_startswith(buf, "From "))
439  {
440  mutt_debug(LL_DEBUG1, "bad content-length in message %d (cl=" OFF_T_FMT ")\n",
441  e_cur->index, e_cur->body->length);
442  mutt_debug(LL_DEBUG1, " LINE: %s", buf);
443  /* nope, return the previous position */
444  if ((loc < 0) || (fseeko(adata->fp, loc, SEEK_SET) != 0))
445  {
446  mutt_debug(LL_DEBUG1, "#1 fseek() failed\n");
447  }
448  e_cur->body->length = -1;
449  }
450  }
451  else if (tmploc != m->size)
452  {
453  /* content-length would put us past the end of the file, so it
454  * must be wrong */
455  e_cur->body->length = -1;
456  }
457 
458  if (e_cur->body->length != -1)
459  {
460  /* good content-length. check to see if we know how many lines
461  * are in this message. */
462  if (e_cur->lines == 0)
463  {
464  int cl = e_cur->body->length;
465 
466  /* count the number of lines in this message */
467  if ((loc < 0) || (fseeko(adata->fp, loc, SEEK_SET) != 0))
468  mutt_debug(LL_DEBUG1, "#2 fseek() failed\n");
469  while (cl-- > 0)
470  {
471  if (fgetc(adata->fp) == '\n')
472  e_cur->lines++;
473  }
474  }
475 
476  /* return to the offset of the next message separator */
477  if (fseeko(adata->fp, tmploc, SEEK_SET) != 0)
478  mutt_debug(LL_DEBUG1, "#3 fseek() failed\n");
479  }
480  }
481 
482  m->msg_count++;
483 
484  if (TAILQ_EMPTY(&e_cur->env->return_path) && return_path[0])
485  {
486  mutt_addrlist_parse(&e_cur->env->return_path, return_path);
487  }
488 
489  if (TAILQ_EMPTY(&e_cur->env->from))
490  mutt_addrlist_copy(&e_cur->env->from, &e_cur->env->return_path, false);
491 
492  lines = 0;
493  }
494  else
495  lines++;
496 
497  loc = ftello(adata->fp);
498  }
499 
500  /* Only set the content-length of the previous message if we have read more
501  * than one message during _this_ invocation. If this routine is called
502  * when new mail is received, we need to make sure not to clobber what
503  * previously was the last message since the headers may be sorted. */
504  if (count > 0)
505  {
506  struct Email *e = m->emails[m->msg_count - 1];
507  if (e->body->length < 0)
508  {
509  e->body->length = ftello(adata->fp) - e->body->offset - 1;
510  if (e->body->length < 0)
511  e->body->length = 0;
512  }
513 
514  if (!e->lines)
515  e->lines = lines ? lines - 1 : 0;
516  }
517 
518  if (SigInt == 1)
519  {
520  SigInt = 0;
521  return MX_OPEN_ABORT; /* action aborted */
522  }
523 
524  return MX_OPEN_OK;
525 }
526 
533 static int reopen_mailbox(struct Mailbox *m)
534 {
535  if (!m)
536  return -1;
537 
538  struct MboxAccountData *adata = mbox_adata_get(m);
539  if (!adata)
540  return -1;
541 
542  bool (*cmp_headers)(const struct Email *, const struct Email *) = NULL;
543  struct Email **e_old = NULL;
544  int old_msg_count;
545  bool msg_mod = false;
546  int rc = -1;
547 
548  /* silent operations */
549  m->verbose = false;
550 
551  /* our heuristics require the old mailbox to be unsorted */
552  if (C_Sort != SORT_ORDER)
553  {
554  short old_sort = C_Sort;
555  C_Sort = SORT_ORDER;
557  C_Sort = old_sort;
558  }
559 
560  e_old = NULL;
561  old_msg_count = 0;
562 
563  /* simulate a close */
564  mutt_hash_free(&m->id_hash);
567  FREE(&m->v2r);
568  if (m->readonly)
569  {
570  for (int i = 0; i < m->msg_count; i++)
571  email_free(&(m->emails[i])); /* nothing to do! */
572  FREE(&m->emails);
573  }
574  else
575  {
576  /* save the old headers */
577  old_msg_count = m->msg_count;
578  e_old = m->emails;
579  m->emails = NULL;
580  }
581 
582  m->email_max = 0; /* force allocation of new headers */
583  m->msg_count = 0;
584  m->vcount = 0;
585  m->msg_tagged = 0;
586  m->msg_deleted = 0;
587  m->msg_new = 0;
588  m->msg_unread = 0;
589  m->msg_flagged = 0;
590  m->changed = false;
591  m->id_hash = NULL;
592  m->subj_hash = NULL;
594 
595  switch (m->type)
596  {
597  case MUTT_MBOX:
598  case MUTT_MMDF:
599  cmp_headers = email_cmp_strict;
600  mutt_file_fclose(&adata->fp);
601  adata->fp = mutt_file_fopen(mailbox_path(m), "r");
602  if (!adata->fp)
603  rc = -1;
604  else if (m->type == MUTT_MBOX)
605  rc = mbox_parse_mailbox(m);
606  else
607  rc = mmdf_parse_mailbox(m);
608  break;
609 
610  default:
611  rc = -1;
612  break;
613  }
614 
615  if (rc == -1)
616  {
617  /* free the old headers */
618  for (int i = 0; i < old_msg_count; i++)
619  email_free(&(e_old[i]));
620  FREE(&e_old);
621 
622  m->verbose = true;
623  return -1;
624  }
625 
626  mutt_file_touch_atime(fileno(adata->fp));
627 
628  /* now try to recover the old flags */
629 
630  if (!m->readonly)
631  {
632  for (int i = 0; i < m->msg_count; i++)
633  {
634  bool found = false;
635 
636  /* some messages have been deleted, and new messages have been
637  * appended at the end; the heuristic is that old messages have then
638  * "advanced" towards the beginning of the folder, so we begin the
639  * search at index "i" */
640  int j;
641  for (j = i; j < old_msg_count; j++)
642  {
643  if (!e_old[j])
644  continue;
645  if (cmp_headers(m->emails[i], e_old[j]))
646  {
647  found = true;
648  break;
649  }
650  }
651  if (!found)
652  {
653  for (j = 0; (j < i) && (j < old_msg_count); j++)
654  {
655  if (!e_old[j])
656  continue;
657  if (cmp_headers(m->emails[i], e_old[j]))
658  {
659  found = true;
660  break;
661  }
662  }
663  }
664 
665  if (found)
666  {
667  m->changed = true;
668  if (e_old[j]->changed)
669  {
670  /* Only update the flags if the old header was changed;
671  * otherwise, the header may have been modified externally,
672  * and we don't want to lose _those_ changes */
673  mutt_set_flag(m, m->emails[i], MUTT_FLAG, e_old[j]->flagged);
674  mutt_set_flag(m, m->emails[i], MUTT_REPLIED, e_old[j]->replied);
675  mutt_set_flag(m, m->emails[i], MUTT_OLD, e_old[j]->old);
676  mutt_set_flag(m, m->emails[i], MUTT_READ, e_old[j]->read);
677  }
678  mutt_set_flag(m, m->emails[i], MUTT_DELETE, e_old[j]->deleted);
679  mutt_set_flag(m, m->emails[i], MUTT_PURGE, e_old[j]->purge);
680  mutt_set_flag(m, m->emails[i], MUTT_TAG, e_old[j]->tagged);
681 
682  /* we don't need this header any more */
683  email_free(&(e_old[j]));
684  }
685  }
686 
687  /* free the remaining old headers */
688  for (int j = 0; j < old_msg_count; j++)
689  {
690  if (e_old[j])
691  {
692  email_free(&(e_old[j]));
693  msg_mod = true;
694  }
695  }
696  FREE(&e_old);
697  }
698 
700  m->verbose = true;
701 
702  return (m->changed || msg_mod) ? MX_STATUS_REOPENED : MX_STATUS_NEW_MAIL;
703 }
704 
711 static bool mbox_has_new(struct Mailbox *m)
712 {
713  for (int i = 0; i < m->msg_count; i++)
714  {
715  struct Email *e = m->emails[i];
716  if (!e)
717  break;
718  if (!e->deleted && !e->read && !e->old)
719  return true;
720  }
721  return false;
722 }
723 
730 static int fseek_last_message(FILE *fp)
731 {
732  LOFF_T pos;
733  char buf[BUFSIZ + 9] = { 0 }; /* 7 for "\n\nFrom " */
734  size_t bytes_read;
735 
736  fseek(fp, 0, SEEK_END);
737  pos = ftello(fp);
738 
739  /* Set 'bytes_read' to the size of the last, probably partial, buf;
740  * 0 < 'bytes_read' <= 'BUFSIZ'. */
741  bytes_read = pos % BUFSIZ;
742  if (bytes_read == 0)
743  bytes_read = BUFSIZ;
744  /* Make 'pos' a multiple of 'BUFSIZ' (0 if the file is short), so that all
745  * reads will be on block boundaries, which might increase efficiency. */
746  while ((pos -= bytes_read) >= 0)
747  {
748  /* we save in the buf at the end the first 7 chars from the last read */
749  strncpy(buf + BUFSIZ, buf, 5 + 2); /* 2 == 2 * mutt_str_len(CRLF) */
750  fseeko(fp, pos, SEEK_SET);
751  bytes_read = fread(buf, sizeof(char), bytes_read, fp);
752  if (bytes_read == 0)
753  return -1;
754  /* 'i' is Index into 'buf' for scanning. */
755  for (int i = bytes_read; i >= 0; i--)
756  {
757  if (mutt_str_startswith(buf + i, "\n\nFrom "))
758  { /* found it - go to the beginning of the From */
759  fseeko(fp, pos + i + 2, SEEK_SET);
760  return 0;
761  }
762  }
763  bytes_read = BUFSIZ;
764  }
765 
766  /* here we are at the beginning of the file */
767  if (mutt_str_startswith(buf, "From "))
768  {
769  fseek(fp, 0, SEEK_SET);
770  return 0;
771  }
772 
773  return -1;
774 }
775 
781 static bool test_last_status_new(FILE *fp)
782 {
783  struct Email *e = NULL;
784  struct Envelope *tmp_envelope = NULL;
785  bool rc = false;
786 
787  if (fseek_last_message(fp) == -1)
788  return false;
789 
790  e = email_new();
791  tmp_envelope = mutt_rfc822_read_header(fp, e, false, false);
792  if (!e->read && !e->old)
793  rc = true;
794 
795  mutt_env_free(&tmp_envelope);
796  email_free(&e);
797 
798  return rc;
799 }
800 
806 bool mbox_test_new_folder(const char *path)
807 {
808  bool rc = false;
809 
810  enum MailboxType type = mx_path_probe(path);
811 
812  if ((type != MUTT_MBOX) && (type != MUTT_MMDF))
813  return false;
814 
815  FILE *fp = fopen(path, "rb");
816  if (fp)
817  {
818  rc = test_last_status_new(fp);
819  mutt_file_fclose(&fp);
820  }
821 
822  return rc;
823 }
824 
833 void mbox_reset_atime(struct Mailbox *m, struct stat *st)
834 {
835  struct utimbuf utimebuf;
836  struct stat st2;
837 
838  if (!st)
839  {
840  if (stat(mailbox_path(m), &st2) < 0)
841  return;
842  st = &st2;
843  }
844 
845  utimebuf.actime = st->st_atime;
846  utimebuf.modtime = st->st_mtime;
847 
848  /* When $mbox_check_recent is set, existing new mail is ignored, so do not
849  * reset the atime to mtime-1 to signal new mail. */
850  if (!C_MailCheckRecent && (utimebuf.actime >= utimebuf.modtime) && mbox_has_new(m))
851  {
852  utimebuf.actime = utimebuf.modtime - 1;
853  }
854 
855  utime(mailbox_path(m), &utimebuf);
856 }
857 
861 static bool mbox_ac_owns_path(struct Account *a, const char *path)
862 {
863  if ((a->type != MUTT_MBOX) && (a->type != MUTT_MMDF))
864  return false;
865 
866  struct MailboxNode *np = STAILQ_FIRST(&a->mailboxes);
867  if (!np)
868  return false;
869 
870  return mutt_str_equal(mailbox_path(np->mailbox), path);
871 }
872 
876 static bool mbox_ac_add(struct Account *a, struct Mailbox *m)
877 {
878  return true;
879 }
880 
888 static FILE *mbox_open_readwrite(struct Mailbox *m)
889 {
890  FILE *fp = fopen(mailbox_path(m), "r+");
891  if (fp)
892  m->readonly = false;
893  return fp;
894 }
895 
903 static FILE *mbox_open_readonly(struct Mailbox *m)
904 {
905  FILE *fp = fopen(mailbox_path(m), "r");
906  if (fp)
907  m->readonly = true;
908  return fp;
909 }
910 
914 static enum MxOpenReturns mbox_mbox_open(struct Mailbox *m)
915 {
916  if (init_mailbox(m) != 0)
917  return MX_OPEN_ERROR;
918 
919  struct MboxAccountData *adata = mbox_adata_get(m);
920  if (!adata)
921  return MX_OPEN_ERROR;
922 
923  adata->fp = mbox_open_readwrite(m);
924  if (!adata->fp)
925  {
926  adata->fp = mbox_open_readonly(m);
927  }
928  if (!adata->fp)
929  {
931  return MX_OPEN_ERROR;
932  }
933 
934  mutt_sig_block();
935  if (mbox_lock_mailbox(m, false, true) == -1)
936  {
938  return MX_OPEN_ERROR;
939  }
940 
941  m->has_new = true;
942  enum MxOpenReturns rc = MX_OPEN_ERROR;
943  if (m->type == MUTT_MBOX)
944  rc = mbox_parse_mailbox(m);
945  else if (m->type == MUTT_MMDF)
946  rc = mmdf_parse_mailbox(m);
947  else
948  rc = MX_OPEN_ERROR;
949 
950  if (!mbox_has_new(m))
951  m->has_new = false;
952  clearerr(adata->fp); // Clear the EOF flag
953  mutt_file_touch_atime(fileno(adata->fp));
954 
957  return rc;
958 }
959 
963 static bool mbox_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
964 {
965  if (init_mailbox(m) != 0)
966  return false;
967 
968  struct MboxAccountData *adata = mbox_adata_get(m);
969  if (!adata)
970  return false;
971 
972  if (!adata->fp)
973  {
974  // create dir recursively
975  char *tmp_path = mutt_path_dirname(mailbox_path(m));
976  if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
977  {
979  FREE(&tmp_path);
980  return false;
981  }
982  FREE(&tmp_path);
983 
984  adata->fp =
985  mutt_file_fopen(mailbox_path(m), (flags & MUTT_NEWFOLDER) ? "w+" : "a+");
986  if (!adata->fp)
987  {
989  return false;
990  }
991 
992  if (mbox_lock_mailbox(m, true, true) != false)
993  {
994  mutt_error(_("Couldn't lock %s"), mailbox_path(m));
995  mutt_file_fclose(&adata->fp);
996  return false;
997  }
998  }
999 
1000  fseek(adata->fp, 0, SEEK_END);
1001 
1002  return true;
1003 }
1004 
1012 static enum MxStatus mbox_mbox_check(struct Mailbox *m)
1013 {
1014  struct MboxAccountData *adata = mbox_adata_get(m);
1015  if (!adata)
1016  return MX_STATUS_ERROR;
1017 
1018  if (!adata->fp)
1019  {
1020  if (mbox_mbox_open(m) != MX_OPEN_OK)
1021  return MX_STATUS_ERROR;
1023  }
1024 
1025  struct stat st;
1026  bool unlock = false;
1027  bool modified = false;
1028 
1029  if (stat(mailbox_path(m), &st) == 0)
1030  {
1031  if ((mutt_file_stat_timespec_compare(&st, MUTT_STAT_MTIME, &m->mtime) == 0) &&
1032  (st.st_size == m->size))
1033  {
1034  return MX_STATUS_OK;
1035  }
1036 
1037  if (st.st_size == m->size)
1038  {
1039  /* the file was touched, but it is still the same length, so just exit */
1041  return MX_STATUS_OK;
1042  }
1043 
1044  if (st.st_size > m->size)
1045  {
1046  /* lock the file if it isn't already */
1047  if (!adata->locked)
1048  {
1049  mutt_sig_block();
1050  if (mbox_lock_mailbox(m, false, false) == -1)
1051  {
1052  mutt_sig_unblock();
1053  /* we couldn't lock the mailbox, but nothing serious happened:
1054  * probably the new mail arrived: no reason to wait till we can
1055  * parse it: we'll get it on the next pass */
1056  return MX_STATUS_LOCKED;
1057  }
1058  unlock = 1;
1059  }
1060 
1061  /* Check to make sure that the only change to the mailbox is that
1062  * message(s) were appended to this file. My heuristic is that we should
1063  * see the message separator at *exactly* what used to be the end of the
1064  * folder. */
1065  char buf[1024];
1066  if (fseeko(adata->fp, m->size, SEEK_SET) != 0)
1067  mutt_debug(LL_DEBUG1, "#1 fseek() failed\n");
1068  if (fgets(buf, sizeof(buf), adata->fp))
1069  {
1070  if (((m->type == MUTT_MBOX) && mutt_str_startswith(buf, "From ")) ||
1071  ((m->type == MUTT_MMDF) && mutt_str_equal(buf, MMDF_SEP)))
1072  {
1073  if (fseeko(adata->fp, m->size, SEEK_SET) != 0)
1074  mutt_debug(LL_DEBUG1, "#2 fseek() failed\n");
1075 
1076  int old_msg_count = m->msg_count;
1077  if (m->type == MUTT_MBOX)
1078  mbox_parse_mailbox(m);
1079  else
1080  mmdf_parse_mailbox(m);
1081 
1082  if (m->msg_count > old_msg_count)
1084 
1085  /* Only unlock the folder if it was locked inside of this routine.
1086  * It may have been locked elsewhere, like in
1087  * mutt_checkpoint_mailbox(). */
1088  if (unlock)
1089  {
1091  mutt_sig_unblock();
1092  }
1093 
1094  return MX_STATUS_NEW_MAIL; /* signal that new mail arrived */
1095  }
1096  else
1097  modified = true;
1098  }
1099  else
1100  {
1101  mutt_debug(LL_DEBUG1, "fgets returned NULL\n");
1102  modified = true;
1103  }
1104  }
1105  else
1106  modified = true;
1107  }
1108 
1109  if (modified)
1110  {
1111  if (reopen_mailbox(m) != -1)
1112  {
1114  if (unlock)
1115  {
1117  mutt_sig_unblock();
1118  }
1119  return MX_STATUS_REOPENED;
1120  }
1121  }
1122 
1123  /* fatal error */
1124 
1127  mutt_sig_unblock();
1128  mutt_error(_("Mailbox was corrupted"));
1129  return MX_STATUS_ERROR;
1130 }
1131 
1135 static enum MxStatus mbox_mbox_sync(struct Mailbox *m)
1136 {
1137  struct MboxAccountData *adata = mbox_adata_get(m);
1138  if (!adata)
1139  return MX_STATUS_ERROR;
1140 
1141  struct Buffer *tempfile = NULL;
1142  char buf[32];
1143  int j;
1144  enum SortType save_sort = SORT_ORDER;
1145  bool unlink_tempfile = false;
1146  int need_sort = 0; /* flag to resort mailbox if new mail arrives */
1147  int first = -1; /* first message to be written */
1148  LOFF_T offset; /* location in mailbox to write changed messages */
1149  struct stat statbuf;
1150  struct MUpdate *new_offset = NULL;
1151  struct MUpdate *old_offset = NULL;
1152  FILE *fp = NULL;
1153  struct Progress progress;
1154  enum MxStatus rc = MX_STATUS_ERROR;
1155 
1156  /* sort message by their position in the mailbox on disk */
1157  if (C_Sort != SORT_ORDER)
1158  {
1159  save_sort = C_Sort;
1160  C_Sort = SORT_ORDER;
1162  C_Sort = save_sort;
1163  need_sort = 1;
1164  }
1165 
1166  /* need to open the file for writing in such a way that it does not truncate
1167  * the file, so use read-write mode. */
1168  adata->fp = freopen(mailbox_path(m), "r+", adata->fp);
1169  if (!adata->fp)
1170  {
1172  mutt_error(_("Fatal error! Could not reopen mailbox!"));
1173  goto fatal;
1174  }
1175 
1176  mutt_sig_block();
1177 
1178  if (mbox_lock_mailbox(m, true, true) == -1)
1179  {
1180  mutt_sig_unblock();
1181  mutt_error(_("Unable to lock mailbox"));
1182  goto bail;
1183  }
1184 
1185  /* Check to make sure that the file hasn't changed on disk */
1186  enum MxStatus check = mbox_mbox_check(m);
1187  if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
1188  {
1189  /* new mail arrived, or mailbox reopened */
1190  rc = check;
1191  goto bail;
1192  }
1193  else if (check < 0)
1194  {
1195  goto fatal;
1196  }
1197 
1198  /* Create a temporary file to write the new version of the mailbox in. */
1199  tempfile = mutt_buffer_pool_get();
1200  mutt_buffer_mktemp(tempfile);
1201  int fd = open(mutt_buffer_string(tempfile), O_WRONLY | O_EXCL | O_CREAT, 0600);
1202  if ((fd == -1) || !(fp = fdopen(fd, "w")))
1203  {
1204  if (fd != -1)
1205  {
1206  close(fd);
1207  unlink_tempfile = true;
1208  }
1209  mutt_error(_("Could not create temporary file"));
1210  goto bail;
1211  }
1212  unlink_tempfile = true;
1213 
1214  /* find the first deleted/changed message. we save a lot of time by only
1215  * rewriting the mailbox from the point where it has actually changed. */
1216  int i = 0;
1217  for (; (i < m->msg_count) && !m->emails[i]->deleted &&
1218  !m->emails[i]->changed && !m->emails[i]->attach_del;
1219  i++)
1220  {
1221  }
1222  if (i == m->msg_count)
1223  {
1224  /* this means ctx->changed or m->msg_deleted was set, but no
1225  * messages were found to be changed or deleted. This should
1226  * never happen, is we presume it is a bug in neomutt. */
1227  mutt_error(
1228  _("sync: mbox modified, but no modified messages (report this bug)"));
1229  mutt_debug(LL_DEBUG1, "no modified messages\n");
1230  goto bail;
1231  }
1232 
1233  /* save the index of the first changed/deleted message */
1234  first = i;
1235  /* where to start overwriting */
1236  offset = m->emails[i]->offset;
1237 
1238  /* the offset stored in the header does not include the MMDF_SEP, so make
1239  * sure we seek to the correct location */
1240  if (m->type == MUTT_MMDF)
1241  offset -= (sizeof(MMDF_SEP) - 1);
1242 
1243  /* allocate space for the new offsets */
1244  new_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate));
1245  old_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate));
1246 
1247  if (m->verbose)
1248  {
1249  char msg[PATH_MAX];
1250  snprintf(msg, sizeof(msg), _("Writing %s..."), mailbox_path(m));
1252  }
1253 
1254  for (i = first, j = 0; i < m->msg_count; i++)
1255  {
1256  if (m->verbose)
1257  mutt_progress_update(&progress, i, i / (m->msg_count / 100 + 1));
1258  /* back up some information which is needed to restore offsets when
1259  * something fails. */
1260 
1261  old_offset[i - first].valid = true;
1262  old_offset[i - first].hdr = m->emails[i]->offset;
1263  old_offset[i - first].body = m->emails[i]->body->offset;
1264  old_offset[i - first].lines = m->emails[i]->lines;
1265  old_offset[i - first].length = m->emails[i]->body->length;
1266 
1267  if (!m->emails[i]->deleted)
1268  {
1269  j++;
1270 
1271  if (m->type == MUTT_MMDF)
1272  {
1273  if (fputs(MMDF_SEP, fp) == EOF)
1274  {
1275  mutt_perror(mutt_buffer_string(tempfile));
1276  goto bail;
1277  }
1278  }
1279 
1280  /* save the new offset for this message. we add 'offset' because the
1281  * temporary file only contains saved message which are located after
1282  * 'offset' in the real mailbox */
1283  new_offset[i - first].hdr = ftello(fp) + offset;
1284 
1285  if (mutt_copy_message(fp, m, m->emails[i], MUTT_CM_UPDATE,
1286  CH_FROM | CH_UPDATE | CH_UPDATE_LEN, 0) != 0)
1287  {
1288  mutt_perror(mutt_buffer_string(tempfile));
1289  goto bail;
1290  }
1291 
1292  /* Since messages could have been deleted, the offsets stored in memory
1293  * will be wrong, so update what we can, which is the offset of this
1294  * message, and the offset of the body. If this is a multipart message,
1295  * we just flush the in memory cache so that the message will be reparsed
1296  * if the user accesses it later. */
1297  new_offset[i - first].body = ftello(fp) - m->emails[i]->body->length + offset;
1298  mutt_body_free(&m->emails[i]->body->parts);
1299 
1300  switch (m->type)
1301  {
1302  case MUTT_MMDF:
1303  if (fputs(MMDF_SEP, fp) == EOF)
1304  {
1305  mutt_perror(mutt_buffer_string(tempfile));
1306  goto bail;
1307  }
1308  break;
1309  default:
1310  if (fputs("\n", fp) == EOF)
1311  {
1312  mutt_perror(mutt_buffer_string(tempfile));
1313  goto bail;
1314  }
1315  }
1316  }
1317  }
1318 
1319  if (mutt_file_fclose(&fp) != 0)
1320  {
1321  mutt_debug(LL_DEBUG1, "mutt_file_fclose (&) returned non-zero\n");
1322  mutt_perror(mutt_buffer_string(tempfile));
1323  goto bail;
1324  }
1325 
1326  /* Save the state of this folder. */
1327  if (stat(mailbox_path(m), &statbuf) == -1)
1328  {
1330  goto bail;
1331  }
1332 
1333  unlink_tempfile = false;
1334 
1335  fp = fopen(mutt_buffer_string(tempfile), "r");
1336  if (!fp)
1337  {
1338  mutt_sig_unblock();
1340  mutt_debug(LL_DEBUG1, "unable to reopen temp copy of mailbox!\n");
1341  mutt_perror(mutt_buffer_string(tempfile));
1342  FREE(&new_offset);
1343  FREE(&old_offset);
1344  goto fatal;
1345  }
1346 
1347  if ((fseeko(adata->fp, offset, SEEK_SET) != 0) || /* seek the append location */
1348  /* do a sanity check to make sure the mailbox looks ok */
1349  !fgets(buf, sizeof(buf), adata->fp) ||
1350  ((m->type == MUTT_MBOX) && !mutt_str_startswith(buf, "From ")) ||
1351  ((m->type == MUTT_MMDF) && !mutt_str_equal(MMDF_SEP, buf)))
1352  {
1353  mutt_debug(LL_DEBUG1, "message not in expected position\n");
1354  mutt_debug(LL_DEBUG1, " LINE: %s\n", buf);
1355  i = -1;
1356  }
1357  else
1358  {
1359  if (fseeko(adata->fp, offset, SEEK_SET) != 0) /* return to proper offset */
1360  {
1361  i = -1;
1362  mutt_debug(LL_DEBUG1, "fseek() failed\n");
1363  }
1364  else
1365  {
1366  /* copy the temp mailbox back into place starting at the first
1367  * change/deleted message */
1368  if (m->verbose)
1369  mutt_message(_("Committing changes..."));
1370  i = mutt_file_copy_stream(fp, adata->fp);
1371 
1372  if (ferror(adata->fp))
1373  i = -1;
1374  }
1375  if (i >= 0)
1376  {
1377  m->size = ftello(adata->fp); /* update the mailbox->size of the mailbox */
1378  if ((m->size < 0) || (ftruncate(fileno(adata->fp), m->size) != 0))
1379  {
1380  i = -1;
1381  mutt_debug(LL_DEBUG1, "ftruncate() failed\n");
1382  }
1383  }
1384  }
1385 
1386  mutt_file_fclose(&fp);
1387  fp = NULL;
1389 
1390  if ((mutt_file_fclose(&adata->fp) != 0) || (i == -1))
1391  {
1392  /* error occurred while writing the mailbox back, so keep the temp copy around */
1393 
1394  struct Buffer *savefile = mutt_buffer_pool_get();
1395 
1396  mutt_buffer_printf(savefile, "%s/neomutt.%s-%s-%u", NONULL(C_Tmpdir), NONULL(Username),
1397  NONULL(ShortHostname), (unsigned int) getpid());
1398  rename(mutt_buffer_string(tempfile), mutt_buffer_string(savefile));
1399  mutt_sig_unblock();
1401  mutt_buffer_pretty_mailbox(savefile);
1402  mutt_error(_("Write failed! Saved partial mailbox to %s"), mutt_buffer_string(savefile));
1403  mutt_buffer_pool_release(&savefile);
1404  FREE(&new_offset);
1405  FREE(&old_offset);
1406  goto fatal;
1407  }
1408 
1409  /* Restore the previous access/modification times */
1410  mbox_reset_atime(m, &statbuf);
1411 
1412  /* reopen the mailbox in read-only mode */
1413  adata->fp = mbox_open_readwrite(m);
1414  if (!adata->fp)
1415  {
1416  adata->fp = mbox_open_readonly(m);
1417  }
1418  if (!adata->fp)
1419  {
1420  unlink(mutt_buffer_string(tempfile));
1421  mutt_sig_unblock();
1423  mutt_error(_("Fatal error! Could not reopen mailbox!"));
1424  FREE(&new_offset);
1425  FREE(&old_offset);
1426  goto fatal;
1427  }
1428 
1429  /* update the offsets of the rewritten messages */
1430  for (i = first, j = first; i < m->msg_count; i++)
1431  {
1432  if (!m->emails[i]->deleted)
1433  {
1434  m->emails[i]->offset = new_offset[i - first].hdr;
1435  m->emails[i]->body->hdr_offset = new_offset[i - first].hdr;
1436  m->emails[i]->body->offset = new_offset[i - first].body;
1437  m->emails[i]->index = j++;
1438  }
1439  }
1440  FREE(&new_offset);
1441  FREE(&old_offset);
1442  unlink(mutt_buffer_string(tempfile)); /* remove partial copy of the mailbox */
1443  mutt_buffer_pool_release(&tempfile);
1444  mutt_sig_unblock();
1445 
1446  if (C_CheckMboxSize)
1447  {
1448  struct Mailbox *m_tmp = mailbox_find(mailbox_path(m));
1449  if (m_tmp && !m_tmp->has_new)
1450  mailbox_update(m_tmp);
1451  }
1452 
1453  return 0; /* signal success */
1454 
1455 bail: /* Come here in case of disaster */
1456 
1457  mutt_file_fclose(&fp);
1458 
1459  if (tempfile && unlink_tempfile)
1460  unlink(mutt_buffer_string(tempfile));
1461 
1462  /* restore offsets, as far as they are valid */
1463  if ((first >= 0) && old_offset)
1464  {
1465  for (i = first; (i < m->msg_count) && old_offset[i - first].valid; i++)
1466  {
1467  m->emails[i]->offset = old_offset[i - first].hdr;
1468  m->emails[i]->body->hdr_offset = old_offset[i - first].hdr;
1469  m->emails[i]->body->offset = old_offset[i - first].body;
1470  m->emails[i]->lines = old_offset[i - first].lines;
1471  m->emails[i]->body->length = old_offset[i - first].length;
1472  }
1473  }
1474 
1475  /* this is ok to call even if we haven't locked anything */
1477 
1478  mutt_sig_unblock();
1479  FREE(&new_offset);
1480  FREE(&old_offset);
1481 
1482  adata->fp = freopen(mailbox_path(m), "r", adata->fp);
1483  if (!adata->fp)
1484  {
1485  mutt_error(_("Could not reopen mailbox"));
1487  goto fatal;
1488  }
1489 
1491  if (need_sort)
1492  {
1493  /* if the mailbox was reopened, the thread tree will be invalid so make
1494  * sure to start threading from scratch. */
1496  }
1497 
1498 fatal:
1499  mutt_buffer_pool_release(&tempfile);
1500  return rc;
1501 }
1502 
1506 static enum MxStatus mbox_mbox_close(struct Mailbox *m)
1507 {
1508  struct MboxAccountData *adata = mbox_adata_get(m);
1509  if (!adata)
1510  return MX_STATUS_ERROR;
1511 
1512  if (!adata->fp)
1513  return MX_STATUS_OK;
1514 
1515  if (adata->append)
1516  {
1517  mutt_file_unlock(fileno(adata->fp));
1518  mutt_sig_unblock();
1519  }
1520 
1521  mutt_file_fclose(&adata->fp);
1522 
1523  /* fix up the times so mailbox won't get confused */
1524  if (m->peekonly && !mutt_buffer_is_empty(&m->pathbuf) &&
1525  (mutt_file_timespec_compare(&m->mtime, &adata->atime) > 0))
1526  {
1527 #ifdef HAVE_UTIMENSAT
1528  struct timespec ts[2];
1529  ts[0] = adata->atime;
1530  ts[1] = m->mtime;
1531  utimensat(AT_FDCWD, m->path, ts, 0);
1532 #else
1533  struct utimbuf ut;
1534  ut.actime = adata->atime.tv_sec;
1535  ut.modtime = m->mtime.tv_sec;
1536  utime(mailbox_path(m), &ut);
1537 #endif
1538  }
1539 
1540  return MX_STATUS_OK;
1541 }
1542 
1546 static bool mbox_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
1547 {
1548  struct MboxAccountData *adata = mbox_adata_get(m);
1549  if (!adata)
1550  return false;
1551 
1552  msg->fp = mutt_file_fopen(mailbox_path(m), "r");
1553  if (!msg->fp)
1554  return false;
1555 
1556  return true;
1557 }
1558 
1562 static bool mbox_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
1563 {
1564  struct MboxAccountData *adata = mbox_adata_get(m);
1565  if (!adata)
1566  return false;
1567 
1568  msg->fp = adata->fp;
1569  return true;
1570 }
1571 
1575 static int mbox_msg_commit(struct Mailbox *m, struct Message *msg)
1576 {
1577  if (fputc('\n', msg->fp) == EOF)
1578  return -1;
1579 
1580  if ((fflush(msg->fp) == EOF) || (fsync(fileno(msg->fp)) == -1))
1581  {
1582  mutt_perror(_("Can't write message"));
1583  return -1;
1584  }
1585 
1586  return 0;
1587 }
1588 
1592 static int mbox_msg_close(struct Mailbox *m, struct Message *msg)
1593 {
1594  if (msg->write)
1595  msg->fp = NULL;
1596  else
1597  mutt_file_fclose(&msg->fp);
1598 
1599  return 0;
1600 }
1601 
1607 static int mbox_msg_padding_size(struct Mailbox *m)
1608 {
1609  return 1;
1610 }
1611 
1615 enum MailboxType mbox_path_probe(const char *path, const struct stat *st)
1616 {
1617  if (!st)
1618  return MUTT_UNKNOWN;
1619 
1620  if (S_ISDIR(st->st_mode))
1621  return MUTT_UNKNOWN;
1622 
1623  if (st->st_size == 0)
1624  return MUTT_MBOX;
1625 
1626  FILE *fp = fopen(path, "r");
1627  if (!fp)
1628  return MUTT_UNKNOWN;
1629 
1630  int ch;
1631  while ((ch = fgetc(fp)) != EOF)
1632  {
1633  /* Some mailbox creation tools erroneously append a blank line to
1634  * a file before appending a mail message. This allows neomutt to
1635  * detect type for and thus open those files. */
1636  if ((ch != '\n') && (ch != '\r'))
1637  {
1638  ungetc(ch, fp);
1639  break;
1640  }
1641  }
1642 
1643  enum MailboxType type = MUTT_UNKNOWN;
1644  char tmp[256];
1645  if (fgets(tmp, sizeof(tmp), fp))
1646  {
1647  if (mutt_str_startswith(tmp, "From "))
1648  type = MUTT_MBOX;
1649  else if (mutt_str_equal(tmp, MMDF_SEP))
1650  type = MUTT_MMDF;
1651  }
1652  mutt_file_fclose(&fp);
1653 
1654  if (!C_CheckMboxSize)
1655  {
1656  /* need to restore the times here, the file was not really accessed,
1657  * only the type was accessed. This is important, because detection
1658  * of "new mail" depends on those times set correctly. */
1659 #ifdef HAVE_UTIMENSAT
1660  struct timespec ts[2];
1663  utimensat(AT_FDCWD, path, ts, 0);
1664 #else
1665  struct utimbuf times;
1666  times.actime = st->st_atime;
1667  times.modtime = st->st_mtime;
1668  utime(path, &times);
1669 #endif
1670  }
1671 
1672  return type;
1673 }
1674 
1678 static int mbox_path_canon(char *buf, size_t buflen)
1679 {
1680  mutt_path_canon(buf, buflen, HomeDir, false);
1681  return 0;
1682 }
1683 
1687 static int mbox_path_pretty(char *buf, size_t buflen, const char *folder)
1688 {
1689  if (mutt_path_abbr_folder(buf, buflen, folder))
1690  return 0;
1691 
1692  if (mutt_path_pretty(buf, buflen, HomeDir, false))
1693  return 0;
1694 
1695  return -1;
1696 }
1697 
1701 static int mbox_path_parent(char *buf, size_t buflen)
1702 {
1703  if (mutt_path_parent(buf, buflen))
1704  return 0;
1705 
1706  if (buf[0] == '~')
1707  mutt_path_canon(buf, buflen, HomeDir, false);
1708 
1709  if (mutt_path_parent(buf, buflen))
1710  return 0;
1711 
1712  return -1;
1713 }
1714 
1718 static int mbox_path_is_empty(const char *path)
1719 {
1720  return mutt_file_check_empty(path);
1721 }
1722 
1726 static int mmdf_msg_commit(struct Mailbox *m, struct Message *msg)
1727 {
1728  if (fputs(MMDF_SEP, msg->fp) == EOF)
1729  return -1;
1730 
1731  if ((fflush(msg->fp) == EOF) || (fsync(fileno(msg->fp)) == -1))
1732  {
1733  mutt_perror(_("Can't write message"));
1734  return -1;
1735  }
1736 
1737  return 0;
1738 }
1739 
1745 static int mmdf_msg_padding_size(struct Mailbox *m)
1746 {
1747  return 10;
1748 }
1749 
1753 static enum MxStatus mbox_mbox_check_stats(struct Mailbox *m, uint8_t flags)
1754 {
1755  struct stat sb = { 0 };
1756  if (stat(mailbox_path(m), &sb) != 0)
1757  return MX_STATUS_ERROR;
1758 
1759  bool new_or_changed;
1760 
1761  if (C_CheckMboxSize)
1762  new_or_changed = (sb.st_size > m->size);
1763  else
1764  {
1765  new_or_changed =
1767  (m->newly_created &&
1770  }
1771 
1772  if (new_or_changed)
1773  {
1774  if (!C_MailCheckRecent ||
1776  {
1777  m->has_new = true;
1778  }
1779  }
1780  else if (C_CheckMboxSize)
1781  {
1782  /* some other program has deleted mail from the folder */
1783  m->size = (off_t) sb.st_size;
1784  }
1785 
1786  if (m->newly_created && ((sb.st_ctime != sb.st_mtime) || (sb.st_ctime != sb.st_atime)))
1787  m->newly_created = false;
1788 
1789  if ((flags != 0) && mutt_file_stat_timespec_compare(&sb, MUTT_STAT_MTIME,
1790  &m->stats_last_checked) > 0)
1791  {
1792  bool old_peek = m->peekonly;
1793  struct Context *ctx = mx_mbox_open(m, MUTT_QUIET | MUTT_NOSORT | MUTT_PEEK);
1794  mx_mbox_close(&ctx);
1795  m->peekonly = old_peek;
1796  }
1797 
1798  if (m->has_new || m->msg_new)
1799  return MX_STATUS_NEW_MAIL;
1800  return MX_STATUS_OK;
1801 }
1802 
1803 // clang-format off
1807 struct MxOps MxMboxOps = {
1808  .type = MUTT_MBOX,
1809  .name = "mbox",
1810  .is_local = true,
1811  .ac_owns_path = mbox_ac_owns_path,
1812  .ac_add = mbox_ac_add,
1813  .mbox_open = mbox_mbox_open,
1814  .mbox_open_append = mbox_mbox_open_append,
1815  .mbox_check = mbox_mbox_check,
1816  .mbox_check_stats = mbox_mbox_check_stats,
1817  .mbox_sync = mbox_mbox_sync,
1818  .mbox_close = mbox_mbox_close,
1819  .msg_open = mbox_msg_open,
1820  .msg_open_new = mbox_msg_open_new,
1821  .msg_commit = mbox_msg_commit,
1822  .msg_close = mbox_msg_close,
1823  .msg_padding_size = mbox_msg_padding_size,
1824  .msg_save_hcache = NULL,
1825  .tags_edit = NULL,
1826  .tags_commit = NULL,
1827  .path_probe = mbox_path_probe,
1828  .path_canon = mbox_path_canon,
1829  .path_pretty = mbox_path_pretty,
1830  .path_parent = mbox_path_parent,
1831  .path_is_empty = mbox_path_is_empty,
1832 };
1833 
1837 struct MxOps MxMmdfOps = {
1838  .type = MUTT_MMDF,
1839  .name = "mmdf",
1840  .is_local = true,
1841  .ac_owns_path = mbox_ac_owns_path,
1842  .ac_add = mbox_ac_add,
1843  .mbox_open = mbox_mbox_open,
1844  .mbox_open_append = mbox_mbox_open_append,
1845  .mbox_check = mbox_mbox_check,
1846  .mbox_check_stats = mbox_mbox_check_stats,
1847  .mbox_sync = mbox_mbox_sync,
1848  .mbox_close = mbox_mbox_close,
1849  .msg_open = mbox_msg_open,
1850  .msg_open_new = mbox_msg_open_new,
1851  .msg_commit = mmdf_msg_commit,
1852  .msg_close = mbox_msg_close,
1853  .msg_padding_size = mmdf_msg_padding_size,
1854  .msg_save_hcache = NULL,
1855  .tags_edit = NULL,
1856  .tags_commit = NULL,
1857  .path_probe = mbox_path_probe,
1858  .path_canon = mbox_path_canon,
1859  .path_pretty = mbox_path_pretty,
1860  .path_parent = mbox_path_parent,
1861  .path_is_empty = mbox_path_is_empty,
1862 };
1863 // clang-format on
Body::hdr_offset
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:42
Envelope
The header of an Email.
Definition: envelope.h:54
Mailbox::subj_hash
struct HashTable * subj_hash
Hash Table by subject.
Definition: mailbox.h:128
MX_STATUS_LOCKED
@ MX_STATUS_LOCKED
Couldn't lock the Mailbox.
Definition: mx.h:76
CH_FROM
#define CH_FROM
Retain the "From " message separator?
Definition: copy.h:55
MUTT_PEEK
#define MUTT_PEEK
Revert atime back after taking a look (if applicable)
Definition: mx.h:59
mutt_file_lock
int mutt_file_lock(int fd, bool excl, bool timeout)
(try to) lock a file using fcntl()
Definition: file.c:1175
mbox_open_readwrite
static FILE * mbox_open_readwrite(struct Mailbox *m)
Open an mbox read-write.
Definition: mbox.c:888
mbox_adata_get
static struct MboxAccountData * mbox_adata_get(struct Mailbox *m)
Get the private data associated with a Mailbox.
Definition: mbox.c:100
SigInt
WHERE SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: mutt_globals.h:74
mbox_msg_open
static bool mbox_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
Open an email message in a Mailbox - Implements MxOps::msg_open()
Definition: mbox.c:1546
MUTT_FLAG
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:102
MUTT_MMDF
@ MUTT_MMDF
'mmdf' Mailbox type
Definition: mailbox.h:49
MxStatus
MxStatus
Return values from mx_mbox_check(), mx_mbox_sync(), and mx_mbox_close()
Definition: mx.h:71
mbox_reset_atime
void mbox_reset_atime(struct Mailbox *m, struct stat *st)
Reset the access time on the mailbox file.
Definition: mbox.c:833
MailboxNode
List of Mailboxes.
Definition: mailbox.h:152
mutt_mem_calloc
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
MxOps::type
enum MailboxType type
Mailbox type, e.g. MUTT_IMAP.
Definition: mx.h:118
_
#define _(a)
Definition: message.h:28
NONULL
#define NONULL(x)
Definition: string2.h:37
Mailbox
A mailbox.
Definition: mailbox.h:81
Email::lines
int lines
How many lines in the body of this message?
Definition: email.h:85
mbox_msg_open_new
static bool mbox_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
Open a new message in a Mailbox - Implements MxOps::msg_open_new()
Definition: mbox.c:1562
Mailbox::v2r
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:101
Mailbox::emails
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
NT_MAILBOX_INVALID
@ NT_MAILBOX_INVALID
Email list was changed.
Definition: mailbox.h:173
Buffer
String manipulation buffer.
Definition: buffer.h:33
mutt_path_abbr_folder
bool mutt_path_abbr_folder(char *buf, size_t buflen, const char *folder)
Create a folder abbreviation.
Definition: path.c:492
Account::type
enum MailboxType type
Type of Mailboxes this Account contains.
Definition: account.h:38
Body::offset
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
mmdf_msg_commit
static int mmdf_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit()
Definition: mbox.c:1726
Mailbox::msg_deleted
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:96
MUpdate::valid
bool valid
Definition: mbox.c:65
mutt_sig_unblock
void mutt_sig_unblock(void)
Restore previously blocked signals.
Definition: signal.c:168
mutt_file_fclose
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
mutt_buffer_pretty_mailbox
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:599
mutt_buffer_is_empty
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
mbox_lock_mailbox
static int mbox_lock_mailbox(struct Mailbox *m, bool excl, bool retry)
Lock a mailbox.
Definition: mbox.c:140
mutt_sig_block
void mutt_sig_block(void)
Block signals during critical operations.
Definition: signal.c:150
mbox_msg_close
static int mbox_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close()
Definition: mbox.c:1592
MUTT_STAT_MTIME
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition: file.h:64
mbox_has_new
static bool mbox_has_new(struct Mailbox *m)
Does the mailbox have new mail.
Definition: mbox.c:711
MxOpenReturns
MxOpenReturns
Return values for mbox_open()
Definition: mx.h:84
MUTT_PROGRESS_WRITE
@ MUTT_PROGRESS_WRITE
Progress tracks elements, according to $write_inc
Definition: progress.h:43
mutt_buffer_mktemp
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:77
MxOps
The Mailbox API.
Definition: mx.h:116
TAILQ_EMPTY
#define TAILQ_EMPTY(head)
Definition: queue.h:714
Email::offset
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:84
MUTT_STAT_ATIME
@ MUTT_STAT_ATIME
File/dir's atime - last accessed time.
Definition: file.h:63
SortType
SortType
Methods for sorting.
Definition: sort2.h:43
mbox_open_readonly
static FILE * mbox_open_readonly(struct Mailbox *m)
Open an mbox read-only.
Definition: mbox.c:903
init_mailbox
static int init_mailbox(struct Mailbox *m)
Add Mbox data to the Mailbox.
Definition: mbox.c:118
MUTT_NEWFOLDER
#define MUTT_NEWFOLDER
Create a new folder - same as MUTT_APPEND,.
Definition: mx.h:56
Context
The "current" mailbox.
Definition: context.h:38
mutt_globals.h
mutt_file_fopen
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
LL_DEBUG1
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
Mailbox::size
off_t size
Size of the Mailbox.
Definition: mailbox.h:87
Mailbox::has_new
bool has_new
Mailbox has new mail.
Definition: mailbox.h:88
FREE
#define FREE(x)
Definition: memory.h:40
STAILQ_FIRST
#define STAILQ_FIRST(head)
Definition: queue.h:347
mutt_perror
#define mutt_perror(...)
Definition: logging.h:85
mutt_addrlist_parse
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:458
MX_OPEN_OK
@ MX_OPEN_OK
Open succeeded.
Definition: mx.h:86
C_CheckMboxSize
bool C_CheckMboxSize
Config: (mbox,mmdf) Use mailbox size as an indicator of new mail.
Definition: config.c:36
mutt_path_canon
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
PATH_MAX
#define PATH_MAX
Definition: mutt.h:44
MailboxNode::mailbox
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:154
copy.h
mutt_buffer_pool_release
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
mbox_path_canon
static int mbox_path_canon(char *buf, size_t buflen)
Canonicalise a Mailbox path - Implements MxOps::path_canon()
Definition: mbox.c:1678
Username
WHERE char * Username
User's login name.
Definition: mutt_globals.h:52
mx_path_probe
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1335
mutt_header.h
email_new
struct Email * email_new(void)
Create a new Email.
Definition: email.c:72
mutt_body_free
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
MUTT_STAT_CTIME
@ MUTT_STAT_CTIME
File/dir's ctime - creation time.
Definition: file.h:65
Mailbox::label_hash
struct HashTable * label_hash
Hash Table for x-labels.
Definition: mailbox.h:129
Mailbox::vcount
int vcount
The number of virtual messages.
Definition: mailbox.h:102
MX_STATUS_REOPENED
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition: mx.h:77
Account::adata_free
void(* adata_free)(void **ptr)
Free the private data attached to the Account.
Definition: account.h:49
MUTT_READ
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:96
mutt_str_equal
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
MX_STATUS_NEW_MAIL
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition: mx.h:75
lib.h
mmdf_msg_padding_size
static int mmdf_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Implements MxOps::msg_padding_size()
Definition: mbox.c:1745
MUpdate::lines
long lines
Definition: mbox.c:68
mutt_file_touch_atime
void mutt_file_touch_atime(int fd)
Set the access time to current time.
Definition: file.c:1026
Email::old
bool old
Email is seen, but unread.
Definition: email.h:50
Email::received
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:83
protos.h
mutt_progress_init
void mutt_progress_init(struct Progress *progress, const char *msg, enum ProgressType type, size_t size)
Set up a progress bar.
Definition: progress.c:153
MUTT_PURGE
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition: mutt.h:100
mbox_mbox_check_stats
static enum MxStatus mbox_mbox_check_stats(struct Mailbox *m, uint8_t flags)
Check the Mailbox statistics - Implements MxOps::mbox_check_stats()
Definition: mbox.c:1753
fseek_last_message
static int fseek_last_message(FILE *fp)
Find the last message in the file.
Definition: mbox.c:730
mx_alloc_memory
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1230
mbox_path_probe
enum MailboxType mbox_path_probe(const char *path, const struct stat *st)
Is this an mbox Mailbox? - Implements MxOps::path_probe()
Definition: mbox.c:1615
mutt_env_free
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:96
mutt_buffer_pool_get
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
timespec::tv_sec
time_t tv_sec
Definition: file.h:51
mutt_file_check_empty
int mutt_file_check_empty(const char *path)
Is the mailbox empty.
Definition: file.c:1412
Mailbox::type
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
NT_MAILBOX_UPDATE
@ NT_MAILBOX_UPDATE
Update internal tables.
Definition: mailbox.h:176
MboxAccountData::fp
FILE * fp
Mailbox file.
Definition: lib.h:52
Mailbox::stats_last_checked
struct timespec stats_last_checked
Mtime of mailbox the last time stats where checked.
Definition: mailbox.h:109
lib.h
Account
A group of associated Mailboxes.
Definition: account.h:36
mutt_file_copy_stream
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:271
muttlib.h
Email::tagged
bool tagged
Email is tagged.
Definition: email.h:44
Message::write
bool write
nonzero if message is open for writing
Definition: mx.h:99
MUpdate::length
LOFF_T length
Definition: mbox.c:69
mutt_file_unlock
int mutt_file_unlock(int fd)
Unlock a file previously locked by mutt_file_lock()
Definition: file.c:1223
mx_mbox_close
enum MxStatus mx_mbox_close(struct Context **ptr)
Save changes and close mailbox.
Definition: mx.c:632
Progress
A progress bar.
Definition: progress.h:50
Body::length
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
Mailbox::changed
bool changed
Mailbox has been modified.
Definition: mailbox.h:114
mbox_adata_new
static struct MboxAccountData * mbox_adata_new(void)
Create a new MboxAccountData struct.
Definition: mbox.c:90
Mailbox::account
struct Account * account
Account that owns this Mailbox.
Definition: mailbox.h:131
lib.h
Account::mailboxes
struct MailboxList mailboxes
List of Mailboxes.
Definition: account.h:41
is_from
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition: from.c:48
lib.h
Mailbox::msg_count
int msg_count
Total number of messages.
Definition: mailbox.h:91
mbox_unlock_mailbox
static void mbox_unlock_mailbox(struct Mailbox *m)
Unlock a mailbox.
Definition: mbox.c:162
Mailbox::mtime
struct timespec mtime
Time Mailbox was last changed.
Definition: mailbox.h:107
MUTT_UNKNOWN
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition: mailbox.h:47
mutt_date_local_tz
time_t mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition: date.c:206
MboxAccountData
Mbox-specific Account data -.
Definition: lib.h:50
Mailbox::newly_created
bool newly_created
Mbox or mmdf just popped into existence.
Definition: mailbox.h:106
MUTT_NOSORT
#define MUTT_NOSORT
Do not sort the mailbox after opening it.
Definition: mx.h:52
MboxAccountData::locked
bool locked
is the mailbox locked?
Definition: lib.h:55
Body::parts
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:54
MUTT_PROGRESS_READ
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc
Definition: progress.h:42
Email::env
struct Envelope * env
Envelope information.
Definition: email.h:90
mutt_debug
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Mailbox::email_max
int email_max
Number of pointers in emails.
Definition: mailbox.h:100
MUTT_REPLIED
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:95
mailbox_update
void mailbox_update(struct Mailbox *m)
Get the mailbox's current size.
Definition: mailbox.c:168
Email::purge
bool purge
Skip trash folder when deleting.
Definition: email.h:46
C_Tmpdir
char * C_Tmpdir
Config: Directory for temporary files.
Definition: file.c:56
CH_UPDATE
#define CH_UPDATE
Update the status and x-status fields?
Definition: copy.h:51
mbox_ac_owns_path
static bool mbox_ac_owns_path(struct Account *a, const char *path)
Check whether an Account owns a Mailbox path - Implements MxOps::ac_owns_path()
Definition: mbox.c:861
Mailbox::msg_flagged
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:93
Message::fp
FILE * fp
pointer to the message data
Definition: mx.h:96
MX_STATUS_ERROR
@ MX_STATUS_ERROR
An error occurred.
Definition: mx.h:73
MUTT_QUIET
#define MUTT_QUIET
Do not print any messages.
Definition: mx.h:55
Email::flagged
bool flagged
Marked important?
Definition: email.h:43
mutt_buffer_string
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
Mailbox::verbose
bool verbose
Display status messages?
Definition: mailbox.h:118
mbox_path_pretty
static int mbox_path_pretty(char *buf, size_t buflen, const char *folder)
Abbreviate a Mailbox path - Implements MxOps::path_pretty()
Definition: mbox.c:1687
SORT_ORDER
@ SORT_ORDER
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:50
mutt_file_stat_compare
int mutt_file_stat_compare(struct stat *sba, enum MuttStatType sba_type, struct stat *sbb, enum MuttStatType sbb_type)
Compare two stat infos.
Definition: file.c:1599
MUTT_OLD
@ MUTT_OLD
Old messages.
Definition: mutt.h:94
mbox_ac_add
static bool mbox_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Implements MxOps::ac_add()
Definition: mbox.c:876
MxMboxOps
struct MxOps MxMboxOps
Mbox Mailbox - Implements MxOps.
Definition: mbox.c:1807
mbox_mbox_check
static enum MxStatus mbox_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check()
Definition: mbox.c:1012
lib.h
Email::deleted
bool deleted
Email is deleted.
Definition: email.h:45
mutt_file_mkdir
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:875
Mailbox::readonly
bool readonly
Don't allow changes to the mailbox.
Definition: mailbox.h:119
MUpdate::hdr
LOFF_T hdr
Definition: mbox.c:66
mmdf_parse_mailbox
static enum MxOpenReturns mmdf_parse_mailbox(struct Mailbox *m)
Read a mailbox in MMDF format.
Definition: mbox.c:182
Envelope::from
struct AddressList from
Email's 'From' list.
Definition: envelope.h:57
mutt_path_parent
bool mutt_path_parent(char *buf, size_t buflen)
Find the parent of a path.
Definition: path.c:459
OpenMailboxFlags
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition: mx.h:50
MUTT_DELETE
@ MUTT_DELETE
Messages to be deleted.
Definition: mutt.h:98
HomeDir
char * HomeDir
User's home directory.
Definition: mutt_globals.h:49
C_MailCheckRecent
WHERE bool C_MailCheckRecent
Config: Notify the user about new mail since the last time the mailbox was opened.
Definition: mutt_globals.h:150
C_Sort
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:60
Progress::msg
char msg[1024]
Definition: progress.h:52
sort.h
MMDF_SEP
#define MMDF_SEP
Definition: lib.h:64
ShortHostname
WHERE char * ShortHostname
Short version of the hostname.
Definition: mutt_globals.h:50
mutt_file_timespec_compare
int mutt_file_timespec_compare(struct timespec *a, struct timespec *b)
Compare to time values.
Definition: file.c:1515
Mailbox::last_visited
struct timespec last_visited
Time of last exit from this mailbox.
Definition: mailbox.h:108
MUTT_TAG
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:103
progress.h
mbox_path_parent
static int mbox_path_parent(char *buf, size_t buflen)
Find the parent of a Mailbox path - Implements MxOps::path_parent()
Definition: mbox.c:1701
mutt.h
mbox_parse_mailbox
static enum MxOpenReturns mbox_parse_mailbox(struct Mailbox *m)
Read a mailbox from disk.
Definition: mbox.c:344
mbox_mbox_open
static enum MxOpenReturns mbox_mbox_open(struct Mailbox *m)
Open a Mailbox - Implements MxOps::mbox_open()
Definition: mbox.c:914
mx_mbox_open
struct Context * mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:309
mbox_mbox_close
static enum MxStatus mbox_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close()
Definition: mbox.c:1506
MUTT_CM_UPDATE
#define MUTT_CM_UPDATE
Update structs on sync.
Definition: copy.h:39
mailbox_changed
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:186
mbox_mbox_sync
static enum MxStatus mbox_mbox_sync(struct Mailbox *m)
Save changes to the Mailbox - Implements MxOps::mbox_sync()
Definition: mbox.c:1135
mutt_file_get_stat_timespec
void mutt_file_get_stat_timespec(struct timespec *dest, struct stat *sb, enum MuttStatType type)
Read the stat() time into a time value.
Definition: file.c:1537
mbox_adata_free
static void mbox_adata_free(void **ptr)
Free the private Account data - Implements Account::adata_free()
Definition: mbox.c:75
Account::adata
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
MX_OPEN_ABORT
@ MX_OPEN_ABORT
Open was aborted.
Definition: mx.h:88
mbox_test_new_folder
bool mbox_test_new_folder(const char *path)
Test if an mbox or mmdf mailbox has new mail.
Definition: mbox.c:806
MailboxType
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
MUpdate
Store of new offsets, used by mutt_sync_mailbox()
Definition: mbox.c:63
mbox_mbox_open_append
static bool mbox_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append()
Definition: mbox.c:963
Email::index
int index
The absolute (unsorted) message number.
Definition: email.h:86
mutt_rfc822_read_header
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
parses an RFC822 header
Definition: parse.c:1112
email_cmp_strict
bool email_cmp_strict(const struct Email *e1, const struct Email *e2)
Strictly compare message emails.
Definition: email.c:92
email_free
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:43
mutt_progress_update
void mutt_progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:212
Mailbox::msg_unread
int msg_unread
Number of unread messages.
Definition: mailbox.h:92
Progress::size
size_t size
Definition: progress.h:55
Mailbox::msg_tagged
int msg_tagged
How many messages are tagged?
Definition: mailbox.h:97
mbox_path_is_empty
static int mbox_path_is_empty(const char *path)
Is the mailbox empty - Implements MxOps::path_is_empty()
Definition: mbox.c:1718
Envelope::return_path
struct AddressList return_path
Return path for the Email.
Definition: envelope.h:56
MboxAccountData::atime
struct timespec atime
File's last-access time.
Definition: lib.h:53
Email::attach_del
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:49
mailbox_path
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:206
MUpdate::body
LOFF_T body
Definition: mbox.c:67
mx_fastclose_mailbox
void mx_fastclose_mailbox(struct Mailbox *m)
free up memory associated with the Mailbox
Definition: mx.c:451
mx.h
mutt_path_pretty
bool mutt_path_pretty(char *buf, size_t buflen, const char *homedir, bool is_dir)
Tidy a filesystem path.
Definition: path.c:186
timespec
Time value with nanosecond precision.
Definition: file.h:49
mbox_msg_padding_size
static int mbox_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Implements MxOps::msg_padding_size()
Definition: mbox.c:1607
test_last_status_new
static bool test_last_status_new(FILE *fp)
Is the last message new.
Definition: mbox.c:781
mbox_msg_commit
static int mbox_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit()
Definition: mbox.c:1575
reopen_mailbox
static int reopen_mailbox(struct Mailbox *m)
Close and reopen a mailbox.
Definition: mbox.c:533
Email::replied
bool replied
Email has been replied to.
Definition: email.h:54
Email
The envelope/body of an email.
Definition: email.h:37
mutt_str_startswith
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
mutt_message
#define mutt_message(...)
Definition: logging.h:83
mutt_set_flag
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:67
Mailbox::msg_new
int msg_new
Number of new messages.
Definition: mailbox.h:95
MX_OPEN_ERROR
@ MX_OPEN_ERROR
Open failed with an error.
Definition: mx.h:87
mutt_buffer_printf
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
mutt_file_stat_timespec_compare
int mutt_file_stat_timespec_compare(struct stat *sba, enum MuttStatType type, struct timespec *b)
Compare stat info with a time value.
Definition: file.c:1577
mutt_addrlist_copy
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
mutt_copy_message
int mutt_copy_message(FILE *fp_out, struct Mailbox *m, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Copy a message from a Mailbox.
Definition: copy.c:835
NT_MAILBOX_RESORT
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition: mailbox.h:174
CH_UPDATE_LEN
#define CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition: copy.h:61
Email::read
bool read
Email is read.
Definition: email.h:51
MUTT_MBOX
@ MUTT_MBOX
'mbox' Mailbox type
Definition: mailbox.h:48
mutt_make_label_hash
void mutt_make_label_hash(struct Mailbox *m)
Create a Hash Table to store the labels.
Definition: mutt_header.c:368
Mailbox::id_hash
struct HashTable * id_hash
Hash Table by msg id.
Definition: mailbox.h:127
MxMmdfOps
struct MxOps MxMmdfOps
MMDF Mailbox - Implements MxOps.
Definition: mbox.c:1837
Mailbox::peekonly
bool peekonly
Just taking a glance, revert atime.
Definition: mailbox.h:117
mutt_hash_free
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:447
Mailbox::pathbuf
struct Buffer pathbuf
Definition: mailbox.h:83
Email::changed
bool changed
Email has been edited.
Definition: email.h:48
Message
A local copy of an email.
Definition: mx.h:94
mailbox_find
struct Mailbox * mailbox_find(const char *path)
Find the mailbox with a given path.
Definition: mailbox.c:103
MX_STATUS_OK
@ MX_STATUS_OK
No changes.
Definition: mx.h:74
Email::body
struct Body * body
List of MIME parts.
Definition: email.h:91
mutt_error
#define mutt_error(...)
Definition: logging.h:84
mutt_path_dirname
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final '/'.
Definition: path.c:376