NeoMutt  2024-04-25-76-g20fe7b
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
mbox.c
Go to the documentation of this file.
1
37#include "config.h"
38#include <errno.h>
39#include <fcntl.h>
40#include <inttypes.h>
41#include <stdbool.h>
42#include <stdio.h>
43#include <string.h>
44#include <sys/stat.h>
45#include <sys/types.h>
46#include <time.h>
47#include <unistd.h>
48#include <utime.h>
49#include "mutt/lib.h"
50#include "address/lib.h"
51#include "config/lib.h"
52#include "email/lib.h"
53#include "core/lib.h"
54#include "mutt.h"
55#include "lib.h"
56#include "progress/lib.h"
57#include "copy.h"
58#include "globals.h"
59#include "mutt_header.h"
60#include "muttlib.h"
61#include "mx.h"
62#include "protos.h"
63#include "sort.h"
64
68struct MUpdate
69{
70 bool valid;
71 LOFF_T hdr;
72 LOFF_T body;
73 long lines;
74 LOFF_T length;
75};
76
80static void mbox_adata_free(void **ptr)
81{
82 if (!ptr || !*ptr)
83 return;
84
85 struct MboxAccountData *m = *ptr;
86
88 FREE(ptr);
89}
90
95static struct MboxAccountData *mbox_adata_new(void)
96{
97 return mutt_mem_calloc(1, sizeof(struct MboxAccountData));
98}
99
106static int init_mailbox(struct Mailbox *m)
107{
108 if (!m || !m->account)
109 return -1;
110 if ((m->type != MUTT_MBOX) && (m->type != MUTT_MMDF))
111 return -1;
112 if (m->account->adata)
113 return 0;
114
117 return 0;
118}
119
125static struct MboxAccountData *mbox_adata_get(struct Mailbox *m)
126{
127 if (init_mailbox(m) == -1)
128 return NULL;
129 return m->account->adata;
130}
131
140static int mbox_lock_mailbox(struct Mailbox *m, bool excl, bool retry)
141{
143 if (!adata)
144 return -1;
145
146 int rc = mutt_file_lock(fileno(adata->fp), excl, retry);
147 if (rc == 0)
148 {
149 adata->locked = true;
150 }
151 else if (retry && !excl)
152 {
153 m->readonly = true;
154 return 0;
155 }
156
157 return rc;
158}
159
164static void mbox_unlock_mailbox(struct Mailbox *m)
165{
167 if (!adata)
168 return;
169
170 if (adata->locked)
171 {
172 fflush(adata->fp);
173
174 mutt_file_unlock(fileno(adata->fp));
175 adata->locked = false;
176 }
177}
178
185{
186 if (!m)
187 return MX_OPEN_ERROR;
188
190 if (!adata)
191 return MX_OPEN_ERROR;
192
193 char buf[8192] = { 0 };
194 char return_path[1024] = { 0 };
195 int count = 0;
196 int lines;
197 time_t t = 0;
198 LOFF_T loc, tmploc;
199 struct Email *e = NULL;
200 struct stat st = { 0 };
201 struct Progress *progress = NULL;
203
204 if (stat(mailbox_path(m), &st) == -1)
205 {
206 mutt_perror("%s", mailbox_path(m));
207 goto fail;
208 }
211 m->size = st.st_size;
212
213 buf[sizeof(buf) - 1] = '\0';
214
215 if (m->verbose)
216 {
217 progress = progress_new(MUTT_PROGRESS_READ, 0);
218 progress_set_message(progress, _("Reading %s..."), mailbox_path(m));
219 }
220
221 while (true)
222 {
223 if (!fgets(buf, sizeof(buf) - 1, adata->fp))
224 break;
225
226 if (SigInt)
227 break;
228
229 if (mutt_str_equal(buf, MMDF_SEP))
230 {
231 loc = ftello(adata->fp);
232 if (loc < 0)
233 goto fail;
234
235 count++;
236 progress_update(progress, count, (int) (loc / (m->size / 100 + 1)));
237
239 e = email_new();
240 m->emails[m->msg_count] = e;
241 e->offset = loc;
242 e->index = m->msg_count;
243
244 if (!fgets(buf, sizeof(buf) - 1, adata->fp))
245 {
246 /* TODO: memory leak??? */
247 mutt_debug(LL_DEBUG1, "unexpected EOF\n");
248 break;
249 }
250
251 return_path[0] = '\0';
252
253 if (!is_from(buf, return_path, sizeof(return_path), &t))
254 {
255 if (!mutt_file_seek(adata->fp, loc, SEEK_SET))
256 {
257 mutt_error(_("Mailbox is corrupt"));
258 goto fail;
259 }
260 }
261 else
262 {
263 e->received = t - mutt_date_local_tz(t);
264 }
265
266 e->env = mutt_rfc822_read_header(adata->fp, e, false, false);
267
268 loc = ftello(adata->fp);
269 if (loc < 0)
270 goto fail;
271
272 if ((e->body->length > 0) && (e->lines > 0))
273 {
274 tmploc = loc + e->body->length;
275
276 if ((tmploc > 0) && (tmploc < m->size))
277 {
278 if (!mutt_file_seek(adata->fp, tmploc, SEEK_SET) ||
279 !fgets(buf, sizeof(buf) - 1, adata->fp) || !mutt_str_equal(MMDF_SEP, buf))
280 {
281 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
282 e->body->length = -1;
283 }
284 }
285 else
286 {
287 e->body->length = -1;
288 }
289 }
290 else
291 {
292 e->body->length = -1;
293 }
294
295 if (e->body->length < 0)
296 {
297 lines = -1;
298 do
299 {
300 loc = ftello(adata->fp);
301 if (loc < 0)
302 goto fail;
303 if (!fgets(buf, sizeof(buf) - 1, adata->fp))
304 break;
305 lines++;
306 } while (!mutt_str_equal(buf, MMDF_SEP));
307
308 e->lines = lines;
309 e->body->length = loc - e->body->offset;
310 }
311
312 if (TAILQ_EMPTY(&e->env->return_path) && return_path[0])
313 mutt_addrlist_parse(&e->env->return_path, return_path);
314
315 if (TAILQ_EMPTY(&e->env->from))
316 mutt_addrlist_copy(&e->env->from, &e->env->return_path, false);
317
318 m->msg_count++;
319 }
320 else
321 {
322 mutt_debug(LL_DEBUG1, "corrupt mailbox\n");
323 mutt_error(_("Mailbox is corrupt"));
324 goto fail;
325 }
326 }
327
328 if (SigInt)
329 {
330 SigInt = false;
331 rc = MX_OPEN_ABORT; /* action aborted */
332 goto fail;
333 }
334
335 rc = MX_OPEN_OK;
336fail:
337 progress_free(&progress);
338 return rc;
339}
340
353{
354 if (!m)
355 return MX_OPEN_ERROR;
356
358 if (!adata)
359 return MX_OPEN_ERROR;
360
361 struct stat st = { 0 };
362 char buf[8192] = { 0 };
363 char return_path[256] = { 0 };
364 struct Email *e_cur = NULL;
365 time_t t = 0;
366 int count = 0, lines = 0;
367 LOFF_T loc;
368 struct Progress *progress = NULL;
370
371 /* Save information about the folder at the time we opened it. */
372 if (stat(mailbox_path(m), &st) == -1)
373 {
374 mutt_perror("%s", mailbox_path(m));
375 goto fail;
376 }
377
378 m->size = st.st_size;
381
382 if (!m->readonly)
383 m->readonly = access(mailbox_path(m), W_OK) ? true : false;
384
385 if (m->verbose)
386 {
387 progress = progress_new(MUTT_PROGRESS_READ, 0);
388 progress_set_message(progress, _("Reading %s..."), mailbox_path(m));
389 }
390
391 loc = ftello(adata->fp);
392 if (loc < 0)
393 {
394 mutt_debug(LL_DEBUG1, "ftello: %s (errno %d)\n", strerror(errno), errno);
395 loc = 0;
396 }
397
398 while ((fgets(buf, sizeof(buf), adata->fp)) && !SigInt)
399 {
400 if (is_from(buf, return_path, sizeof(return_path), &t))
401 {
402 /* Save the Content-Length of the previous message */
403 if (count > 0)
404 {
405 struct Email *e = m->emails[m->msg_count - 1];
406 if (e->body->length < 0)
407 {
408 e->body->length = loc - e->body->offset - 1;
409 if (e->body->length < 0)
410 e->body->length = 0;
411 }
412 if (e->lines == 0)
413 e->lines = lines ? lines - 1 : 0;
414 }
415
416 count++;
417
418 progress_update(progress, count, (int) (ftello(adata->fp) / (m->size / 100 + 1)));
419
421
422 m->emails[m->msg_count] = email_new();
423 e_cur = m->emails[m->msg_count];
424 e_cur->received = t - mutt_date_local_tz(t);
425 e_cur->offset = loc;
426 e_cur->index = m->msg_count;
427
428 e_cur->env = mutt_rfc822_read_header(adata->fp, e_cur, false, false);
429
430 /* if we know how long this message is, either just skip over the body,
431 * or if we don't know how many lines there are, count them now (this will
432 * save time by not having to search for the next message marker). */
433 if (e_cur->body->length > 0)
434 {
435 LOFF_T tmploc;
436
437 loc = ftello(adata->fp);
438 if (loc < 0)
439 {
440 mutt_debug(LL_DEBUG1, "ftello: %s (errno %d)\n", strerror(errno), errno);
441 loc = 0;
442 }
443
444 /* The test below avoids a potential integer overflow if the
445 * content-length is huge (thus necessarily invalid). */
446 tmploc = (e_cur->body->length < m->size) ? (loc + e_cur->body->length + 1) : -1;
447
448 if ((tmploc > 0) && (tmploc < m->size))
449 {
450 /* check to see if the content-length looks valid. we expect to
451 * to see a valid message separator at this point in the stream */
452 if (!mutt_file_seek(adata->fp, tmploc, SEEK_SET) ||
453 !fgets(buf, sizeof(buf), adata->fp) || !mutt_str_startswith(buf, "From "))
454 {
455 mutt_debug(LL_DEBUG1, "bad content-length in message %d (cl=" OFF_T_FMT ")\n",
456 e_cur->index, e_cur->body->length);
457 mutt_debug(LL_DEBUG1, " LINE: %s", buf);
458 /* nope, return the previous position */
459 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
460 e_cur->body->length = -1;
461 }
462 }
463 else if (tmploc != m->size)
464 {
465 /* content-length would put us past the end of the file, so it
466 * must be wrong */
467 e_cur->body->length = -1;
468 }
469
470 if (e_cur->body->length != -1)
471 {
472 /* good content-length. check to see if we know how many lines
473 * are in this message. */
474 if (e_cur->lines == 0)
475 {
476 int cl = e_cur->body->length;
477
478 /* count the number of lines in this message */
479 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
480 while (cl-- > 0)
481 {
482 if (fgetc(adata->fp) == '\n')
483 e_cur->lines++;
484 }
485 }
486
487 /* return to the offset of the next message separator */
488 (void) mutt_file_seek(adata->fp, tmploc, SEEK_SET);
489 }
490 }
491
492 m->msg_count++;
493
494 if (TAILQ_EMPTY(&e_cur->env->return_path) && return_path[0])
495 {
496 mutt_addrlist_parse(&e_cur->env->return_path, return_path);
497 }
498
499 if (TAILQ_EMPTY(&e_cur->env->from))
500 mutt_addrlist_copy(&e_cur->env->from, &e_cur->env->return_path, false);
501
502 lines = 0;
503 }
504 else
505 {
506 lines++;
507 }
508
509 loc = ftello(adata->fp);
510 }
511
512 /* Only set the content-length of the previous message if we have read more
513 * than one message during _this_ invocation. If this routine is called
514 * when new mail is received, we need to make sure not to clobber what
515 * previously was the last message since the headers may be sorted. */
516 if (count > 0)
517 {
518 struct Email *e = m->emails[m->msg_count - 1];
519 if (e->body->length < 0)
520 {
521 e->body->length = ftello(adata->fp) - e->body->offset - 1;
522 if (e->body->length < 0)
523 e->body->length = 0;
524 }
525
526 if (e->lines == 0)
527 e->lines = lines ? lines - 1 : 0;
528 }
529
530 if (SigInt)
531 {
532 SigInt = false;
533 rc = MX_OPEN_ABORT;
534 goto fail; /* action aborted */
535 }
536
537 rc = MX_OPEN_OK;
538fail:
539 progress_free(&progress);
540 return rc;
541}
542
549static int reopen_mailbox(struct Mailbox *m)
550{
551 if (!m)
552 return -1;
553
555 if (!adata)
556 return -1;
557
558 bool (*cmp_headers)(const struct Email *, const struct Email *) = NULL;
559 struct Email **e_old = NULL;
560 int old_msg_count;
561 bool msg_mod = false;
562 int rc = -1;
563
564 /* silent operations */
565 m->verbose = false;
566
567 /* our heuristics require the old mailbox to be unsorted */
568 const enum SortType c_sort = cs_subset_sort(NeoMutt->sub, "sort");
569 if (c_sort != SORT_ORDER)
570 {
573 cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
574 }
575
576 e_old = NULL;
577 old_msg_count = 0;
578
579 /* simulate a close */
583 FREE(&m->v2r);
584 if (m->readonly)
585 {
586 for (int i = 0; i < m->msg_count; i++)
587 email_free(&(m->emails[i])); /* nothing to do! */
588 FREE(&m->emails);
589 }
590 else
591 {
592 /* save the old headers */
593 old_msg_count = m->msg_count;
594 e_old = m->emails;
595 m->emails = NULL;
596 }
597
598 m->email_max = 0; /* force allocation of new headers */
599 m->msg_count = 0;
600 m->vcount = 0;
601 m->msg_tagged = 0;
602 m->msg_deleted = 0;
603 m->msg_new = 0;
604 m->msg_unread = 0;
605 m->msg_flagged = 0;
606 m->changed = false;
607 m->id_hash = NULL;
608 m->subj_hash = NULL;
610
611 switch (m->type)
612 {
613 case MUTT_MBOX:
614 case MUTT_MMDF:
615 cmp_headers = email_cmp_strict;
616 mutt_file_fclose(&adata->fp);
617 adata->fp = mutt_file_fopen(mailbox_path(m), "r");
618 if (!adata->fp)
619 rc = -1;
620 else if (m->type == MUTT_MBOX)
621 rc = mbox_parse_mailbox(m);
622 else
623 rc = mmdf_parse_mailbox(m);
624 break;
625
626 default:
627 rc = -1;
628 break;
629 }
630
631 if (rc == -1)
632 {
633 /* free the old headers */
634 for (int i = 0; i < old_msg_count; i++)
635 email_free(&(e_old[i]));
636 FREE(&e_old);
637
638 m->verbose = true;
639 return -1;
640 }
641
642 mutt_file_touch_atime(fileno(adata->fp));
643
644 /* now try to recover the old flags */
645
646 if (!m->readonly)
647 {
648 for (int i = 0; i < m->msg_count; i++)
649 {
650 bool found = false;
651
652 /* some messages have been deleted, and new messages have been
653 * appended at the end; the heuristic is that old messages have then
654 * "advanced" towards the beginning of the folder, so we begin the
655 * search at index "i" */
656 int j;
657 for (j = i; j < old_msg_count; j++)
658 {
659 if (!e_old[j])
660 continue;
661 if (cmp_headers(m->emails[i], e_old[j]))
662 {
663 found = true;
664 break;
665 }
666 }
667 if (!found)
668 {
669 for (j = 0; (j < i) && (j < old_msg_count); j++)
670 {
671 if (!e_old[j])
672 continue;
673 if (cmp_headers(m->emails[i], e_old[j]))
674 {
675 found = true;
676 break;
677 }
678 }
679 }
680
681 if (found)
682 {
683 m->changed = true;
684 if (e_old[j]->changed)
685 {
686 /* Only update the flags if the old header was changed;
687 * otherwise, the header may have been modified externally,
688 * and we don't want to lose _those_ changes */
689 mutt_set_flag(m, m->emails[i], MUTT_FLAG, e_old[j]->flagged, true);
690 mutt_set_flag(m, m->emails[i], MUTT_REPLIED, e_old[j]->replied, true);
691 mutt_set_flag(m, m->emails[i], MUTT_OLD, e_old[j]->old, true);
692 mutt_set_flag(m, m->emails[i], MUTT_READ, e_old[j]->read, true);
693 }
694 mutt_set_flag(m, m->emails[i], MUTT_DELETE, e_old[j]->deleted, true);
695 mutt_set_flag(m, m->emails[i], MUTT_PURGE, e_old[j]->purge, true);
696 mutt_set_flag(m, m->emails[i], MUTT_TAG, e_old[j]->tagged, true);
697
698 /* we don't need this header any more */
699 email_free(&(e_old[j]));
700 }
701 }
702
703 /* free the remaining old emails */
704 for (int j = 0; j < old_msg_count; j++)
705 {
706 if (e_old[j])
707 {
708 email_free(&(e_old[j]));
709 msg_mod = true;
710 }
711 }
712 FREE(&e_old);
713 }
714
716 m->verbose = true;
717
718 return (m->changed || msg_mod) ? MX_STATUS_REOPENED : MX_STATUS_NEW_MAIL;
719}
720
727static bool mbox_has_new(struct Mailbox *m)
728{
729 for (int i = 0; i < m->msg_count; i++)
730 {
731 struct Email *e = m->emails[i];
732 if (!e)
733 break;
734 if (!e->deleted && !e->read && !e->old)
735 return true;
736 }
737 return false;
738}
739
748void mbox_reset_atime(struct Mailbox *m, struct stat *st)
749{
750 struct stat st2 = { 0 };
751 if (!st)
752 {
753 if (stat(mailbox_path(m), &st2) < 0)
754 return;
755 st = &st2;
756 }
757
758 struct utimbuf utimebuf = { 0 };
759 utimebuf.actime = st->st_atime;
760 utimebuf.modtime = st->st_mtime;
761
762 /* When $mbox_check_recent is set, existing new mail is ignored, so do not
763 * reset the atime to mtime-1 to signal new mail. */
764 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
765 if (!c_mail_check_recent && (utimebuf.actime >= utimebuf.modtime) && mbox_has_new(m))
766 {
767 utimebuf.actime = utimebuf.modtime - 1;
768 }
769
770 utime(mailbox_path(m), &utimebuf);
771}
772
776static bool mbox_ac_owns_path(struct Account *a, const char *path)
777{
778 if ((a->type != MUTT_MBOX) && (a->type != MUTT_MMDF))
779 return false;
780
781 struct MailboxNode *np = STAILQ_FIRST(&a->mailboxes);
782 if (!np)
783 return false;
784
785 return mutt_str_equal(mailbox_path(np->mailbox), path);
786}
787
791static bool mbox_ac_add(struct Account *a, struct Mailbox *m)
792{
793 return true;
794}
795
803static FILE *mbox_open_readwrite(struct Mailbox *m)
804{
805 FILE *fp = mutt_file_fopen(mailbox_path(m), "r+");
806 if (fp)
807 m->readonly = false;
808 return fp;
809}
810
818static FILE *mbox_open_readonly(struct Mailbox *m)
819{
820 FILE *fp = mutt_file_fopen(mailbox_path(m), "r");
821 if (fp)
822 m->readonly = true;
823 return fp;
824}
825
830{
831 if (init_mailbox(m) != 0)
832 return MX_OPEN_ERROR;
833
835 if (!adata)
836 return MX_OPEN_ERROR;
837
838 adata->fp = m->readonly ? NULL : mbox_open_readwrite(m);
839 if (!adata->fp)
840 {
841 adata->fp = mbox_open_readonly(m);
842 }
843 if (!adata->fp)
844 {
845 mutt_perror("%s", mailbox_path(m));
846 return MX_OPEN_ERROR;
847 }
848
850 if (mbox_lock_mailbox(m, false, true) == -1)
851 {
853 return MX_OPEN_ERROR;
854 }
855
856 m->has_new = true;
858 if (m->type == MUTT_MBOX)
859 rc = mbox_parse_mailbox(m);
860 else if (m->type == MUTT_MMDF)
861 rc = mmdf_parse_mailbox(m);
862 else
863 rc = MX_OPEN_ERROR;
864
865 if (!mbox_has_new(m))
866 m->has_new = false;
867 clearerr(adata->fp); // Clear the EOF flag
868 mutt_file_touch_atime(fileno(adata->fp));
869
872 return rc;
873}
874
878static bool mbox_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
879{
880 if (init_mailbox(m) != 0)
881 return false;
882
884 if (!adata)
885 return false;
886
887 if (!adata->fp)
888 {
889 // create dir recursively
890 char *tmp_path = mutt_path_dirname(mailbox_path(m));
891 if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
892 {
893 mutt_perror("%s", mailbox_path(m));
894 FREE(&tmp_path);
895 return false;
896 }
897 FREE(&tmp_path);
898
899 adata->fp = mutt_file_fopen(mailbox_path(m), (flags & MUTT_NEWFOLDER) ? "w+" : "a+");
900 if (!adata->fp)
901 {
902 mutt_perror("%s", mailbox_path(m));
903 return false;
904 }
905
906 if (mbox_lock_mailbox(m, true, true) != false)
907 {
908 mutt_error(_("Couldn't lock %s"), mailbox_path(m));
910 return false;
911 }
912 }
913
914 if (!mutt_file_seek(adata->fp, 0, SEEK_END))
915 {
917 return false;
918 }
919
920 return true;
921}
922
930static enum MxStatus mbox_mbox_check(struct Mailbox *m)
931{
933 if (!adata)
934 return MX_STATUS_ERROR;
935
936 if (!adata->fp)
937 {
938 if (mbox_mbox_open(m) != MX_OPEN_OK)
939 return MX_STATUS_ERROR;
941 }
942 if (!adata->fp)
943 return MX_STATUS_ERROR;
944
945 struct stat st = { 0 };
946 bool unlock = false;
947 bool modified = false;
948
949 if (stat(mailbox_path(m), &st) == 0)
950 {
951 if ((mutt_file_stat_timespec_compare(&st, MUTT_STAT_MTIME, &adata->mtime) == 0) &&
952 (st.st_size == m->size))
953 {
954 return MX_STATUS_OK;
955 }
956
957 if (st.st_size == m->size)
958 {
959 /* the file was touched, but it is still the same length, so just exit */
961 return MX_STATUS_OK;
962 }
963
964 if (st.st_size > m->size)
965 {
966 /* lock the file if it isn't already */
967 if (!adata->locked)
968 {
970 if (mbox_lock_mailbox(m, false, false) == -1)
971 {
973 /* we couldn't lock the mailbox, but nothing serious happened:
974 * probably the new mail arrived: no reason to wait till we can
975 * parse it: we'll get it on the next pass */
976 return MX_STATUS_LOCKED;
977 }
978 unlock = 1;
979 }
980
981 /* Check to make sure that the only change to the mailbox is that
982 * message(s) were appended to this file. My heuristic is that we should
983 * see the message separator at *exactly* what used to be the end of the
984 * folder. */
985 char buf[1024] = { 0 };
986 if (!mutt_file_seek(adata->fp, m->size, SEEK_SET))
987 {
988 goto error;
989 }
990 if (fgets(buf, sizeof(buf), adata->fp))
991 {
992 if (((m->type == MUTT_MBOX) && mutt_str_startswith(buf, "From ")) ||
993 ((m->type == MUTT_MMDF) && mutt_str_equal(buf, MMDF_SEP)))
994 {
995 if (!mutt_file_seek(adata->fp, m->size, SEEK_SET))
996 {
997 goto error;
998 }
999
1000 int old_msg_count = m->msg_count;
1001 if (m->type == MUTT_MBOX)
1003 else
1005
1006 if (m->msg_count > old_msg_count)
1008
1009 /* Only unlock the folder if it was locked inside of this routine.
1010 * It may have been locked elsewhere, like in
1011 * mutt_checkpoint_mailbox(). */
1012 if (unlock)
1013 {
1016 }
1017
1018 return MX_STATUS_NEW_MAIL; /* signal that new mail arrived */
1019 }
1020 else
1021 {
1022 modified = true;
1023 }
1024 }
1025 else
1026 {
1027 mutt_debug(LL_DEBUG1, "fgets returned NULL\n");
1028 modified = true;
1029 }
1030 }
1031 else
1032 {
1033 modified = true;
1034 }
1035 }
1036
1037 if (modified)
1038 {
1039 if (reopen_mailbox(m) != -1)
1040 {
1042 if (unlock)
1043 {
1046 }
1047 return MX_STATUS_REOPENED;
1048 }
1049 }
1050
1051 /* fatal error */
1052
1053error:
1055 mx_fastclose_mailbox(m, false);
1057 mutt_error(_("Mailbox was corrupted"));
1058 return MX_STATUS_ERROR;
1059}
1060
1064static enum MxStatus mbox_mbox_sync(struct Mailbox *m)
1065{
1067 if (!adata)
1068 return MX_STATUS_ERROR;
1069
1070 struct Buffer *tempfile = NULL;
1071 char buf[32] = { 0 };
1072 int j;
1073 bool unlink_tempfile = false;
1074 bool need_sort = false; /* flag to resort mailbox if new mail arrives */
1075 int first = -1; /* first message to be written */
1076 LOFF_T offset; /* location in mailbox to write changed messages */
1077 struct stat st = { 0 };
1078 struct MUpdate *new_offset = NULL;
1079 struct MUpdate *old_offset = NULL;
1080 FILE *fp = NULL;
1081 struct Progress *progress = NULL;
1082 enum MxStatus rc = MX_STATUS_ERROR;
1083
1084 /* sort message by their position in the mailbox on disk */
1085 const enum SortType c_sort = cs_subset_sort(NeoMutt->sub, "sort");
1086 if (c_sort != SORT_ORDER)
1087 {
1088 mutt_sort_order(m);
1089 need_sort = true;
1090 }
1091
1092 /* need to open the file for writing in such a way that it does not truncate
1093 * the file, so use read-write mode. */
1094 adata->fp = freopen(mailbox_path(m), "r+", adata->fp);
1095 if (!adata->fp)
1096 {
1097 mx_fastclose_mailbox(m, false);
1098 mutt_error(_("Fatal error! Could not reopen mailbox!"));
1099 goto fatal;
1100 }
1101
1103
1104 if (mbox_lock_mailbox(m, true, true) == -1)
1105 {
1107 mutt_error(_("Unable to lock mailbox"));
1108 goto bail;
1109 }
1110
1111 /* Check to make sure that the file hasn't changed on disk */
1112 enum MxStatus check = mbox_mbox_check(m);
1113 if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
1114 {
1115 /* new mail arrived, or mailbox reopened */
1116 rc = check;
1117 goto bail;
1118 }
1119 else if (check < 0)
1120 {
1121 goto fatal;
1122 }
1123
1124 /* Create a temporary file to write the new version of the mailbox in. */
1125 tempfile = buf_pool_get();
1126 buf_mktemp(tempfile);
1127 int fd = open(buf_string(tempfile), O_WRONLY | O_EXCL | O_CREAT, 0600);
1128 if ((fd == -1) || !(fp = fdopen(fd, "w")))
1129 {
1130 if (fd != -1)
1131 {
1132 close(fd);
1133 unlink_tempfile = true;
1134 }
1135 mutt_error(_("Could not create temporary file"));
1136 goto bail;
1137 }
1138 unlink_tempfile = true;
1139
1140 /* find the first deleted/changed message. we save a lot of time by only
1141 * rewriting the mailbox from the point where it has actually changed. */
1142 int i = 0;
1143 for (; (i < m->msg_count) && !m->emails[i]->deleted &&
1144 !m->emails[i]->changed && !m->emails[i]->attach_del;
1145 i++)
1146 {
1147 }
1148 if (i == m->msg_count)
1149 {
1150 /* this means m->changed or m->msg_deleted was set, but no
1151 * messages were found to be changed or deleted. This should
1152 * never happen, is we presume it is a bug in neomutt. */
1153 mutt_error(_("sync: mbox modified, but no modified messages (report this bug)"));
1154 mutt_debug(LL_DEBUG1, "no modified messages\n");
1155 goto bail;
1156 }
1157
1158 /* save the index of the first changed/deleted message */
1159 first = i;
1160 /* where to start overwriting */
1161 offset = m->emails[i]->offset;
1162
1163 /* the offset stored in the header does not include the MMDF_SEP, so make
1164 * sure we seek to the correct location */
1165 if (m->type == MUTT_MMDF)
1166 offset -= (sizeof(MMDF_SEP) - 1);
1167
1168 /* allocate space for the new offsets */
1169 new_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate));
1170 old_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate));
1171
1172 if (m->verbose)
1173 {
1175 progress_set_message(progress, _("Writing %s..."), mailbox_path(m));
1176 }
1177
1178 for (i = first, j = 0; i < m->msg_count; i++)
1179 {
1180 progress_update(progress, i, i / (m->msg_count / 100 + 1));
1181 /* back up some information which is needed to restore offsets when
1182 * something fails. */
1183
1184 old_offset[i - first].valid = true;
1185 old_offset[i - first].hdr = m->emails[i]->offset;
1186 old_offset[i - first].body = m->emails[i]->body->offset;
1187 old_offset[i - first].lines = m->emails[i]->lines;
1188 old_offset[i - first].length = m->emails[i]->body->length;
1189
1190 if (!m->emails[i]->deleted)
1191 {
1192 j++;
1193
1194 if (m->type == MUTT_MMDF)
1195 {
1196 if (fputs(MMDF_SEP, fp) == EOF)
1197 {
1198 mutt_perror("%s", buf_string(tempfile));
1199 goto bail;
1200 }
1201 }
1202
1203 /* save the new offset for this message. we add 'offset' because the
1204 * temporary file only contains saved message which are located after
1205 * 'offset' in the real mailbox */
1206 new_offset[i - first].hdr = ftello(fp) + offset;
1207
1208 struct Message *msg = mx_msg_open(m, m->emails[i]);
1209 const int rc2 = mutt_copy_message(fp, m->emails[i], msg, MUTT_CM_UPDATE,
1211 mx_msg_close(m, &msg);
1212 if (rc2 != 0)
1213 {
1214 mutt_perror("%s", buf_string(tempfile));
1215 goto bail;
1216 }
1217
1218 /* Since messages could have been deleted, the offsets stored in memory
1219 * will be wrong, so update what we can, which is the offset of this
1220 * message, and the offset of the body. If this is a multipart message,
1221 * we just flush the in memory cache so that the message will be reparsed
1222 * if the user accesses it later. */
1223 new_offset[i - first].body = ftello(fp) - m->emails[i]->body->length + offset;
1224 mutt_body_free(&m->emails[i]->body->parts);
1225
1226 if (m->type == MUTT_MMDF)
1227 {
1228 if (fputs(MMDF_SEP, fp) == EOF)
1229 {
1230 mutt_perror("%s", buf_string(tempfile));
1231 goto bail;
1232 }
1233 }
1234 else
1235 {
1236 if (fputs("\n", fp) == EOF)
1237 {
1238 mutt_perror("%s", buf_string(tempfile));
1239 goto bail;
1240 }
1241 }
1242 }
1243 }
1244
1245 if (mutt_file_fclose(&fp) != 0)
1246 {
1247 mutt_debug(LL_DEBUG1, "mutt_file_fclose (&) returned non-zero\n");
1248 mutt_perror("%s", buf_string(tempfile));
1249 goto bail;
1250 }
1251
1252 /* Save the state of this folder. */
1253 if (stat(mailbox_path(m), &st) == -1)
1254 {
1255 mutt_perror("%s", mailbox_path(m));
1256 goto bail;
1257 }
1258
1259 unlink_tempfile = false;
1260
1261 fp = mutt_file_fopen(buf_string(tempfile), "r");
1262 if (!fp)
1263 {
1265 mx_fastclose_mailbox(m, false);
1266 mutt_debug(LL_DEBUG1, "unable to reopen temp copy of mailbox!\n");
1267 mutt_perror("%s", buf_string(tempfile));
1268 FREE(&new_offset);
1269 FREE(&old_offset);
1270 goto fatal;
1271 }
1272
1273 if (!mutt_file_seek(adata->fp, offset, SEEK_SET) || /* seek the append location */
1274 /* do a sanity check to make sure the mailbox looks ok */
1275 !fgets(buf, sizeof(buf), adata->fp) ||
1276 ((m->type == MUTT_MBOX) && !mutt_str_startswith(buf, "From ")) ||
1277 ((m->type == MUTT_MMDF) && !mutt_str_equal(MMDF_SEP, buf)))
1278 {
1279 mutt_debug(LL_DEBUG1, "message not in expected position\n");
1280 mutt_debug(LL_DEBUG1, " LINE: %s\n", buf);
1281 i = -1;
1282 }
1283 else
1284 {
1285 if (!mutt_file_seek(adata->fp, offset, SEEK_SET)) /* return to proper offset */
1286 {
1287 i = -1;
1288 }
1289 else
1290 {
1291 /* copy the temp mailbox back into place starting at the first
1292 * change/deleted message */
1293 if (m->verbose)
1294 mutt_message(_("Committing changes..."));
1295 i = mutt_file_copy_stream(fp, adata->fp);
1296
1297 if (ferror(adata->fp))
1298 i = -1;
1299 }
1300 if (i >= 0)
1301 {
1302 m->size = ftello(adata->fp); /* update the mailbox->size of the mailbox */
1303 if ((m->size < 0) || (ftruncate(fileno(adata->fp), m->size) != 0))
1304 {
1305 i = -1;
1306 mutt_debug(LL_DEBUG1, "ftruncate() failed\n");
1307 }
1308 }
1309 }
1310
1313
1314 if ((mutt_file_fclose(&adata->fp) != 0) || (i == -1))
1315 {
1316 /* error occurred while writing the mailbox back, so keep the temp copy around */
1317
1318 struct Buffer *savefile = buf_pool_get();
1319
1320 const char *const c_tmp_dir = cs_subset_path(NeoMutt->sub, "tmp_dir");
1321 buf_printf(savefile, "%s/neomutt.%s-%s-%u", NONULL(c_tmp_dir),
1322 NONULL(Username), NONULL(ShortHostname), (unsigned int) getpid());
1323 rename(buf_string(tempfile), buf_string(savefile));
1325 mx_fastclose_mailbox(m, false);
1326 buf_pretty_mailbox(savefile);
1327 mutt_error(_("Write failed! Saved partial mailbox to %s"), buf_string(savefile));
1328 buf_pool_release(&savefile);
1329 FREE(&new_offset);
1330 FREE(&old_offset);
1331 goto fatal;
1332 }
1333
1334 /* Restore the previous access/modification times */
1335 mbox_reset_atime(m, &st);
1336
1337 /* reopen the mailbox in read-only mode */
1338 adata->fp = mbox_open_readwrite(m);
1339 if (!adata->fp)
1340 {
1341 adata->fp = mbox_open_readonly(m);
1342 }
1343 if (!adata->fp)
1344 {
1345 unlink(buf_string(tempfile));
1347 mx_fastclose_mailbox(m, false);
1348 mutt_error(_("Fatal error! Could not reopen mailbox!"));
1349 FREE(&new_offset);
1350 FREE(&old_offset);
1351 goto fatal;
1352 }
1353
1354 /* update the offsets of the rewritten messages */
1355 for (i = first, j = first; i < m->msg_count; i++)
1356 {
1357 if (!m->emails[i]->deleted)
1358 {
1359 m->emails[i]->offset = new_offset[i - first].hdr;
1360 m->emails[i]->body->hdr_offset = new_offset[i - first].hdr;
1361 m->emails[i]->body->offset = new_offset[i - first].body;
1362 m->emails[i]->index = j++;
1363 }
1364 }
1365 FREE(&new_offset);
1366 FREE(&old_offset);
1367 unlink(buf_string(tempfile)); /* remove partial copy of the mailbox */
1368 buf_pool_release(&tempfile);
1370
1371 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
1372 if (c_check_mbox_size)
1373 {
1374 struct Mailbox *m_tmp = mailbox_find(mailbox_path(m));
1375 if (m_tmp && !m_tmp->has_new)
1376 mailbox_update(m_tmp);
1377 }
1378
1379 progress_free(&progress);
1380 return 0; /* signal success */
1381
1382bail: /* Come here in case of disaster */
1383
1384 mutt_file_fclose(&fp);
1385
1386 if (tempfile && unlink_tempfile)
1387 unlink(buf_string(tempfile));
1388
1389 /* restore offsets, as far as they are valid */
1390 if ((first >= 0) && old_offset)
1391 {
1392 for (i = first; (i < m->msg_count) && old_offset[i - first].valid; i++)
1393 {
1394 m->emails[i]->offset = old_offset[i - first].hdr;
1395 m->emails[i]->body->hdr_offset = old_offset[i - first].hdr;
1396 m->emails[i]->body->offset = old_offset[i - first].body;
1397 m->emails[i]->lines = old_offset[i - first].lines;
1398 m->emails[i]->body->length = old_offset[i - first].length;
1399 }
1400 }
1401
1402 /* this is ok to call even if we haven't locked anything */
1404
1406 FREE(&new_offset);
1407 FREE(&old_offset);
1408
1409 adata->fp = freopen(mailbox_path(m), "r", adata->fp);
1410 if (!adata->fp)
1411 {
1412 mutt_error(_("Could not reopen mailbox"));
1413 mx_fastclose_mailbox(m, false);
1414 goto fatal;
1415 }
1416
1418 if (need_sort)
1419 {
1420 /* if the mailbox was reopened, the thread tree will be invalid so make
1421 * sure to start threading from scratch. */
1423 }
1424
1425fatal:
1426 buf_pool_release(&tempfile);
1427 progress_free(&progress);
1428 return rc;
1429}
1430
1434static enum MxStatus mbox_mbox_close(struct Mailbox *m)
1435{
1437 if (!adata)
1438 return MX_STATUS_ERROR;
1439
1440 if (!adata->fp)
1441 return MX_STATUS_OK;
1442
1443 if (adata->append)
1444 {
1445 mutt_file_unlock(fileno(adata->fp));
1447 }
1448
1449 mutt_file_fclose(&adata->fp);
1450
1451 /* fix up the times so mailbox won't get confused */
1452 if (m->peekonly && !buf_is_empty(&m->pathbuf) &&
1453 (mutt_file_timespec_compare(&adata->mtime, &adata->atime) > 0))
1454 {
1455#ifdef HAVE_UTIMENSAT
1456 struct timespec ts[2] = { { 0 }, { 0 } };
1457 ts[0] = adata->atime;
1458 ts[1] = adata->mtime;
1459 utimensat(AT_FDCWD, m->path, ts, 0);
1460#else
1461 struct utimbuf ut = { 0 };
1462 ut.actime = adata->atime.tv_sec;
1463 ut.modtime = adata->mtime.tv_sec;
1464 utime(mailbox_path(m), &ut);
1465#endif
1466 }
1467
1468 return MX_STATUS_OK;
1469}
1470
1474static bool mbox_msg_open(struct Mailbox *m, struct Message *msg, struct Email *e)
1475{
1477 if (!adata)
1478 return false;
1479
1480 msg->fp = mutt_file_fopen(mailbox_path(m), "r");
1481 if (!msg->fp)
1482 return false;
1483
1484 return true;
1485}
1486
1490static bool mbox_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
1491{
1493 if (!adata)
1494 return false;
1495
1496 msg->fp = adata->fp;
1497 return true;
1498}
1499
1503static int mbox_msg_commit(struct Mailbox *m, struct Message *msg)
1504{
1505 if (fputc('\n', msg->fp) == EOF)
1506 return -1;
1507
1508 if ((fflush(msg->fp) == EOF) || (fsync(fileno(msg->fp)) == -1))
1509 {
1510 mutt_perror(_("Can't write message"));
1511 return -1;
1512 }
1513
1514 return 0;
1515}
1516
1520static int mbox_msg_close(struct Mailbox *m, struct Message *msg)
1521{
1522 if (msg->write)
1523 msg->fp = NULL;
1524 else
1525 mutt_file_fclose(&msg->fp);
1526
1527 return 0;
1528}
1529
1535static int mbox_msg_padding_size(struct Mailbox *m)
1536{
1537 return 1;
1538}
1539
1543enum MailboxType mbox_path_probe(const char *path, const struct stat *st)
1544{
1545 if (!st)
1546 return MUTT_UNKNOWN;
1547
1548 if (S_ISDIR(st->st_mode))
1549 return MUTT_UNKNOWN;
1550
1551 if (st->st_size == 0)
1552 return MUTT_MBOX;
1553
1554 FILE *fp = mutt_file_fopen(path, "r");
1555 if (!fp)
1556 return MUTT_UNKNOWN;
1557
1558 int ch;
1559 while ((ch = fgetc(fp)) != EOF)
1560 {
1561 /* Some mailbox creation tools erroneously append a blank line to
1562 * a file before appending a mail message. This allows neomutt to
1563 * detect type for and thus open those files. */
1564 if ((ch != '\n') && (ch != '\r'))
1565 {
1566 ungetc(ch, fp);
1567 break;
1568 }
1569 }
1570
1572 char tmp[256] = { 0 };
1573 if (fgets(tmp, sizeof(tmp), fp))
1574 {
1575 if (mutt_str_startswith(tmp, "From "))
1576 type = MUTT_MBOX;
1577 else if (mutt_str_equal(tmp, MMDF_SEP))
1578 type = MUTT_MMDF;
1579 }
1581
1582 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
1583 if (!c_check_mbox_size)
1584 {
1585 /* need to restore the times here, the file was not really accessed,
1586 * only the type was accessed. This is important, because detection
1587 * of "new mail" depends on those times set correctly. */
1588#ifdef HAVE_UTIMENSAT
1589 struct timespec ts[2] = { { 0 }, { 0 } };
1592 utimensat(AT_FDCWD, path, ts, 0);
1593#else
1594 struct utimbuf times = { 0 };
1595 times.actime = st->st_atime;
1596 times.modtime = st->st_mtime;
1597 utime(path, &times);
1598#endif
1599 }
1600
1601 return type;
1602}
1603
1607static int mbox_path_canon(struct Buffer *path)
1608{
1609 mutt_path_canon(path, HomeDir, false);
1610 return 0;
1611}
1612
1616static int mbox_path_is_empty(struct Buffer *path)
1617{
1618 return mutt_file_check_empty(buf_string(path));
1619}
1620
1624static int mmdf_msg_commit(struct Mailbox *m, struct Message *msg)
1625{
1626 if (fputs(MMDF_SEP, msg->fp) == EOF)
1627 return -1;
1628
1629 if ((fflush(msg->fp) == EOF) || (fsync(fileno(msg->fp)) == -1))
1630 {
1631 mutt_perror(_("Can't write message"));
1632 return -1;
1633 }
1634
1635 return 0;
1636}
1637
1643static int mmdf_msg_padding_size(struct Mailbox *m)
1644{
1645 return 10;
1646}
1647
1651static enum MxStatus mbox_mbox_check_stats(struct Mailbox *m, uint8_t flags)
1652{
1653 struct stat st = { 0 };
1654 if (stat(mailbox_path(m), &st) != 0)
1655 return MX_STATUS_ERROR;
1656
1657 bool new_or_changed;
1658
1659 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
1660 if (c_check_mbox_size)
1661 {
1662 new_or_changed = (st.st_size > m->size);
1663 }
1664 else
1665 {
1666 new_or_changed =
1668 (m->newly_created &&
1671 }
1672
1673 if (new_or_changed)
1674 {
1675 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
1676 if (!c_mail_check_recent ||
1678 {
1679 m->has_new = true;
1680 }
1681 }
1682 else if (c_check_mbox_size)
1683 {
1684 /* some other program has deleted mail from the folder */
1685 m->size = (off_t) st.st_size;
1686 }
1687
1688 if (m->newly_created && ((st.st_ctime != st.st_mtime) || (st.st_ctime != st.st_atime)))
1689 m->newly_created = false;
1690
1692 {
1695 &adata->stats_last_checked) > 0)
1696 {
1697 bool old_peek = m->peekonly;
1699 mx_mbox_close(m);
1700 m->peekonly = old_peek;
1701 mutt_time_now(&adata->stats_last_checked);
1702 }
1703 }
1704
1705 if (m->has_new || m->msg_new)
1706 return MX_STATUS_NEW_MAIL;
1707 return MX_STATUS_OK;
1708}
1709
1713const struct MxOps MxMboxOps = {
1714 // clang-format off
1715 .type = MUTT_MBOX,
1716 .name = "mbox",
1717 .is_local = true,
1718 .ac_owns_path = mbox_ac_owns_path,
1719 .ac_add = mbox_ac_add,
1720 .mbox_open = mbox_mbox_open,
1721 .mbox_open_append = mbox_mbox_open_append,
1722 .mbox_check = mbox_mbox_check,
1723 .mbox_check_stats = mbox_mbox_check_stats,
1724 .mbox_sync = mbox_mbox_sync,
1725 .mbox_close = mbox_mbox_close,
1726 .msg_open = mbox_msg_open,
1727 .msg_open_new = mbox_msg_open_new,
1728 .msg_commit = mbox_msg_commit,
1729 .msg_close = mbox_msg_close,
1730 .msg_padding_size = mbox_msg_padding_size,
1731 .msg_save_hcache = NULL,
1732 .tags_edit = NULL,
1733 .tags_commit = NULL,
1734 .path_probe = mbox_path_probe,
1735 .path_canon = mbox_path_canon,
1736 .path_is_empty = mbox_path_is_empty,
1737 // clang-format on
1738};
1739
1743const struct MxOps MxMmdfOps = {
1744 // clang-format off
1745 .type = MUTT_MMDF,
1746 .name = "mmdf",
1747 .is_local = true,
1748 .ac_owns_path = mbox_ac_owns_path,
1749 .ac_add = mbox_ac_add,
1750 .mbox_open = mbox_mbox_open,
1751 .mbox_open_append = mbox_mbox_open_append,
1752 .mbox_check = mbox_mbox_check,
1753 .mbox_check_stats = mbox_mbox_check_stats,
1754 .mbox_sync = mbox_mbox_sync,
1755 .mbox_close = mbox_mbox_close,
1756 .msg_open = mbox_msg_open,
1757 .msg_open_new = mbox_msg_open_new,
1758 .msg_commit = mmdf_msg_commit,
1759 .msg_close = mbox_msg_close,
1760 .msg_padding_size = mmdf_msg_padding_size,
1761 .msg_save_hcache = NULL,
1762 .tags_edit = NULL,
1763 .tags_commit = NULL,
1764 .path_probe = mbox_path_probe,
1765 .path_canon = mbox_path_canon,
1766 .path_is_empty = mbox_path_is_empty,
1767 // clang-format on
1768};
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:765
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:480
Email Address Handling.
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:161
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:291
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:168
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:47
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:266
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:907
Duplicate the structure of an entire email.
#define MUTT_CM_UPDATE
Update structs on sync.
Definition: copy.h:42
#define CH_UPDATE
Update the status and x-status fields?
Definition: copy.h:54
#define CH_FROM
Retain the "From " message separator?
Definition: copy.h:58
#define CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition: copy.h:64
Convenience wrapper for the core headers.
void mailbox_update(struct Mailbox *m)
Get the mailbox's current size.
Definition: mailbox.c:215
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:233
struct Mailbox * mailbox_find(const char *path)
Find the mailbox with a given path.
Definition: mailbox.c:150
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition: mailbox.h:190
@ NT_MAILBOX_INVALID
Email list was changed.
Definition: mailbox.h:189
@ NT_MAILBOX_UPDATE
Update internal tables.
Definition: mailbox.h:191
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:223
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
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:58
bool email_cmp_strict(const struct Email *e1, const struct Email *e2)
Strictly compare message emails.
Definition: email.c:102
struct Email * email_new(void)
Create a new Email.
Definition: email.c:80
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:46
Structs that make up an email.
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition: parse.c:1200
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:1579
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:287
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:1641
void mutt_file_touch_atime(int fd)
Set the access time to current time.
Definition: file.c:1091
int mutt_file_check_empty(const char *path)
Is the mailbox empty.
Definition: file.c:1436
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:974
int mutt_file_lock(int fd, bool excl, bool timeout)
(Try to) Lock a file using fcntl()
Definition: file.c:1202
int mutt_file_timespec_compare(struct timespec *a, struct timespec *b)
Compare to time values.
Definition: file.c:1557
int mutt_file_unlock(int fd)
Unlock a file previously locked by mutt_file_lock()
Definition: file.c:1249
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:778
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:1619
#define mutt_file_fclose(FP)
Definition: file.h:149
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:148
@ MUTT_STAT_CTIME
File/dir's ctime - creation time.
Definition: file.h:66
@ MUTT_STAT_ATIME
File/dir's atime - last accessed time.
Definition: file.h:64
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition: file.h:65
void mutt_set_flag(struct Mailbox *m, struct Email *e, enum MessageType flag, bool bf, bool upd_mbox)
Set a flag on an email.
Definition: flags.c:57
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition: from.c:49
char * ShortHostname
Short version of the hostname.
Definition: globals.c:39
char * Username
User's login name.
Definition: globals.c:41
static void mbox_adata_free(void **ptr)
Free the private Account data - Implements Account::adata_free() -.
Definition: mbox.c:80
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_message(...)
Definition: logging2.h:91
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
#define mutt_perror(...)
Definition: logging2.h:93
static bool mbox_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Implements MxOps::ac_add() -.
Definition: mbox.c:791
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:776
const struct MxOps MxMboxOps
Mbox Mailbox - Implements MxOps -.
Definition: mbox.c:1713
const struct MxOps MxMmdfOps
MMDF Mailbox - Implements MxOps -.
Definition: mbox.c:1743
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:1651
static enum MxStatus mbox_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check() -.
Definition: mbox.c:930
static enum MxStatus mbox_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close() -.
Definition: mbox.c:1434
static bool mbox_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
Definition: mbox.c:878
static enum MxOpenReturns mbox_mbox_open(struct Mailbox *m)
Open a Mailbox - Implements MxOps::mbox_open() -.
Definition: mbox.c:829
static enum MxStatus mbox_mbox_sync(struct Mailbox *m)
Save changes to the Mailbox - Implements MxOps::mbox_sync() -.
Definition: mbox.c:1064
static int mbox_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition: mbox.c:1520
static int mbox_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: mbox.c:1503
static int mmdf_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: mbox.c:1624
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:1490
static bool mbox_msg_open(struct Mailbox *m, struct Message *msg, struct Email *e)
Open an email message in a Mailbox - Implements MxOps::msg_open() -.
Definition: mbox.c:1474
static int mbox_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Implements MxOps::msg_padding_size() -.
Definition: mbox.c:1535
static int mmdf_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Implements MxOps::msg_padding_size() -.
Definition: mbox.c:1643
static int mbox_path_canon(struct Buffer *path)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition: mbox.c:1607
static int mbox_path_is_empty(struct Buffer *path)
Is the mailbox empty - Implements MxOps::path_is_empty() -.
Definition: mbox.c:1616
enum MailboxType mbox_path_probe(const char *path, const struct stat *st)
Is this an mbox Mailbox? - Implements MxOps::path_probe() -.
Definition: mbox.c:1543
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:457
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
#define MMDF_SEP
Definition: lib.h:62
static enum MxOpenReturns mbox_parse_mailbox(struct Mailbox *m)
Read a mailbox from disk.
Definition: mbox.c:352
static struct MboxAccountData * mbox_adata_new(void)
Create a new MboxAccountData struct.
Definition: mbox.c:95
static bool mbox_has_new(struct Mailbox *m)
Does the mailbox have new mail.
Definition: mbox.c:727
static int mbox_lock_mailbox(struct Mailbox *m, bool excl, bool retry)
Lock a mailbox.
Definition: mbox.c:140
static struct MboxAccountData * mbox_adata_get(struct Mailbox *m)
Get the private data associated with a Mailbox.
Definition: mbox.c:125
static int init_mailbox(struct Mailbox *m)
Add Mbox data to the Mailbox.
Definition: mbox.c:106
static FILE * mbox_open_readwrite(struct Mailbox *m)
Open an mbox read-write.
Definition: mbox.c:803
static FILE * mbox_open_readonly(struct Mailbox *m)
Open an mbox read-only.
Definition: mbox.c:818
static void mbox_unlock_mailbox(struct Mailbox *m)
Unlock a mailbox.
Definition: mbox.c:164
static enum MxOpenReturns mmdf_parse_mailbox(struct Mailbox *m)
Read a mailbox in MMDF format.
Definition: mbox.c:184
void mbox_reset_atime(struct Mailbox *m, struct stat *st)
Reset the access time on the mailbox file.
Definition: mbox.c:748
static int reopen_mailbox(struct Mailbox *m)
Close and reopen a mailbox.
Definition: mbox.c:549
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:51
#define FREE(x)
Definition: memory.h:45
int mutt_date_local_tz(time_t t)
Calculate the local timezone in seconds east of UTC.
Definition: date.c:219
void mutt_time_now(struct timespec *tp)
Set the provided time field to the current time.
Definition: date.c:480
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool mutt_path_canon(struct Buffer *path, const char *homedir, bool is_dir)
Create the canonical version of a path.
Definition: path.c:248
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final '/'.
Definition: path.c:312
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:660
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:230
Many unsorted constants and some structs.
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:73
@ MUTT_OLD
Old messages.
Definition: mutt.h:71
@ MUTT_PURGE
Messages to be purged (bypass trash)
Definition: mutt.h:77
@ MUTT_TAG
Tagged messages.
Definition: mutt.h:80
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:79
@ MUTT_DELETE
Messages to be deleted.
Definition: mutt.h:75
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:72
void mutt_make_label_hash(struct Mailbox *m)
Create a Hash Table to store the labels.
Definition: mutt_header.c:420
Representation of the email's header.
void buf_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:554
Some miscellaneous functions.
void mx_alloc_memory(struct Mailbox *m, int req_size)
Create storage for the emails.
Definition: mx.c:1206
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition: mx.c:1180
void mx_fastclose_mailbox(struct Mailbox *m, bool keep_account)
Free up memory associated with the Mailbox.
Definition: mx.c:414
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:288
struct Message * mx_msg_open(struct Mailbox *m, struct Email *e)
Return a stream pointer for a message.
Definition: mx.c:1134
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition: mx.c:598
API for mailboxes.
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition: mxapi.h:39
#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:45
#define MUTT_QUIET
Do not print any messages.
Definition: mxapi.h:44
MxOpenReturns
Return values for mbox_open()
Definition: mxapi.h:76
@ MX_OPEN_ERROR
Open failed with an error.
Definition: mxapi.h:78
@ MX_OPEN_ABORT
Open was aborted.
Definition: mxapi.h:79
@ MX_OPEN_OK
Open succeeded.
Definition: mxapi.h:77
#define MUTT_PEEK
Revert atime back after taking a look (if applicable)
Definition: mxapi.h:48
#define MUTT_NOSORT
Do not sort the mailbox after opening it.
Definition: mxapi.h:41
#define MUTT_MAILBOX_CHECK_FORCE_STATS
Ignore MailboxType and calculate statistics.
Definition: mxapi.h:55
#define MUTT_MAILBOX_CHECK_FORCE
Ignore MailboxTime and check for new mail.
Definition: mxapi.h:54
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_sync(), and mbox_close()
Definition: mxapi.h:63
@ MX_STATUS_LOCKED
Couldn't lock the Mailbox.
Definition: mxapi.h:67
@ MX_STATUS_ERROR
An error occurred.
Definition: mxapi.h:64
@ MX_STATUS_OK
No changes.
Definition: mxapi.h:65
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition: mxapi.h:68
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition: mxapi.h:66
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
Progress Bar.
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc
Definition: lib.h:82
@ MUTT_PROGRESS_WRITE
Progress tracks elements, according to $write_inc
Definition: lib.h:83
struct Progress * progress_new(enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition: progress.c:139
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition: progress.c:110
void progress_set_message(struct Progress *progress, const char *fmt,...) __attribute__((__format__(__printf__
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:80
Prototypes for many functions.
#define STAILQ_FIRST(head)
Definition: queue.h:350
#define TAILQ_EMPTY(head)
Definition: queue.h:721
volatile sig_atomic_t SigInt
true after SIGINT is received
Definition: signal.c:66
void mutt_sig_block(void)
Block signals during critical operations.
Definition: signal.c:212
void mutt_sig_unblock(void)
Restore previously blocked signals.
Definition: signal.c:230
SortType
Methods for sorting.
Definition: sort2.h:34
@ SORT_ORDER
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:40
void mutt_sort_order(struct Mailbox *m)
Sort emails by their disk order.
Definition: sort.c:444
Assorted sorting methods.
Key value store.
#define NONULL(x)
Definition: string2.h:37
A group of associated Mailboxes.
Definition: account.h:36
enum MailboxType type
Type of Mailboxes this Account contains.
Definition: account.h:37
void(* adata_free)(void **ptr)
Definition: account.h:53
void * adata
Private data (for Mailbox backends)
Definition: account.h:42
struct MailboxList mailboxes
List of Mailboxes.
Definition: account.h:40
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:36
The envelope/body of an email.
Definition: email.h:39
bool read
Email is read.
Definition: email.h:50
bool purge
Skip trash folder when deleting.
Definition: email.h:79
struct Envelope * env
Envelope information.
Definition: email.h:68
int lines
How many lines in the body of this message?
Definition: email.h:62
struct Body * body
List of MIME parts.
Definition: email.h:69
bool old
Email is seen, but unread.
Definition: email.h:49
bool changed
Email has been edited.
Definition: email.h:77
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:71
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:102
bool flagged
Marked important?
Definition: email.h:47
bool replied
Email has been replied to.
Definition: email.h:51
bool deleted
Email is deleted.
Definition: email.h:78
int index
The absolute (unsorted) message number.
Definition: email.h:113
bool tagged
Email is tagged.
Definition: email.h:110
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:61
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:69
long lines
Definition: mbox.c:73
LOFF_T hdr
Definition: mbox.c:71
LOFF_T length
Definition: mbox.c:74
LOFF_T body
Definition: mbox.c:72
bool valid
Definition: mbox.c:70
List of Mailboxes.
Definition: mailbox.h:166
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:167
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
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
Size of emails array.
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: "subject" -> Email.
Definition: mailbox.h:124
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
struct HashTable * id_hash
Hash Table: "message-id" -> Email.
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:114
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: "x-labels" -> Email.
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:104
bool readonly
Don't allow changes to the mailbox.
Definition: mailbox.h:116
int msg_tagged
How many messages are tagged?
Definition: mailbox.h:94
bool verbose
Display status messages?
Definition: mailbox.h:117
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:55
struct timespec atime
File's last-access time.
Definition: lib.h:52
struct timespec mtime
Time Mailbox was last changed.
Definition: lib.h:51
A local copy of an email.
Definition: message.h:34
FILE * fp
pointer to the message data
Definition: message.h:35
bool write
nonzero if message is open for writing
Definition: message.h:38
Definition: mxapi.h:91
enum MailboxType type
Mailbox type, e.g. MUTT_IMAP.
Definition: mxapi.h:92
Container for Accounts, Notifications.
Definition: neomutt.h:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
Time value with nanosecond precision.
Definition: file.h:51
time_t tv_sec
Number of seconds since the epoch.
Definition: file.h:52
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:297
#define buf_mktemp(buf)
Definition: tmp.h:33