NeoMutt  2022-04-29-247-gc6aae8
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
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 adata->locked = true;
143 else if (retry && !excl)
144 {
145 m->readonly = true;
146 return 0;
147 }
148
149 return rc;
150}
151
156static void mbox_unlock_mailbox(struct Mailbox *m)
157{
159 if (!adata)
160 return;
161
162 if (adata->locked)
163 {
164 fflush(adata->fp);
165
166 mutt_file_unlock(fileno(adata->fp));
167 adata->locked = false;
168 }
169}
170
177{
178 if (!m)
179 return MX_OPEN_ERROR;
180
182 if (!adata)
183 return MX_OPEN_ERROR;
184
185 char buf[8192] = { 0 };
186 char return_path[1024] = { 0 };
187 int count = 0;
188 int lines;
189 time_t t = 0;
190 LOFF_T loc, tmploc;
191 struct Email *e = NULL;
192 struct stat st = { 0 };
193 struct Progress *progress = NULL;
195
196 if (stat(mailbox_path(m), &st) == -1)
197 {
199 goto fail;
200 }
203 m->size = st.st_size;
204
205 buf[sizeof(buf) - 1] = '\0';
206
207 if (m->verbose)
208 {
209 char msg[PATH_MAX] = { 0 };
210 snprintf(msg, sizeof(msg), _("Reading %s..."), mailbox_path(m));
211 progress = progress_new(msg, MUTT_PROGRESS_READ, 0);
212 }
213
214 while (true)
215 {
216 if (!fgets(buf, sizeof(buf) - 1, adata->fp))
217 break;
218
219 if (SigInt)
220 break;
221
222 if (mutt_str_equal(buf, MMDF_SEP))
223 {
224 loc = ftello(adata->fp);
225 if (loc < 0)
226 goto fail;
227
228 count++;
229 if (m->verbose)
230 progress_update(progress, count, (int) (loc / (m->size / 100 + 1)));
231
232 if (m->msg_count == m->email_max)
234 e = email_new();
235 m->emails[m->msg_count] = e;
236 e->offset = loc;
237 e->index = m->msg_count;
238
239 if (!fgets(buf, sizeof(buf) - 1, adata->fp))
240 {
241 /* TODO: memory leak??? */
242 mutt_debug(LL_DEBUG1, "unexpected EOF\n");
243 break;
244 }
245
246 return_path[0] = '\0';
247
248 if (!is_from(buf, return_path, sizeof(return_path), &t))
249 {
250 if (!mutt_file_seek(adata->fp, loc, SEEK_SET))
251 {
252 mutt_error(_("Mailbox is corrupt"));
253 goto fail;
254 }
255 }
256 else
257 {
258 e->received = t - mutt_date_local_tz(t);
259 }
260
261 e->env = mutt_rfc822_read_header(adata->fp, e, false, false);
262
263 loc = ftello(adata->fp);
264 if (loc < 0)
265 goto fail;
266
267 if ((e->body->length > 0) && (e->lines > 0))
268 {
269 tmploc = loc + e->body->length;
270
271 if ((tmploc > 0) && (tmploc < m->size))
272 {
273 if (!mutt_file_seek(adata->fp, tmploc, SEEK_SET) ||
274 !fgets(buf, sizeof(buf) - 1, adata->fp) || !mutt_str_equal(MMDF_SEP, buf))
275 {
276 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
277 e->body->length = -1;
278 }
279 }
280 else
281 e->body->length = -1;
282 }
283 else
284 e->body->length = -1;
285
286 if (e->body->length < 0)
287 {
288 lines = -1;
289 do
290 {
291 loc = ftello(adata->fp);
292 if (loc < 0)
293 goto fail;
294 if (!fgets(buf, sizeof(buf) - 1, adata->fp))
295 break;
296 lines++;
297 } while (!mutt_str_equal(buf, MMDF_SEP));
298
299 e->lines = lines;
300 e->body->length = loc - e->body->offset;
301 }
302
303 if (TAILQ_EMPTY(&e->env->return_path) && return_path[0])
304 mutt_addrlist_parse(&e->env->return_path, return_path);
305
306 if (TAILQ_EMPTY(&e->env->from))
307 mutt_addrlist_copy(&e->env->from, &e->env->return_path, false);
308
309 m->msg_count++;
310 }
311 else
312 {
313 mutt_debug(LL_DEBUG1, "corrupt mailbox\n");
314 mutt_error(_("Mailbox is corrupt"));
315 goto fail;
316 }
317 }
318
319 if (SigInt)
320 {
321 SigInt = false;
322 rc = MX_OPEN_ABORT; /* action aborted */
323 goto fail;
324 }
325
326 rc = MX_OPEN_OK;
327fail:
328 progress_free(&progress);
329 return rc;
330}
331
344{
345 if (!m)
346 return MX_OPEN_ERROR;
347
349 if (!adata)
350 return MX_OPEN_ERROR;
351
352 struct stat st = { 0 };
353 char buf[8192], return_path[256];
354 struct Email *e_cur = NULL;
355 time_t t = 0;
356 int count = 0, lines = 0;
357 LOFF_T loc;
358 struct Progress *progress = NULL;
360
361 /* Save information about the folder at the time we opened it. */
362 if (stat(mailbox_path(m), &st) == -1)
363 {
365 goto fail;
366 }
367
368 m->size = st.st_size;
371
372 if (!m->readonly)
373 m->readonly = access(mailbox_path(m), W_OK) ? true : false;
374
375 if (m->verbose)
376 {
377 char msg[PATH_MAX] = { 0 };
378 snprintf(msg, sizeof(msg), _("Reading %s..."), mailbox_path(m));
379 progress = progress_new(msg, MUTT_PROGRESS_READ, 0);
380 }
381
382 loc = ftello(adata->fp);
383 while ((fgets(buf, sizeof(buf), adata->fp)) && !SigInt)
384 {
385 if (is_from(buf, return_path, sizeof(return_path), &t))
386 {
387 /* Save the Content-Length of the previous message */
388 if (count > 0)
389 {
390 struct Email *e = m->emails[m->msg_count - 1];
391 if (e->body->length < 0)
392 {
393 e->body->length = loc - e->body->offset - 1;
394 if (e->body->length < 0)
395 e->body->length = 0;
396 }
397 if (!e->lines)
398 e->lines = lines ? lines - 1 : 0;
399 }
400
401 count++;
402
403 if (m->verbose)
404 {
405 progress_update(progress, count, (int) (ftello(adata->fp) / (m->size / 100 + 1)));
406 }
407
408 if (m->msg_count == m->email_max)
410
411 m->emails[m->msg_count] = email_new();
412 e_cur = m->emails[m->msg_count];
413 e_cur->received = t - mutt_date_local_tz(t);
414 e_cur->offset = loc;
415 e_cur->index = m->msg_count;
416
417 e_cur->env = mutt_rfc822_read_header(adata->fp, e_cur, false, false);
418
419 /* if we know how long this message is, either just skip over the body,
420 * or if we don't know how many lines there are, count them now (this will
421 * save time by not having to search for the next message marker). */
422 if (e_cur->body->length > 0)
423 {
424 LOFF_T tmploc;
425
426 loc = ftello(adata->fp);
427
428 /* The test below avoids a potential integer overflow if the
429 * content-length is huge (thus necessarily invalid). */
430 tmploc = (e_cur->body->length < m->size) ? (loc + e_cur->body->length + 1) : -1;
431
432 if ((tmploc > 0) && (tmploc < m->size))
433 {
434 /* check to see if the content-length looks valid. we expect to
435 * to see a valid message separator at this point in the stream */
436 if (!mutt_file_seek(adata->fp, tmploc, SEEK_SET) ||
437 !fgets(buf, sizeof(buf), adata->fp) || !mutt_str_startswith(buf, "From "))
438 {
439 mutt_debug(LL_DEBUG1, "bad content-length in message %d (cl=" OFF_T_FMT ")\n",
440 e_cur->index, e_cur->body->length);
441 mutt_debug(LL_DEBUG1, " LINE: %s", buf);
442 /* nope, return the previous position */
443 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
444 e_cur->body->length = -1;
445 }
446 }
447 else if (tmploc != m->size)
448 {
449 /* content-length would put us past the end of the file, so it
450 * must be wrong */
451 e_cur->body->length = -1;
452 }
453
454 if (e_cur->body->length != -1)
455 {
456 /* good content-length. check to see if we know how many lines
457 * are in this message. */
458 if (e_cur->lines == 0)
459 {
460 int cl = e_cur->body->length;
461
462 /* count the number of lines in this message */
463 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
464 while (cl-- > 0)
465 {
466 if (fgetc(adata->fp) == '\n')
467 e_cur->lines++;
468 }
469 }
470
471 /* return to the offset of the next message separator */
472 (void) mutt_file_seek(adata->fp, tmploc, SEEK_SET);
473 }
474 }
475
476 m->msg_count++;
477
478 if (TAILQ_EMPTY(&e_cur->env->return_path) && return_path[0])
479 {
480 mutt_addrlist_parse(&e_cur->env->return_path, return_path);
481 }
482
483 if (TAILQ_EMPTY(&e_cur->env->from))
484 mutt_addrlist_copy(&e_cur->env->from, &e_cur->env->return_path, false);
485
486 lines = 0;
487 }
488 else
489 lines++;
490
491 loc = ftello(adata->fp);
492 }
493
494 /* Only set the content-length of the previous message if we have read more
495 * than one message during _this_ invocation. If this routine is called
496 * when new mail is received, we need to make sure not to clobber what
497 * previously was the last message since the headers may be sorted. */
498 if (count > 0)
499 {
500 struct Email *e = m->emails[m->msg_count - 1];
501 if (e->body->length < 0)
502 {
503 e->body->length = ftello(adata->fp) - e->body->offset - 1;
504 if (e->body->length < 0)
505 e->body->length = 0;
506 }
507
508 if (!e->lines)
509 e->lines = lines ? lines - 1 : 0;
510 }
511
512 if (SigInt)
513 {
514 SigInt = false;
515 rc = MX_OPEN_ABORT;
516 goto fail; /* action aborted */
517 }
518
519 rc = MX_OPEN_OK;
520fail:
521 progress_free(&progress);
522 return rc;
523}
524
531static int reopen_mailbox(struct Mailbox *m)
532{
533 if (!m)
534 return -1;
535
537 if (!adata)
538 return -1;
539
540 bool (*cmp_headers)(const struct Email *, const struct Email *) = NULL;
541 struct Email **e_old = NULL;
542 int old_msg_count;
543 bool msg_mod = false;
544 int rc = -1;
545
546 /* silent operations */
547 m->verbose = false;
548
549 /* our heuristics require the old mailbox to be unsorted */
550 const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
551 if (c_sort != SORT_ORDER)
552 {
555 cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
556 }
557
558 e_old = NULL;
559 old_msg_count = 0;
560
561 /* simulate a close */
565 FREE(&m->v2r);
566 if (m->readonly)
567 {
568 for (int i = 0; i < m->msg_count; i++)
569 email_free(&(m->emails[i])); /* nothing to do! */
570 FREE(&m->emails);
571 }
572 else
573 {
574 /* save the old headers */
575 old_msg_count = m->msg_count;
576 e_old = m->emails;
577 m->emails = NULL;
578 }
579
580 m->email_max = 0; /* force allocation of new headers */
581 m->msg_count = 0;
582 m->vcount = 0;
583 m->msg_tagged = 0;
584 m->msg_deleted = 0;
585 m->msg_new = 0;
586 m->msg_unread = 0;
587 m->msg_flagged = 0;
588 m->changed = false;
589 m->id_hash = NULL;
590 m->subj_hash = NULL;
592
593 switch (m->type)
594 {
595 case MUTT_MBOX:
596 case MUTT_MMDF:
597 cmp_headers = email_cmp_strict;
598 mutt_file_fclose(&adata->fp);
599 adata->fp = mutt_file_fopen(mailbox_path(m), "r");
600 if (!adata->fp)
601 rc = -1;
602 else if (m->type == MUTT_MBOX)
603 rc = mbox_parse_mailbox(m);
604 else
605 rc = mmdf_parse_mailbox(m);
606 break;
607
608 default:
609 rc = -1;
610 break;
611 }
612
613 if (rc == -1)
614 {
615 /* free the old headers */
616 for (int i = 0; i < old_msg_count; i++)
617 email_free(&(e_old[i]));
618 FREE(&e_old);
619
620 m->verbose = true;
621 return -1;
622 }
623
624 mutt_file_touch_atime(fileno(adata->fp));
625
626 /* now try to recover the old flags */
627
628 if (!m->readonly)
629 {
630 for (int i = 0; i < m->msg_count; i++)
631 {
632 bool found = false;
633
634 /* some messages have been deleted, and new messages have been
635 * appended at the end; the heuristic is that old messages have then
636 * "advanced" towards the beginning of the folder, so we begin the
637 * search at index "i" */
638 int j;
639 for (j = i; j < old_msg_count; j++)
640 {
641 if (!e_old[j])
642 continue;
643 if (cmp_headers(m->emails[i], e_old[j]))
644 {
645 found = true;
646 break;
647 }
648 }
649 if (!found)
650 {
651 for (j = 0; (j < i) && (j < old_msg_count); j++)
652 {
653 if (!e_old[j])
654 continue;
655 if (cmp_headers(m->emails[i], e_old[j]))
656 {
657 found = true;
658 break;
659 }
660 }
661 }
662
663 if (found)
664 {
665 m->changed = true;
666 if (e_old[j]->changed)
667 {
668 /* Only update the flags if the old header was changed;
669 * otherwise, the header may have been modified externally,
670 * and we don't want to lose _those_ changes */
671 mutt_set_flag(m, m->emails[i], MUTT_FLAG, e_old[j]->flagged);
672 mutt_set_flag(m, m->emails[i], MUTT_REPLIED, e_old[j]->replied);
673 mutt_set_flag(m, m->emails[i], MUTT_OLD, e_old[j]->old);
674 mutt_set_flag(m, m->emails[i], MUTT_READ, e_old[j]->read);
675 }
676 mutt_set_flag(m, m->emails[i], MUTT_DELETE, e_old[j]->deleted);
677 mutt_set_flag(m, m->emails[i], MUTT_PURGE, e_old[j]->purge);
678 mutt_set_flag(m, m->emails[i], MUTT_TAG, e_old[j]->tagged);
679
680 /* we don't need this header any more */
681 email_free(&(e_old[j]));
682 }
683 }
684
685 /* free the remaining old headers */
686 for (int j = 0; j < old_msg_count; j++)
687 {
688 if (e_old[j])
689 {
690 email_free(&(e_old[j]));
691 msg_mod = true;
692 }
693 }
694 FREE(&e_old);
695 }
696
698 m->verbose = true;
699
700 return (m->changed || msg_mod) ? MX_STATUS_REOPENED : MX_STATUS_NEW_MAIL;
701}
702
709static bool mbox_has_new(struct Mailbox *m)
710{
711 for (int i = 0; i < m->msg_count; i++)
712 {
713 struct Email *e = m->emails[i];
714 if (!e)
715 break;
716 if (!e->deleted && !e->read && !e->old)
717 return true;
718 }
719 return false;
720}
721
728static int fseek_last_message(FILE *fp)
729{
730 LOFF_T pos;
731 char buf[BUFSIZ + 7] = { 0 }; // 7 for "\n\nFrom "
732 size_t bytes_read;
733
734 if (!mutt_file_seek(fp, 0, SEEK_END))
735 {
736 return -1;
737 }
738 pos = ftello(fp);
739
740 /* Set 'bytes_read' to the size of the last, probably partial, buf;
741 * 0 < 'bytes_read' <= 'BUFSIZ'. */
742 bytes_read = pos % BUFSIZ;
743 if (bytes_read == 0)
744 bytes_read = BUFSIZ;
745 /* Make 'pos' a multiple of 'BUFSIZ' (0 if the file is short), so that all
746 * reads will be on block boundaries, which might increase efficiency. */
747 while ((pos -= bytes_read) >= 0)
748 {
749 /* we save in the buf at the end the first 7 chars from the last read */
750 memcpy(buf + BUFSIZ, buf, 7);
751 if (!mutt_file_seek(fp, pos, SEEK_SET))
752 {
753 return -1;
754 }
755 bytes_read = fread(buf, sizeof(char), bytes_read, fp);
756 if (bytes_read == 0)
757 return -1;
758 /* 'i' is Index into 'buf' for scanning. */
759 for (int i = bytes_read; i >= 0; i--)
760 {
761 if (mutt_str_startswith(buf + i, "\n\nFrom "))
762 { /* found it - go to the beginning of the From */
763 if (!mutt_file_seek(fp, pos + i + 2, SEEK_SET))
764 {
765 return -1;
766 }
767 return 0;
768 }
769 }
770 bytes_read = BUFSIZ;
771 }
772
773 /* here we are at the beginning of the file */
774 if (mutt_str_startswith(buf, "From "))
775 {
776 if (!mutt_file_seek(fp, 0, SEEK_SET))
777 {
778 return -1;
779 }
780 return 0;
781 }
782
783 return -1;
784}
785
791static bool test_last_status_new(FILE *fp)
792{
793 struct Email *e = NULL;
794 struct Envelope *tmp_envelope = NULL;
795 bool rc = false;
796
797 if (fseek_last_message(fp) == -1)
798 return false;
799
800 e = email_new();
801 tmp_envelope = mutt_rfc822_read_header(fp, e, false, false);
802 if (!e->read && !e->old)
803 rc = true;
804
805 mutt_env_free(&tmp_envelope);
806 email_free(&e);
807
808 return rc;
809}
810
816bool mbox_test_new_folder(const char *path)
817{
818 bool rc = false;
819
820 enum MailboxType type = mx_path_probe(path);
821
822 if ((type != MUTT_MBOX) && (type != MUTT_MMDF))
823 return false;
824
825 FILE *fp = fopen(path, "rb");
826 if (fp)
827 {
828 rc = test_last_status_new(fp);
829 mutt_file_fclose(&fp);
830 }
831
832 return rc;
833}
834
843void mbox_reset_atime(struct Mailbox *m, struct stat *st)
844{
845 struct utimbuf utimebuf;
846 struct stat st2 = { 0 };
847
848 if (!st)
849 {
850 if (stat(mailbox_path(m), &st2) < 0)
851 return;
852 st = &st2;
853 }
854
855 utimebuf.actime = st->st_atime;
856 utimebuf.modtime = st->st_mtime;
857
858 /* When $mbox_check_recent is set, existing new mail is ignored, so do not
859 * reset the atime to mtime-1 to signal new mail. */
860 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
861 if (!c_mail_check_recent && (utimebuf.actime >= utimebuf.modtime) && mbox_has_new(m))
862 {
863 utimebuf.actime = utimebuf.modtime - 1;
864 }
865
866 utime(mailbox_path(m), &utimebuf);
867}
868
872static bool mbox_ac_owns_path(struct Account *a, const char *path)
873{
874 if ((a->type != MUTT_MBOX) && (a->type != MUTT_MMDF))
875 return false;
876
877 struct MailboxNode *np = STAILQ_FIRST(&a->mailboxes);
878 if (!np)
879 return false;
880
881 return mutt_str_equal(mailbox_path(np->mailbox), path);
882}
883
887static bool mbox_ac_add(struct Account *a, struct Mailbox *m)
888{
889 return true;
890}
891
899static FILE *mbox_open_readwrite(struct Mailbox *m)
900{
901 FILE *fp = fopen(mailbox_path(m), "r+");
902 if (fp)
903 m->readonly = false;
904 return fp;
905}
906
914static FILE *mbox_open_readonly(struct Mailbox *m)
915{
916 FILE *fp = fopen(mailbox_path(m), "r");
917 if (fp)
918 m->readonly = true;
919 return fp;
920}
921
926{
927 if (init_mailbox(m) != 0)
928 return MX_OPEN_ERROR;
929
931 if (!adata)
932 return MX_OPEN_ERROR;
933
934 adata->fp = m->readonly ? NULL : mbox_open_readwrite(m);
935 if (!adata->fp)
936 {
937 adata->fp = mbox_open_readonly(m);
938 }
939 if (!adata->fp)
940 {
942 return MX_OPEN_ERROR;
943 }
944
946 if (mbox_lock_mailbox(m, false, true) == -1)
947 {
949 return MX_OPEN_ERROR;
950 }
951
952 m->has_new = true;
954 if (m->type == MUTT_MBOX)
955 rc = mbox_parse_mailbox(m);
956 else if (m->type == MUTT_MMDF)
957 rc = mmdf_parse_mailbox(m);
958 else
959 rc = MX_OPEN_ERROR;
960
961 if (!mbox_has_new(m))
962 m->has_new = false;
963 clearerr(adata->fp); // Clear the EOF flag
964 mutt_file_touch_atime(fileno(adata->fp));
965
968 return rc;
969}
970
974static bool mbox_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
975{
976 if (init_mailbox(m) != 0)
977 return false;
978
980 if (!adata)
981 return false;
982
983 if (!adata->fp)
984 {
985 // create dir recursively
986 char *tmp_path = mutt_path_dirname(mailbox_path(m));
987 if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
988 {
990 FREE(&tmp_path);
991 return false;
992 }
993 FREE(&tmp_path);
994
995 adata->fp = mutt_file_fopen(mailbox_path(m), (flags & MUTT_NEWFOLDER) ? "w+" : "a+");
996 if (!adata->fp)
997 {
999 return false;
1000 }
1001
1002 if (mbox_lock_mailbox(m, true, true) != false)
1003 {
1004 mutt_error(_("Couldn't lock %s"), mailbox_path(m));
1005 mutt_file_fclose(&adata->fp);
1006 return false;
1007 }
1008 }
1009
1010 if (!mutt_file_seek(adata->fp, 0, SEEK_END))
1011 {
1012 mutt_file_fclose(&adata->fp);
1013 return false;
1014 }
1015
1016 return true;
1017}
1018
1026static enum MxStatus mbox_mbox_check(struct Mailbox *m)
1027{
1029 if (!adata)
1030 return MX_STATUS_ERROR;
1031
1032 if (!adata->fp)
1033 {
1034 if (mbox_mbox_open(m) != MX_OPEN_OK)
1035 return MX_STATUS_ERROR;
1037 }
1038 if (!adata->fp)
1039 return MX_STATUS_ERROR;
1040
1041 struct stat st = { 0 };
1042 bool unlock = false;
1043 bool modified = false;
1044
1045 if (stat(mailbox_path(m), &st) == 0)
1046 {
1048 (st.st_size == m->size))
1049 {
1050 return MX_STATUS_OK;
1051 }
1052
1053 if (st.st_size == m->size)
1054 {
1055 /* the file was touched, but it is still the same length, so just exit */
1057 return MX_STATUS_OK;
1058 }
1059
1060 if (st.st_size > m->size)
1061 {
1062 /* lock the file if it isn't already */
1063 if (!adata->locked)
1064 {
1066 if (mbox_lock_mailbox(m, false, false) == -1)
1067 {
1069 /* we couldn't lock the mailbox, but nothing serious happened:
1070 * probably the new mail arrived: no reason to wait till we can
1071 * parse it: we'll get it on the next pass */
1072 return MX_STATUS_LOCKED;
1073 }
1074 unlock = 1;
1075 }
1076
1077 /* Check to make sure that the only change to the mailbox is that
1078 * message(s) were appended to this file. My heuristic is that we should
1079 * see the message separator at *exactly* what used to be the end of the
1080 * folder. */
1081 char buf[1024] = { 0 };
1082 if (!mutt_file_seek(adata->fp, m->size, SEEK_SET))
1083 {
1084 goto error;
1085 }
1086 if (fgets(buf, sizeof(buf), adata->fp))
1087 {
1088 if (((m->type == MUTT_MBOX) && mutt_str_startswith(buf, "From ")) ||
1089 ((m->type == MUTT_MMDF) && mutt_str_equal(buf, MMDF_SEP)))
1090 {
1091 if (!mutt_file_seek(adata->fp, m->size, SEEK_SET))
1092 {
1093 goto error;
1094 }
1095
1096 int old_msg_count = m->msg_count;
1097 if (m->type == MUTT_MBOX)
1099 else
1101
1102 if (m->msg_count > old_msg_count)
1104
1105 /* Only unlock the folder if it was locked inside of this routine.
1106 * It may have been locked elsewhere, like in
1107 * mutt_checkpoint_mailbox(). */
1108 if (unlock)
1109 {
1112 }
1113
1114 return MX_STATUS_NEW_MAIL; /* signal that new mail arrived */
1115 }
1116 else
1117 modified = true;
1118 }
1119 else
1120 {
1121 mutt_debug(LL_DEBUG1, "fgets returned NULL\n");
1122 modified = true;
1123 }
1124 }
1125 else
1126 modified = true;
1127 }
1128
1129 if (modified)
1130 {
1131 if (reopen_mailbox(m) != -1)
1132 {
1134 if (unlock)
1135 {
1138 }
1139 return MX_STATUS_REOPENED;
1140 }
1141 }
1142
1143 /* fatal error */
1144
1145error:
1147 mx_fastclose_mailbox(m, false);
1149 mutt_error(_("Mailbox was corrupted"));
1150 return MX_STATUS_ERROR;
1151}
1152
1156static enum MxStatus mbox_mbox_sync(struct Mailbox *m)
1157{
1159 if (!adata)
1160 return MX_STATUS_ERROR;
1161
1162 struct Buffer *tempfile = NULL;
1163 char buf[32] = { 0 };
1164 int j;
1165 bool unlink_tempfile = false;
1166 bool need_sort = false; /* flag to resort mailbox if new mail arrives */
1167 int first = -1; /* first message to be written */
1168 LOFF_T offset; /* location in mailbox to write changed messages */
1169 struct stat st = { 0 };
1170 struct MUpdate *new_offset = NULL;
1171 struct MUpdate *old_offset = NULL;
1172 FILE *fp = NULL;
1173 struct Progress *progress = NULL;
1174 enum MxStatus rc = MX_STATUS_ERROR;
1175
1176 /* sort message by their position in the mailbox on disk */
1177 const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
1178 const unsigned char c_use_threads = cs_subset_enum(NeoMutt->sub, "use_threads");
1179 if (c_sort != SORT_ORDER)
1180 {
1182 cs_subset_str_native_set(NeoMutt->sub, "use_threads", UT_FLAT, NULL);
1184 cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
1185 cs_subset_str_native_set(NeoMutt->sub, "use_threads", c_use_threads, NULL);
1186 need_sort = true;
1187 }
1188
1189 /* need to open the file for writing in such a way that it does not truncate
1190 * the file, so use read-write mode. */
1191 adata->fp = freopen(mailbox_path(m), "r+", adata->fp);
1192 if (!adata->fp)
1193 {
1194 mx_fastclose_mailbox(m, false);
1195 mutt_error(_("Fatal error! Could not reopen mailbox!"));
1196 goto fatal;
1197 }
1198
1200
1201 if (mbox_lock_mailbox(m, true, true) == -1)
1202 {
1204 mutt_error(_("Unable to lock mailbox"));
1205 goto bail;
1206 }
1207
1208 /* Check to make sure that the file hasn't changed on disk */
1209 enum MxStatus check = mbox_mbox_check(m);
1210 if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
1211 {
1212 /* new mail arrived, or mailbox reopened */
1213 rc = check;
1214 goto bail;
1215 }
1216 else if (check < 0)
1217 {
1218 goto fatal;
1219 }
1220
1221 /* Create a temporary file to write the new version of the mailbox in. */
1222 tempfile = mutt_buffer_pool_get();
1223 mutt_buffer_mktemp(tempfile);
1224 int fd = open(mutt_buffer_string(tempfile), O_WRONLY | O_EXCL | O_CREAT, 0600);
1225 if ((fd == -1) || !(fp = fdopen(fd, "w")))
1226 {
1227 if (fd != -1)
1228 {
1229 close(fd);
1230 unlink_tempfile = true;
1231 }
1232 mutt_error(_("Could not create temporary file"));
1233 goto bail;
1234 }
1235 unlink_tempfile = true;
1236
1237 /* find the first deleted/changed message. we save a lot of time by only
1238 * rewriting the mailbox from the point where it has actually changed. */
1239 int i = 0;
1240 for (; (i < m->msg_count) && !m->emails[i]->deleted &&
1241 !m->emails[i]->changed && !m->emails[i]->attach_del;
1242 i++)
1243 {
1244 }
1245 if (i == m->msg_count)
1246 {
1247 /* this means m->changed or m->msg_deleted was set, but no
1248 * messages were found to be changed or deleted. This should
1249 * never happen, is we presume it is a bug in neomutt. */
1250 mutt_error(_("sync: mbox modified, but no modified messages (report this bug)"));
1251 mutt_debug(LL_DEBUG1, "no modified messages\n");
1252 goto bail;
1253 }
1254
1255 /* save the index of the first changed/deleted message */
1256 first = i;
1257 /* where to start overwriting */
1258 offset = m->emails[i]->offset;
1259
1260 /* the offset stored in the header does not include the MMDF_SEP, so make
1261 * sure we seek to the correct location */
1262 if (m->type == MUTT_MMDF)
1263 offset -= (sizeof(MMDF_SEP) - 1);
1264
1265 /* allocate space for the new offsets */
1266 new_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate));
1267 old_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate));
1268
1269 if (m->verbose)
1270 {
1271 char msg[PATH_MAX] = { 0 };
1272 snprintf(msg, sizeof(msg), _("Writing %s..."), mailbox_path(m));
1273 progress = progress_new(msg, MUTT_PROGRESS_WRITE, m->msg_count);
1274 }
1275
1276 for (i = first, j = 0; i < m->msg_count; i++)
1277 {
1278 if (m->verbose)
1279 progress_update(progress, i, i / (m->msg_count / 100 + 1));
1280 /* back up some information which is needed to restore offsets when
1281 * something fails. */
1282
1283 old_offset[i - first].valid = true;
1284 old_offset[i - first].hdr = m->emails[i]->offset;
1285 old_offset[i - first].body = m->emails[i]->body->offset;
1286 old_offset[i - first].lines = m->emails[i]->lines;
1287 old_offset[i - first].length = m->emails[i]->body->length;
1288
1289 if (!m->emails[i]->deleted)
1290 {
1291 j++;
1292
1293 if (m->type == MUTT_MMDF)
1294 {
1295 if (fputs(MMDF_SEP, fp) == EOF)
1296 {
1298 goto bail;
1299 }
1300 }
1301
1302 /* save the new offset for this message. we add 'offset' because the
1303 * temporary file only contains saved message which are located after
1304 * 'offset' in the real mailbox */
1305 new_offset[i - first].hdr = ftello(fp) + offset;
1306
1307 struct Message *msg = mx_msg_open(m, m->emails[i]->msgno);
1308 const int rc2 = mutt_copy_message(fp, m->emails[i], msg, MUTT_CM_UPDATE,
1310 mx_msg_close(m, &msg);
1311 if (rc2 != 0)
1312 {
1314 goto bail;
1315 }
1316
1317 /* Since messages could have been deleted, the offsets stored in memory
1318 * will be wrong, so update what we can, which is the offset of this
1319 * message, and the offset of the body. If this is a multipart message,
1320 * we just flush the in memory cache so that the message will be reparsed
1321 * if the user accesses it later. */
1322 new_offset[i - first].body = ftello(fp) - m->emails[i]->body->length + offset;
1323 mutt_body_free(&m->emails[i]->body->parts);
1324
1325 switch (m->type)
1326 {
1327 case MUTT_MMDF:
1328 if (fputs(MMDF_SEP, fp) == EOF)
1329 {
1331 goto bail;
1332 }
1333 break;
1334 default:
1335 if (fputs("\n", fp) == EOF)
1336 {
1338 goto bail;
1339 }
1340 }
1341 }
1342 }
1343
1344 if (mutt_file_fclose(&fp) != 0)
1345 {
1346 mutt_debug(LL_DEBUG1, "mutt_file_fclose (&) returned non-zero\n");
1348 goto bail;
1349 }
1350
1351 /* Save the state of this folder. */
1352 if (stat(mailbox_path(m), &st) == -1)
1353 {
1355 goto bail;
1356 }
1357
1358 unlink_tempfile = false;
1359
1360 fp = fopen(mutt_buffer_string(tempfile), "r");
1361 if (!fp)
1362 {
1364 mx_fastclose_mailbox(m, false);
1365 mutt_debug(LL_DEBUG1, "unable to reopen temp copy of mailbox!\n");
1367 FREE(&new_offset);
1368 FREE(&old_offset);
1369 goto fatal;
1370 }
1371
1372 if (!mutt_file_seek(adata->fp, offset, SEEK_SET) || /* seek the append location */
1373 /* do a sanity check to make sure the mailbox looks ok */
1374 !fgets(buf, sizeof(buf), adata->fp) ||
1375 ((m->type == MUTT_MBOX) && !mutt_str_startswith(buf, "From ")) ||
1376 ((m->type == MUTT_MMDF) && !mutt_str_equal(MMDF_SEP, buf)))
1377 {
1378 mutt_debug(LL_DEBUG1, "message not in expected position\n");
1379 mutt_debug(LL_DEBUG1, " LINE: %s\n", buf);
1380 i = -1;
1381 }
1382 else
1383 {
1384 if (!mutt_file_seek(adata->fp, offset, SEEK_SET)) /* return to proper offset */
1385 {
1386 i = -1;
1387 }
1388 else
1389 {
1390 /* copy the temp mailbox back into place starting at the first
1391 * change/deleted message */
1392 if (m->verbose)
1393 mutt_message(_("Committing changes..."));
1394 i = mutt_file_copy_stream(fp, adata->fp);
1395
1396 if (ferror(adata->fp))
1397 i = -1;
1398 }
1399 if (i >= 0)
1400 {
1401 m->size = ftello(adata->fp); /* update the mailbox->size of the mailbox */
1402 if ((m->size < 0) || (ftruncate(fileno(adata->fp), m->size) != 0))
1403 {
1404 i = -1;
1405 mutt_debug(LL_DEBUG1, "ftruncate() failed\n");
1406 }
1407 }
1408 }
1409
1411 fp = NULL;
1413
1414 if ((mutt_file_fclose(&adata->fp) != 0) || (i == -1))
1415 {
1416 /* error occurred while writing the mailbox back, so keep the temp copy around */
1417
1418 struct Buffer *savefile = mutt_buffer_pool_get();
1419
1420 const char *const c_tmpdir = cs_subset_path(NeoMutt->sub, "tmpdir");
1421 mutt_buffer_printf(savefile, "%s/neomutt.%s-%s-%u", NONULL(c_tmpdir), NONULL(Username),
1422 NONULL(ShortHostname), (unsigned int) getpid());
1423 rename(mutt_buffer_string(tempfile), mutt_buffer_string(savefile));
1425 mx_fastclose_mailbox(m, false);
1427 mutt_error(_("Write failed! Saved partial mailbox to %s"), mutt_buffer_string(savefile));
1428 mutt_buffer_pool_release(&savefile);
1429 FREE(&new_offset);
1430 FREE(&old_offset);
1431 goto fatal;
1432 }
1433
1434 /* Restore the previous access/modification times */
1435 mbox_reset_atime(m, &st);
1436
1437 /* reopen the mailbox in read-only mode */
1438 adata->fp = mbox_open_readwrite(m);
1439 if (!adata->fp)
1440 {
1441 adata->fp = mbox_open_readonly(m);
1442 }
1443 if (!adata->fp)
1444 {
1445 unlink(mutt_buffer_string(tempfile));
1447 mx_fastclose_mailbox(m, false);
1448 mutt_error(_("Fatal error! Could not reopen mailbox!"));
1449 FREE(&new_offset);
1450 FREE(&old_offset);
1451 goto fatal;
1452 }
1453
1454 /* update the offsets of the rewritten messages */
1455 for (i = first, j = first; i < m->msg_count; i++)
1456 {
1457 if (!m->emails[i]->deleted)
1458 {
1459 m->emails[i]->offset = new_offset[i - first].hdr;
1460 m->emails[i]->body->hdr_offset = new_offset[i - first].hdr;
1461 m->emails[i]->body->offset = new_offset[i - first].body;
1462 m->emails[i]->index = j++;
1463 }
1464 }
1465 FREE(&new_offset);
1466 FREE(&old_offset);
1467 unlink(mutt_buffer_string(tempfile)); /* remove partial copy of the mailbox */
1468 mutt_buffer_pool_release(&tempfile);
1470
1471 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
1472 if (c_check_mbox_size)
1473 {
1474 struct Mailbox *m_tmp = mailbox_find(mailbox_path(m));
1475 if (m_tmp && !m_tmp->has_new)
1476 mailbox_update(m_tmp);
1477 }
1478
1479 progress_free(&progress);
1480 return 0; /* signal success */
1481
1482bail: /* Come here in case of disaster */
1483
1484 mutt_file_fclose(&fp);
1485
1486 if (tempfile && unlink_tempfile)
1487 unlink(mutt_buffer_string(tempfile));
1488
1489 /* restore offsets, as far as they are valid */
1490 if ((first >= 0) && old_offset)
1491 {
1492 for (i = first; (i < m->msg_count) && old_offset[i - first].valid; i++)
1493 {
1494 m->emails[i]->offset = old_offset[i - first].hdr;
1495 m->emails[i]->body->hdr_offset = old_offset[i - first].hdr;
1496 m->emails[i]->body->offset = old_offset[i - first].body;
1497 m->emails[i]->lines = old_offset[i - first].lines;
1498 m->emails[i]->body->length = old_offset[i - first].length;
1499 }
1500 }
1501
1502 /* this is ok to call even if we haven't locked anything */
1504
1506 FREE(&new_offset);
1507 FREE(&old_offset);
1508
1509 adata->fp = freopen(mailbox_path(m), "r", adata->fp);
1510 if (!adata->fp)
1511 {
1512 mutt_error(_("Could not reopen mailbox"));
1513 mx_fastclose_mailbox(m, false);
1514 goto fatal;
1515 }
1516
1518 if (need_sort)
1519 {
1520 /* if the mailbox was reopened, the thread tree will be invalid so make
1521 * sure to start threading from scratch. */
1523 }
1524
1525fatal:
1526 mutt_buffer_pool_release(&tempfile);
1527 progress_free(&progress);
1528 return rc;
1529}
1530
1534static enum MxStatus mbox_mbox_close(struct Mailbox *m)
1535{
1537 if (!adata)
1538 return MX_STATUS_ERROR;
1539
1540 if (!adata->fp)
1541 return MX_STATUS_OK;
1542
1543 if (adata->append)
1544 {
1545 mutt_file_unlock(fileno(adata->fp));
1547 }
1548
1549 mutt_file_fclose(&adata->fp);
1550
1551 /* fix up the times so mailbox won't get confused */
1552 if (m->peekonly && !mutt_buffer_is_empty(&m->pathbuf) &&
1553 (mutt_file_timespec_compare(&m->mtime, &adata->atime) > 0))
1554 {
1555#ifdef HAVE_UTIMENSAT
1556 struct timespec ts[2];
1557 ts[0] = adata->atime;
1558 ts[1] = m->mtime;
1559 utimensat(AT_FDCWD, m->path, ts, 0);
1560#else
1561 struct utimbuf ut;
1562 ut.actime = adata->atime.tv_sec;
1563 ut.modtime = m->mtime.tv_sec;
1564 utime(mailbox_path(m), &ut);
1565#endif
1566 }
1567
1568 return MX_STATUS_OK;
1569}
1570
1574static bool mbox_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
1575{
1577 if (!adata)
1578 return false;
1579
1580 msg->fp = mutt_file_fopen(mailbox_path(m), "r");
1581 if (!msg->fp)
1582 return false;
1583
1584 return true;
1585}
1586
1590static bool mbox_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
1591{
1593 if (!adata)
1594 return false;
1595
1596 msg->fp = adata->fp;
1597 return true;
1598}
1599
1603static int mbox_msg_commit(struct Mailbox *m, struct Message *msg)
1604{
1605 if (fputc('\n', msg->fp) == EOF)
1606 return -1;
1607
1608 if ((fflush(msg->fp) == EOF) || (fsync(fileno(msg->fp)) == -1))
1609 {
1610 mutt_perror(_("Can't write message"));
1611 return -1;
1612 }
1613
1614 return 0;
1615}
1616
1620static int mbox_msg_close(struct Mailbox *m, struct Message *msg)
1621{
1622 if (msg->write)
1623 msg->fp = NULL;
1624 else
1625 mutt_file_fclose(&msg->fp);
1626
1627 return 0;
1628}
1629
1635static int mbox_msg_padding_size(struct Mailbox *m)
1636{
1637 return 1;
1638}
1639
1643enum MailboxType mbox_path_probe(const char *path, const struct stat *st)
1644{
1645 if (!st)
1646 return MUTT_UNKNOWN;
1647
1648 if (S_ISDIR(st->st_mode))
1649 return MUTT_UNKNOWN;
1650
1651 if (st->st_size == 0)
1652 return MUTT_MBOX;
1653
1654 FILE *fp = fopen(path, "r");
1655 if (!fp)
1656 return MUTT_UNKNOWN;
1657
1658 int ch;
1659 while ((ch = fgetc(fp)) != EOF)
1660 {
1661 /* Some mailbox creation tools erroneously append a blank line to
1662 * a file before appending a mail message. This allows neomutt to
1663 * detect type for and thus open those files. */
1664 if ((ch != '\n') && (ch != '\r'))
1665 {
1666 ungetc(ch, fp);
1667 break;
1668 }
1669 }
1670
1672 char tmp[256] = { 0 };
1673 if (fgets(tmp, sizeof(tmp), fp))
1674 {
1675 if (mutt_str_startswith(tmp, "From "))
1676 type = MUTT_MBOX;
1677 else if (mutt_str_equal(tmp, MMDF_SEP))
1678 type = MUTT_MMDF;
1679 }
1681
1682 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
1683 if (!c_check_mbox_size)
1684 {
1685 /* need to restore the times here, the file was not really accessed,
1686 * only the type was accessed. This is important, because detection
1687 * of "new mail" depends on those times set correctly. */
1688#ifdef HAVE_UTIMENSAT
1689 struct timespec ts[2];
1692 utimensat(AT_FDCWD, path, ts, 0);
1693#else
1694 struct utimbuf times;
1695 times.actime = st->st_atime;
1696 times.modtime = st->st_mtime;
1697 utime(path, &times);
1698#endif
1699 }
1700
1701 return type;
1702}
1703
1707static int mbox_path_canon(char *buf, size_t buflen)
1708{
1709 mutt_path_canon(buf, buflen, HomeDir, false);
1710 return 0;
1711}
1712
1716static int mbox_path_pretty(char *buf, size_t buflen, const char *folder)
1717{
1718 if (mutt_path_abbr_folder(buf, folder))
1719 return 0;
1720
1721 if (mutt_path_pretty(buf, buflen, HomeDir, false))
1722 return 0;
1723
1724 return -1;
1725}
1726
1730static int mbox_path_parent(char *buf, size_t buflen)
1731{
1732 if (mutt_path_parent(buf))
1733 return 0;
1734
1735 if (buf[0] == '~')
1736 mutt_path_canon(buf, buflen, HomeDir, false);
1737
1738 if (mutt_path_parent(buf))
1739 return 0;
1740
1741 return -1;
1742}
1743
1747static int mbox_path_is_empty(const char *path)
1748{
1749 return mutt_file_check_empty(path);
1750}
1751
1755static int mmdf_msg_commit(struct Mailbox *m, struct Message *msg)
1756{
1757 if (fputs(MMDF_SEP, msg->fp) == EOF)
1758 return -1;
1759
1760 if ((fflush(msg->fp) == EOF) || (fsync(fileno(msg->fp)) == -1))
1761 {
1762 mutt_perror(_("Can't write message"));
1763 return -1;
1764 }
1765
1766 return 0;
1767}
1768
1774static int mmdf_msg_padding_size(struct Mailbox *m)
1775{
1776 return 10;
1777}
1778
1782static enum MxStatus mbox_mbox_check_stats(struct Mailbox *m, uint8_t flags)
1783{
1784 struct stat st = { 0 };
1785 if (stat(mailbox_path(m), &st) != 0)
1786 return MX_STATUS_ERROR;
1787
1788 bool new_or_changed;
1789
1790 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
1791 if (c_check_mbox_size)
1792 new_or_changed = (st.st_size > m->size);
1793 else
1794 {
1795 new_or_changed =
1797 (m->newly_created &&
1800 }
1801
1802 if (new_or_changed)
1803 {
1804 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
1805 if (!c_mail_check_recent ||
1807 {
1808 m->has_new = true;
1809 }
1810 }
1811 else if (c_check_mbox_size)
1812 {
1813 /* some other program has deleted mail from the folder */
1814 m->size = (off_t) st.st_size;
1815 }
1816
1817 if (m->newly_created && ((st.st_ctime != st.st_mtime) || (st.st_ctime != st.st_atime)))
1818 m->newly_created = false;
1819
1821 {
1824 &adata->stats_last_checked) > 0)
1825 {
1826 bool old_peek = m->peekonly;
1828 mx_mbox_close(m);
1829 m->peekonly = old_peek;
1830 adata->stats_last_checked.tv_sec = mutt_date_epoch();
1831 }
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:260
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: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_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:428
int 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:887
static bool mbox_ac_owns_path(struct Account *a, const char *path)
Check whether an Account owns a Mailbox path - Implements MxOps::ac_owns_path() -.
Definition: mbox.c:872
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:1782
static enum MxStatus mbox_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check() -.
Definition: mbox.c:1026
static enum MxStatus mbox_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close() -.
Definition: mbox.c:1534
static bool mbox_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
Definition: mbox.c:974
static enum MxOpenReturns mbox_mbox_open(struct Mailbox *m)
Open a Mailbox - Implements MxOps::mbox_open() -.
Definition: mbox.c:925
static enum MxStatus mbox_mbox_sync(struct Mailbox *m)
Save changes to the Mailbox - Implements MxOps::mbox_sync() -.
Definition: mbox.c:1156
static int mbox_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition: mbox.c:1620
static int mbox_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: mbox.c:1603
static int mmdf_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: mbox.c:1755
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:1590
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:1574
static int mbox_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Implements MxOps::msg_padding_size() -.
Definition: mbox.c:1635
static int mmdf_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Implements MxOps::msg_padding_size() -.
Definition: mbox.c:1774
static int mbox_path_canon(char *buf, size_t buflen)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition: mbox.c:1707
static int mbox_path_is_empty(const char *path)
Is the mailbox empty - Implements MxOps::path_is_empty() -.
Definition: mbox.c:1747
static int mbox_path_parent(char *buf, size_t buflen)
Find the parent of a Mailbox path - Implements MxOps::path_parent() -.
Definition: mbox.c:1730
static int mbox_path_pretty(char *buf, size_t buflen, const char *folder)
Abbreviate a Mailbox path - Implements MxOps::path_pretty() -.
Definition: mbox.c:1716
enum MailboxType mbox_path_probe(const char *path, const struct stat *st)
Is this an mbox Mailbox? - Implements MxOps::path_probe() -.
Definition: mbox.c:1643
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:177
@ NT_MAILBOX_INVALID
Email list was changed.
Definition: mailbox.h:176
@ NT_MAILBOX_UPDATE
Update internal tables.
Definition: mailbox.h:178
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:210
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:343
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:709
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:899
static bool test_last_status_new(FILE *fp)
Is the last message new.
Definition: mbox.c:791
static int fseek_last_message(FILE *fp)
Find the last message in the file.
Definition: mbox.c:728
static FILE * mbox_open_readonly(struct Mailbox *m)
Open an mbox read-only.
Definition: mbox.c:914
static void mbox_unlock_mailbox(struct Mailbox *m)
Unlock a mailbox.
Definition: mbox.c:156
bool mbox_test_new_folder(const char *path)
Test if an mbox or mmdf mailbox has new mail.
Definition: mbox.c:816
static enum MxOpenReturns mmdf_parse_mailbox(struct Mailbox *m)
Read a mailbox in MMDF format.
Definition: mbox.c:176
void mbox_reset_atime(struct Mailbox *m, struct stat *st)
Reset the access time on the mailbox file.
Definition: mbox.c:843
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:531
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: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:1157
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: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:153
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:154
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: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