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