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