NeoMutt  2025-12-11-694-ga89709
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 LOFF_T loc;
371 struct Progress *progress = NULL;
373
374 /* Save information about the folder at the time we opened it. */
375 if (stat(mailbox_path(m), &st) == -1)
376 {
377 mutt_perror("%s", mailbox_path(m));
378 goto fail;
379 }
380
381 m->size = st.st_size;
384
385 if (!m->readonly)
386 m->readonly = access(mailbox_path(m), W_OK) ? true : false;
387
388 if (m->verbose)
389 {
390 progress = progress_new(MUTT_PROGRESS_READ, 0);
391 progress_set_message(progress, _("Reading %s..."), mailbox_path(m));
392 }
393
394 loc = ftello(adata->fp);
395 if (loc < 0)
396 {
397 mutt_debug(LL_DEBUG1, "ftello: %s (errno %d)\n", strerror(errno), errno);
398 loc = 0;
399 }
400
401 while ((fgets(buf, sizeof(buf), adata->fp)) && !SigInt)
402 {
403 if (is_from(buf, return_path, sizeof(return_path), &t))
404 {
405 /* Save the Content-Length of the previous message */
406 if (count > 0)
407 {
408 struct Email *e = m->emails[m->msg_count - 1];
409 if (e->body->length < 0)
410 {
411 e->body->length = loc - e->body->offset - 1;
412 if (e->body->length < 0)
413 e->body->length = 0;
414 }
415 if (e->lines == 0)
416 e->lines = lines ? lines - 1 : 0;
417 }
418
419 count++;
420
421 progress_update(progress, count, (int) (ftello(adata->fp) / (m->size / 100 + 1)));
422
424
425 m->emails[m->msg_count] = email_new();
426 e_cur = m->emails[m->msg_count];
427 e_cur->received = t - mutt_date_local_tz(t);
428 e_cur->offset = loc;
429 e_cur->index = m->msg_count;
430
431 e_cur->env = mutt_rfc822_read_header(adata->fp, e_cur, false, false);
432
433 /* if we know how long this message is, either just skip over the body,
434 * or if we don't know how many lines there are, count them now (this will
435 * save time by not having to search for the next message marker). */
436 if (e_cur->body->length > 0)
437 {
438 LOFF_T tmploc;
439
440 loc = ftello(adata->fp);
441 if (loc < 0)
442 {
443 mutt_debug(LL_DEBUG1, "ftello: %s (errno %d)\n", strerror(errno), errno);
444 loc = 0;
445 }
446
447 /* The test below avoids a potential integer overflow if the
448 * content-length is huge (thus necessarily invalid). */
449 tmploc = (e_cur->body->length < m->size) ? (loc + e_cur->body->length + 1) : -1;
450
451 if ((tmploc > 0) && (tmploc < m->size))
452 {
453 /* check to see if the content-length looks valid. we expect to
454 * to see a valid message separator at this point in the stream */
455 if (!mutt_file_seek(adata->fp, tmploc, SEEK_SET) ||
456 !fgets(buf, sizeof(buf), adata->fp) || !mutt_str_startswith(buf, "From "))
457 {
458 mutt_debug(LL_DEBUG1, "bad content-length in message %d (cl=" OFF_T_FMT ")\n",
459 e_cur->index, e_cur->body->length);
460 mutt_debug(LL_DEBUG1, " LINE: %s", buf);
461 /* nope, return the previous position */
462 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
463 e_cur->body->length = -1;
464 }
465 }
466 else if (tmploc != m->size)
467 {
468 /* content-length would put us past the end of the file, so it
469 * must be wrong */
470 e_cur->body->length = -1;
471 }
472
473 if (e_cur->body->length != -1)
474 {
475 /* good content-length. check to see if we know how many lines
476 * are in this message. */
477 if (e_cur->lines == 0)
478 {
479 LOFF_T cl = e_cur->body->length;
480
481 /* count the number of lines in this message */
482 (void) mutt_file_seek(adata->fp, loc, SEEK_SET);
483 while (cl-- > 0)
484 {
485 if (fgetc(adata->fp) == '\n')
486 e_cur->lines++;
487 }
488 }
489
490 /* return to the offset of the next message separator */
491 (void) mutt_file_seek(adata->fp, tmploc, SEEK_SET);
492 }
493 }
494
495 m->msg_count++;
496
497 if (TAILQ_EMPTY(&e_cur->env->return_path) && return_path[0])
498 {
499 mutt_addrlist_parse(&e_cur->env->return_path, return_path);
500 }
501
502 if (TAILQ_EMPTY(&e_cur->env->from))
503 mutt_addrlist_copy(&e_cur->env->from, &e_cur->env->return_path, false);
504
505 lines = 0;
506 }
507 else
508 {
509 lines++;
510 }
511
512 loc = ftello(adata->fp);
513 }
514
515 /* Only set the content-length of the previous message if we have read more
516 * than one message during _this_ invocation. If this routine is called
517 * when new mail is received, we need to make sure not to clobber what
518 * previously was the last message since the headers may be sorted. */
519 if (count > 0)
520 {
521 struct Email *e = m->emails[m->msg_count - 1];
522 if (e->body->length < 0)
523 {
524 e->body->length = ftello(adata->fp) - e->body->offset - 1;
525 if (e->body->length < 0)
526 e->body->length = 0;
527 }
528
529 if (e->lines == 0)
530 e->lines = lines ? lines - 1 : 0;
531 }
532
533 if (SigInt)
534 {
535 SigInt = false;
536 rc = MX_OPEN_ABORT;
537 goto fail; /* action aborted */
538 }
539
540 rc = MX_OPEN_OK;
541fail:
542 progress_free(&progress);
543 return rc;
544}
545
552static int reopen_mailbox(struct Mailbox *m)
553{
554 if (!m)
555 return -1;
556
558 if (!adata)
559 return -1;
560
561 bool (*cmp_headers)(const struct Email *, const struct Email *) = NULL;
562 struct Email **e_old = NULL;
563 int old_msg_count;
564 bool msg_mod = false;
565 int rc = -1;
566
567 /* silent operations */
568 m->verbose = false;
569
570 /* our heuristics require the old mailbox to be unsorted */
571 const enum EmailSortType c_sort = cs_subset_sort(NeoMutt->sub, "sort");
572 if (c_sort != EMAIL_SORT_UNSORTED)
573 {
576 cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
577 }
578
579 e_old = NULL;
580 old_msg_count = 0;
581
582 /* simulate a close */
586 FREE(&m->v2r);
587 if (m->readonly)
588 {
589 for (int i = 0; i < m->msg_count; i++)
590 email_free(&(m->emails[i])); /* nothing to do! */
591 FREE(&m->emails);
592 }
593 else
594 {
595 /* save the old headers */
596 old_msg_count = m->msg_count;
597 e_old = m->emails;
598 m->emails = NULL;
599 }
600
601 m->email_max = 0; /* force allocation of new headers */
602 m->msg_count = 0;
603 m->vcount = 0;
604 m->msg_tagged = 0;
605 m->msg_deleted = 0;
606 m->msg_new = 0;
607 m->msg_unread = 0;
608 m->msg_flagged = 0;
609 m->changed = false;
610 m->id_hash = NULL;
611 m->subj_hash = NULL;
613
614 switch (m->type)
615 {
616 case MUTT_MBOX:
617 case MUTT_MMDF:
618 cmp_headers = email_cmp_strict;
619 mutt_file_fclose(&adata->fp);
620 adata->fp = mutt_file_fopen(mailbox_path(m), "r");
621 if (!adata->fp)
622 rc = -1;
623 else if (m->type == MUTT_MBOX)
624 rc = mbox_parse_mailbox(m);
625 else
626 rc = mmdf_parse_mailbox(m);
627 break;
628
629 default:
630 rc = -1;
631 break;
632 }
633
634 if (rc == -1)
635 {
636 /* free the old headers */
637 for (int i = 0; i < old_msg_count; i++)
638 email_free(&(e_old[i]));
639 FREE(&e_old);
640
641 m->verbose = true;
642 return -1;
643 }
644
645 mutt_file_touch_atime(fileno(adata->fp));
646
647 /* now try to recover the old flags */
648
649 if (!m->readonly)
650 {
651 for (int i = 0; i < m->msg_count; i++)
652 {
653 bool found = false;
654
655 /* some messages have been deleted, and new messages have been
656 * appended at the end; the heuristic is that old messages have then
657 * "advanced" towards the beginning of the folder, so we begin the
658 * search at index "i" */
659 int j;
660 for (j = i; j < old_msg_count; j++)
661 {
662 if (!e_old[j])
663 continue;
664 if (cmp_headers(m->emails[i], e_old[j]))
665 {
666 found = true;
667 break;
668 }
669 }
670 if (!found)
671 {
672 for (j = 0; (j < i) && (j < old_msg_count); j++)
673 {
674 if (!e_old[j])
675 continue;
676 if (cmp_headers(m->emails[i], e_old[j]))
677 {
678 found = true;
679 break;
680 }
681 }
682 }
683
684 if (found)
685 {
686 m->changed = true;
687 if (e_old[j]->changed)
688 {
689 /* Only update the flags if the old header was changed;
690 * otherwise, the header may have been modified externally,
691 * and we don't want to lose _those_ changes */
692 mutt_set_flag(m, m->emails[i], MUTT_FLAG, e_old[j]->flagged, true);
693 mutt_set_flag(m, m->emails[i], MUTT_REPLIED, e_old[j]->replied, true);
694 mutt_set_flag(m, m->emails[i], MUTT_OLD, e_old[j]->old, true);
695 mutt_set_flag(m, m->emails[i], MUTT_READ, e_old[j]->read, true);
696 }
697 mutt_set_flag(m, m->emails[i], MUTT_DELETE, e_old[j]->deleted, true);
698 mutt_set_flag(m, m->emails[i], MUTT_PURGE, e_old[j]->purge, true);
699 mutt_set_flag(m, m->emails[i], MUTT_TAG, e_old[j]->tagged, true);
700
701 /* we don't need this header any more */
702 email_free(&(e_old[j]));
703 }
704 }
705
706 /* free the remaining old emails */
707 for (int j = 0; j < old_msg_count; j++)
708 {
709 if (e_old[j])
710 {
711 email_free(&(e_old[j]));
712 msg_mod = true;
713 }
714 }
715 FREE(&e_old);
716 }
717
719 m->verbose = true;
720
721 return (m->changed || msg_mod) ? MX_STATUS_REOPENED : MX_STATUS_NEW_MAIL;
722}
723
730static bool mbox_has_new(struct Mailbox *m)
731{
732 for (int i = 0; i < m->msg_count; i++)
733 {
734 struct Email *e = m->emails[i];
735 if (!e)
736 break;
737 if (!e->deleted && !e->read && !e->old)
738 return true;
739 }
740 return false;
741}
742
751void mbox_reset_atime(struct Mailbox *m, struct stat *st)
752{
753 struct stat st2 = { 0 };
754 if (!st)
755 {
756 if (stat(mailbox_path(m), &st2) < 0)
757 return;
758 st = &st2;
759 }
760
761 struct utimbuf utimebuf = { 0 };
762 utimebuf.actime = st->st_atime;
763 utimebuf.modtime = st->st_mtime;
764
765 /* When $mbox_check_recent is set, existing new mail is ignored, so do not
766 * reset the atime to mtime-1 to signal new mail. */
767 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
768 if (!c_mail_check_recent && (utimebuf.actime >= utimebuf.modtime) && mbox_has_new(m))
769 {
770 utimebuf.actime = utimebuf.modtime - 1;
771 }
772
773 utime(mailbox_path(m), &utimebuf);
774}
775
779static bool mbox_ac_owns_path(struct Account *a, const char *path)
780{
781 if ((a->type != MUTT_MBOX) && (a->type != MUTT_MMDF))
782 return false;
783
784 struct Mailbox **mp = ARRAY_FIRST(&a->mailboxes);
785 if (!mp)
786 return false;
787
788 return mutt_str_equal(mailbox_path(*mp), path);
789}
790
794static bool mbox_ac_add(struct Account *a, struct Mailbox *m)
795{
796 return true;
797}
798
806static FILE *mbox_open_readwrite(struct Mailbox *m)
807{
808 FILE *fp = mutt_file_fopen(mailbox_path(m), "r+");
809 if (fp)
810 m->readonly = false;
811 return fp;
812}
813
821static FILE *mbox_open_readonly(struct Mailbox *m)
822{
823 FILE *fp = mutt_file_fopen(mailbox_path(m), "r");
824 if (fp)
825 m->readonly = true;
826 return fp;
827}
828
833{
834 if (init_mailbox(m) != 0)
835 return MX_OPEN_ERROR;
836
838 if (!adata)
839 return MX_OPEN_ERROR;
840
841 adata->fp = m->readonly ? NULL : mbox_open_readwrite(m);
842 if (!adata->fp)
843 {
844 adata->fp = mbox_open_readonly(m);
845 }
846 if (!adata->fp)
847 {
848 mutt_perror("%s", mailbox_path(m));
849 return MX_OPEN_ERROR;
850 }
851
853 if (mbox_lock_mailbox(m, false, true) == -1)
854 {
856 return MX_OPEN_ERROR;
857 }
858
859 m->has_new = true;
861 if (m->type == MUTT_MBOX)
862 rc = mbox_parse_mailbox(m);
863 else if (m->type == MUTT_MMDF)
864 rc = mmdf_parse_mailbox(m);
865 else
866 rc = MX_OPEN_ERROR;
867
868 if (!mbox_has_new(m))
869 m->has_new = false;
870 clearerr(adata->fp); // Clear the EOF flag
871 mutt_file_touch_atime(fileno(adata->fp));
872
875 return rc;
876}
877
881static bool mbox_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
882{
883 if (init_mailbox(m) != 0)
884 return false;
885
887 if (!adata)
888 return false;
889
890 if (!adata->fp)
891 {
892 // create dir recursively
893 char *tmp_path = mutt_path_dirname(mailbox_path(m));
894 if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
895 {
896 mutt_perror("%s", mailbox_path(m));
897 FREE(&tmp_path);
898 return false;
899 }
900 FREE(&tmp_path);
901
902 adata->fp = mutt_file_fopen(mailbox_path(m), "a+");
903 if (!adata->fp)
904 {
905 mutt_perror("%s", mailbox_path(m));
906 return false;
907 }
908
909 if (mbox_lock_mailbox(m, true, true) != 0)
910 {
911 mutt_error(_("Couldn't lock %s"), mailbox_path(m));
913 return false;
914 }
915 }
916
917 if (!mutt_file_seek(adata->fp, 0, SEEK_END))
918 {
920 return false;
921 }
922
923 return true;
924}
925
933static enum MxStatus mbox_mbox_check(struct Mailbox *m)
934{
936 if (!adata)
937 return MX_STATUS_ERROR;
938
939 if (!adata->fp)
940 {
941 if (mbox_mbox_open(m) != MX_OPEN_OK)
942 return MX_STATUS_ERROR;
944 }
945 if (!adata->fp)
946 return MX_STATUS_ERROR;
947
948 struct stat st = { 0 };
949 bool unlock = false;
950 bool modified = false;
951
952 if (stat(mailbox_path(m), &st) == 0)
953 {
954 if ((mutt_file_stat_timespec_compare(&st, MUTT_STAT_MTIME, &adata->mtime) == 0) &&
955 (st.st_size == m->size))
956 {
957 return MX_STATUS_OK;
958 }
959
960 if (st.st_size == m->size)
961 {
962 /* the file was touched, but it is still the same length, so just exit */
964 return MX_STATUS_OK;
965 }
966
967 if (st.st_size > m->size)
968 {
969 /* lock the file if it isn't already */
970 if (!adata->locked)
971 {
973 if (mbox_lock_mailbox(m, false, false) == -1)
974 {
976 /* we couldn't lock the mailbox, but nothing serious happened:
977 * probably the new mail arrived: no reason to wait till we can
978 * parse it: we'll get it on the next pass */
979 return MX_STATUS_LOCKED;
980 }
981 unlock = 1;
982 }
983
984 /* Check to make sure that the only change to the mailbox is that
985 * message(s) were appended to this file. My heuristic is that we should
986 * see the message separator at *exactly* what used to be the end of the
987 * folder. */
988 char buf[1024] = { 0 };
989 if (!mutt_file_seek(adata->fp, m->size, SEEK_SET))
990 {
991 goto error;
992 }
993 if (fgets(buf, sizeof(buf), adata->fp))
994 {
995 if (((m->type == MUTT_MBOX) && mutt_str_startswith(buf, "From ")) ||
996 ((m->type == MUTT_MMDF) && mutt_str_equal(buf, MMDF_SEP)))
997 {
998 if (!mutt_file_seek(adata->fp, m->size, SEEK_SET))
999 {
1000 goto error;
1001 }
1002
1003 int old_msg_count = m->msg_count;
1004 if (m->type == MUTT_MBOX)
1006 else
1008
1009 if (m->msg_count > old_msg_count)
1011
1012 /* Only unlock the folder if it was locked inside of this routine.
1013 * It may have been locked elsewhere, like in
1014 * mutt_checkpoint_mailbox(). */
1015 if (unlock)
1016 {
1019 }
1020
1021 return MX_STATUS_NEW_MAIL; /* signal that new mail arrived */
1022 }
1023 else
1024 {
1025 modified = true;
1026 }
1027 }
1028 else
1029 {
1030 mutt_debug(LL_DEBUG1, "fgets returned NULL\n");
1031 modified = true;
1032 }
1033 }
1034 else
1035 {
1036 modified = true;
1037 }
1038 }
1039
1040 if (modified)
1041 {
1042 if (reopen_mailbox(m) != -1)
1043 {
1045 if (unlock)
1046 {
1049 }
1050 return MX_STATUS_REOPENED;
1051 }
1052 }
1053
1054 /* fatal error */
1055
1056error:
1058 mx_fastclose_mailbox(m, false);
1060 mutt_error(_("Mailbox was corrupted"));
1061 return MX_STATUS_ERROR;
1062}
1063
1067static enum MxStatus mbox_mbox_sync(struct Mailbox *m)
1068{
1070 if (!adata)
1071 return MX_STATUS_ERROR;
1072
1073 struct Buffer *tempfile = NULL;
1074 char buf[32] = { 0 };
1075 int j;
1076 bool unlink_tempfile = false;
1077 bool need_sort = false; /* flag to resort mailbox if new mail arrives */
1078 int first = -1; /* first message to be written */
1079 LOFF_T offset; /* location in mailbox to write changed messages */
1080 struct stat st = { 0 };
1081 struct MUpdate *new_offset = NULL;
1082 struct MUpdate *old_offset = NULL;
1083 FILE *fp = NULL;
1084 struct Progress *progress = NULL;
1085 enum MxStatus rc = MX_STATUS_ERROR;
1086
1087 /* sort message by their position in the mailbox on disk */
1088 const enum EmailSortType c_sort = cs_subset_sort(NeoMutt->sub, "sort");
1089 if (c_sort != EMAIL_SORT_UNSORTED)
1090 {
1092 need_sort = true;
1093 }
1094
1095 /* need to open the file for writing in such a way that it does not truncate
1096 * the file, so use read-write mode. */
1097 adata->fp = freopen(mailbox_path(m), "r+", adata->fp);
1098 if (!adata->fp)
1099 {
1100 mx_fastclose_mailbox(m, false);
1101 mutt_error(_("Fatal error! Could not reopen mailbox!"));
1102 goto fatal;
1103 }
1104
1106
1107 if (mbox_lock_mailbox(m, true, true) == -1)
1108 {
1110 mutt_error(_("Unable to lock mailbox"));
1111 goto bail;
1112 }
1113
1114 /* Check to make sure that the file hasn't changed on disk */
1115 enum MxStatus check = mbox_mbox_check(m);
1116 if ((check == MX_STATUS_NEW_MAIL) || (check == MX_STATUS_REOPENED))
1117 {
1118 /* new mail arrived, or mailbox reopened */
1119 rc = check;
1120 goto bail;
1121 }
1122 else if (check < 0)
1123 {
1124 goto fatal;
1125 }
1126
1127 /* Create a temporary file to write the new version of the mailbox in. */
1128 tempfile = buf_pool_get();
1129 buf_mktemp(tempfile);
1130 int fd = open(buf_string(tempfile), O_WRONLY | O_EXCL | O_CREAT, 0600);
1131 if ((fd == -1) || !(fp = fdopen(fd, "w")))
1132 {
1133 if (fd != -1)
1134 {
1135 close(fd);
1136 unlink_tempfile = true;
1137 }
1138 mutt_error(_("Could not create temporary file"));
1139 goto bail;
1140 }
1141 unlink_tempfile = true;
1142
1143 /* find the first deleted/changed message. we save a lot of time by only
1144 * rewriting the mailbox from the point where it has actually changed. */
1145 int i = 0;
1146 for (; (i < m->msg_count) && !m->emails[i]->deleted &&
1147 !m->emails[i]->changed && !m->emails[i]->attach_del;
1148 i++)
1149 {
1150 }
1151 if (i == m->msg_count)
1152 {
1153 /* this means m->changed or m->msg_deleted was set, but no
1154 * messages were found to be changed or deleted. This should
1155 * never happen, is we presume it is a bug in neomutt. */
1156 mutt_error(_("sync: mbox modified, but no modified messages (report this bug)"));
1157 mutt_debug(LL_DEBUG1, "no modified messages\n");
1158 goto bail;
1159 }
1160
1161 /* save the index of the first changed/deleted message */
1162 first = i;
1163 /* where to start overwriting */
1164 offset = m->emails[i]->offset;
1165
1166 /* the offset stored in the header does not include the MMDF_SEP, so make
1167 * sure we seek to the correct location */
1168 if (m->type == MUTT_MMDF)
1169 offset -= (sizeof(MMDF_SEP) - 1);
1170
1171 /* allocate space for the new offsets */
1172 new_offset = MUTT_MEM_CALLOC(m->msg_count - first, struct MUpdate);
1173 old_offset = MUTT_MEM_CALLOC(m->msg_count - first, struct MUpdate);
1174
1175 if (m->verbose)
1176 {
1178 progress_set_message(progress, _("Writing %s..."), mailbox_path(m));
1179 }
1180
1181 for (i = first, j = 0; i < m->msg_count; i++)
1182 {
1183 progress_update(progress, i, i / (m->msg_count / 100 + 1));
1184 /* back up some information which is needed to restore offsets when
1185 * something fails. */
1186
1187 old_offset[i - first].valid = true;
1188 old_offset[i - first].hdr = m->emails[i]->offset;
1189 old_offset[i - first].body = m->emails[i]->body->offset;
1190 old_offset[i - first].lines = m->emails[i]->lines;
1191 old_offset[i - first].length = m->emails[i]->body->length;
1192
1193 if (!m->emails[i]->deleted)
1194 {
1195 j++;
1196
1197 if (m->type == MUTT_MMDF)
1198 {
1199 if (fputs(MMDF_SEP, fp) == EOF)
1200 {
1201 mutt_perror("%s", buf_string(tempfile));
1202 goto bail;
1203 }
1204 }
1205
1206 /* save the new offset for this message. we add 'offset' because the
1207 * temporary file only contains saved message which are located after
1208 * 'offset' in the real mailbox */
1209 new_offset[i - first].hdr = ftello(fp) + offset;
1210
1211 struct Message *msg = mx_msg_open(m, m->emails[i]);
1212 const int rc2 = mutt_copy_message(fp, m->emails[i], msg, MUTT_CM_UPDATE,
1214 mx_msg_close(m, &msg);
1215 if (rc2 != 0)
1216 {
1217 mutt_perror("%s", buf_string(tempfile));
1218 goto bail;
1219 }
1220
1221 /* Since messages could have been deleted, the offsets stored in memory
1222 * will be wrong, so update what we can, which is the offset of this
1223 * message, and the offset of the body. If this is a multipart message,
1224 * we just flush the in memory cache so that the message will be reparsed
1225 * if the user accesses it later. */
1226 new_offset[i - first].body = ftello(fp) - m->emails[i]->body->length + offset;
1227 mutt_body_free(&m->emails[i]->body->parts);
1228
1229 if (m->type == MUTT_MMDF)
1230 {
1231 if (fputs(MMDF_SEP, fp) == EOF)
1232 {
1233 mutt_perror("%s", buf_string(tempfile));
1234 goto bail;
1235 }
1236 }
1237 else
1238 {
1239 if (fputs("\n", fp) == EOF)
1240 {
1241 mutt_perror("%s", buf_string(tempfile));
1242 goto bail;
1243 }
1244 }
1245 }
1246 }
1247
1248 if (mutt_file_fclose(&fp) != 0)
1249 {
1250 mutt_debug(LL_DEBUG1, "mutt_file_fclose (&) returned non-zero\n");
1251 mutt_perror("%s", buf_string(tempfile));
1252 goto bail;
1253 }
1254
1255 /* Save the state of this folder. */
1256 if (stat(mailbox_path(m), &st) == -1)
1257 {
1258 mutt_perror("%s", mailbox_path(m));
1259 goto bail;
1260 }
1261
1262 unlink_tempfile = false;
1263
1264 fp = mutt_file_fopen(buf_string(tempfile), "r");
1265 if (!fp)
1266 {
1268 mx_fastclose_mailbox(m, false);
1269 mutt_debug(LL_DEBUG1, "unable to reopen temp copy of mailbox!\n");
1270 mutt_perror("%s", buf_string(tempfile));
1271 FREE(&new_offset);
1272 FREE(&old_offset);
1273 goto fatal;
1274 }
1275
1276 if (!mutt_file_seek(adata->fp, offset, SEEK_SET) || /* seek the append location */
1277 /* do a sanity check to make sure the mailbox looks ok */
1278 !fgets(buf, sizeof(buf), adata->fp) ||
1279 ((m->type == MUTT_MBOX) && !mutt_str_startswith(buf, "From ")) ||
1280 ((m->type == MUTT_MMDF) && !mutt_str_equal(MMDF_SEP, buf)))
1281 {
1282 mutt_debug(LL_DEBUG1, "message not in expected position\n");
1283 mutt_debug(LL_DEBUG1, " LINE: %s\n", buf);
1284 i = -1;
1285 }
1286 else
1287 {
1288 if (!mutt_file_seek(adata->fp, offset, SEEK_SET)) /* return to proper offset */
1289 {
1290 i = -1;
1291 }
1292 else
1293 {
1294 /* copy the temp mailbox back into place starting at the first
1295 * change/deleted message */
1296 if (m->verbose)
1297 mutt_message(_("Committing changes..."));
1298 i = mutt_file_copy_stream(fp, adata->fp);
1299
1300 if (ferror(adata->fp))
1301 i = -1;
1302 }
1303 if (i >= 0)
1304 {
1305 m->size = ftello(adata->fp); /* update the mailbox->size of the mailbox */
1306 if ((m->size < 0) || (ftruncate(fileno(adata->fp), m->size) != 0))
1307 {
1308 i = -1;
1309 mutt_debug(LL_DEBUG1, "ftruncate() failed\n");
1310 }
1311 }
1312 }
1313
1316
1317 if ((mutt_file_fclose(&adata->fp) != 0) || (i == -1))
1318 {
1319 /* error occurred while writing the mailbox back, so keep the temp copy around */
1320
1321 struct Buffer *savefile = buf_pool_get();
1322
1323 const char *const c_tmp_dir = cs_subset_path(NeoMutt->sub, "tmp_dir");
1324 buf_printf(savefile, "%s/neomutt.%s-%s-%u", NONULL(c_tmp_dir),
1325 NONULL(NeoMutt->username), NONULL(ShortHostname), (unsigned int) getpid());
1326 rename(buf_string(tempfile), buf_string(savefile));
1328 mx_fastclose_mailbox(m, false);
1329 pretty_mailbox(savefile);
1330 mutt_error(_("Write failed! Saved partial mailbox to %s"), buf_string(savefile));
1331 buf_pool_release(&savefile);
1332 FREE(&new_offset);
1333 FREE(&old_offset);
1334 goto fatal;
1335 }
1336
1337 /* Restore the previous access/modification times */
1338 mbox_reset_atime(m, &st);
1339
1340 /* reopen the mailbox in read-only mode */
1341 adata->fp = mbox_open_readwrite(m);
1342 if (!adata->fp)
1343 {
1344 adata->fp = mbox_open_readonly(m);
1345 }
1346 if (!adata->fp)
1347 {
1348 unlink(buf_string(tempfile));
1350 mx_fastclose_mailbox(m, false);
1351 mutt_error(_("Fatal error! Could not reopen mailbox!"));
1352 FREE(&new_offset);
1353 FREE(&old_offset);
1354 goto fatal;
1355 }
1356
1357 /* update the offsets of the rewritten messages */
1358 for (i = first, j = first; i < m->msg_count; i++)
1359 {
1360 if (!m->emails[i]->deleted)
1361 {
1362 m->emails[i]->offset = new_offset[i - first].hdr;
1363 m->emails[i]->body->hdr_offset = new_offset[i - first].hdr;
1364 m->emails[i]->body->offset = new_offset[i - first].body;
1365 m->emails[i]->index = j++;
1366 }
1367 }
1368 FREE(&new_offset);
1369 FREE(&old_offset);
1370 unlink(buf_string(tempfile)); /* remove partial copy of the mailbox */
1371 buf_pool_release(&tempfile);
1373
1374 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
1375 if (c_check_mbox_size)
1376 {
1377 struct Mailbox *m_tmp = mailbox_find(mailbox_path(m));
1378 if (m_tmp && !m_tmp->has_new)
1379 mailbox_update(m_tmp);
1380 }
1381
1382 progress_free(&progress);
1383 return 0; /* signal success */
1384
1385bail: /* Come here in case of disaster */
1386
1387 mutt_file_fclose(&fp);
1388
1389 if (tempfile && unlink_tempfile)
1390 unlink(buf_string(tempfile));
1391
1392 /* restore offsets, as far as they are valid */
1393 if ((first >= 0) && old_offset)
1394 {
1395 for (i = first; (i < m->msg_count) && old_offset[i - first].valid; i++)
1396 {
1397 m->emails[i]->offset = old_offset[i - first].hdr;
1398 m->emails[i]->body->hdr_offset = old_offset[i - first].hdr;
1399 m->emails[i]->body->offset = old_offset[i - first].body;
1400 m->emails[i]->lines = old_offset[i - first].lines;
1401 m->emails[i]->body->length = old_offset[i - first].length;
1402 }
1403 }
1404
1405 /* this is ok to call even if we haven't locked anything */
1407
1409 FREE(&new_offset);
1410 FREE(&old_offset);
1411
1412 adata->fp = freopen(mailbox_path(m), "r", adata->fp);
1413 if (!adata->fp)
1414 {
1415 mutt_error(_("Could not reopen mailbox"));
1416 mx_fastclose_mailbox(m, false);
1417 goto fatal;
1418 }
1419
1421 if (need_sort)
1422 {
1423 /* if the mailbox was reopened, the thread tree will be invalid so make
1424 * sure to start threading from scratch. */
1426 }
1427
1428fatal:
1429 buf_pool_release(&tempfile);
1430 progress_free(&progress);
1431 return rc;
1432}
1433
1437static enum MxStatus mbox_mbox_close(struct Mailbox *m)
1438{
1440 if (!adata)
1441 return MX_STATUS_ERROR;
1442
1443 if (!adata->fp)
1444 return MX_STATUS_OK;
1445
1446 if (adata->append)
1447 {
1448 mutt_file_unlock(fileno(adata->fp));
1450 }
1451
1452 mutt_file_fclose(&adata->fp);
1453
1454 /* fix up the times so mailbox won't get confused */
1455 if (m->peekonly && !buf_is_empty(&m->pathbuf) &&
1456 (mutt_file_timespec_compare(&adata->mtime, &adata->atime) > 0))
1457 {
1458#ifdef HAVE_UTIMENSAT
1459 struct timespec ts[2] = { { 0 }, { 0 } };
1460 ts[0] = adata->atime;
1461 ts[1] = adata->mtime;
1462 utimensat(AT_FDCWD, mailbox_path(m), ts, 0);
1463#else
1464 struct utimbuf ut = { 0 };
1465 ut.actime = adata->atime.tv_sec;
1466 ut.modtime = adata->mtime.tv_sec;
1467 utime(mailbox_path(m), &ut);
1468#endif
1469 }
1470
1471 return MX_STATUS_OK;
1472}
1473
1477static bool mbox_msg_open(struct Mailbox *m, struct Message *msg, struct Email *e)
1478{
1480 if (!adata)
1481 return false;
1482
1483 msg->fp = mutt_file_fopen(mailbox_path(m), "r");
1484 if (!msg->fp)
1485 return false;
1486
1487 return true;
1488}
1489
1493static bool mbox_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
1494{
1496 if (!adata)
1497 return false;
1498
1499 msg->fp = adata->fp;
1500 return true;
1501}
1502
1506static int mbox_msg_commit(struct Mailbox *m, struct Message *msg)
1507{
1508 if (fputc('\n', msg->fp) == EOF)
1509 return -1;
1510
1511 if ((fflush(msg->fp) == EOF) || (fsync(fileno(msg->fp)) == -1))
1512 {
1513 mutt_perror(_("Can't write message"));
1514 return -1;
1515 }
1516
1517 return 0;
1518}
1519
1523static int mbox_msg_close(struct Mailbox *m, struct Message *msg)
1524{
1525 if (msg->write)
1526 msg->fp = NULL;
1527 else
1528 mutt_file_fclose(&msg->fp);
1529
1530 return 0;
1531}
1532
1538static int mbox_msg_padding_size(struct Mailbox *m)
1539{
1540 return 1;
1541}
1542
1546enum MailboxType mbox_path_probe(const char *path, const struct stat *st)
1547{
1548 if (!st)
1549 return MUTT_UNKNOWN;
1550
1551 if (S_ISDIR(st->st_mode))
1552 return MUTT_UNKNOWN;
1553
1554 if (st->st_size == 0)
1555 return MUTT_MBOX;
1556
1557 FILE *fp = mutt_file_fopen(path, "r");
1558 if (!fp)
1559 return MUTT_UNKNOWN;
1560
1561 int ch;
1562 while ((ch = fgetc(fp)) != EOF)
1563 {
1564 /* Some mailbox creation tools erroneously append a blank line to
1565 * a file before appending a mail message. This allows neomutt to
1566 * detect type for and thus open those files. */
1567 if ((ch != '\n') && (ch != '\r'))
1568 {
1569 ungetc(ch, fp);
1570 break;
1571 }
1572 }
1573
1575 char tmp[256] = { 0 };
1576 if (fgets(tmp, sizeof(tmp), fp))
1577 {
1578 if (mutt_str_startswith(tmp, "From "))
1579 type = MUTT_MBOX;
1580 else if (mutt_str_equal(tmp, MMDF_SEP))
1581 type = MUTT_MMDF;
1582 }
1584
1585 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
1586 if (!c_check_mbox_size)
1587 {
1588 /* need to restore the times here, the file was not really accessed,
1589 * only the type was accessed. This is important, because detection
1590 * of "new mail" depends on those times set correctly. */
1591#ifdef HAVE_UTIMENSAT
1592 struct timespec ts[2] = { { 0 }, { 0 } };
1595 utimensat(AT_FDCWD, path, ts, 0);
1596#else
1597 struct utimbuf times = { 0 };
1598 times.actime = st->st_atime;
1599 times.modtime = st->st_mtime;
1600 utime(path, &times);
1601#endif
1602 }
1603
1604 return type;
1605}
1606
1610static int mbox_path_canon(struct Buffer *path)
1611{
1612 mutt_path_canon(path, NeoMutt->home_dir, false);
1613 return 0;
1614}
1615
1619static int mbox_path_is_empty(struct Buffer *path)
1620{
1621 return mutt_file_check_empty(buf_string(path));
1622}
1623
1627static int mmdf_msg_commit(struct Mailbox *m, struct Message *msg)
1628{
1629 if (fputs(MMDF_SEP, msg->fp) == EOF)
1630 return -1;
1631
1632 if ((fflush(msg->fp) == EOF) || (fsync(fileno(msg->fp)) == -1))
1633 {
1634 mutt_perror(_("Can't write message"));
1635 return -1;
1636 }
1637
1638 return 0;
1639}
1640
1646static int mmdf_msg_padding_size(struct Mailbox *m)
1647{
1648 return 10;
1649}
1650
1654static enum MxStatus mbox_mbox_check_stats(struct Mailbox *m, uint8_t flags)
1655{
1656 struct stat st = { 0 };
1657 if (stat(mailbox_path(m), &st) != 0)
1658 return MX_STATUS_ERROR;
1659
1660 bool new_or_changed;
1661
1662 const bool c_check_mbox_size = cs_subset_bool(NeoMutt->sub, "check_mbox_size");
1663 if (c_check_mbox_size)
1664 {
1665 new_or_changed = (st.st_size > m->size);
1666 }
1667 else
1668 {
1669 new_or_changed =
1671 (m->newly_created &&
1674 }
1675
1676 if (new_or_changed)
1677 {
1678 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
1679 if (!c_mail_check_recent ||
1681 {
1682 m->has_new = true;
1683 }
1684 }
1685 else if (c_check_mbox_size)
1686 {
1687 /* some other program has deleted mail from the folder */
1688 m->size = (off_t) st.st_size;
1689 }
1690
1691 if (m->newly_created && ((st.st_ctime != st.st_mtime) || (st.st_ctime != st.st_atime)))
1692 m->newly_created = false;
1693
1694 if (flags & MUTT_MAILBOX_CHECK_STATS)
1695 {
1698 &adata->stats_last_checked) > 0)
1699 {
1700 bool old_peek = m->peekonly;
1702 mx_mbox_close(m);
1703 m->peekonly = old_peek;
1704 mutt_time_now(&adata->stats_last_checked);
1705 }
1706 }
1707
1708 if (m->has_new || m->msg_new)
1709 return MX_STATUS_NEW_MAIL;
1710 return MX_STATUS_OK;
1711}
1712
1716const struct MxOps MxMboxOps = {
1717 // clang-format off
1718 .type = MUTT_MBOX,
1719 .name = "mbox",
1720 .is_local = true,
1721 .ac_owns_path = mbox_ac_owns_path,
1722 .ac_add = mbox_ac_add,
1723 .mbox_open = mbox_mbox_open,
1724 .mbox_open_append = mbox_mbox_open_append,
1725 .mbox_check = mbox_mbox_check,
1726 .mbox_check_stats = mbox_mbox_check_stats,
1727 .mbox_sync = mbox_mbox_sync,
1728 .mbox_close = mbox_mbox_close,
1729 .msg_open = mbox_msg_open,
1730 .msg_open_new = mbox_msg_open_new,
1731 .msg_commit = mbox_msg_commit,
1732 .msg_close = mbox_msg_close,
1733 .msg_padding_size = mbox_msg_padding_size,
1734 .msg_save_hcache = NULL,
1735 .tags_edit = NULL,
1736 .tags_commit = NULL,
1737 .path_probe = mbox_path_probe,
1738 .path_canon = mbox_path_canon,
1739 .path_is_empty = mbox_path_is_empty,
1740 // clang-format on
1741};
1742
1746const struct MxOps MxMmdfOps = {
1747 // clang-format off
1748 .type = MUTT_MMDF,
1749 .name = "mmdf",
1750 .is_local = true,
1751 .ac_owns_path = mbox_ac_owns_path,
1752 .ac_add = mbox_ac_add,
1753 .mbox_open = mbox_mbox_open,
1754 .mbox_open_append = mbox_mbox_open_append,
1755 .mbox_check = mbox_mbox_check,
1756 .mbox_check_stats = mbox_mbox_check_stats,
1757 .mbox_sync = mbox_mbox_sync,
1758 .mbox_close = mbox_mbox_close,
1759 .msg_open = mbox_msg_open,
1760 .msg_open_new = mbox_msg_open_new,
1761 .msg_commit = mmdf_msg_commit,
1762 .msg_close = mbox_msg_close,
1763 .msg_padding_size = mmdf_msg_padding_size,
1764 .msg_save_hcache = NULL,
1765 .tags_edit = NULL,
1766 .tags_commit = NULL,
1767 .path_probe = mbox_path_probe,
1768 .path_canon = mbox_path_canon,
1769 .path_is_empty = mbox_path_is_empty,
1770 // clang-format on
1771};
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:917
#define MUTT_CM_UPDATE
Update structs on sync.
Definition copy_email.h:42
#define CH_UPDATE
Update the status and x-status fields?
Definition copy_email.h:56
#define CH_FROM
Retain the "From " message separator?
Definition copy_email.h:60
#define CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition copy_email.h:66
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:180
@ NT_MAILBOX_INVALID
Email list was changed.
Definition mailbox.h:179
@ NT_MAILBOX_UPDATE
Update internal tables.
Definition mailbox.h:181
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition mailbox.h:213
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:1210
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:139
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
@ MUTT_STAT_CTIME
File/dir's ctime - creation time.
Definition file.h:55
@ MUTT_STAT_ATIME
File/dir's atime - last accessed time.
Definition file.h:53
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition file.h:54
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:794
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:779
const struct MxOps MxMboxOps
Mbox Mailbox - Implements MxOps -.
Definition mbox.c:1716
const struct MxOps MxMmdfOps
MMDF Mailbox - Implements MxOps -.
Definition mbox.c:1746
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:1654
static enum MxStatus mbox_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check() -.
Definition mbox.c:933
static enum MxStatus mbox_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close() -.
Definition mbox.c:1437
static bool mbox_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
Definition mbox.c:881
static enum MxOpenReturns mbox_mbox_open(struct Mailbox *m)
Open a Mailbox - Implements MxOps::mbox_open() -.
Definition mbox.c:832
static enum MxStatus mbox_mbox_sync(struct Mailbox *m)
Save changes to the Mailbox - Implements MxOps::mbox_sync() -.
Definition mbox.c:1067
static int mbox_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition mbox.c:1523
static int mbox_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition mbox.c:1506
static int mmdf_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition mbox.c:1627
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:1493
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:1477
static int mbox_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Implements MxOps::msg_padding_size() -.
Definition mbox.c:1538
static int mmdf_msg_padding_size(struct Mailbox *m)
Bytes of padding between messages - Implements MxOps::msg_padding_size() -.
Definition mbox.c:1646
static int mbox_path_canon(struct Buffer *path)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition mbox.c:1610
static int mbox_path_is_empty(struct Buffer *path)
Is the mailbox empty - Implements MxOps::path_is_empty() -.
Definition mbox.c:1619
enum MailboxType mbox_path_probe(const char *path, const struct stat *st)
Is this an mbox Mailbox?
Definition mbox.c:1546
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:730
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:806
static FILE * mbox_open_readonly(struct Mailbox *m)
Open an mbox read-only.
Definition mbox.c:821
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:751
static int reopen_mailbox(struct Mailbox *m)
Close and reopen a mailbox.
Definition mbox.c:552
#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.
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition mxapi.h:38
#define MUTT_MAILBOX_CHECK_STATS
Ignore mail_check_stats and calculate statistics (used by <check-stats>)
Definition mxapi.h:51
#define MUTT_QUIET
Do not print any messages.
Definition mxapi.h:43
MxOpenReturns
Return values for mbox_open()
Definition mxapi.h:72
@ MX_OPEN_ERROR
Open failed with an error.
Definition mxapi.h:74
@ MX_OPEN_ABORT
Open was aborted.
Definition mxapi.h:75
@ MX_OPEN_OK
Open succeeded.
Definition mxapi.h:73
#define MUTT_PEEK
Revert atime back after taking a look (if applicable)
Definition mxapi.h:44
#define MUTT_NOSORT
Do not sort the mailbox after opening it.
Definition mxapi.h:40
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_sync(), and mbox_close()
Definition mxapi.h:59
@ MX_STATUS_LOCKED
Couldn't lock the Mailbox.
Definition mxapi.h:63
@ MX_STATUS_ERROR
An error occurred.
Definition mxapi.h:60
@ MX_STATUS_OK
No changes.
Definition mxapi.h:61
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition mxapi.h:64
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition mxapi.h:62
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:78
int vcount
The number of virtual messages.
Definition mailbox.h:98
bool changed
Mailbox has been modified.
Definition mailbox.h:109
bool has_new
Mailbox has new mail.
Definition mailbox.h:84
int * v2r
Mapping from virtual to real msgno.
Definition mailbox.h:97
int msg_new
Number of new messages.
Definition mailbox.h:91
int msg_count
Total number of messages.
Definition mailbox.h:87
int email_max
Size of emails array.
Definition mailbox.h:96
enum MailboxType type
Mailbox type.
Definition mailbox.h:101
bool newly_created
Mbox or mmdf just popped into existence.
Definition mailbox.h:102
struct HashTable * subj_hash
Hash Table: "Subject" -> Email.
Definition mailbox.h:123
struct Email ** emails
Array of Emails.
Definition mailbox.h:95
struct HashTable * id_hash
Hash Table: "Message-ID" -> Email.
Definition mailbox.h:122
struct Buffer pathbuf
Path of the Mailbox.
Definition mailbox.h:79
bool peekonly
Just taking a glance, revert atime.
Definition mailbox.h:113
int msg_deleted
Number of deleted messages.
Definition mailbox.h:92
struct Account * account
Account that owns this Mailbox.
Definition mailbox.h:126
off_t size
Size of the Mailbox.
Definition mailbox.h:83
struct HashTable * label_hash
Hash Table: "X-Label" -> Email.
Definition mailbox.h:124
int msg_flagged
Number of flagged messages.
Definition mailbox.h:89
struct timespec last_visited
Time of last exit from this mailbox.
Definition mailbox.h:103
bool readonly
Don't allow changes to the mailbox.
Definition mailbox.h:115
int msg_tagged
How many messages are tagged?
Definition mailbox.h:93
bool verbose
Display status messages?
Definition mailbox.h:116
int msg_unread
Number of unread messages.
Definition mailbox.h:88
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:87
Container for Accounts, Notifications.
Definition neomutt.h:41
char * username
User's login name.
Definition neomutt.h:57
char * home_dir
User's home directory.
Definition neomutt.h:56
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