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