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