NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
maildir.c
Go to the documentation of this file.
1
34#include "config.h"
35#include <dirent.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <inttypes.h>
39#include <limits.h>
40#include <stdbool.h>
41#include <stdio.h>
42#include <string.h>
43#include <sys/stat.h>
44#include <unistd.h>
45#include <utime.h>
46#include "private.h"
47#include "mutt/lib.h"
48#include "config/lib.h"
49#include "email/lib.h"
50#include "core/lib.h"
51#include "lib.h"
52#include "progress/lib.h"
53#include "copy.h"
54#include "edata.h"
55#include "globals.h" // IWYU pragma: keep
56#include "mdata.h"
57#include "mdemail.h"
58#include "mx.h"
59#include "sort.h"
60#ifdef USE_INOTIFY
61#include "monitor.h"
62#endif
63#ifdef USE_HCACHE
64#include "hcache/lib.h"
65#endif
66#ifdef USE_NOTMUCH
67#include "notmuch/lib.h"
68#endif
69
70struct Progress;
71
72// Flags for maildir_check()
73#define MMC_NO_DIRS 0
74#define MMC_NEW_DIR (1 << 0)
75#define MMC_CUR_DIR (1 << 1)
76
86{
87 struct Email *e = email_new();
90
91 return e;
92}
93
103static void maildir_check_dir(struct Mailbox *m, const char *dir_name,
104 bool check_new, bool check_stats)
105{
106 DIR *dir = NULL;
107 struct dirent *de = NULL;
108 char *p = NULL;
109 struct stat st = { 0 };
110
111 struct Buffer *path = buf_pool_get();
112 struct Buffer *msgpath = buf_pool_get();
113 buf_printf(path, "%s/%s", mailbox_path(m), dir_name);
114
115 /* when $mail_check_recent is set, if the new/ directory hasn't been modified since
116 * the user last exited the mailbox, then we know there is no recent mail. */
117 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
118 if (check_new && c_mail_check_recent)
119 {
120 if ((stat(buf_string(path), &st) == 0) &&
122 {
123 check_new = false;
124 }
125 }
126
127 if (!(check_new || check_stats))
128 goto cleanup;
129
131 if (!dir)
132 {
133 m->type = MUTT_UNKNOWN;
134 goto cleanup;
135 }
136
137 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
138
139 char delimiter_version[8] = { 0 };
140 snprintf(delimiter_version, sizeof(delimiter_version), "%c2,", c_maildir_field_delimiter);
141 while ((de = readdir(dir)))
142 {
143 if (*de->d_name == '.')
144 continue;
145
146 p = strstr(de->d_name, delimiter_version);
147 if (p && strchr(p + 3, 'T'))
148 continue;
149
150 if (check_stats)
151 {
152 m->msg_count++;
153 if (p && strchr(p + 3, 'F'))
154 m->msg_flagged++;
155 }
156 if (!p || !strchr(p + 3, 'S'))
157 {
158 if (check_stats)
159 m->msg_unread++;
160 if (check_new)
161 {
162 if (c_mail_check_recent)
163 {
164 buf_printf(msgpath, "%s/%s", buf_string(path), de->d_name);
165 /* ensure this message was received since leaving this m */
166 if ((stat(buf_string(msgpath), &st) == 0) &&
168 {
169 continue;
170 }
171 }
172 m->has_new = true;
173 if (check_stats)
174 {
175 m->msg_new++;
176 }
177 else
178 {
179 break;
180 }
181 }
182 }
183 }
184
185 closedir(dir);
186
187cleanup:
188 buf_pool_release(&path);
189 buf_pool_release(&msgpath);
190}
191
195static int maildir_sort_flags(const void *a, const void *b, void *sdata)
196{
197 return mutt_numeric_cmp(*((const char *) a), *((const char *) b));
198}
199
206void maildir_gen_flags(char *dest, size_t destlen, struct Email *e)
207{
208 *dest = '\0';
209
210 const char *flags = NULL;
211
213 if (edata)
214 flags = edata->maildir_flags;
215
216 /* The maildir specification requires that all files in the cur
217 * subdirectory have the :unique string appended, regardless of whether
218 * or not there are any flags. If .old is set, we know that this message
219 * will end up in the cur directory, so we include it in the following
220 * test even though there is no associated flag. */
221
222 if (e->flagged || e->replied || e->read || e->deleted || e->old || flags)
223 {
224 char tmp[1024] = { 0 };
225 snprintf(tmp, sizeof(tmp), "%s%s%s%s%s", e->flagged ? "F" : "", e->replied ? "R" : "",
226 e->read ? "S" : "", e->deleted ? "T" : "", NONULL(flags));
227 if (flags)
228 mutt_qsort_r(tmp, strlen(tmp), 1, maildir_sort_flags, NULL);
229
230 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
231 snprintf(dest, destlen, "%c2,%s", c_maildir_field_delimiter, tmp);
232 }
233}
234
259static int maildir_commit_message(struct Mailbox *m, struct Message *msg, struct Email *e)
260{
261 char subdir[4] = { 0 };
262 char suffix[16] = { 0 };
263 int rc = 0;
264
265 if (mutt_file_fsync_close(&msg->fp))
266 {
267 mutt_perror(_("Could not flush message to disk"));
268 return -1;
269 }
270
271 /* extract the subdir */
272 char *s = strrchr(msg->path, '/') + 1;
273 mutt_str_copy(subdir, s, 4);
274
275 /* extract the flags */
276 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
277 s = strchr(s, c_maildir_field_delimiter);
278 if (s)
279 mutt_str_copy(suffix, s, sizeof(suffix));
280 else
281 suffix[0] = '\0';
282
283 /* construct a new file name. */
284 struct Buffer *path = buf_pool_get();
285 struct Buffer *full = buf_pool_get();
286 while (true)
287 {
288 buf_printf(path, "%s/%lld.R%" PRIu64 ".%s%s", subdir, (long long) mutt_date_now(),
289 mutt_rand64(), NONULL(ShortHostname), suffix);
290 buf_printf(full, "%s/%s", mailbox_path(m), buf_string(path));
291
292 mutt_debug(LL_DEBUG2, "renaming %s to %s\n", msg->path, buf_string(full));
293
294 if (mutt_file_safe_rename(msg->path, buf_string(full)) == 0)
295 {
296 /* Adjust the mtime on the file to match the time at which this
297 * message was received. Currently this is only set when copying
298 * messages between mailboxes, so we test to ensure that it is
299 * actually set. */
300 if (msg->received != 0)
301 {
302 struct utimbuf ut = { 0 };
303 int rc_utime;
304
305 ut.actime = msg->received;
306 ut.modtime = msg->received;
307 do
308 {
309 rc_utime = utime(buf_string(full), &ut);
310 } while ((rc_utime == -1) && (errno == EINTR));
311 if (rc_utime == -1)
312 {
313 mutt_perror(_("maildir_commit_message(): unable to set time on file"));
314 rc = -1;
315 goto cleanup;
316 }
317 }
318
319#ifdef USE_NOTMUCH
320 if (m->type == MUTT_NOTMUCH)
321 nm_update_filename(m, e->path, buf_string(full), e);
322#endif
323 if (e)
324 mutt_str_replace(&e->path, buf_string(path));
326 FREE(&msg->path);
327
328 goto cleanup;
329 }
330 else if (errno != EEXIST)
331 {
332 mutt_perror("%s", mailbox_path(m));
333 rc = -1;
334 goto cleanup;
335 }
336 }
337
338cleanup:
339 buf_pool_release(&path);
340 buf_pool_release(&full);
341
342 return rc;
343}
344
352static int maildir_rewrite_message(struct Mailbox *m, struct Email *e)
353{
354 if (!m || !e)
355 return -1;
356
357 bool restore = true;
358
359 long old_body_offset = e->body->offset;
360 long old_body_length = e->body->length;
361 long old_hdr_lines = e->lines;
362
363 struct Message *src = mx_msg_open(m, e);
364 struct Message *dest = mx_msg_open_new(m, e, MUTT_MSG_NO_FLAGS);
365 if (!src || !dest)
366 return -1;
367
368 int rc = mutt_copy_message(dest->fp, e, src, MUTT_CM_UPDATE, CH_UPDATE | CH_UPDATE_LEN, 0);
369 if (rc == 0)
370 {
371 char oldpath[PATH_MAX] = { 0 };
372 char partpath[PATH_MAX] = { 0 };
373 snprintf(oldpath, sizeof(oldpath), "%s/%s", mailbox_path(m), e->path);
374 mutt_str_copy(partpath, e->path, sizeof(partpath));
375
376 rc = maildir_commit_message(m, dest, e);
377
378 if (rc == 0)
379 {
380 unlink(oldpath);
381 restore = false;
382 }
383 }
384 mx_msg_close(m, &src);
385 mx_msg_close(m, &dest);
386
387 if ((rc == -1) && restore)
388 {
389 e->body->offset = old_body_offset;
390 e->body->length = old_body_length;
391 e->lines = old_hdr_lines;
392 }
393
395 return rc;
396}
397
405static int maildir_sync_message(struct Mailbox *m, struct Email *e)
406{
407 if (!m || !e)
408 return -1;
409
410 struct Buffer *newpath = NULL;
411 struct Buffer *partpath = NULL;
412 struct Buffer *fullpath = NULL;
413 struct Buffer *oldpath = NULL;
414 char suffix[16] = { 0 };
415 int rc = 0;
416
417 if (e->attach_del || e->env->changed)
418 {
419 /* when doing attachment deletion/rethreading, fall back to the MH case. */
420 if (maildir_rewrite_message(m, e) != 0)
421 return -1;
422 e->env->changed = false;
423 }
424 else
425 {
426 /* we just have to rename the file. */
427
428 char *p = strrchr(e->path, '/');
429 if (!p)
430 {
431 mutt_debug(LL_DEBUG1, "%s: unable to find subdir!\n", e->path);
432 return -1;
433 }
434 p++;
435 newpath = buf_pool_get();
436 partpath = buf_pool_get();
437 fullpath = buf_pool_get();
438 oldpath = buf_pool_get();
439
440 buf_strcpy(newpath, p);
441
442 /* kill the previous flags */
443 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
444 p = strchr(newpath->data, c_maildir_field_delimiter);
445 if (p)
446 {
447 *p = '\0';
448 newpath->dptr = p; /* fix buffer up, just to be safe */
449 }
450
451 maildir_gen_flags(suffix, sizeof(suffix), e);
452
453 buf_printf(partpath, "%s/%s%s", (e->read || e->old) ? "cur" : "new",
454 buf_string(newpath), suffix);
455 buf_printf(fullpath, "%s/%s", mailbox_path(m), buf_string(partpath));
456 buf_printf(oldpath, "%s/%s", mailbox_path(m), e->path);
457
458 if (mutt_str_equal(buf_string(fullpath), buf_string(oldpath)))
459 {
460 /* message hasn't really changed */
461 goto cleanup;
462 }
463
464 /* record that the message is possibly marked as trashed on disk */
465 e->trash = e->deleted;
466
467 struct stat st = { 0 };
468 if (stat(buf_string(oldpath), &st) == -1)
469 {
470 mutt_debug(LL_DEBUG1, "File already removed (just continuing)");
471 goto cleanup;
472 }
473
474 if (rename(buf_string(oldpath), buf_string(fullpath)) != 0)
475 {
476 mutt_perror("rename");
477 rc = -1;
478 goto cleanup;
479 }
480 mutt_str_replace(&e->path, buf_string(partpath));
481 }
482
483cleanup:
484 buf_pool_release(&newpath);
485 buf_pool_release(&partpath);
486 buf_pool_release(&fullpath);
487 buf_pool_release(&oldpath);
488
489 return rc;
490}
491
496static void maildir_update_mtime(struct Mailbox *m)
497{
498 char buf[PATH_MAX] = { 0 };
499 struct stat st = { 0 };
501
502 snprintf(buf, sizeof(buf), "%s/%s", mailbox_path(m), "cur");
503 if (stat(buf, &st) == 0)
505
506 snprintf(buf, sizeof(buf), "%s/%s", mailbox_path(m), "new");
507 if (stat(buf, &st) == 0)
509}
510
514static int maildir_sort_inode(const void *a, const void *b, void *sdata)
515{
516 const struct MdEmail *ma = *(struct MdEmail **) a;
517 const struct MdEmail *mb = *(struct MdEmail **) b;
518
519 return mutt_numeric_cmp(ma->inode, mb->inode);
520}
521
532static int maildir_parse_dir(struct Mailbox *m, struct MdEmailArray *mda,
533 const char *subdir, struct Progress *progress)
534{
535 struct dirent *de = NULL;
536 int rc = 0;
537 bool is_old = false;
538 struct MdEmail *entry = NULL;
539 struct Email *e = NULL;
540
541 struct Buffer *buf = buf_pool_get();
542
543 buf_printf(buf, "%s/%s", mailbox_path(m), subdir);
544 is_old = mutt_str_equal("cur", subdir);
545
547 if (!dir)
548 {
549 rc = -1;
550 goto cleanup;
551 }
552
553 while (((de = readdir(dir))) && !SigInt)
554 {
555 if (*de->d_name == '.')
556 continue;
557
558 mutt_debug(LL_DEBUG2, "queueing %s\n", de->d_name);
559
560 e = maildir_email_new();
561 e->old = is_old;
562 maildir_parse_flags(e, de->d_name);
563
564 progress_update(progress, ARRAY_SIZE(mda) + 1, -1);
565
566 buf_printf(buf, "%s/%s", subdir, de->d_name);
567 e->path = buf_strdup(buf);
568
569 entry = maildir_entry_new();
570 entry->email = e;
571 entry->inode = de->d_ino;
572 ARRAY_ADD(mda, entry);
573 }
574
575 closedir(dir);
576
577 if (SigInt)
578 {
579 SigInt = false;
580 return -2; /* action aborted */
581 }
582
583 ARRAY_SORT(mda, maildir_sort_inode, NULL);
584
585cleanup:
586 buf_pool_release(&buf);
587
588 return rc;
589}
590
591#ifdef USE_HCACHE
599static size_t maildir_hcache_keylen(const char *fn)
600{
601 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
602 const char *p = strrchr(fn, c_maildir_field_delimiter);
603 return p ? (size_t) (p - fn) : mutt_str_len(fn);
604}
605#endif
606
613static void maildir_delayed_parsing(struct Mailbox *m, struct MdEmailArray *mda,
614 struct Progress *progress)
615{
616 char fn[PATH_MAX] = { 0 };
617
618#ifdef USE_HCACHE
619 const char *const c_header_cache = cs_subset_path(NeoMutt->sub, "header_cache");
620 struct HeaderCache *hc = hcache_open(c_header_cache, mailbox_path(m), NULL);
621 const bool c_maildir_header_cache_verify = cs_subset_bool(NeoMutt->sub, "maildir_header_cache_verify");
622#endif
623
624 struct MdEmail *md = NULL;
625 struct MdEmail **mdp = NULL;
626 ARRAY_FOREACH(mdp, mda)
627 {
628 md = *mdp;
629 if (!md || !md->email || md->header_parsed)
630 continue;
631
632 progress_update(progress, ARRAY_FOREACH_IDX, -1);
633
634 snprintf(fn, sizeof(fn), "%s/%s", mailbox_path(m), md->email->path);
635
636#ifdef USE_HCACHE
637 struct stat st_lastchanged = { 0 };
638 int rc = 0;
639
640 const char *key = md->email->path + 3;
641 size_t keylen = maildir_hcache_keylen(key);
642 struct HCacheEntry hce = { 0 };
643
644 if (hc)
645 {
646 hce = hcache_fetch(hc, key, keylen, 0);
647 }
648
649 if (hce.email && c_maildir_header_cache_verify)
650 {
651 rc = stat(fn, &st_lastchanged);
652 }
653
654 if (hce.email && (rc == 0) && (st_lastchanged.st_mtime <= hce.uidvalidity))
655 {
658 hce.email->old = md->email->old;
659 hce.email->path = mutt_str_dup(md->email->path);
660 email_free(&md->email);
661 md->email = hce.email;
662 maildir_parse_flags(md->email, fn);
663 }
664 else
665#endif
666 {
667 if (maildir_parse_message(m->type, fn, md->email->old, md->email))
668 {
669 md->header_parsed = true;
670#ifdef USE_HCACHE
671 key = md->email->path + 3;
672 keylen = maildir_hcache_keylen(key);
673 hcache_store(hc, key, keylen, md->email, 0);
674#endif
675 }
676 else
677 {
678 email_free(&md->email);
679 }
680 }
681 }
682#ifdef USE_HCACHE
683 hcache_close(&hc);
684#endif
685}
686
694static int maildir_read_dir(struct Mailbox *m, const char *subdir)
695{
696 if (!m)
697 return -1;
698
699 struct Progress *progress = NULL;
700
701 if (m->verbose)
702 {
703 char msg[PATH_MAX] = { 0 };
704 snprintf(msg, sizeof(msg), _("Scanning %s..."), mailbox_path(m));
705 progress = progress_new(msg, MUTT_PROGRESS_READ, 0);
706 }
707
709 if (!mdata)
710 {
712 m->mdata = mdata;
714 }
715
716 struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
717 int rc = maildir_parse_dir(m, &mda, subdir, progress);
718 progress_free(&progress);
719 if (rc < 0)
720 return -1;
721
722 if (m->verbose)
723 {
724 char msg[PATH_MAX] = { 0 };
725 snprintf(msg, sizeof(msg), _("Reading %s..."), mailbox_path(m));
726 progress = progress_new(msg, MUTT_PROGRESS_READ, ARRAY_SIZE(&mda));
727 }
728 maildir_delayed_parsing(m, &mda, progress);
729 progress_free(&progress);
730
732 maildirarray_clear(&mda);
733
734 if (!mdata->mh_umask)
735 mdata->mh_umask = mh_umask(m);
736
737 return 0;
738}
739
750static void maildir_canon_filename(struct Buffer *dest, const char *src)
751{
752 if (!dest || !src)
753 return;
754
755 char *t = strrchr(src, '/');
756 if (t)
757 src = t + 1;
758
759 buf_strcpy(dest, src);
760
761 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
762
763 char searchable_bytes[8] = { 0 };
764 snprintf(searchable_bytes, sizeof(searchable_bytes), ",%c", c_maildir_field_delimiter);
765 char *u = strpbrk(dest->data, searchable_bytes);
766
767 if (u)
768 {
769 *u = '\0';
770 dest->dptr = u;
771 }
772}
773
786static FILE *maildir_open_find_message_dir(const char *folder, const char *unique,
787 const char *subfolder, char **newname)
788{
789 struct Buffer *dirname = buf_pool_get();
790 struct Buffer *tunique = buf_pool_get();
791 struct Buffer *fname = buf_pool_get();
792
793 struct dirent *de = NULL;
794
795 FILE *fp = NULL;
796 int oe = ENOENT;
797
798 buf_printf(dirname, "%s/%s", folder, subfolder);
799
801 if (!dir)
802 {
803 errno = ENOENT;
804 goto cleanup;
805 }
806
807 while ((de = readdir(dir)))
808 {
809 maildir_canon_filename(tunique, de->d_name);
810
811 if (mutt_str_equal(buf_string(tunique), unique))
812 {
813 buf_printf(fname, "%s/%s/%s", folder, subfolder, de->d_name);
814 fp = fopen(buf_string(fname), "r");
815 oe = errno;
816 break;
817 }
818 }
819
820 closedir(dir);
821
822 if (newname && fp)
823 *newname = buf_strdup(fname);
824
825 errno = oe;
826
827cleanup:
828 buf_pool_release(&dirname);
829 buf_pool_release(&tunique);
830 buf_pool_release(&fname);
831
832 return fp;
833}
834
840void maildir_parse_flags(struct Email *e, const char *path)
841{
842 char *q = NULL;
843
844 e->flagged = false;
845 e->read = false;
846 e->replied = false;
847
849
850 const char c_maildir_field_delimiter = *cc_maildir_field_delimiter();
851 char *p = strrchr(path, c_maildir_field_delimiter);
852 if (p && mutt_str_startswith(p + 1, "2,"))
853 {
854 p += 3;
855
856 mutt_str_replace(&edata->maildir_flags, p);
857 q = edata->maildir_flags;
858
859 while (*p)
860 {
861 switch (*p)
862 {
863 case 'F': // Flagged
864 e->flagged = true;
865 break;
866
867 case 'R': // Replied
868 e->replied = true;
869 break;
870
871 case 'S': // Seen
872 e->read = true;
873 break;
874
875 case 'T': // Trashed
876 {
877 const bool c_flag_safe = cs_subset_bool(NeoMutt->sub, "flag_safe");
878 if (!e->flagged || !c_flag_safe)
879 {
880 e->trash = true;
881 e->deleted = true;
882 }
883 break;
884 }
885
886 default:
887 *q++ = *p;
888 break;
889 }
890 p++;
891 }
892 }
893
894 if (q == edata->maildir_flags)
895 FREE(&edata->maildir_flags);
896 else if (q)
897 *q = '\0';
898}
899
912bool maildir_parse_stream(enum MailboxType type, FILE *fp, const char *fname,
913 bool is_old, struct Email *e)
914{
915 if (!fp || !fname || !e)
916 return false;
917
918 const long size = mutt_file_get_size_fp(fp);
919 if (size == 0)
920 return false;
921
922 e->env = mutt_rfc822_read_header(fp, e, false, false);
923
924 if (e->received == 0)
925 e->received = e->date_sent;
926
927 /* always update the length since we have fresh information available. */
928 e->body->length = size - e->body->offset;
929
930 e->index = -1;
931
932 if (type == MUTT_MAILDIR)
933 {
934 /* maildir stores its flags in the filename, so ignore the
935 * flags in the header of the message */
936
937 e->old = is_old;
938 maildir_parse_flags(e, fname);
939 }
940 return e;
941}
942
954bool maildir_parse_message(enum MailboxType type, const char *fname,
955 bool is_old, struct Email *e)
956{
957 if (!fname || !e)
958 return false;
959
960 FILE *fp = fopen(fname, "r");
961 if (!fp)
962 return false;
963
964 bool rc = maildir_parse_stream(type, fp, fname, is_old, e);
965 mutt_file_fclose(&fp);
966 return rc;
967}
968
977bool maildir_sync_mailbox_message(struct Mailbox *m, struct Email *e, struct HeaderCache *hc)
978{
979 if (!e)
980 return false;
981
982 const bool c_maildir_trash = cs_subset_bool(NeoMutt->sub, "maildir_trash");
983 if (e->deleted && !c_maildir_trash)
984 {
985 char path[PATH_MAX] = { 0 };
986 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), e->path);
987#ifdef USE_HCACHE
988 if (hc)
989 {
990 const char *key = e->path + 3;
991 size_t keylen = maildir_hcache_keylen(key);
992 hcache_delete_record(hc, key, keylen);
993 }
994#endif
995 unlink(path);
996 }
997 else if (e->changed || e->attach_del ||
998 ((c_maildir_trash || e->trash) && (e->deleted != e->trash)))
999 {
1000 if (maildir_sync_message(m, e) == -1)
1001 return false;
1002 }
1003
1004#ifdef USE_HCACHE
1005 if (hc && e->changed)
1006 {
1007 const char *key = e->path + 3;
1008 size_t keylen = maildir_hcache_keylen(key);
1009 hcache_store(hc, key, keylen, e, 0);
1010 }
1011#endif
1012
1013 return true;
1014}
1015
1023FILE *maildir_open_find_message(const char *folder, const char *msg, char **newname)
1024{
1025 static unsigned int new_hits = 0, cur_hits = 0; /* simple dynamic optimization */
1026
1027 struct Buffer *unique = buf_pool_get();
1028 maildir_canon_filename(unique, msg);
1029
1030 FILE *fp = maildir_open_find_message_dir(folder, buf_string(unique),
1031 (new_hits > cur_hits) ? "new" : "cur", newname);
1032 if (fp || (errno != ENOENT))
1033 {
1034 if ((new_hits < UINT_MAX) && (cur_hits < UINT_MAX))
1035 {
1036 new_hits += ((new_hits > cur_hits) ? 1 : 0);
1037 cur_hits += ((new_hits > cur_hits) ? 0 : 1);
1038 }
1039
1040 goto cleanup;
1041 }
1042 fp = maildir_open_find_message_dir(folder, buf_string(unique),
1043 (new_hits > cur_hits) ? "cur" : "new", newname);
1044 if (fp || (errno != ENOENT))
1045 {
1046 if ((new_hits < UINT_MAX) && (cur_hits < UINT_MAX))
1047 {
1048 new_hits += ((new_hits > cur_hits) ? 0 : 1);
1049 cur_hits += ((new_hits > cur_hits) ? 1 : 0);
1050 }
1051
1052 goto cleanup;
1053 }
1054
1055 fp = NULL;
1056
1057cleanup:
1058 buf_pool_release(&unique);
1059
1060 return fp;
1061}
1062
1071{
1072 DIR *dir = NULL;
1073 struct dirent *de = NULL;
1074 int rc = 1; /* assume empty until we find a message */
1075 char realpath[PATH_MAX] = { 0 };
1076 int iter = 0;
1077
1078 /* Strategy here is to look for any file not beginning with a period */
1079
1080 do
1081 {
1082 /* we do "cur" on the first iteration since it's more likely that we'll
1083 * find old messages without having to scan both subdirs */
1084 snprintf(realpath, sizeof(realpath), "%s/%s", buf_string(path),
1085 (iter == 0) ? "cur" : "new");
1086 dir = mutt_file_opendir(realpath, MUTT_OPENDIR_CREATE);
1087 if (!dir)
1088 return -1;
1089 while ((de = readdir(dir)))
1090 {
1091 if (*de->d_name != '.')
1092 {
1093 rc = 0;
1094 break;
1095 }
1096 }
1097 closedir(dir);
1098 iter++;
1099 } while (rc && iter < 2);
1100
1101 return rc;
1102}
1103
1107static bool maildir_ac_owns_path(struct Account *a, const char *path)
1108{
1109 return true;
1110}
1111
1115static bool maildir_ac_add(struct Account *a, struct Mailbox *m)
1116{
1117 return true;
1118}
1119
1124{
1125 /* maildir looks sort of like MH, except that there are two subdirectories
1126 * of the main folder path from which to read messages */
1127 if ((maildir_read_dir(m, "new") == -1) || (maildir_read_dir(m, "cur") == -1))
1128 return MX_OPEN_ERROR;
1129
1130 return MX_OPEN_OK;
1131}
1132
1137{
1138 if (!(flags & (MUTT_APPEND | MUTT_APPENDNEW | MUTT_NEWFOLDER)))
1139 {
1140 return true;
1141 }
1142
1143 errno = 0;
1144 if ((mutt_file_mkdir(mailbox_path(m), S_IRWXU) != 0) && (errno != EEXIST))
1145 {
1146 mutt_perror("%s", mailbox_path(m));
1147 return false;
1148 }
1149
1150 char tmp[PATH_MAX] = { 0 };
1151 snprintf(tmp, sizeof(tmp), "%s/cur", mailbox_path(m));
1152 errno = 0;
1153 if ((mkdir(tmp, S_IRWXU) != 0) && (errno != EEXIST))
1154 {
1155 mutt_perror("%s", tmp);
1156 rmdir(mailbox_path(m));
1157 return false;
1158 }
1159
1160 snprintf(tmp, sizeof(tmp), "%s/new", mailbox_path(m));
1161 errno = 0;
1162 if ((mkdir(tmp, S_IRWXU) != 0) && (errno != EEXIST))
1163 {
1164 mutt_perror("%s", tmp);
1165 snprintf(tmp, sizeof(tmp), "%s/cur", mailbox_path(m));
1166 rmdir(tmp);
1167 rmdir(mailbox_path(m));
1168 return false;
1169 }
1170
1171 snprintf(tmp, sizeof(tmp), "%s/tmp", mailbox_path(m));
1172 errno = 0;
1173 if ((mkdir(tmp, S_IRWXU) != 0) && (errno != EEXIST))
1174 {
1175 mutt_perror("%s", tmp);
1176 snprintf(tmp, sizeof(tmp), "%s/cur", mailbox_path(m));
1177 rmdir(tmp);
1178 snprintf(tmp, sizeof(tmp), "%s/new", mailbox_path(m));
1179 rmdir(tmp);
1180 rmdir(mailbox_path(m));
1181 return false;
1182 }
1183
1184 return true;
1185}
1186
1199static enum MxStatus maildir_check(struct Mailbox *m)
1200{
1201 struct stat st_new = { 0 }; /* status of the "new" subdirectory */
1202 struct stat st_cur = { 0 }; /* status of the "cur" subdirectory */
1203 int changed = MMC_NO_DIRS; /* which subdirectories have changed */
1204 bool occult = false; /* messages were removed from the mailbox */
1205 int num_new = 0; /* number of new messages added to the mailbox */
1206 bool flags_changed = false; /* message flags were changed in the mailbox */
1207 struct HashTable *hash_names = NULL; // Hash Table: "base-filename" -> MdEmail
1209
1210 /* XXX seems like this check belongs in mx_mbox_check() rather than here. */
1211 const bool c_check_new = cs_subset_bool(NeoMutt->sub, "check_new");
1212 if (!c_check_new)
1213 return MX_STATUS_OK;
1214
1215 struct Buffer *buf = buf_pool_get();
1216 buf_printf(buf, "%s/new", mailbox_path(m));
1217 if (stat(buf_string(buf), &st_new) == -1)
1218 {
1219 buf_pool_release(&buf);
1220 return MX_STATUS_ERROR;
1221 }
1222
1223 buf_printf(buf, "%s/cur", mailbox_path(m));
1224 if (stat(buf_string(buf), &st_cur) == -1)
1225 {
1226 buf_pool_release(&buf);
1227 return MX_STATUS_ERROR;
1228 }
1229
1230 /* determine which subdirectories need to be scanned */
1231 if (mutt_file_stat_timespec_compare(&st_new, MUTT_STAT_MTIME, &mdata->mtime) > 0)
1232 changed = MMC_NEW_DIR;
1234 changed |= MMC_CUR_DIR;
1235
1236 if (changed == MMC_NO_DIRS)
1237 {
1238 buf_pool_release(&buf);
1239 return MX_STATUS_OK; /* nothing to do */
1240 }
1241
1242 /* Update the modification times on the mailbox.
1243 *
1244 * The monitor code notices changes in the open mailbox too quickly.
1245 * In practice, this sometimes leads to all the new messages not being
1246 * noticed during the SAME group of mtime stat updates. To work around
1247 * the problem, don't update the stat times for a monitor caused check. */
1248#ifdef USE_INOTIFY
1250 {
1251 MonitorContextChanged = false;
1252 }
1253 else
1254#endif
1255 {
1258 }
1259
1260 /* do a fast scan of just the filenames in
1261 * the subdirectories that have changed. */
1262 struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
1263 if (changed & MMC_NEW_DIR)
1264 maildir_parse_dir(m, &mda, "new", NULL);
1265 if (changed & MMC_CUR_DIR)
1266 maildir_parse_dir(m, &mda, "cur", NULL);
1267
1268 /* we create a hash table keyed off the canonical (sans flags) filename
1269 * of each message we scanned. This is used in the loop over the
1270 * existing messages below to do some correlation. */
1271 hash_names = mutt_hash_new(ARRAY_SIZE(&mda), MUTT_HASH_NO_FLAGS);
1272
1273 struct MdEmail *md = NULL;
1274 struct MdEmail **mdp = NULL;
1275 ARRAY_FOREACH(mdp, &mda)
1276 {
1277 md = *mdp;
1279 md->canon_fname = buf_strdup(buf);
1280 mutt_hash_insert(hash_names, md->canon_fname, md);
1281 }
1282
1283 /* check for modifications and adjust flags */
1284 for (int i = 0; i < m->msg_count; i++)
1285 {
1286 struct Email *e = m->emails[i];
1287 if (!e)
1288 break;
1289
1291 md = mutt_hash_find(hash_names, buf_string(buf));
1292 if (md && md->email)
1293 {
1294 /* message already exists, merge flags */
1295
1296 /* check to see if the message has moved to a different
1297 * subdirectory. If so, update the associated filename. */
1298 if (!mutt_str_equal(e->path, md->email->path))
1299 mutt_str_replace(&e->path, md->email->path);
1300
1301 /* if the user hasn't modified the flags on this message, update
1302 * the flags we just detected. */
1303 if (!e->changed)
1304 if (maildir_update_flags(m, e, md->email))
1305 flags_changed = true;
1306
1307 if (e->deleted == e->trash)
1308 {
1309 if (e->deleted != md->email->deleted)
1310 {
1311 e->deleted = md->email->deleted;
1312 flags_changed = true;
1313 }
1314 }
1315 e->trash = md->email->trash;
1316
1317 /* this is a duplicate of an existing email, so remove it */
1318 email_free(&md->email);
1319 }
1320 /* This message was not in the list of messages we just scanned.
1321 * Check to see if we have enough information to know if the
1322 * message has disappeared out from underneath us. */
1323 else if (((changed & MMC_NEW_DIR) && mutt_strn_equal(e->path, "new/", 4)) ||
1324 ((changed & MMC_CUR_DIR) && mutt_strn_equal(e->path, "cur/", 4)))
1325 {
1326 /* This message disappeared, so we need to simulate a "reopen"
1327 * event. We know it disappeared because we just scanned the
1328 * subdirectory it used to reside in. */
1329 occult = true;
1330 e->deleted = true;
1331 e->purge = true;
1332 }
1333 else
1334 {
1335 /* This message resides in a subdirectory which was not
1336 * modified, so we assume that it is still present and
1337 * unchanged. */
1338 }
1339 }
1340
1341 /* destroy the file name hash */
1342 mutt_hash_free(&hash_names);
1343
1344 /* If we didn't just get new mail, update the tables. */
1345 if (occult)
1347
1348 /* do any delayed parsing we need to do. */
1349 maildir_delayed_parsing(m, &mda, NULL);
1350
1351 /* Incorporate new messages */
1352 num_new = maildir_move_to_mailbox(m, &mda);
1353 maildirarray_clear(&mda);
1354
1355 if (num_new > 0)
1356 {
1358 m->changed = true;
1359 }
1360
1361 buf_pool_release(&buf);
1362
1363 ARRAY_FREE(&mda);
1364 if (occult)
1365 return MX_STATUS_REOPENED;
1366 if (num_new > 0)
1367 return MX_STATUS_NEW_MAIL;
1368 if (flags_changed)
1369 return MX_STATUS_FLAGS;
1370 return MX_STATUS_OK;
1371}
1372
1377{
1378 return maildir_check(m);
1379}
1380
1384static enum MxStatus maildir_mbox_check_stats(struct Mailbox *m, uint8_t flags)
1385{
1386 bool check_stats = flags & MUTT_MAILBOX_CHECK_FORCE_STATS;
1387 bool check_new = true;
1388
1389 if (check_stats)
1390 {
1391 m->msg_new = 0;
1392 m->msg_count = 0;
1393 m->msg_unread = 0;
1394 m->msg_flagged = 0;
1395 }
1396
1397 maildir_check_dir(m, "new", check_new, check_stats);
1398
1399 const bool c_maildir_check_cur = cs_subset_bool(NeoMutt->sub, "maildir_check_cur");
1400 check_new = !m->has_new && c_maildir_check_cur;
1401 if (check_new || check_stats)
1402 maildir_check_dir(m, "cur", check_new, check_stats);
1403
1405}
1406
1413static enum MxStatus maildir_mbox_sync(struct Mailbox *m)
1414{
1415 enum MxStatus check = maildir_check(m);
1416 if (check == MX_STATUS_ERROR)
1417 return check;
1418
1419 struct HeaderCache *hc = NULL;
1420#ifdef USE_HCACHE
1421 const char *const c_header_cache = cs_subset_path(NeoMutt->sub, "header_cache");
1422 if (m->type == MUTT_MAILDIR)
1423 hc = hcache_open(c_header_cache, mailbox_path(m), NULL);
1424#endif
1425
1426 struct Progress *progress = NULL;
1427 if (m->verbose)
1428 {
1429 char msg[PATH_MAX] = { 0 };
1430 snprintf(msg, sizeof(msg), _("Writing %s..."), mailbox_path(m));
1431 progress = progress_new(msg, MUTT_PROGRESS_WRITE, m->msg_count);
1432 }
1433
1434 for (int i = 0; i < m->msg_count; i++)
1435 {
1436 progress_update(progress, i, -1);
1437
1438 struct Email *e = m->emails[i];
1439 if (!maildir_sync_mailbox_message(m, e, hc))
1440 {
1441 progress_free(&progress);
1442 goto err;
1443 }
1444 }
1445 progress_free(&progress);
1446
1447#ifdef USE_HCACHE
1448 if (m->type == MUTT_MAILDIR)
1449 hcache_close(&hc);
1450#endif
1451
1452 /* XXX race condition? */
1453
1455
1456 /* adjust indices */
1457
1458 if (m->msg_deleted)
1459 {
1460 const bool c_maildir_trash = cs_subset_bool(NeoMutt->sub, "maildir_trash");
1461 for (int i = 0, j = 0; i < m->msg_count; i++)
1462 {
1463 struct Email *e = m->emails[i];
1464 if (!e)
1465 break;
1466
1467 if (!e->deleted || c_maildir_trash)
1468 e->index = j++;
1469 }
1470 }
1471
1472 return check;
1473
1474err:
1475#ifdef USE_HCACHE
1476 if (m->type == MUTT_MAILDIR)
1477 hcache_close(&hc);
1478#endif
1479 return MX_STATUS_ERROR;
1480}
1481
1487{
1488 return MX_STATUS_OK;
1489}
1490
1494static bool maildir_msg_open(struct Mailbox *m, struct Message *msg, struct Email *e)
1495{
1496 char path[PATH_MAX] = { 0 };
1497
1498 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), e->path);
1499
1500 msg->fp = fopen(path, "r");
1501 if (!msg->fp && (errno == ENOENT))
1502 msg->fp = maildir_open_find_message(mailbox_path(m), e->path, NULL);
1503
1504 if (!msg->fp)
1505 {
1506 mutt_perror("%s", path);
1507 mutt_debug(LL_DEBUG1, "fopen: %s: %s (errno %d)\n", path, strerror(errno), errno);
1508 return false;
1509 }
1510
1511 return true;
1512}
1513
1522bool maildir_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
1523{
1524 int fd;
1525 char path[PATH_MAX] = { 0 };
1526 char suffix[16] = { 0 };
1527 char subdir[16] = { 0 };
1528
1529 if (e)
1530 {
1531 struct Email tmp = *e;
1532 tmp.deleted = false;
1533 tmp.edata = NULL;
1534 maildir_gen_flags(suffix, sizeof(suffix), &tmp);
1535 }
1536 else
1537 {
1538 *suffix = '\0';
1539 }
1540
1541 if (e && (e->read || e->old))
1542 mutt_str_copy(subdir, "cur", sizeof(subdir));
1543 else
1544 mutt_str_copy(subdir, "new", sizeof(subdir));
1545
1546 mode_t omask = umask(mh_umask(m));
1547 while (true)
1548 {
1549 snprintf(path, sizeof(path), "%s/tmp/%s.%lld.R%" PRIu64 ".%s%s",
1550 mailbox_path(m), subdir, (long long) mutt_date_now(),
1551 mutt_rand64(), NONULL(ShortHostname), suffix);
1552
1553 mutt_debug(LL_DEBUG2, "Trying %s\n", path);
1554
1555 fd = open(path, O_WRONLY | O_EXCL | O_CREAT, 0666);
1556 if (fd == -1)
1557 {
1558 if (errno != EEXIST)
1559 {
1560 umask(omask);
1561 mutt_perror("%s", path);
1562 return false;
1563 }
1564 }
1565 else
1566 {
1567 mutt_debug(LL_DEBUG2, "Success\n");
1568 msg->path = mutt_str_dup(path);
1569 break;
1570 }
1571 }
1572 umask(omask);
1573
1574 msg->fp = fdopen(fd, "w");
1575 if (!msg->fp)
1576 {
1577 FREE(&msg->path);
1578 close(fd);
1579 unlink(path);
1580 return false;
1581 }
1582
1583 return true;
1584}
1585
1589static int maildir_msg_commit(struct Mailbox *m, struct Message *msg)
1590{
1591 return maildir_commit_message(m, msg, NULL);
1592}
1593
1599static int maildir_msg_close(struct Mailbox *m, struct Message *msg)
1600{
1601 return mutt_file_fclose(&msg->fp);
1602}
1603
1607static int maildir_msg_save_hcache(struct Mailbox *m, struct Email *e)
1608{
1609 int rc = 0;
1610#ifdef USE_HCACHE
1611 const char *const c_header_cache = cs_subset_path(NeoMutt->sub, "header_cache");
1612 struct HeaderCache *hc = hcache_open(c_header_cache, mailbox_path(m), NULL);
1613 char *key = e->path + 3;
1614 int keylen = maildir_hcache_keylen(key);
1615 rc = hcache_store(hc, key, keylen, e, 0);
1616 hcache_close(&hc);
1617#endif
1618 return rc;
1619}
1620
1624static int maildir_path_canon(struct Buffer *path)
1625{
1626 mutt_path_canon(path, HomeDir, true);
1627 return 0;
1628}
1629
1633static int maildir_path_parent(struct Buffer *path)
1634{
1635 if (mutt_path_parent(path))
1636 return 0;
1637
1638 if (buf_at(path, 0) == '~')
1639 mutt_path_canon(path, HomeDir, true);
1640
1641 if (mutt_path_parent(path))
1642 return 0;
1643
1644 return -1;
1645}
1646
1650static int maildir_path_pretty(struct Buffer *path, const char *folder)
1651{
1652 if (mutt_path_abbr_folder(path, folder))
1653 return 0;
1654
1655 if (mutt_path_pretty(path, HomeDir, false))
1656 return 0;
1657
1658 return -1;
1659}
1660
1664static enum MailboxType maildir_path_probe(const char *path, const struct stat *st)
1665{
1666 if (!st || !S_ISDIR(st->st_mode))
1667 return MUTT_UNKNOWN;
1668
1669 char sub[PATH_MAX] = { 0 };
1670 struct stat stsub = { 0 };
1671 char *subs[] = { "cur", "new" };
1672 for (size_t i = 0; i < mutt_array_size(subs); ++i)
1673 {
1674 snprintf(sub, sizeof(sub), "%s/%s", path, subs[i]);
1675 if ((stat(sub, &stsub) == 0) && S_ISDIR(stsub.st_mode))
1676 return MUTT_MAILDIR;
1677 }
1678
1679 return MUTT_UNKNOWN;
1680}
1681
1685const struct MxOps MxMaildirOps = {
1686 // clang-format off
1687 .type = MUTT_MAILDIR,
1688 .name = "maildir",
1689 .is_local = true,
1690 .ac_owns_path = maildir_ac_owns_path,
1691 .ac_add = maildir_ac_add,
1692 .mbox_open = maildir_mbox_open,
1693 .mbox_open_append = maildir_mbox_open_append,
1694 .mbox_check = maildir_mbox_check,
1695 .mbox_check_stats = maildir_mbox_check_stats,
1696 .mbox_sync = maildir_mbox_sync,
1697 .mbox_close = maildir_mbox_close,
1698 .msg_open = maildir_msg_open,
1699 .msg_open_new = maildir_msg_open_new,
1700 .msg_commit = maildir_msg_commit,
1701 .msg_close = maildir_msg_close,
1702 .msg_padding_size = NULL,
1703 .msg_save_hcache = maildir_msg_save_hcache,
1704 .tags_edit = NULL,
1705 .tags_commit = NULL,
1706 .path_probe = maildir_path_probe,
1707 .path_canon = maildir_path_canon,
1708 .path_pretty = maildir_path_pretty,
1709 .path_parent = maildir_path_parent,
1710 .path_is_empty = maildir_check_empty,
1711 // clang-format on
1712};
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition: array.h:278
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:155
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:211
#define ARRAY_SIZE(head)
The number of elements stored.
Definition: array.h:86
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:203
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:57
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:173
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition: buffer.c:638
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:542
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
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
Convenience wrapper for the config headers.
char * HomeDir
User's home directory.
Definition: globals.c:39
const char * cc_maildir_field_delimiter(void)
Get the cached value of $maildir_field_delimiter.
Definition: config_cache.c:130
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_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
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_safe_rename(const char *src, const char *target)
NFS-safe renaming of files.
Definition: file.c:346
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:977
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition: file.c:1578
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition: file.c:616
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
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition: file.c:168
@ MUTT_OPENDIR_CREATE
Create the directory if it doesn't exist.
Definition: file.h:74
@ MUTT_STAT_CTIME
File/dir's ctime - creation time.
Definition: file.h:65
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition: file.h:64
char * ShortHostname
Short version of the hostname.
Definition: globals.c:40
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: globals.c:59
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
#define mutt_perror(...)
Definition: logging2.h:93
static bool maildir_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Implements MxOps::ac_add() -.
Definition: maildir.c:1115
static bool maildir_ac_owns_path(struct Account *a, const char *path)
Check whether an Account own a Mailbox path - Implements MxOps::ac_owns_path() -.
Definition: maildir.c:1107
const struct MxOps MxMaildirOps
Maildir Mailbox - Implements MxOps -.
Definition: maildir.c:1685
static enum MxStatus maildir_mbox_check_stats(struct Mailbox *m, uint8_t flags)
Check the Mailbox statistics - Implements MxOps::mbox_check_stats() -.
Definition: maildir.c:1384
static enum MxStatus maildir_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check() -.
Definition: maildir.c:1376
static enum MxStatus maildir_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close() -.
Definition: maildir.c:1486
static bool maildir_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
Definition: maildir.c:1136
static enum MxOpenReturns maildir_mbox_open(struct Mailbox *m)
Open a Mailbox - Implements MxOps::mbox_open() -.
Definition: maildir.c:1123
static enum MxStatus maildir_mbox_sync(struct Mailbox *m)
Save changes to the Mailbox - Implements MxOps::mbox_sync() -.
Definition: maildir.c:1413
static int maildir_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition: maildir.c:1599
static int maildir_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: maildir.c:1589
bool maildir_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: maildir.c:1522
static bool maildir_msg_open(struct Mailbox *m, struct Message *msg, struct Email *e)
Open an email message in a Mailbox - Implements MxOps::msg_open() -.
Definition: maildir.c:1494
static int maildir_msg_save_hcache(struct Mailbox *m, struct Email *e)
Save message to the header cache - Implements MxOps::msg_save_hcache() -.
Definition: maildir.c:1607
static int maildir_path_canon(struct Buffer *path)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition: maildir.c:1624
static int maildir_path_parent(struct Buffer *path)
Find the parent of a Mailbox path - Implements MxOps::path_parent() -.
Definition: maildir.c:1633
static int maildir_path_pretty(struct Buffer *path, const char *folder)
Abbreviate a Mailbox path - Implements MxOps::path_pretty() -.
Definition: maildir.c:1650
static enum MailboxType maildir_path_probe(const char *path, const struct stat *st)
Is this a Maildir Mailbox? - Implements MxOps::path_probe() -.
Definition: maildir.c:1664
static int maildir_sort_inode(const void *a, const void *b, void *sdata)
Compare two Maildirs by inode number - Implements sort_t -.
Definition: maildir.c:514
static int maildir_sort_flags(const void *a, const void *b, void *sdata)
Compare two flag characters - Implements sort_t -.
Definition: maildir.c:195
struct HashElem * mutt_hash_insert(struct HashTable *table, const char *strkey, void *data)
Add a new element to the Hash Table (with string keys)
Definition: hash.c:335
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:362
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition: hash.c:259
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:457
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:110
Header cache multiplexor.
int hcache_store(struct HeaderCache *hc, const char *key, size_t keylen, struct Email *e, uint32_t uidvalidity)
Multiplexor for StoreOps::store.
Definition: hcache.c:686
struct HCacheEntry hcache_fetch(struct HeaderCache *hc, const char *key, size_t keylen, uint32_t uidvalidity)
Multiplexor for StoreOps::fetch.
Definition: hcache.c:583
struct HeaderCache * hcache_open(const char *path, const char *folder, hcache_namer_t namer)
Multiplexor for StoreOps::open.
Definition: hcache.c:494
void hcache_close(struct HeaderCache **ptr)
Multiplexor for StoreOps::close.
Definition: hcache.c:563
int hcache_delete_record(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:759
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:226
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition: mailbox.h:177
@ NT_MAILBOX_INVALID
Email list was changed.
Definition: mailbox.h:176
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_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:51
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition: mailbox.h:44
@ MUTT_MAILDIR
'Maildir' Mailbox type
Definition: mailbox.h:48
struct MaildirEmailData * maildir_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:64
void maildir_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: edata.c:38
struct MaildirEmailData * maildir_edata_new(void)
Create a new MaildirEmailData object.
Definition: edata.c:53
bool maildir_update_flags(struct Mailbox *m, struct Email *e_old, struct Email *e_new)
Update the mailbox flags.
Definition: shared.c:120
void maildir_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free()
Definition: mdata.c:37
struct MaildirMboxData * maildir_mdata_get(struct Mailbox *m)
Get the private data for this Mailbox.
Definition: mdata.c:60
struct MaildirMboxData * maildir_mdata_new(void)
Create a new MaildirMboxData object.
Definition: mdata.c:49
mode_t mh_umask(struct Mailbox *m)
Create a umask from the mailbox directory.
Definition: shared.c:52
int maildir_move_to_mailbox(struct Mailbox *m, const struct MdEmailArray *mda)
Copy the Maildir list to the Mailbox.
Definition: shared.c:75
static enum MxStatus maildir_check(struct Mailbox *m)
Check for new mail.
Definition: maildir.c:1199
static int maildir_sync_message(struct Mailbox *m, struct Email *e)
Sync an email to a Maildir folder.
Definition: maildir.c:405
static size_t maildir_hcache_keylen(const char *fn)
Calculate the length of the Maildir path.
Definition: maildir.c:599
static void maildir_delayed_parsing(struct Mailbox *m, struct MdEmailArray *mda, struct Progress *progress)
This function does the second parsing pass.
Definition: maildir.c:613
int maildir_check_empty(struct Buffer *path)
Is the mailbox empty.
Definition: maildir.c:1070
bool maildir_parse_stream(enum MailboxType type, FILE *fp, const char *fname, bool is_old, struct Email *e)
Parse a Maildir message.
Definition: maildir.c:912
static int maildir_read_dir(struct Mailbox *m, const char *subdir)
Read a Maildir style mailbox.
Definition: maildir.c:694
FILE * maildir_open_find_message(const char *folder, const char *msg, char **newname)
Find a message by name.
Definition: maildir.c:1023
#define MMC_CUR_DIR
'cur' directory changed
Definition: maildir.c:75
static void maildir_canon_filename(struct Buffer *dest, const char *src)
Generate the canonical filename for a Maildir folder.
Definition: maildir.c:750
bool maildir_parse_message(enum MailboxType type, const char *fname, bool is_old, struct Email *e)
Actually parse a maildir message.
Definition: maildir.c:954
static FILE * maildir_open_find_message_dir(const char *folder, const char *unique, const char *subfolder, char **newname)
Find a message in a maildir folder.
Definition: maildir.c:786
struct Email * maildir_email_new(void)
Create a Maildir Email.
Definition: maildir.c:85
static void maildir_update_mtime(struct Mailbox *m)
Update our record of the Maildir modification time.
Definition: maildir.c:496
#define MMC_NO_DIRS
No directories changed.
Definition: maildir.c:73
static void maildir_check_dir(struct Mailbox *m, const char *dir_name, bool check_new, bool check_stats)
Check for new mail / mail counts.
Definition: maildir.c:103
bool maildir_sync_mailbox_message(struct Mailbox *m, struct Email *e, struct HeaderCache *hc)
Save changes to the mailbox.
Definition: maildir.c:977
static int maildir_parse_dir(struct Mailbox *m, struct MdEmailArray *mda, const char *subdir, struct Progress *progress)
Read a Maildir mailbox.
Definition: maildir.c:532
#define MMC_NEW_DIR
'new' directory changed
Definition: maildir.c:74
static int maildir_rewrite_message(struct Mailbox *m, struct Email *e)
Sync a message in an MH folder.
Definition: maildir.c:352
void maildir_gen_flags(char *dest, size_t destlen, struct Email *e)
Generate the Maildir flags for an email.
Definition: maildir.c:206
static int maildir_commit_message(struct Mailbox *m, struct Message *msg, struct Email *e)
Commit a message to a maildir folder.
Definition: maildir.c:259
void maildir_parse_flags(struct Email *e, const char *path)
Parse Maildir file flags.
Definition: maildir.c:840
struct MdEmail * maildir_entry_new(void)
Create a new Maildir entry.
Definition: mdemail.c:39
void maildirarray_clear(struct MdEmailArray *mda)
Free a Maildir array.
Definition: mdemail.c:64
Maildir Email helper.
#define FREE(x)
Definition: memory.h:45
#define mutt_array_size(x)
Definition: memory.h:38
bool MonitorContextChanged
Set to true when the current mailbox has changed.
Definition: monitor.c:51
Monitor files for changes.
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
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
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:497
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:653
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:327
#define PATH_MAX
Definition: mutt.h:41
int mx_msg_close(struct Mailbox *m, struct Message **ptr)
Close a message.
Definition: mx.c:1206
struct Message * mx_msg_open(struct Mailbox *m, struct Email *e)
Return a stream pointer for a message.
Definition: mx.c:1160
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1066
API for mailboxes.
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition: mx.h:39
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_APPEND
Open mailbox for appending messages.
Definition: mxapi.h:42
MxOpenReturns
Return values for mbox_open()
Definition: mxapi.h:76
@ MX_OPEN_ERROR
Open failed with an error.
Definition: mxapi.h:78
@ MX_OPEN_OK
Open succeeded.
Definition: mxapi.h:77
#define MUTT_MAILBOX_CHECK_FORCE_STATS
Ignore MailboxType and calculate statistics.
Definition: mxapi.h:55
#define MUTT_APPENDNEW
Set in mx_open_mailbox_append if the mailbox doesn't exist.
Definition: mxapi.h:49
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_snc(), and mbox_close()
Definition: mxapi.h:63
@ MX_STATUS_ERROR
An error occurred.
Definition: mxapi.h:64
@ MX_STATUS_OK
No changes.
Definition: mxapi.h:65
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition: mxapi.h:69
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition: mxapi.h:68
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition: mxapi.h:66
Notmuch virtual mailbox type.
int nm_update_filename(struct Mailbox *m, const char *old_file, const char *new_file, struct Email *e)
Change the filename.
Definition: notmuch.c:1749
Notmuch-specific Mailbox data.
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
Pop-specific Email data.
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
void mutt_qsort_r(void *base, size_t nmemb, size_t size, sort_t compar, void *sdata)
Sort an array, where the comparator has access to opaque data rather than requiring global variables.
Definition: qsort_r.c:66
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: random.c:134
GUI display the mailboxes in a side panel.
Assorted sorting methods.
#define mutt_numeric_cmp(a, b)
Definition: sort.h:34
Key value store.
#define NONULL(x)
Definition: string2.h:37
A group of associated Mailboxes.
Definition: account.h:37
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
String manipulation buffer.
Definition: buffer.h:34
char * dptr
Current read/write position.
Definition: buffer.h:36
char * data
Pointer to data.
Definition: buffer.h:35
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
void * edata
Driver-specific data.
Definition: email.h:72
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
void(* edata_free)(void **ptr)
Free the private data attached to the Email.
Definition: email.h:86
bool changed
Email has been edited.
Definition: email.h:75
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:98
bool flagged
Marked important?
Definition: email.h:45
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:58
bool replied
Email has been replied to.
Definition: email.h:49
char * path
Path of Email (for local Mailboxes)
Definition: email.h:68
bool deleted
Email is deleted.
Definition: email.h:76
int index
The absolute (unsorted) message number.
Definition: email.h:109
bool trash
Message is marked as trashed on disk (used by the maildir_trash option)
Definition: email.h:51
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:59
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:92
Wrapper for Email retrieved from the header cache.
Definition: lib.h:101
uint32_t uidvalidity
IMAP-specific UIDVALIDITY.
Definition: lib.h:102
struct Email * email
Retrieved email.
Definition: lib.h:104
A Hash Table.
Definition: hash.h:98
Header Cache.
Definition: lib.h:88
char * folder
Folder name.
Definition: lib.h:89
A mailbox.
Definition: mailbox.h:79
void(* mdata_free)(void **ptr)
Free the private data attached to the Mailbox.
Definition: mailbox.h:142
bool changed
Mailbox has been modified.
Definition: mailbox.h:109
bool has_new
Mailbox has new mail.
Definition: mailbox.h:85
int msg_new
Number of new messages.
Definition: mailbox.h:92
int msg_count
Total number of messages.
Definition: mailbox.h:88
enum MailboxType type
Mailbox type.
Definition: mailbox.h:102
void * mdata
Driver specific data.
Definition: mailbox.h:133
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:93
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 verbose
Display status messages?
Definition: mailbox.h:116
int msg_unread
Number of unread messages.
Definition: mailbox.h:89
Maildir-specific Email data -.
Definition: edata.h:34
Maildir-specific Mailbox data -.
Definition: mdata.h:35
mode_t mh_umask
umask to use when creating files
Definition: mdata.h:38
struct timespec mtime_cur
Timestamp of the 'cur' dir.
Definition: mdata.h:37
struct timespec mtime
Time Mailbox was last changed.
Definition: mdata.h:36
A Maildir Email helper.
Definition: mdemail.h:34
bool header_parsed
Definition: mdemail.h:37
char * canon_fname
Definition: mdemail.h:36
struct Email * email
Definition: mdemail.h:35
ino_t inode
Definition: mdemail.h:38
A local copy of an email.
Definition: message.h:34
FILE * fp
pointer to the message data
Definition: message.h:35
char * path
path to temp file
Definition: message.h:36
char * committed_path
the final path generated by mx_msg_commit()
Definition: message.h:37
time_t received
Time at which this message was received.
Definition: message.h:46
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