NeoMutt  2023-03-22-27-g3cb248
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>
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 "globals.h" // IWYU pragma: keep
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
100static int init_mailbox(struct Mailbox *m)
101{
102 if (!m || !m->account)
103 return -1;
104 if ((m->type != MUTT_MBOX) && (m->type != MUTT_MMDF))
105 return -1;
106 if (m->account->adata)
107 return 0;
108
111 return 0;
112}
113
119static struct MboxAccountData *mbox_adata_get(struct Mailbox *m)
120{
121 if (init_mailbox(m) == -1)
122 return NULL;
123 return m->account->adata;
124}
125
134static int mbox_lock_mailbox(struct Mailbox *m, bool excl, bool retry)
135{
137 if (!adata)
138 return -1;
139
140 int rc = mutt_file_lock(fileno(adata->fp), excl, retry);
141 if (rc == 0)
142 {
143 adata->locked = true;
144 }
145 else if (retry && !excl)
146 {
147 m->readonly = true;
148 return 0;
149 }
150
151 return rc;
152}
153
158static void mbox_unlock_mailbox(struct Mailbox *m)
159{
161 if (!adata)
162 return;
163
164 if (adata->locked)
165 {
166 fflush(adata->fp);
167
168 mutt_file_unlock(fileno(adata->fp));
169 adata->locked = false;
170 }
171}
172
179{
180 if (!m)
181 return MX_OPEN_ERROR;
182
184 if (!adata)
185 return MX_OPEN_ERROR;
186
187 char buf[8192] = { 0 };
188 char return_path[1024] = { 0 };
189 int count = 0;
190 int lines;
191 time_t t = 0;
192 LOFF_T loc, tmploc;
193 struct Email *e = NULL;
194 struct stat st = { 0 };
195 struct Progress *progress = NULL;
197
198 if (stat(mailbox_path(m), &st) == -1)
199 {
201 goto fail;
202 }
205 m->size = st.st_size;
206
207 buf[sizeof(buf) - 1] = '\0';
208
209 if (m->verbose)
210 {
211 char msg[PATH_MAX] = { 0 };
212 snprintf(msg, sizeof(msg), _("Reading %s..."), mailbox_path(m));
213 progress = progress_new(msg, MUTT_PROGRESS_READ, 0);
214 }
215
216 while (true)
217 {
218 if (!fgets(buf, sizeof(buf) - 1, adata->fp))
219 break;
220
221 if (SigInt)
222 break;
223
224 if (mutt_str_equal(buf, MMDF_SEP))
225 {
226 loc = ftello(adata->fp);
227 if (loc < 0)
228 goto fail;
229
230 count++;
231 if (m->verbose)
232 progress_update(progress, count, (int) (loc / (m->size / 100 + 1)));
233
234 if (m->msg_count == m->email_max)
236 e = email_new();
237 m->emails[m->msg_count] = e;
238 e->offset = loc;
239 e->index = m->msg_count;
240
241 if (!fgets(buf, sizeof(buf) - 1, adata->fp))
242 {
243 /* TODO: memory leak??? */
244 mutt_debug(LL_DEBUG1, "unexpected EOF\n");
245 break;
246 }
247
248 return_path[0] = '\0';
249
250 if (!is_from(buf, return_path, sizeof(return_path), &t))
251 {
252 if (!mutt_file_seek(adata->fp, loc, SEEK_SET))
253 {
254 mutt_error(_("Mailbox is corrupt"));
255 goto fail;
256 }
257 }
258 else
259 {
260 e->received = t - mutt_date_local_tz(t);
261 }
262
263 e->env = mutt_rfc822_read_header(adata->fp, e, false, false);
264
265 loc = ftello(adata->fp);
266 if (loc < 0)
267 goto fail;
268
269 if ((e->body->length > 0) && (e->lines > 0))
270 {
271 tmploc = loc + e->body->length;
272
273 if ((tmploc > 0) && (tmploc < m->size))
274 {
275 if (!mutt_file_seek(adata->fp, tmploc, SEEK_SET) ||
276 !fgets(buf, sizeof(buf) - 1, adata->fp) || !mutt_str_equal(MMDF_SEP, buf))
277 {
278 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
279 e->body->length = -1;
280 }
281 }
282 else
283 {
284 e->body->length = -1;
285 }
286 }
287 else
288 {
289 e->body->length = -1;
290 }
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;
333fail:
334 progress_free(&progress);
335 return rc;
336}
337
350{
351 if (!m)
352 return MX_OPEN_ERROR;
353
355 if (!adata)
356 return MX_OPEN_ERROR;
357
358 struct stat st = { 0 };
359 char buf[8192], return_path[256];
360 struct Email *e_cur = NULL;
361 time_t t = 0;
362 int count = 0, lines = 0;
363 LOFF_T loc;
364 struct Progress *progress = NULL;
366
367 /* Save information about the folder at the time we opened it. */
368 if (stat(mailbox_path(m), &st) == -1)
369 {
371 goto fail;
372 }
373
374 m->size = st.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] = { 0 };
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)
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 (!mutt_file_seek(adata->fp, tmploc, SEEK_SET) ||
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 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
450 e_cur->body->length = -1;
451 }
452 }
453 else if (tmploc != m->size)
454 {
455 /* content-length would put us past the end of the file, so it
456 * must be wrong */
457 e_cur->body->length = -1;
458 }
459
460 if (e_cur->body->length != -1)
461 {
462 /* good content-length. check to see if we know how many lines
463 * are in this message. */
464 if (e_cur->lines == 0)
465 {
466 int cl = e_cur->body->length;
467
468 /* count the number of lines in this message */
469 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
470 while (cl-- > 0)
471 {
472 if (fgetc(adata->fp) == '\n')
473 e_cur->lines++;
474 }
475 }
476
477 /* return to the offset of the next message separator */
478 (void) mutt_file_seek(adata->fp, tmploc, SEEK_SET);
479 }
480 }
481
482 m->msg_count++;
483
484 if (TAILQ_EMPTY(&e_cur->env->return_path) && return_path[0])
485 {
486 mutt_addrlist_parse(&e_cur->env->return_path, return_path);
487 }
488
489 if (TAILQ_EMPTY(&e_cur->env->from))
490 mutt_addrlist_copy(&e_cur->env->from, &e_cur->env->return_path, false);
491
492 lines = 0;
493 }
494 else
495 {
496 lines++;
497 }
498
499 loc = ftello(adata->fp);
500 }
501
502 /* Only set the content-length of the previous message if we have read more
503 * than one message during _this_ invocation. If this routine is called
504 * when new mail is received, we need to make sure not to clobber what
505 * previously was the last message since the headers may be sorted. */
506 if (count > 0)
507 {
508 struct Email *e = m->emails[m->msg_count - 1];
509 if (e->body->length < 0)
510 {
511 e->body->length = ftello(adata->fp) - e->body->offset - 1;
512 if (e->body->length < 0)
513 e->body->length = 0;
514 }
515
516 if (!e->lines)
517 e->lines = lines ? lines - 1 : 0;
518 }
519
520 if (SigInt)
521 {
522 SigInt = false;
523 rc = MX_OPEN_ABORT;
524 goto fail; /* action aborted */
525 }
526
527 rc = MX_OPEN_OK;
528fail:
529 progress_free(&progress);
530 return rc;
531}
532
539static int reopen_mailbox(struct Mailbox *m)
540{
541 if (!m)
542 return -1;
543
545 if (!adata)
546 return -1;
547
548 bool (*cmp_headers)(const struct Email *, const struct Email *) = NULL;
549 struct Email **e_old = NULL;
550 int old_msg_count;
551 bool msg_mod = false;
552 int rc = -1;
553
554 /* silent operations */
555 m->verbose = false;
556
557 /* our heuristics require the old mailbox to be unsorted */
558 const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
559 if (c_sort != SORT_ORDER)
560 {
563 cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
564 }
565
566 e_old = NULL;
567 old_msg_count = 0;
568
569 /* simulate a close */
573 FREE(&m->v2r);
574 if (m->readonly)
575 {
576 for (int i = 0; i < m->msg_count; i++)
577 email_free(&(m->emails[i])); /* nothing to do! */
578 FREE(&m->emails);
579 }
580 else
581 {
582 /* save the old headers */
583 old_msg_count = m->msg_count;
584 e_old = m->emails;
585 m->emails = NULL;
586 }
587
588 m->email_max = 0; /* force allocation of new headers */
589 m->msg_count = 0;
590 m->vcount = 0;
591 m->msg_tagged = 0;
592 m->msg_deleted = 0;
593 m->msg_new = 0;
594 m->msg_unread = 0;
595 m->msg_flagged = 0;
596 m->changed = false;
597 m->id_hash = NULL;
598 m->subj_hash = NULL;
600
601 switch (m->type)
602 {
603 case MUTT_MBOX:
604 case MUTT_MMDF:
605 cmp_headers = email_cmp_strict;
606 mutt_file_fclose(&adata->fp);
607 adata->fp = mutt_file_fopen(mailbox_path(m), "r");
608 if (!adata->fp)
609 rc = -1;
610 else if (m->type == MUTT_MBOX)
611 rc = mbox_parse_mailbox(m);
612 else
613 rc = mmdf_parse_mailbox(m);
614 break;
615
616 default:
617 rc = -1;
618 break;
619 }
620
621 if (rc == -1)
622 {
623 /* free the old headers */
624 for (int i = 0; i < old_msg_count; i++)
625 email_free(&(e_old[i]));
626 FREE(&e_old);
627
628 m->verbose = true;
629 return -1;
630 }
631
632 mutt_file_touch_atime(fileno(adata->fp));
633
634 /* now try to recover the old flags */
635
636 if (!m->readonly)
637 {
638 for (int i = 0; i < m->msg_count; i++)
639 {
640 bool found = false;
641
642 /* some messages have been deleted, and new messages have been
643 * appended at the end; the heuristic is that old messages have then
644 * "advanced" towards the beginning of the folder, so we begin the
645 * search at index "i" */
646 int j;
647 for (j = i; j < old_msg_count; j++)
648 {
649 if (!e_old[j])
650 continue;
651 if (cmp_headers(m->emails[i], e_old[j]))
652 {
653 found = true;
654 break;
655 }
656 }
657 if (!found)
658 {
659 for (j = 0; (j < i) && (j < old_msg_count); j++)
660 {
661 if (!e_old[j])
662 continue;
663 if (cmp_headers(m->emails[i], e_old[j]))
664 {
665 found = true;
666 break;
667 }
668 }
669 }
670
671 if (found)
672 {
673 m->changed = true;
674 if (e_old[j]->changed)
675 {
676 /* Only update the flags if the old header was changed;
677 * otherwise, the header may have been modified externally,
678 * and we don't want to lose _those_ changes */
679 mutt_set_flag(m, m->emails[i], MUTT_FLAG, e_old[j]->flagged);
680 mutt_set_flag(m, m->emails[i], MUTT_REPLIED, e_old[j]->replied);
681 mutt_set_flag(m, m->emails[i], MUTT_OLD, e_old[j]->old);
682 mutt_set_flag(m, m->emails[i], MUTT_READ, e_old[j]->read);
683 }
684 mutt_set_flag(m, m->emails[i], MUTT_DELETE, e_old[j]->deleted);
685 mutt_set_flag(m, m->emails[i], MUTT_PURGE, e_old[j]->purge);
686 mutt_set_flag(m, m->emails[i], MUTT_TAG, e_old[j]->tagged);
687
688 /* we don't need this header any more */
689 email_free(&(e_old[j]));
690 }
691 }
692
693 /* free the remaining old headers */
694 for (int j = 0; j < old_msg_count; j++)
695 {
696 if (e_old[j])
697 {
698 email_free(&(e_old[j]));
699 msg_mod = true;
700 }
701 }
702 FREE(&e_old);
703 }
704
706 m->verbose = true;
707
708 return (m->changed || msg_mod) ? MX_STATUS_REOPENED : MX_STATUS_NEW_MAIL;
709}
710
717static bool mbox_has_new(struct Mailbox *m)
718{
719 for (int i = 0; i < m->msg_count; i++)
720 {
721 struct Email *e = m->emails[i];
722 if (!e)
723 break;
724 if (!e->deleted && !e->read && !e->old)
725 return true;
726 }
727 return false;
728}
729
736static int fseek_last_message(FILE *fp)
737{
738 LOFF_T pos;
739 char buf[BUFSIZ + 7] = { 0 }; // 7 for "\n\nFrom "
740 size_t bytes_read;
741
742 if (!mutt_file_seek(fp, 0, SEEK_END))
743 {
744 return -1;
745 }
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 memcpy(buf + BUFSIZ, buf, 7);
759 if (!mutt_file_seek(fp, pos, SEEK_SET))
760 {
761 return -1;
762 }
763 bytes_read = fread(buf, sizeof(char), bytes_read, fp);
764 if (bytes_read == 0)
765 return -1;
766 /* 'i' is Index into 'buf' for scanning. */
767 for (int i = bytes_read; i >= 0; i--)
768 {
769 if (mutt_str_startswith(buf + i, "\n\nFrom "))
770 { /* found it - go to the beginning of the From */
771 if (!mutt_file_seek(fp, pos + i + 2, SEEK_SET))
772 {
773 return -1;
774 }
775 return 0;
776 }
777 }
778 bytes_read = BUFSIZ;
779 }
780
781 /* here we are at the beginning of the file */
782 if (mutt_str_startswith(buf, "From "))
783 {
784 if (!mutt_file_seek(fp, 0, SEEK_SET))
785 {
786 return -1;
787 }
788 return 0;
789 }
790
791 return -1;
792}
793
799static bool test_last_status_new(FILE *fp)
800{
801 struct Email *e = NULL;
802 struct Envelope *tmp_envelope = NULL;
803 bool rc = false;
804
805 if (fseek_last_message(fp) == -1)
806 return false;
807
808 e = email_new();
809 tmp_envelope = mutt_rfc822_read_header(fp, e, false, false);
810 if (!e->read && !e->old)
811 rc = true;
812
813 mutt_env_free(&tmp_envelope);
814 email_free(&e);
815
816 return rc;
817}
818
824bool mbox_test_new_folder(const char *path)
825{
826 bool rc = false;
827
828 enum MailboxType type = mx_path_probe(path);
829
830 if ((type != MUTT_MBOX) && (type != MUTT_MMDF))
831 return false;
832
833 FILE *fp = fopen(path, "rb");
834 if (fp)
835 {
836 rc = test_last_status_new(fp);
837 mutt_file_fclose(&fp);
838 }
839
840 return rc;
841}
842
851void mbox_reset_atime(struct Mailbox *m, struct stat *st)
852{
853 struct utimbuf utimebuf;
854 struct stat st2 = { 0 };
855
856 if (!st)
857 {
858 if (stat(mailbox_path(m), &st2) < 0)
859 return;
860 st = &st2;
861 }
862
863 utimebuf.actime = st->st_atime;
864 utimebuf.modtime = st->st_mtime;
865
866 /* When $mbox_check_recent is set, existing new mail is ignored, so do not
867 * reset the atime to mtime-1 to signal new mail. */
868 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
869 if (!c_mail_check_recent && (utimebuf.actime >= utimebuf.modtime) && mbox_has_new(m))
870 {
871 utimebuf.actime = utimebuf.modtime - 1;
872 }
873
874 utime(mailbox_path(m), &utimebuf);
875}
876
880static bool mbox_ac_owns_path(struct Account *a, const char *path)
881{
882 if ((a->type != MUTT_MBOX) && (a->type != MUTT_MMDF))
883 return false;
884
885 struct MailboxNode *np = STAILQ_FIRST(&a->mailboxes);
886 if (!np)
887 return false;
888
889 return mutt_str_equal(mailbox_path(np->mailbox), path);
890}
891
895static bool mbox_ac_add(struct Account *a, struct Mailbox *m)
896{
897 return true;
898}
899
907static FILE *mbox_open_readwrite(struct Mailbox *m)
908{
909 FILE *fp = fopen(mailbox_path(m), "r+");
910 if (fp)
911 m->readonly = false;
912 return fp;
913}
914
922static FILE *mbox_open_readonly(struct Mailbox *m)
923{
924 FILE *fp = fopen(mailbox_path(m), "r");
925 if (fp)
926 m->readonly = true;
927 return fp;
928}
929
934{
935 if (init_mailbox(m) != 0)
936 return MX_OPEN_ERROR;
937
939 if (!adata)
940 return MX_OPEN_ERROR;
941
942 adata->fp = m->readonly ? NULL : mbox_open_readwrite(m);
943 if (!adata->fp)
944 {
945 adata->fp = mbox_open_readonly(m);
946 }
947 if (!adata->fp)
948 {
950 return MX_OPEN_ERROR;
951 }
952
954 if (mbox_lock_mailbox(m, false, true) == -1)
955 {
957 return MX_OPEN_ERROR;
958 }
959
960 m->has_new = true;
962 if (m->type == MUTT_MBOX)
963 rc = mbox_parse_mailbox(m);
964 else if (m->type == MUTT_MMDF)
965 rc = mmdf_parse_mailbox(m);
966 else
967 rc = MX_OPEN_ERROR;
968
969 if (!mbox_has_new(m))
970 m->has_new = false;
971 clearerr(adata->fp); // Clear the EOF flag
972 mutt_file_touch_atime(fileno(adata->fp));
973
976 return rc;
977}
978
982static bool mbox_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
983{
984 if (init_mailbox(m) != 0)
985 return false;
986
988 if (!adata)
989 return false;
990
991 if (!adata->fp)
992 {
993 // create dir recursively
994 char *tmp_path = mutt_path_dirname(mailbox_path(m));
995 if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
996 {
998 FREE(&tmp_path);
999 return false;
1000 }
1001 FREE(&tmp_path);
1002
1003 adata->fp = mutt_file_fopen(mailbox_path(m), (flags & MUTT_NEWFOLDER) ? "w+" : "a+");
1004 if (!adata->fp)
1005 {
1007 return false;
1008 }
1009
1010 if (mbox_lock_mailbox(m, true, true) != false)
1011 {
1012 mutt_error(_("Couldn't lock %s"), mailbox_path(m));
1013 mutt_file_fclose(&adata->fp);
1014 return false;
1015 }
1016 }
1017
1018 if (!mutt_file_seek(adata->fp, 0, SEEK_END))
1019 {
1020 mutt_file_fclose(&adata->fp);
1021 return false;
1022 }
1023
1024 return true;
1025}
1026
1034static enum MxStatus mbox_mbox_check(struct Mailbox *m)
1035{
1037 if (!adata)
1038 return MX_STATUS_ERROR;
1039
1040 if (!adata->fp)
1041 {
1042 if (mbox_mbox_open(m) != MX_OPEN_OK)
1043 return MX_STATUS_ERROR;
1045 }
1046 if (!adata->fp)
1047 return MX_STATUS_ERROR;
1048
1049 struct stat st = { 0 };
1050 bool unlock = false;
1051 bool modified = false;
1052
1053 if (stat(mailbox_path(m), &st) == 0)
1054 {
1056 (st.st_size == m->size))
1057 {
1058 return MX_STATUS_OK;
1059 }
1060
1061 if (st.st_size == m->size)
1062 {
1063 /* the file was touched, but it is still the same length, so just exit */
1065 return MX_STATUS_OK;
1066 }
1067
1068 if (st.st_size > m->size)
1069 {
1070 /* lock the file if it isn't already */
1071 if (!adata->locked)
1072 {
1074 if (mbox_lock_mailbox(m, false, false) == -1)
1075 {
1077 /* we couldn't lock the mailbox, but nothing serious happened:
1078 * probably the new mail arrived: no reason to wait till we can
1079 * parse it: we'll get it on the next pass */
1080 return MX_STATUS_LOCKED;
1081 }
1082 unlock = 1;
1083 }
1084
1085 /* Check to make sure that the only change to the mailbox is that
1086 * message(s) were appended to this file. My heuristic is that we should
1087 * see the message separator at *exactly* what used to be the end of the
1088 * folder. */
1089 char buf[1024] = { 0 };
1090 if (!mutt_file_seek(adata->fp, m->size, SEEK_SET))
1091 {
1092 goto error;
1093 }
1094 if (fgets(buf, sizeof(buf), adata->fp))
1095 {
1096 if (((m->type == MUTT_MBOX) && mutt_str_startswith(buf, "From ")) ||
1097 ((m->type == MUTT_MMDF) && mutt_str_equal(buf, MMDF_SEP)))
1098 {
1099 if (!mutt_file_seek(adata->fp, m->size, SEEK_SET))
1100 {
1101 goto error;
1102 }
1103
1104 int old_msg_count = m->msg_count;
1105 if (m->type == MUTT_MBOX)
1107 else
1109
1110 if (m->msg_count > old_msg_count)
1112
1113 /* Only unlock the folder if it was locked inside of this routine.
1114 * It may have been locked elsewhere, like in
1115 * mutt_checkpoint_mailbox(). */
1116 if (unlock)
1117 {
1120 }
1121
1122 return MX_STATUS_NEW_MAIL; /* signal that new mail arrived */
1123 }
1124 else
1125 {
1126 modified = true;
1127 }
1128 }
1129 else
1130 {
1131 mutt_debug(LL_DEBUG1, "fgets returned NULL\n");
1132 modified = true;
1133 }
1134 }
1135 else
1136 {
1137 modified = true;
1138 }
1139 }
1140
1141 if (modified)
1142 {
1143 if (reopen_mailbox(m) != -1)
1144 {
1146 if (unlock)
1147 {
1150 }
1151 return MX_STATUS_REOPENED;
1152 }
1153 }
1154
1155 /* fatal error */
1156
1157error:
1159 mx_fastclose_mailbox(m, false);
1161 mutt_error(_("Mailbox was corrupted"));
1162 return MX_STATUS_ERROR;
1163}
1164
1168static enum MxStatus mbox_mbox_sync(struct Mailbox *m)
1169{
1171 if (!adata)
1172 return MX_STATUS_ERROR;
1173
1174 struct Buffer *tempfile = NULL;
1175 char buf[32] = { 0 };
1176 int j;
1177 bool unlink_tempfile = false;
1178 bool need_sort = false; /* flag to resort mailbox if new mail arrives */
1179 int first = -1; /* first message to be written */
1180 LOFF_T offset; /* location in mailbox to write changed messages */
1181 struct stat st = { 0 };
1182 struct MUpdate *new_offset = NULL;
1183 struct MUpdate *old_offset = NULL;
1184 FILE *fp = NULL;
1185 struct Progress *progress = NULL;
1186 enum MxStatus rc = MX_STATUS_ERROR;
1187
1188 /* sort message by their position in the mailbox on disk */
1189 const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
1190 const unsigned char c_use_threads = cs_subset_enum(NeoMutt->sub, "use_threads");
1191 if (c_sort != SORT_ORDER)
1192 {
1194 cs_subset_str_native_set(NeoMutt->sub, "use_threads", UT_FLAT, NULL);
1196 cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
1197 cs_subset_str_native_set(NeoMutt->sub, "use_threads", c_use_threads, NULL);
1198 need_sort = true;
1199 }
1200
1201 /* need to open the file for writing in such a way that it does not truncate
1202 * the file, so use read-write mode. */
1203 adata->fp = freopen(mailbox_path(m), "r+", adata->fp);
1204 if (!adata->fp)
1205 {
1206 mx_fastclose_mailbox(m, false);
1207 mutt_error(_("Fatal error! Could not reopen mailbox!"));
1208 goto fatal;
1209 }
1210
1212
1213 if (mbox_lock_mailbox(m, true, true) == -1)
1214 {
1216 mutt_error(_("Unable to lock mailbox"));
1217 goto bail;
1218 }
1219
1220 /* Check to make sure that the file hasn't changed on disk */
1221 enum MxStatus check = mbox_mbox_check(m);
1222 if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
1223 {
1224 /* new mail arrived, or mailbox reopened */
1225 rc = check;
1226 goto bail;
1227 }
1228 else if (check < 0)
1229 {
1230 goto fatal;
1231 }
1232
1233 /* Create a temporary file to write the new version of the mailbox in. */
1234 tempfile = mutt_buffer_pool_get();
1235 mutt_buffer_mktemp(tempfile);
1236 int fd = open(mutt_buffer_string(tempfile), O_WRONLY | O_EXCL | O_CREAT, 0600);
1237 if ((fd == -1) || !(fp = fdopen(fd, "w")))
1238 {
1239 if (fd != -1)
1240 {
1241 close(fd);
1242 unlink_tempfile = true;
1243 }
1244 mutt_error(_("Could not create temporary file"));
1245 goto bail;
1246 }
1247 unlink_tempfile = true;
1248
1249 /* find the first deleted/changed message. we save a lot of time by only
1250 * rewriting the mailbox from the point where it has actually changed. */
1251 int i = 0;
1252 for (; (i < m->msg_count) && !m->emails[i]->deleted &&
1253 !m->emails[i]->changed && !m->emails[i]->attach_del;
1254 i++)
1255 {
1256 }
1257 if (i == m->msg_count)
1258 {
1259 /* this means m->changed or m->msg_deleted was set, but no
1260 * messages were found to be changed or deleted. This should
1261 * never happen, is we presume it is a bug in neomutt. */
1262 mutt_error(_("sync: mbox modified, but no modified messages (report this bug)"));
1263 mutt_debug(LL_DEBUG1, "no modified messages\n");
1264 goto bail;
1265 }
1266
1267 /* save the index of the first changed/deleted message */
1268 first = i;
1269 /* where to start overwriting */
1270 offset = m->emails[i]->offset;
1271
1272 /* the offset stored in the header does not include the MMDF_SEP, so make
1273 * sure we seek to the correct location */
1274 if (m->type == MUTT_MMDF)
1275 offset -= (sizeof(MMDF_SEP) - 1);
1276
1277 /* allocate space for the new offsets */
1278 new_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate));
1279 old_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate));
1280
1281 if (m->verbose)
1282 {
1283 char msg[PATH_MAX] = { 0 };
1284 snprintf(msg, sizeof(msg), _("Writing %s..."), mailbox_path(m));
1285 progress = progress_new(msg, MUTT_PROGRESS_WRITE, m->msg_count);
1286 }
1287
1288 for (i = first, j = 0; i < m->msg_count; i++)
1289 {
1290 if (m->verbose)
1291 progress_update(progress, i, i / (m->msg_count / 100 + 1));
1292 /* back up some information which is needed to restore offsets when
1293 * something fails. */
1294
1295 old_offset[i - first].valid = true;
1296 old_offset[i - first].hdr = m->emails[i]->offset;
1297 old_offset[i - first].body = m->emails[i]->body->offset;
1298 old_offset[i - first].lines = m->emails[i]->lines;
1299 old_offset[i - first].length = m->emails[i]->body->length;
1300
1301 if (!m->emails[i]->deleted)
1302 {
1303 j++;
1304
1305 if (m->type == MUTT_MMDF)
1306 {
1307 if (fputs(MMDF_SEP, fp) == EOF)
1308 {
1310 goto bail;
1311 }
1312 }
1313
1314 /* save the new offset for this message. we add 'offset' because the
1315 * temporary file only contains saved message which are located after
1316 * 'offset' in the real mailbox */
1317 new_offset[i - first].hdr = ftello(fp) + offset;
1318
1319 struct Message *msg = mx_msg_open(m, m->emails[i]->msgno);
1320 const int rc2 = mutt_copy_message(fp, m->emails[i], msg, MUTT_CM_UPDATE,
1322 mx_msg_close(m, &msg);
1323 if (rc2 != 0)
1324 {
1326 goto bail;
1327 }
1328
1329 /* Since messages could have been deleted, the offsets stored in memory
1330 * will be wrong, so update what we can, which is the offset of this
1331 * message, and the offset of the body. If this is a multipart message,
1332 * we just flush the in memory cache so that the message will be reparsed
1333 * if the user accesses it later. */
1334 new_offset[i - first].body = ftello(fp) - m->emails[i]->body->length + offset;
1335 mutt_body_free(&m->emails[i]->body->parts);
1336
1337 switch (m->type)
1338 {
1339 case MUTT_MMDF:
1340 if (fputs(MMDF_SEP, fp) == EOF)
1341 {
1343 goto bail;
1344 }
1345 break;
1346 default:
1347 if (fputs("\n", fp) == EOF)
1348 {
1350 goto bail;
1351 }
1352 }
1353 }
1354 }
1355
1356 if (mutt_file_fclose(&fp) != 0)
1357 {
1358 mutt_debug(LL_DEBUG1, "mutt_file_fclose (&) returned non-zero\n");
1360 goto bail;
1361 }
1362
1363 /* Save the state of this folder. */
1364 if (stat(mailbox_path(m), &st) == -1)
1365 {
1367 goto bail;
1368 }
1369
1370 unlink_tempfile = false;
1371
1372 fp = fopen(mutt_buffer_string(tempfile), "r");
1373 if (!fp)
1374 {
1376 mx_fastclose_mailbox(m, false);
1377 mutt_debug(LL_DEBUG1, "unable to reopen temp copy of mailbox!\n");
1379 FREE(&new_offset);
1380 FREE(&old_offset);
1381 goto fatal;
1382 }
1383
1384 if (!mutt_file_seek(adata->fp, offset, SEEK_SET) || /* seek the append location */
1385 /* do a sanity check to make sure the mailbox looks ok */
1386 !fgets(buf, sizeof(buf), adata->fp) ||
1387 ((m->type == MUTT_MBOX) && !mutt_str_startswith(buf, "From ")) ||
1388 ((m->type == MUTT_MMDF) && !mutt_str_equal(MMDF_SEP, buf)))
1389 {
1390 mutt_debug(LL_DEBUG1, "message not in expected position\n");
1391 mutt_debug(LL_DEBUG1, " LINE: %s\n", buf);
1392 i = -1;
1393 }
1394 else
1395 {
1396 if (!mutt_file_seek(adata->fp, offset, SEEK_SET)) /* return to proper offset */
1397 {
1398 i = -1;
1399 }
1400 else
1401 {
1402 /* copy the temp mailbox back into place starting at the first
1403 * change/deleted message */
1404 if (m->verbose)
1405 mutt_message(_("Committing changes..."));
1406 i = mutt_file_copy_stream(fp, adata->fp);
1407
1408 if (ferror(adata->fp))
1409 i = -1;
1410 }
1411 if (i >= 0)
1412 {
1413 m->size = ftello(adata->fp); /* update the mailbox->size of the mailbox */
1414 if ((m->size < 0) || (ftruncate(fileno(adata->fp), m->size) != 0))
1415 {
1416 i = -1;
1417 mutt_debug(LL_DEBUG1, "ftruncate() failed\n");
1418 }
1419 }
1420 }
1421
1423 fp = NULL;
1425
1426 if ((mutt_file_fclose(&adata->fp) != 0) || (i == -1))
1427 {
1428 /* error occurred while writing the mailbox back, so keep the temp copy around */
1429
1430 struct Buffer *savefile = mutt_buffer_pool_get();
1431
1432 const char *const c_tmp_dir = cs_subset_path(NeoMutt->sub, "tmp_dir");
1433 mutt_buffer_printf(savefile, "%s/neomutt.%s-%s-%u", NONULL(c_tmp_dir),
1434 NONULL(Username), NONULL(ShortHostname), (unsigned int) getpid());
1435 rename(mutt_buffer_string(tempfile), mutt_buffer_string(savefile));
1437 mx_fastclose_mailbox(m, false);
1439 mutt_error(_("Write failed! Saved partial mailbox to %s"), mutt_buffer_string(savefile));
1440 mutt_buffer_pool_release(&savefile);
1441 FREE(&new_offset);
1442 FREE(&old_offset);
1443 goto fatal;
1444 }
1445
1446 /* Restore the previous access/modification times */
1447 mbox_reset_atime(m, &st);
1448
1449 /* reopen the mailbox in read-only mode */
1450 adata->fp = mbox_open_readwrite(m);
1451 if (!adata->fp)
1452 {
1453 adata->fp = mbox_open_readonly(m);
1454 }
1455 if (!adata->fp)
1456 {
1457 unlink(mutt_buffer_string(tempfile));
1459 mx_fastclose_mailbox(m, false);
1460 mutt_error(_("Fatal error! Could not reopen mailbox!"));
1461 FREE(&new_offset);
1462 FREE(&old_offset);
1463 goto fatal;
1464 }
1465
1466 /* update the offsets of the rewritten messages */
1467 for (i = first, j = first; i < m->msg_count; i++)
1468 {
1469 if (!m->emails[i]->deleted)
1470 {
1471 m->emails[i]->offset = new_offset[i - first].hdr;
1472 m->emails[i]->body->hdr_offset = new_offset[i - first].hdr;
1473 m->emails[i]->body->offset = new_offset[i - first].body;
1474 m->emails[i]->index = j++;
1475 }
1476 }
1477 FREE(&new_offset);
1478 FREE(&old_offset);
1479 unlink(mutt_buffer_string(tempfile)); /* remove partial copy of the mailbox */
1480 mutt_buffer_pool_release(&tempfile);
1482
1483 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
1484 if (c_check_mbox_size)
1485 {
1486 struct Mailbox *m_tmp = mailbox_find(mailbox_path(m));
1487 if (m_tmp && !m_tmp->has_new)
1488 mailbox_update(m_tmp);
1489 }
1490
1491 progress_free(&progress);
1492 return 0; /* signal success */
1493
1494bail: /* Come here in case of disaster */
1495
1496 mutt_file_fclose(&fp);
1497
1498 if (tempfile && unlink_tempfile)
1499 unlink(mutt_buffer_string(tempfile));
1500
1501 /* restore offsets, as far as they are valid */
1502 if ((first >= 0) && old_offset)
1503 {
1504 for (i = first; (i < m->msg_count) && old_offset[i - first].valid; i++)
1505 {
1506 m->emails[i]->offset = old_offset[i - first].hdr;
1507 m->emails[i]->body->hdr_offset = old_offset[i - first].hdr;
1508 m->emails[i]->body->offset = old_offset[i - first].body;
1509 m->emails[i]->lines = old_offset[i - first].lines;
1510 m->emails[i]->body->length = old_offset[i - first].length;
1511 }
1512 }
1513
1514 /* this is ok to call even if we haven't locked anything */
1516
1518 FREE(&new_offset);
1519 FREE(&old_offset);
1520
1521 adata->fp = freopen(mailbox_path(m), "r", adata->fp);
1522 if (!adata->fp)
1523 {
1524 mutt_error(_("Could not reopen mailbox"));
1525 mx_fastclose_mailbox(m, false);
1526 goto fatal;
1527 }
1528
1530 if (need_sort)
1531 {
1532 /* if the mailbox was reopened, the thread tree will be invalid so make
1533 * sure to start threading from scratch. */
1535 }
1536
1537fatal:
1538 mutt_buffer_pool_release(&tempfile);
1539 progress_free(&progress);
1540 return rc;
1541}
1542
1546static enum MxStatus mbox_mbox_close(struct Mailbox *m)
1547{
1549 if (!adata)
1550 return MX_STATUS_ERROR;
1551
1552 if (!adata->fp)
1553 return MX_STATUS_OK;
1554
1555 if (adata->append)
1556 {
1557 mutt_file_unlock(fileno(adata->fp));
1559 }
1560
1561 mutt_file_fclose(&adata->fp);
1562
1563 /* fix up the times so mailbox won't get confused */
1564 if (m->peekonly && !mutt_buffer_is_empty(&m->pathbuf) &&
1565 (mutt_file_timespec_compare(&m->mtime, &adata->atime) > 0))
1566 {
1567#ifdef HAVE_UTIMENSAT
1568 struct timespec ts[2];
1569 ts[0] = adata->atime;
1570 ts[1] = m->mtime;
1571 utimensat(AT_FDCWD, m->path, ts, 0);
1572#else
1573 struct utimbuf ut;
1574 ut.actime = adata->atime.tv_sec;
1575 ut.modtime = m->mtime.tv_sec;
1576 utime(mailbox_path(m), &ut);
1577#endif
1578 }
1579
1580 return MX_STATUS_OK;
1581}
1582
1586static bool mbox_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
1587{
1589 if (!adata)
1590 return false;
1591
1592 msg->fp = mutt_file_fopen(mailbox_path(m), "r");
1593 if (!msg->fp)
1594 return false;
1595
1596 return true;
1597}
1598
1602static bool mbox_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
1603{
1605 if (!adata)
1606 return false;
1607
1608 msg->fp = adata->fp;
1609 return true;
1610}
1611
1615static int mbox_msg_commit(struct Mailbox *m, struct Message *msg)
1616{
1617 if (fputc('\n', msg->fp) == EOF)
1618 return -1;
1619
1620 if ((fflush(msg->fp) == EOF) || (fsync(fileno(msg->fp)) == -1))
1621 {
1622 mutt_perror(_("Can't write message"));
1623 return -1;
1624 }
1625
1626 return 0;
1627}
1628
1632static int mbox_msg_close(struct Mailbox *m, struct Message *msg)
1633{
1634 if (msg->write)
1635 msg->fp = NULL;
1636 else
1637 mutt_file_fclose(&msg->fp);
1638
1639 return 0;
1640}
1641
1647static int mbox_msg_padding_size(struct Mailbox *m)
1648{
1649 return 1;
1650}
1651
1655enum MailboxType mbox_path_probe(const char *path, const struct stat *st)
1656{
1657 if (!st)
1658 return MUTT_UNKNOWN;
1659
1660 if (S_ISDIR(st->st_mode))
1661 return MUTT_UNKNOWN;
1662
1663 if (st->st_size == 0)
1664 return MUTT_MBOX;
1665
1666 FILE *fp = fopen(path, "r");
1667 if (!fp)
1668 return MUTT_UNKNOWN;
1669
1670 int ch;
1671 while ((ch = fgetc(fp)) != EOF)
1672 {
1673 /* Some mailbox creation tools erroneously append a blank line to
1674 * a file before appending a mail message. This allows neomutt to
1675 * detect type for and thus open those files. */
1676 if ((ch != '\n') && (ch != '\r'))
1677 {
1678 ungetc(ch, fp);
1679 break;
1680 }
1681 }
1682
1684 char tmp[256] = { 0 };
1685 if (fgets(tmp, sizeof(tmp), fp))
1686 {
1687 if (mutt_str_startswith(tmp, "From "))
1688 type = MUTT_MBOX;
1689 else if (mutt_str_equal(tmp, MMDF_SEP))
1690 type = MUTT_MMDF;
1691 }
1693
1694 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
1695 if (!c_check_mbox_size)
1696 {
1697 /* need to restore the times here, the file was not really accessed,
1698 * only the type was accessed. This is important, because detection
1699 * of "new mail" depends on those times set correctly. */
1700#ifdef HAVE_UTIMENSAT
1701 struct timespec ts[2];
1704 utimensat(AT_FDCWD, path, ts, 0);
1705#else
1706 struct utimbuf times;
1707 times.actime = st->st_atime;
1708 times.modtime = st->st_mtime;
1709 utime(path, &times);
1710#endif
1711 }
1712
1713 return type;
1714}
1715
1719static int mbox_path_canon(char *buf, size_t buflen)
1720{
1721 mutt_path_canon(buf, buflen, HomeDir, false);
1722 return 0;
1723}
1724
1728static int mbox_path_pretty(char *buf, size_t buflen, const char *folder)
1729{
1730 if (mutt_path_abbr_folder(buf, folder))
1731 return 0;
1732
1733 if (mutt_path_pretty(buf, buflen, HomeDir, false))
1734 return 0;
1735
1736 return -1;
1737}
1738
1742static int mbox_path_parent(char *buf, size_t buflen)
1743{
1744 if (mutt_path_parent(buf))
1745 return 0;
1746
1747 if (buf[0] == '~')
1748 mutt_path_canon(buf, buflen, HomeDir, false);
1749
1750 if (mutt_path_parent(buf))
1751 return 0;
1752
1753 return -1;
1754}
1755
1759static int mbox_path_is_empty(const char *path)
1760{
1761 return mutt_file_check_empty(path);
1762}
1763
1767static int mmdf_msg_commit(struct Mailbox *m, struct Message *msg)
1768{
1769 if (fputs(MMDF_SEP, msg->fp) == EOF)
1770 return -1;
1771
1772 if ((fflush(msg->fp) == EOF) || (fsync(fileno(msg->fp)) == -1))
1773 {
1774 mutt_perror(_("Can't write message"));
1775 return -1;
1776 }
1777
1778 return 0;
1779}
1780
1786static int mmdf_msg_padding_size(struct Mailbox *m)
1787{
1788 return 10;
1789}
1790
1794static enum MxStatus mbox_mbox_check_stats(struct Mailbox *m, uint8_t flags)
1795{
1796 struct stat st = { 0 };
1797 if (stat(mailbox_path(m), &st) != 0)
1798 return MX_STATUS_ERROR;
1799
1800 bool new_or_changed;
1801
1802 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
1803 if (c_check_mbox_size)
1804 {
1805 new_or_changed = (st.st_size > m->size);
1806 }
1807 else
1808 {
1809 new_or_changed =
1811 (m->newly_created &&
1814 }
1815
1816 if (new_or_changed)
1817 {
1818 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
1819 if (!c_mail_check_recent ||
1821 {
1822 m->has_new = true;
1823 }
1824 }
1825 else if (c_check_mbox_size)
1826 {
1827 /* some other program has deleted mail from the folder */
1828 m->size = (off_t) st.st_size;
1829 }
1830
1831 if (m->newly_created && ((st.st_ctime != st.st_mtime) || (st.st_ctime != st.st_atime)))
1832 m->newly_created = false;
1833
1835 {
1838 &adata->stats_last_checked) > 0)
1839 {
1840 bool old_peek = m->peekonly;
1842 mx_mbox_close(m);
1843 m->peekonly = old_peek;
1844 adata->stats_last_checked.tv_sec = mutt_date_now();
1845 }
1846 }
1847
1848 if (m->has_new || m->msg_new)
1849 return MX_STATUS_NEW_MAIL;
1850 return MX_STATUS_OK;
1851}
1852
1857 // clang-format off
1858 .type = MUTT_MBOX,
1859 .name = "mbox",
1860 .is_local = true,
1861 .ac_owns_path = mbox_ac_owns_path,
1862 .ac_add = mbox_ac_add,
1863 .mbox_open = mbox_mbox_open,
1864 .mbox_open_append = mbox_mbox_open_append,
1865 .mbox_check = mbox_mbox_check,
1866 .mbox_check_stats = mbox_mbox_check_stats,
1867 .mbox_sync = mbox_mbox_sync,
1868 .mbox_close = mbox_mbox_close,
1869 .msg_open = mbox_msg_open,
1870 .msg_open_new = mbox_msg_open_new,
1871 .msg_commit = mbox_msg_commit,
1872 .msg_close = mbox_msg_close,
1873 .msg_padding_size = mbox_msg_padding_size,
1874 .msg_save_hcache = NULL,
1875 .tags_edit = NULL,
1876 .tags_commit = NULL,
1877 .path_probe = mbox_path_probe,
1878 .path_canon = mbox_path_canon,
1879 .path_pretty = mbox_path_pretty,
1880 .path_parent = mbox_path_parent,
1881 .path_is_empty = mbox_path_is_empty,
1882 // clang-format on
1883};
1884
1889 // clang-format off
1890 .type = MUTT_MMDF,
1891 .name = "mmdf",
1892 .is_local = true,
1893 .ac_owns_path = mbox_ac_owns_path,
1894 .ac_add = mbox_ac_add,
1895 .mbox_open = mbox_mbox_open,
1896 .mbox_open_append = mbox_mbox_open_append,
1897 .mbox_check = mbox_mbox_check,
1898 .mbox_check_stats = mbox_mbox_check_stats,
1899 .mbox_sync = mbox_mbox_sync,
1900 .mbox_close = mbox_mbox_close,
1901 .msg_open = mbox_msg_open,
1902 .msg_open_new = mbox_msg_open_new,
1903 .msg_commit = mmdf_msg_commit,
1904 .msg_close = mbox_msg_close,
1905 .msg_padding_size = mmdf_msg_padding_size,
1906 .msg_save_hcache = NULL,
1907 .tags_edit = NULL,
1908 .tags_commit = NULL,
1909 .path_probe = mbox_path_probe,
1910 .path_canon = mbox_path_canon,
1911 .path_pretty = mbox_path_pretty,
1912 .path_parent = mbox_path_parent,
1913 .path_is_empty = mbox_path_is_empty,
1914 // clang-format on
1915};
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:745
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:462
Email Address Handling.
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:298
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:168
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:78
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: globals.c:38
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:875
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.
int mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition: date.c:206
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:432
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:1598
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:259
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:634
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:1660
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:149
void mutt_file_touch_atime(int fd)
Set the access time to current time.
Definition: file.c:1069
int mutt_file_check_empty(const char *path)
Is the mailbox empty.
Definition: file.c:1455
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:952
int mutt_file_lock(int fd, bool excl, bool timeout)
(Try to) Lock a file using fcntl()
Definition: file.c:1218
int mutt_file_timespec_compare(struct timespec *a, struct timespec *b)
Compare to time values.
Definition: file.c:1576
int mutt_file_unlock(int fd)
Unlock a file previously locked by mutt_file_lock()
Definition: file.c:1266
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:708
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:1638
@ MUTT_STAT_CTIME
File/dir's ctime - creation time.
Definition: file.h:65
@ MUTT_STAT_ATIME
File/dir's atime - last accessed time.
Definition: file.h:63
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition: file.h:64
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
char * ShortHostname
Short version of the hostname.
Definition: globals.c:39
char * Username
User's login name.
Definition: globals.c:41
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: globals.c:58
#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:895
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:880
struct MxOps MxMmdfOps
MMDF Mailbox - Implements MxOps -.
Definition: mbox.c:1888
struct MxOps MxMboxOps
Mbox Mailbox - Implements MxOps -.
Definition: mbox.c:1856
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:1794
static enum MxStatus mbox_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check() -.
Definition: mbox.c:1034
static enum MxStatus mbox_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close() -.
Definition: mbox.c:1546
static bool mbox_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
Definition: mbox.c:982
static enum MxOpenReturns mbox_mbox_open(struct Mailbox *m)
Open a Mailbox - Implements MxOps::mbox_open() -.
Definition: mbox.c:933
static enum MxStatus mbox_mbox_sync(struct Mailbox *m)
Save changes to the Mailbox - Implements MxOps::mbox_sync() -.
Definition: mbox.c:1168
static int mbox_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition: mbox.c:1632
static int mbox_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: mbox.c:1615
static int mmdf_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: mbox.c:1767
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:1602
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:1586
static int mbox_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Implements MxOps::msg_padding_size() -.
Definition: mbox.c:1647
static int mmdf_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Implements MxOps::msg_padding_size() -.
Definition: mbox.c:1786
static int mbox_path_canon(char *buf, size_t buflen)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition: mbox.c:1719
static int mbox_path_is_empty(const char *path)
Is the mailbox empty - Implements MxOps::path_is_empty() -.
Definition: mbox.c:1759
static int mbox_path_parent(char *buf, size_t buflen)
Find the parent of a Mailbox path - Implements MxOps::path_parent() -.
Definition: mbox.c:1742
static int mbox_path_pretty(char *buf, size_t buflen, const char *folder)
Abbreviate a Mailbox path - Implements MxOps::path_pretty() -.
Definition: mbox.c:1728
enum MailboxType mbox_path_probe(const char *path, const struct stat *st)
Is this an mbox Mailbox? - Implements MxOps::path_probe() -.
Definition: mbox.c:1655
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:176
@ NT_MAILBOX_INVALID
Email list was changed.
Definition: mailbox.h:175
@ NT_MAILBOX_UPDATE
Update internal tables.
Definition: mailbox.h:177
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:209
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:61
static enum MxOpenReturns mbox_parse_mailbox(struct Mailbox *m)
Read a mailbox from disk.
Definition: mbox.c:349
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:717
static int mbox_lock_mailbox(struct Mailbox *m, bool excl, bool retry)
Lock a mailbox.
Definition: mbox.c:134
static struct MboxAccountData * mbox_adata_get(struct Mailbox *m)
Get the private data associated with a Mailbox.
Definition: mbox.c:119
static int init_mailbox(struct Mailbox *m)
Add Mbox data to the Mailbox.
Definition: mbox.c:100
static FILE * mbox_open_readwrite(struct Mailbox *m)
Open an mbox read-write.
Definition: mbox.c:907
static bool test_last_status_new(FILE *fp)
Is the last message new.
Definition: mbox.c:799
static int fseek_last_message(FILE *fp)
Find the last message in the file.
Definition: mbox.c:736
static FILE * mbox_open_readonly(struct Mailbox *m)
Open an mbox read-only.
Definition: mbox.c:922
static void mbox_unlock_mailbox(struct Mailbox *m)
Unlock a mailbox.
Definition: mbox.c:158
bool mbox_test_new_folder(const char *path)
Test if an mbox or mmdf mailbox has new mail.
Definition: mbox.c:824
static enum MxOpenReturns mmdf_parse_mailbox(struct Mailbox *m)
Read a mailbox in MMDF format.
Definition: mbox.c:178
void mbox_reset_atime(struct Mailbox *m, struct stat *st)
Reset the access time on the mailbox file.
Definition: mbox.c:851
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:539
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:807
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:81
@ MUTT_OLD
Old messages.
Definition: mutt.h:79
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition: mutt.h:85
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:88
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:87
@ MUTT_DELETE
Messages to be deleted.
Definition: mutt.h:83
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:80
#define PATH_MAX
Definition: mutt.h:41
void mutt_make_label_hash(struct Mailbox *m)
Create a Hash Table to store the labels.
Definition: mutt_header.c:413
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:546
Some miscellaneous functions.
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1200
void mx_fastclose_mailbox(struct Mailbox *m, bool keep_account)
Free up memory associated with the Mailbox.
Definition: mx.c:431
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:305
struct Message * mx_msg_open(struct Mailbox *m, int msgno)
Return a stream pointer for a message.
Definition: mx.c:1154
void mx_alloc_memory(struct Mailbox *m)
Create storage for the emails.
Definition: mx.c:1226
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1334
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition: mx.c:616
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:1162
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:63
#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:44
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:52
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:98
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:110
bool deleted
Email is deleted.
Definition: email.h:76
int index
The absolute (unsorted) message number.
Definition: email.h:109
bool tagged
Email is tagged.
Definition: email.h:106
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:152
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:153
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:110
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:124
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
struct HashTable * id_hash
Hash Table by msg id.
Definition: mailbox.h:123
struct Buffer pathbuf
Path of the Mailbox.
Definition: mailbox.h:80
bool peekonly
Just taking a glance, revert atime.
Definition: mailbox.h:113
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:93
struct Account * account
Account that owns this Mailbox.
Definition: mailbox.h:127
off_t size
Size of the Mailbox.
Definition: mailbox.h:84
struct HashTable * label_hash
Hash Table for x-labels.
Definition: mailbox.h:125
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:115
int msg_tagged
How many messages are tagged?
Definition: mailbox.h:94
bool verbose
Display status messages?
Definition: mailbox.h:114
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:54
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:50
time_t tv_sec
Number of seconds since the epoch.
Definition: file.h:51
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
#define mutt_buffer_mktemp(buf)
Definition: tmp.h:37