NeoMutt  2023-03-22-27-g3cb248
Teaching an old dog new tricks
DOXYGEN
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 <stdlib.h>
43#include <string.h>
44#include <sys/stat.h>
45#include <unistd.h>
46#include <utime.h>
47#include "private.h"
48#include "mutt/lib.h"
49#include "config/lib.h"
50#include "email/lib.h"
51#include "core/lib.h"
52#include "lib.h"
53#include "progress/lib.h"
54#include "copy.h"
55#include "edata.h"
56#include "globals.h" // IWYU pragma: keep
57#include "mdata.h"
58#include "mdemail.h"
59#include "mx.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_mbox_check()
73#define MMC_NO_DIRS 0
74#define MMC_NEW_DIR (1 << 0)
75#define MMC_CUR_DIR (1 << 1)
76
86static void maildir_check_dir(struct Mailbox *m, const char *dir_name,
87 bool check_new, bool check_stats)
88{
89 DIR *dir = NULL;
90 struct dirent *de = NULL;
91 char *p = NULL;
92 struct stat st = { 0 };
93
94 struct Buffer *path = mutt_buffer_pool_get();
95 struct Buffer *msgpath = mutt_buffer_pool_get();
96 mutt_buffer_printf(path, "%s/%s", mailbox_path(m), dir_name);
97
98 /* when $mail_check_recent is set, if the new/ directory hasn't been modified since
99 * the user last exited the m, then we know there is no recent mail. */
100 const bool c_mail_check_recent = cs_subset_bool(NeoMutt->sub, "mail_check_recent");
101 if (check_new && c_mail_check_recent)
102 {
103 if ((stat(mutt_buffer_string(path), &st) == 0) &&
105 {
106 check_new = false;
107 }
108 }
109
110 if (!(check_new || check_stats))
111 goto cleanup;
112
114 if (!dir)
115 {
116 m->type = MUTT_UNKNOWN;
117 goto cleanup;
118 }
119
120 while ((de = readdir(dir)))
121 {
122 if (*de->d_name == '.')
123 continue;
124
125 p = strstr(de->d_name, ":2,");
126 if (p && strchr(p + 3, 'T'))
127 continue;
128
129 if (check_stats)
130 {
131 m->msg_count++;
132 if (p && strchr(p + 3, 'F'))
133 m->msg_flagged++;
134 }
135 if (!p || !strchr(p + 3, 'S'))
136 {
137 if (check_stats)
138 m->msg_unread++;
139 if (check_new)
140 {
141 if (c_mail_check_recent)
142 {
143 mutt_buffer_printf(msgpath, "%s/%s", mutt_buffer_string(path), de->d_name);
144 /* ensure this message was received since leaving this m */
145 if ((stat(mutt_buffer_string(msgpath), &st) == 0) &&
147 {
148 continue;
149 }
150 }
151 m->has_new = true;
152 check_new = false;
153 m->msg_new++;
154 if (!check_stats)
155 break;
156 }
157 }
158 }
159
160 closedir(dir);
161
162cleanup:
164 mutt_buffer_pool_release(&msgpath);
165}
166
175static int ch_compare(const void *a, const void *b)
176{
177 return (int) (*((const char *) a) - *((const char *) b));
178}
179
186void maildir_gen_flags(char *dest, size_t destlen, struct Email *e)
187{
188 *dest = '\0';
189
190 const char *flags = NULL;
191
193 if (edata)
194 flags = edata->maildir_flags;
195
196 /* The maildir specification requires that all files in the cur
197 * subdirectory have the :unique string appended, regardless of whether
198 * or not there are any flags. If .old is set, we know that this message
199 * will end up in the cur directory, so we include it in the following
200 * test even though there is no associated flag. */
201
202 if (e->flagged || e->replied || e->read || e->deleted || e->old || flags)
203 {
204 char tmp[1024] = { 0 };
205 snprintf(tmp, sizeof(tmp), "%s%s%s%s%s", e->flagged ? "F" : "", e->replied ? "R" : "",
206 e->read ? "S" : "", e->deleted ? "T" : "", NONULL(flags));
207 if (flags)
208 qsort(tmp, strlen(tmp), 1, ch_compare);
209 snprintf(dest, destlen, ":2,%s", tmp);
210 }
211}
212
237static int maildir_commit_message(struct Mailbox *m, struct Message *msg, struct Email *e)
238{
239 char subdir[4] = { 0 };
240 char suffix[16] = { 0 };
241 int rc = 0;
242
243 if (mutt_file_fsync_close(&msg->fp))
244 {
245 mutt_perror(_("Could not flush message to disk"));
246 return -1;
247 }
248
249 /* extract the subdir */
250 char *s = strrchr(msg->path, '/') + 1;
251 mutt_str_copy(subdir, s, 4);
252
253 /* extract the flags */
254 s = strchr(s, ':');
255 if (s)
256 mutt_str_copy(suffix, s, sizeof(suffix));
257 else
258 suffix[0] = '\0';
259
260 /* construct a new file name. */
261 struct Buffer *path = mutt_buffer_pool_get();
262 struct Buffer *full = mutt_buffer_pool_get();
263 while (true)
264 {
265 mutt_buffer_printf(path, "%s/%lld.R%" PRIu64 ".%s%s", subdir,
266 (long long) mutt_date_now(), mutt_rand64(),
267 NONULL(ShortHostname), suffix);
268 mutt_buffer_printf(full, "%s/%s", mailbox_path(m), mutt_buffer_string(path));
269
270 mutt_debug(LL_DEBUG2, "renaming %s to %s\n", msg->path, mutt_buffer_string(full));
271
272 if (mutt_file_safe_rename(msg->path, mutt_buffer_string(full)) == 0)
273 {
274 /* Adjust the mtime on the file to match the time at which this
275 * message was received. Currently this is only set when copying
276 * messages between mailboxes, so we test to ensure that it is
277 * actually set. */
278 if (msg->received != 0)
279 {
280 struct utimbuf ut;
281 int rc_utime;
282
283 ut.actime = msg->received;
284 ut.modtime = msg->received;
285 do
286 {
287 rc_utime = utime(mutt_buffer_string(full), &ut);
288 } while ((rc_utime == -1) && (errno == EINTR));
289 if (rc_utime == -1)
290 {
291 mutt_perror(_("maildir_commit_message(): unable to set time on file"));
292 rc = -1;
293 goto cleanup;
294 }
295 }
296
297#ifdef USE_NOTMUCH
298 if (m->type == MUTT_NOTMUCH)
300#endif
301 if (e)
304 FREE(&msg->path);
305
306 goto cleanup;
307 }
308 else if (errno != EEXIST)
309 {
311 rc = -1;
312 goto cleanup;
313 }
314 }
315
316cleanup:
319
320 return rc;
321}
322
330static int maildir_rewrite_message(struct Mailbox *m, int msgno)
331{
332 if (!m || !m->emails || (msgno >= m->msg_count))
333 return -1;
334
335 struct Email *e = m->emails[msgno];
336 if (!e)
337 return -1;
338
339 bool restore = true;
340
341 long old_body_offset = e->body->offset;
342 long old_body_length = e->body->length;
343 long old_hdr_lines = e->lines;
344
345 struct Message *src = mx_msg_open(m, msgno);
346 struct Message *dest = mx_msg_open_new(m, e, MUTT_MSG_NO_FLAGS);
347 if (!src || !dest)
348 return -1;
349
350 int rc = mutt_copy_message(dest->fp, e, src, MUTT_CM_UPDATE, CH_UPDATE | CH_UPDATE_LEN, 0);
351 if (rc == 0)
352 {
353 char oldpath[PATH_MAX] = { 0 };
354 char partpath[PATH_MAX] = { 0 };
355 snprintf(oldpath, sizeof(oldpath), "%s/%s", mailbox_path(m), e->path);
356 mutt_str_copy(partpath, e->path, sizeof(partpath));
357
358 rc = maildir_commit_message(m, dest, e);
359
360 if (rc == 0)
361 {
362 unlink(oldpath);
363 restore = false;
364 }
365 }
366 mx_msg_close(m, &src);
367 mx_msg_close(m, &dest);
368
369 if ((rc == -1) && restore)
370 {
371 e->body->offset = old_body_offset;
372 e->body->length = old_body_length;
373 e->lines = old_hdr_lines;
374 }
375
377 return rc;
378}
379
387static int maildir_sync_message(struct Mailbox *m, int msgno)
388{
389 if (!m || !m->emails || (msgno >= m->msg_count))
390 return -1;
391
392 struct Email *e = m->emails[msgno];
393 if (!e)
394 return -1;
395
396 struct Buffer *newpath = NULL;
397 struct Buffer *partpath = NULL;
398 struct Buffer *fullpath = NULL;
399 struct Buffer *oldpath = NULL;
400 char suffix[16] = { 0 };
401 int rc = 0;
402
403 /* TODO: why the e->env check? */
404 if (e->attach_del || (e->env && e->env->changed))
405 {
406 /* when doing attachment deletion/rethreading, fall back to the MH case. */
407 if (maildir_rewrite_message(m, msgno) != 0)
408 return -1;
409 /* TODO: why the env check? */
410 if (e->env)
411 e->env->changed = 0;
412 }
413 else
414 {
415 /* we just have to rename the file. */
416
417 char *p = strrchr(e->path, '/');
418 if (!p)
419 {
420 mutt_debug(LL_DEBUG1, "%s: unable to find subdir!\n", e->path);
421 return -1;
422 }
423 p++;
424 newpath = mutt_buffer_pool_get();
425 partpath = mutt_buffer_pool_get();
426 fullpath = mutt_buffer_pool_get();
427 oldpath = mutt_buffer_pool_get();
428
429 mutt_buffer_strcpy(newpath, p);
430
431 /* kill the previous flags */
432 p = strchr(newpath->data, ':');
433 if (p)
434 {
435 *p = '\0';
436 newpath->dptr = p; /* fix buffer up, just to be safe */
437 }
438
439 maildir_gen_flags(suffix, sizeof(suffix), e);
440
441 mutt_buffer_printf(partpath, "%s/%s%s", (e->read || e->old) ? "cur" : "new",
442 mutt_buffer_string(newpath), suffix);
443 mutt_buffer_printf(fullpath, "%s/%s", mailbox_path(m), mutt_buffer_string(partpath));
444 mutt_buffer_printf(oldpath, "%s/%s", mailbox_path(m), e->path);
445
447 {
448 /* message hasn't really changed */
449 goto cleanup;
450 }
451
452 /* record that the message is possibly marked as trashed on disk */
453 e->trash = e->deleted;
454
455 if (rename(mutt_buffer_string(oldpath), mutt_buffer_string(fullpath)) != 0)
456 {
457 mutt_perror("rename");
458 rc = -1;
459 goto cleanup;
460 }
462 }
463
464cleanup:
465 mutt_buffer_pool_release(&newpath);
466 mutt_buffer_pool_release(&partpath);
467 mutt_buffer_pool_release(&fullpath);
468 mutt_buffer_pool_release(&oldpath);
469
470 return rc;
471}
472
477static void maildir_update_mtime(struct Mailbox *m)
478{
479 char buf[PATH_MAX] = { 0 };
480 struct stat st = { 0 };
482
483 snprintf(buf, sizeof(buf), "%s/%s", mailbox_path(m), "cur");
484 if (stat(buf, &st) == 0)
486
487 snprintf(buf, sizeof(buf), "%s/%s", mailbox_path(m), "new");
488 if (stat(buf, &st) == 0)
490}
491
495static int maildir_cmp_inode(const void *a, const void *b)
496{
497 const struct MdEmail *ma = *(struct MdEmail **) a;
498 const struct MdEmail *mb = *(struct MdEmail **) b;
499
500 return ma->inode - mb->inode;
501}
502
513static int maildir_parse_dir(struct Mailbox *m, struct MdEmailArray *mda,
514 const char *subdir, struct Progress *progress)
515{
516 struct dirent *de = NULL;
517 int rc = 0;
518 bool is_old = false;
519 struct MdEmail *entry = NULL;
520 struct Email *e = NULL;
521
522 struct Buffer *buf = mutt_buffer_pool_get();
523
524 mutt_buffer_printf(buf, "%s/%s", mailbox_path(m), subdir);
525 is_old = mutt_str_equal("cur", subdir);
526
528 if (!dir)
529 {
530 rc = -1;
531 goto cleanup;
532 }
533
534 while (((de = readdir(dir))) && !SigInt)
535 {
536 if (*de->d_name == '.')
537 continue;
538
539 /* FOO - really ignore the return value? */
540 mutt_debug(LL_DEBUG2, "queueing %s\n", de->d_name);
541
542 e = email_new();
545
546 e->old = is_old;
547 maildir_parse_flags(e, de->d_name);
548
549 if (m->verbose && progress)
550 progress_update(progress, ARRAY_SIZE(mda) + 1, -1);
551
552 mutt_buffer_printf(buf, "%s/%s", subdir, de->d_name);
553 e->path = mutt_buffer_strdup(buf);
554
555 entry = maildir_entry_new();
556 entry->email = e;
557 entry->inode = de->d_ino;
558 ARRAY_ADD(mda, entry);
559 }
560
561 closedir(dir);
562
563 if (SigInt)
564 {
565 SigInt = false;
566 return -2; /* action aborted */
567 }
568
570
571cleanup:
573
574 return rc;
575}
576
577#ifdef USE_HCACHE
585static size_t maildir_hcache_keylen(const char *fn)
586{
587 const char *p = strrchr(fn, ':');
588 return p ? (size_t) (p - fn) : mutt_str_len(fn);
589}
590#endif
591
598static void maildir_delayed_parsing(struct Mailbox *m, struct MdEmailArray *mda,
599 struct Progress *progress)
600{
601 char fn[PATH_MAX] = { 0 };
602
603#ifdef USE_HCACHE
604 const char *const c_header_cache = cs_subset_path(NeoMutt->sub, "header_cache");
605 struct HeaderCache *hc = mutt_hcache_open(c_header_cache, mailbox_path(m), NULL);
606#endif
607
608 struct MdEmail *md = NULL;
609 struct MdEmail **mdp = NULL;
610 ARRAY_FOREACH(mdp, mda)
611 {
612 md = *mdp;
613 if (!md || !md->email || md->header_parsed)
614 continue;
615
616 if (m->verbose && progress)
617 progress_update(progress, ARRAY_FOREACH_IDX, -1);
618
619 snprintf(fn, sizeof(fn), "%s/%s", mailbox_path(m), md->email->path);
620
621#ifdef USE_HCACHE
622 struct stat st_lastchanged = { 0 };
623 int rc = 0;
624 const bool c_maildir_header_cache_verify = cs_subset_bool(NeoMutt->sub, "maildir_header_cache_verify");
625 if (c_maildir_header_cache_verify)
626 {
627 rc = stat(fn, &st_lastchanged);
628 }
629
630 const char *key = md->email->path + 3;
631 size_t keylen = maildir_hcache_keylen(key);
632 struct HCacheEntry hce = mutt_hcache_fetch(hc, key, keylen, 0);
633
634 if (hce.email && (rc == 0) && (st_lastchanged.st_mtime <= hce.uidvalidity))
635 {
638 hce.email->old = md->email->old;
639 hce.email->path = mutt_str_dup(md->email->path);
640 email_free(&md->email);
641 md->email = hce.email;
642 maildir_parse_flags(md->email, fn);
643 }
644 else
645#endif
646 {
647 if (maildir_parse_message(m->type, fn, md->email->old, md->email))
648 {
649 md->header_parsed = true;
650#ifdef USE_HCACHE
651 key = md->email->path + 3;
652 keylen = maildir_hcache_keylen(key);
653 mutt_hcache_store(hc, key, keylen, md->email, 0);
654#endif
655 }
656 else
657 {
658 email_free(&md->email);
659 }
660 }
661 }
662#ifdef USE_HCACHE
664#endif
665}
666
674static int maildir_read_dir(struct Mailbox *m, const char *subdir)
675{
676 if (!m)
677 return -1;
678
679 struct Progress *progress = NULL;
680
681 if (m->verbose)
682 {
683 char msg[PATH_MAX] = { 0 };
684 snprintf(msg, sizeof(msg), _("Scanning %s..."), mailbox_path(m));
685 progress = progress_new(msg, MUTT_PROGRESS_READ, 0);
686 }
687
689 if (!mdata)
690 {
692 m->mdata = mdata;
694 }
695
696 struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
697 int rc = maildir_parse_dir(m, &mda, subdir, progress);
698 progress_free(&progress);
699 if (rc < 0)
700 return -1;
701
702 if (m->verbose)
703 {
704 char msg[PATH_MAX] = { 0 };
705 snprintf(msg, sizeof(msg), _("Reading %s..."), mailbox_path(m));
706 progress = progress_new(msg, MUTT_PROGRESS_READ, ARRAY_SIZE(&mda));
707 }
708 maildir_delayed_parsing(m, &mda, progress);
709 progress_free(&progress);
710
712
713 if (!mdata->mh_umask)
714 mdata->mh_umask = mh_umask(m);
715
716 return 0;
717}
718
728static void maildir_canon_filename(struct Buffer *dest, const char *src)
729{
730 if (!dest || !src)
731 return;
732
733 char *t = strrchr(src, '/');
734 if (t)
735 src = t + 1;
736
737 mutt_buffer_strcpy(dest, src);
738 char *u = strpbrk(dest->data, ",:");
739 if (u)
740 {
741 *u = '\0';
742 dest->dptr = u;
743 }
744}
745
758static FILE *maildir_open_find_message_dir(const char *folder, const char *unique,
759 const char *subfolder, char **newname)
760{
761 struct Buffer *dirname = mutt_buffer_pool_get();
762 struct Buffer *tunique = mutt_buffer_pool_get();
763 struct Buffer *fname = mutt_buffer_pool_get();
764
765 struct dirent *de = NULL;
766
767 FILE *fp = NULL;
768 int oe = ENOENT;
769
770 mutt_buffer_printf(dirname, "%s/%s", folder, subfolder);
771
773 if (!dir)
774 {
775 errno = ENOENT;
776 goto cleanup;
777 }
778
779 while ((de = readdir(dir)))
780 {
781 maildir_canon_filename(tunique, de->d_name);
782
783 if (mutt_str_equal(mutt_buffer_string(tunique), unique))
784 {
785 mutt_buffer_printf(fname, "%s/%s/%s", folder, subfolder, de->d_name);
786 fp = fopen(mutt_buffer_string(fname), "r");
787 oe = errno;
788 break;
789 }
790 }
791
792 closedir(dir);
793
794 if (newname && fp)
795 *newname = mutt_buffer_strdup(fname);
796
797 errno = oe;
798
799cleanup:
800 mutt_buffer_pool_release(&dirname);
801 mutt_buffer_pool_release(&tunique);
803
804 return fp;
805}
806
812void maildir_parse_flags(struct Email *e, const char *path)
813{
814 char *q = NULL;
815
816 e->flagged = false;
817 e->read = false;
818 e->replied = false;
819
821
822 char *p = strrchr(path, ':');
823 if (p && mutt_str_startswith(p + 1, "2,"))
824 {
825 p += 3;
826
827 mutt_str_replace(&edata->maildir_flags, p);
828 q = edata->maildir_flags;
829
830 while (*p)
831 {
832 switch (*p)
833 {
834 case 'F':
835 e->flagged = true;
836 break;
837
838 case 'R': /* replied */
839 e->replied = true;
840 break;
841
842 case 'S': /* seen */
843 e->read = true;
844 break;
845
846 case 'T': /* trashed */
847 {
848 const bool c_flag_safe = cs_subset_bool(NeoMutt->sub, "flag_safe");
849 if (!e->flagged || !c_flag_safe)
850 {
851 e->trash = true;
852 e->deleted = true;
853 }
854 break;
855 }
856
857 default:
858 *q++ = *p;
859 break;
860 }
861 p++;
862 }
863 }
864
865 if (q == edata->maildir_flags)
866 FREE(&edata->maildir_flags);
867 else if (q)
868 *q = '\0';
869}
870
884struct Email *maildir_parse_stream(enum MailboxType type, FILE *fp,
885 const char *fname, bool is_old, struct Email *e)
886{
887 const long size = mutt_file_get_size_fp(fp);
888 if (size == 0)
889 {
890 return NULL;
891 }
892
893 if (!e)
894 {
895 e = email_new();
898 }
899 e->env = mutt_rfc822_read_header(fp, e, false, false);
900
901 if (e->received == 0)
902 e->received = e->date_sent;
903
904 /* always update the length since we have fresh information available. */
905 e->body->length = size - e->body->offset;
906
907 e->index = -1;
908
909 if (type == MUTT_MAILDIR)
910 {
911 /* maildir stores its flags in the filename, so ignore the
912 * flags in the header of the message */
913
914 e->old = is_old;
915 maildir_parse_flags(e, fname);
916 }
917 return e;
918}
919
931struct Email *maildir_parse_message(enum MailboxType type, const char *fname,
932 bool is_old, struct Email *e)
933{
934 FILE *fp = fopen(fname, "r");
935 if (!fp)
936 return NULL;
937
938 struct Email *e_res = maildir_parse_stream(type, fp, fname, is_old, e);
939 mutt_file_fclose(&fp);
940 return e_res;
941}
942
952{
953 struct Email *e = m->emails[msgno];
954 if (!e)
955 return false;
956
957 const bool c_maildir_trash = cs_subset_bool(NeoMutt->sub, "maildir_trash");
958 if (e->deleted && !c_maildir_trash)
959 {
960 char path[PATH_MAX] = { 0 };
961 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), e->path);
962#ifdef USE_HCACHE
963 if (hc)
964 {
965 const char *key = e->path + 3;
966 size_t keylen = maildir_hcache_keylen(key);
967 mutt_hcache_delete_record(hc, key, keylen);
968 }
969#endif
970 unlink(path);
971 }
972 else if (e->changed || e->attach_del ||
973 ((c_maildir_trash || e->trash) && (e->deleted != e->trash)))
974 {
975 if (maildir_sync_message(m, msgno) == -1)
976 return false;
977 }
978
979#ifdef USE_HCACHE
980 if (hc && e->changed)
981 {
982 const char *key = e->path + 3;
983 size_t keylen = maildir_hcache_keylen(key);
984 mutt_hcache_store(hc, key, keylen, e, 0);
985 }
986#endif
987
988 return true;
989}
990
998FILE *maildir_open_find_message(const char *folder, const char *msg, char **newname)
999{
1000 static unsigned int new_hits = 0, cur_hits = 0; /* simple dynamic optimization */
1001
1002 struct Buffer *unique = mutt_buffer_pool_get();
1003 maildir_canon_filename(unique, msg);
1004
1005 FILE *fp = maildir_open_find_message_dir(folder, mutt_buffer_string(unique),
1006 (new_hits > cur_hits) ? "new" : "cur", newname);
1007 if (fp || (errno != ENOENT))
1008 {
1009 if ((new_hits < UINT_MAX) && (cur_hits < UINT_MAX))
1010 {
1011 new_hits += ((new_hits > cur_hits) ? 1 : 0);
1012 cur_hits += ((new_hits > cur_hits) ? 0 : 1);
1013 }
1014
1015 goto cleanup;
1016 }
1018 (new_hits > cur_hits) ? "cur" : "new", newname);
1019 if (fp || (errno != ENOENT))
1020 {
1021 if ((new_hits < UINT_MAX) && (cur_hits < UINT_MAX))
1022 {
1023 new_hits += ((new_hits > cur_hits) ? 0 : 1);
1024 cur_hits += ((new_hits > cur_hits) ? 1 : 0);
1025 }
1026
1027 goto cleanup;
1028 }
1029
1030 fp = NULL;
1031
1032cleanup:
1033 mutt_buffer_pool_release(&unique);
1034
1035 return fp;
1036}
1037
1045int maildir_check_empty(const char *path)
1046{
1047 DIR *dir = NULL;
1048 struct dirent *de = NULL;
1049 int rc = 1; /* assume empty until we find a message */
1050 char realpath[PATH_MAX] = { 0 };
1051 int iter = 0;
1052
1053 /* Strategy here is to look for any file not beginning with a period */
1054
1055 do
1056 {
1057 /* we do "cur" on the first iteration since it's more likely that we'll
1058 * find old messages without having to scan both subdirs */
1059 snprintf(realpath, sizeof(realpath), "%s/%s", path, (iter == 0) ? "cur" : "new");
1060 dir = mutt_file_opendir(realpath, MUTT_OPENDIR_CREATE);
1061 if (!dir)
1062 return -1;
1063 while ((de = readdir(dir)))
1064 {
1065 if (*de->d_name != '.')
1066 {
1067 rc = 0;
1068 break;
1069 }
1070 }
1071 closedir(dir);
1072 iter++;
1073 } while (rc && iter < 2);
1074
1075 return rc;
1076}
1077
1081static bool maildir_ac_owns_path(struct Account *a, const char *path)
1082{
1083 return true;
1084}
1085
1089static bool maildir_ac_add(struct Account *a, struct Mailbox *m)
1090{
1091 return true;
1092}
1093
1098{
1099 /* maildir looks sort of like MH, except that there are two subdirectories
1100 * of the main folder path from which to read messages */
1101 if ((maildir_read_dir(m, "new") == -1) || (maildir_read_dir(m, "cur") == -1))
1102 return MX_OPEN_ERROR;
1103
1104 return MX_OPEN_OK;
1105}
1106
1111{
1112 if (!(flags & (MUTT_APPEND | MUTT_APPENDNEW | MUTT_NEWFOLDER)))
1113 {
1114 return true;
1115 }
1116
1117 errno = 0;
1118 if ((mutt_file_mkdir(mailbox_path(m), S_IRWXU) != 0) && (errno != EEXIST))
1119 {
1121 return false;
1122 }
1123
1124 char tmp[PATH_MAX] = { 0 };
1125 snprintf(tmp, sizeof(tmp), "%s/cur", mailbox_path(m));
1126 errno = 0;
1127 if ((mkdir(tmp, S_IRWXU) != 0) && (errno != EEXIST))
1128 {
1129 mutt_perror(tmp);
1130 rmdir(mailbox_path(m));
1131 return false;
1132 }
1133
1134 snprintf(tmp, sizeof(tmp), "%s/new", mailbox_path(m));
1135 errno = 0;
1136 if ((mkdir(tmp, S_IRWXU) != 0) && (errno != EEXIST))
1137 {
1138 mutt_perror(tmp);
1139 snprintf(tmp, sizeof(tmp), "%s/cur", mailbox_path(m));
1140 rmdir(tmp);
1141 rmdir(mailbox_path(m));
1142 return false;
1143 }
1144
1145 snprintf(tmp, sizeof(tmp), "%s/tmp", mailbox_path(m));
1146 errno = 0;
1147 if ((mkdir(tmp, S_IRWXU) != 0) && (errno != EEXIST))
1148 {
1149 mutt_perror(tmp);
1150 snprintf(tmp, sizeof(tmp), "%s/cur", mailbox_path(m));
1151 rmdir(tmp);
1152 snprintf(tmp, sizeof(tmp), "%s/new", mailbox_path(m));
1153 rmdir(tmp);
1154 rmdir(mailbox_path(m));
1155 return false;
1156 }
1157
1158 return true;
1159}
1160
1172{
1173 struct stat st_new = { 0 }; /* status of the "new" subdirectory */
1174 struct stat st_cur = { 0 }; /* status of the "cur" subdirectory */
1175 int changed = MMC_NO_DIRS; /* which subdirectories have changed */
1176 bool occult = false; /* messages were removed from the mailbox */
1177 int num_new = 0; /* number of new messages added to the mailbox */
1178 bool flags_changed = false; /* message flags were changed in the mailbox */
1179 struct HashTable *fnames = NULL; /* hash table for quickly looking up the base filename
1180 for a maildir message */
1182
1183 /* XXX seems like this check belongs in mx_mbox_check() rather than here. */
1184 const bool c_check_new = cs_subset_bool(NeoMutt->sub, "check_new");
1185 if (!c_check_new)
1186 return MX_STATUS_OK;
1187
1188 struct Buffer *buf = mutt_buffer_pool_get();
1189 mutt_buffer_printf(buf, "%s/new", mailbox_path(m));
1190 if (stat(mutt_buffer_string(buf), &st_new) == -1)
1191 {
1193 return MX_STATUS_ERROR;
1194 }
1195
1196 mutt_buffer_printf(buf, "%s/cur", mailbox_path(m));
1197 if (stat(mutt_buffer_string(buf), &st_cur) == -1)
1198 {
1200 return MX_STATUS_ERROR;
1201 }
1202
1203 /* determine which subdirectories need to be scanned */
1205 changed = MMC_NEW_DIR;
1207 changed |= MMC_CUR_DIR;
1208
1209 if (changed == MMC_NO_DIRS)
1210 {
1212 return MX_STATUS_OK; /* nothing to do */
1213 }
1214
1215 /* Update the modification times on the mailbox.
1216 *
1217 * The monitor code notices changes in the open mailbox too quickly.
1218 * In practice, this sometimes leads to all the new messages not being
1219 * noticed during the SAME group of mtime stat updates. To work around
1220 * the problem, don't update the stat times for a monitor caused check. */
1221#ifdef USE_INOTIFY
1223 MonitorContextChanged = false;
1224 else
1225#endif
1226 {
1229 }
1230
1231 /* do a fast scan of just the filenames in
1232 * the subdirectories that have changed. */
1233 struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
1234 if (changed & MMC_NEW_DIR)
1235 maildir_parse_dir(m, &mda, "new", NULL);
1236 if (changed & MMC_CUR_DIR)
1237 maildir_parse_dir(m, &mda, "cur", NULL);
1238
1239 /* we create a hash table keyed off the canonical (sans flags) filename
1240 * of each message we scanned. This is used in the loop over the
1241 * existing messages below to do some correlation. */
1243
1244 struct MdEmail *md = NULL;
1245 struct MdEmail **mdp = NULL;
1246 ARRAY_FOREACH(mdp, &mda)
1247 {
1248 md = *mdp;
1251 mutt_hash_insert(fnames, md->canon_fname, md);
1252 }
1253
1254 /* check for modifications and adjust flags */
1255 for (int i = 0; i < m->msg_count; i++)
1256 {
1257 struct Email *e = m->emails[i];
1258 if (!e)
1259 break;
1260
1261 e->active = false;
1263 md = mutt_hash_find(fnames, mutt_buffer_string(buf));
1264 if (md && md->email)
1265 {
1266 /* message already exists, merge flags */
1267 e->active = true;
1268
1269 /* check to see if the message has moved to a different
1270 * subdirectory. If so, update the associated filename. */
1271 if (!mutt_str_equal(e->path, md->email->path))
1272 mutt_str_replace(&e->path, md->email->path);
1273
1274 /* if the user hasn't modified the flags on this message, update
1275 * the flags we just detected. */
1276 if (!e->changed)
1277 if (maildir_update_flags(m, e, md->email))
1278 flags_changed = true;
1279
1280 if (e->deleted == e->trash)
1281 {
1282 if (e->deleted != md->email->deleted)
1283 {
1284 e->deleted = md->email->deleted;
1285 flags_changed = true;
1286 }
1287 }
1288 e->trash = md->email->trash;
1289
1290 /* this is a duplicate of an existing email, so remove it */
1291 email_free(&md->email);
1292 }
1293 /* This message was not in the list of messages we just scanned.
1294 * Check to see if we have enough information to know if the
1295 * message has disappeared out from underneath us. */
1296 else if (((changed & MMC_NEW_DIR) && mutt_strn_equal(e->path, "new/", 4)) ||
1297 ((changed & MMC_CUR_DIR) && mutt_strn_equal(e->path, "cur/", 4)))
1298 {
1299 /* This message disappeared, so we need to simulate a "reopen"
1300 * event. We know it disappeared because we just scanned the
1301 * subdirectory it used to reside in. */
1302 occult = true;
1303 e->deleted = true;
1304 e->purge = true;
1305 }
1306 else
1307 {
1308 /* This message resides in a subdirectory which was not
1309 * modified, so we assume that it is still present and
1310 * unchanged. */
1311 e->active = true;
1312 }
1313 }
1314
1315 /* destroy the file name hash */
1316 mutt_hash_free(&fnames);
1317
1318 /* If we didn't just get new mail, update the tables. */
1319 if (occult)
1321
1322 /* do any delayed parsing we need to do. */
1323 maildir_delayed_parsing(m, &mda, NULL);
1324
1325 /* Incorporate new messages */
1326 num_new = maildir_move_to_mailbox(m, &mda);
1327 if (num_new > 0)
1328 {
1330 m->changed = true;
1331 }
1332
1334
1335 ARRAY_FREE(&mda);
1336 if (occult)
1337 return MX_STATUS_REOPENED;
1338 if (num_new > 0)
1339 return MX_STATUS_NEW_MAIL;
1340 if (flags_changed)
1341 return MX_STATUS_FLAGS;
1342 return MX_STATUS_OK;
1343}
1344
1348static enum MxStatus maildir_mbox_check_stats(struct Mailbox *m, uint8_t flags)
1349{
1350 bool check_stats = flags & MUTT_MAILBOX_CHECK_FORCE_STATS;
1351 bool check_new = true;
1352 m->msg_new = 0;
1353
1354 if (check_stats)
1355 {
1356 m->msg_count = 0;
1357 m->msg_unread = 0;
1358 m->msg_flagged = 0;
1359 }
1360
1361 maildir_check_dir(m, "new", check_new, check_stats);
1362
1363 const bool c_maildir_check_cur = cs_subset_bool(NeoMutt->sub, "maildir_check_cur");
1364 check_new = !m->has_new && c_maildir_check_cur;
1365 if (check_new || check_stats)
1366 maildir_check_dir(m, "cur", check_new, check_stats);
1367
1369}
1370
1377static enum MxStatus maildir_mbox_sync(struct Mailbox *m)
1378{
1379 enum MxStatus check = maildir_mbox_check(m);
1380 if (check == MX_STATUS_ERROR)
1381 return check;
1382
1383 struct HeaderCache *hc = NULL;
1384#ifdef USE_HCACHE
1385 const char *const c_header_cache = cs_subset_path(NeoMutt->sub, "header_cache");
1386 if (m->type == MUTT_MAILDIR)
1387 hc = mutt_hcache_open(c_header_cache, mailbox_path(m), NULL);
1388#endif
1389
1390 struct Progress *progress = NULL;
1391 if (m->verbose)
1392 {
1393 char msg[PATH_MAX] = { 0 };
1394 snprintf(msg, sizeof(msg), _("Writing %s..."), mailbox_path(m));
1395 progress = progress_new(msg, MUTT_PROGRESS_WRITE, m->msg_count);
1396 }
1397
1398 for (int i = 0; i < m->msg_count; i++)
1399 {
1400 if (m->verbose)
1401 progress_update(progress, i, -1);
1402
1403 if (!maildir_sync_mailbox_message(m, i, hc))
1404 {
1405 progress_free(&progress);
1406 goto err;
1407 }
1408 }
1409 progress_free(&progress);
1410
1411#ifdef USE_HCACHE
1412 if (m->type == MUTT_MAILDIR)
1414#endif
1415
1416 /* XXX race condition? */
1417
1419
1420 /* adjust indices */
1421
1422 if (m->msg_deleted)
1423 {
1424 for (int i = 0, j = 0; i < m->msg_count; i++)
1425 {
1426 struct Email *e = m->emails[i];
1427 if (!e)
1428 break;
1429
1430 const bool c_maildir_trash = cs_subset_bool(NeoMutt->sub, "maildir_trash");
1431 if (!e->deleted || c_maildir_trash)
1432 e->index = j++;
1433 }
1434 }
1435
1436 return check;
1437
1438err:
1439#ifdef USE_HCACHE
1440 if (m->type == MUTT_MAILDIR)
1442#endif
1443 return MX_STATUS_ERROR;
1444}
1445
1451{
1452 return MX_STATUS_OK;
1453}
1454
1458static bool maildir_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
1459{
1460 struct Email *e = m->emails[msgno];
1461 if (!e)
1462 return false;
1463
1464 char path[PATH_MAX] = { 0 };
1465
1466 snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), e->path);
1467
1468 msg->fp = fopen(path, "r");
1469 if (!msg->fp && (errno == ENOENT))
1470 msg->fp = maildir_open_find_message(mailbox_path(m), e->path, NULL);
1471
1472 if (!msg->fp)
1473 {
1475 mutt_debug(LL_DEBUG1, "fopen: %s: %s (errno %d)\n", path, strerror(errno), errno);
1476 return false;
1477 }
1478
1479 return true;
1480}
1481
1490bool maildir_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
1491{
1492 int fd;
1493 char path[PATH_MAX] = { 0 };
1494 char suffix[16] = { 0 };
1495 char subdir[16] = { 0 };
1496
1497 if (e)
1498 {
1499 struct Email tmp = *e;
1500 tmp.deleted = false;
1501 tmp.edata = NULL;
1502 maildir_gen_flags(suffix, sizeof(suffix), &tmp);
1503 }
1504 else
1505 *suffix = '\0';
1506
1507 if (e && (e->read || e->old))
1508 mutt_str_copy(subdir, "cur", sizeof(subdir));
1509 else
1510 mutt_str_copy(subdir, "new", sizeof(subdir));
1511
1512 mode_t omask = umask(mh_umask(m));
1513 while (true)
1514 {
1515 snprintf(path, sizeof(path), "%s/tmp/%s.%lld.R%" PRIu64 ".%s%s",
1516 mailbox_path(m), subdir, (long long) mutt_date_now(),
1517 mutt_rand64(), NONULL(ShortHostname), suffix);
1518
1519 mutt_debug(LL_DEBUG2, "Trying %s\n", path);
1520
1521 fd = open(path, O_WRONLY | O_EXCL | O_CREAT, 0666);
1522 if (fd == -1)
1523 {
1524 if (errno != EEXIST)
1525 {
1526 umask(omask);
1528 return false;
1529 }
1530 }
1531 else
1532 {
1533 mutt_debug(LL_DEBUG2, "Success\n");
1534 msg->path = mutt_str_dup(path);
1535 break;
1536 }
1537 }
1538 umask(omask);
1539
1540 msg->fp = fdopen(fd, "w");
1541 if (!msg->fp)
1542 {
1543 FREE(&msg->path);
1544 close(fd);
1545 unlink(path);
1546 return false;
1547 }
1548
1549 return true;
1550}
1551
1555static int maildir_msg_commit(struct Mailbox *m, struct Message *msg)
1556{
1557 return maildir_commit_message(m, msg, NULL);
1558}
1559
1565static int maildir_msg_close(struct Mailbox *m, struct Message *msg)
1566{
1567 return mutt_file_fclose(&msg->fp);
1568}
1569
1573static int maildir_msg_save_hcache(struct Mailbox *m, struct Email *e)
1574{
1575 int rc = 0;
1576#ifdef USE_HCACHE
1577 const char *const c_header_cache = cs_subset_path(NeoMutt->sub, "header_cache");
1578 struct HeaderCache *hc = mutt_hcache_open(c_header_cache, mailbox_path(m), NULL);
1579 char *key = e->path + 3;
1580 int keylen = maildir_hcache_keylen(key);
1581 rc = mutt_hcache_store(hc, key, keylen, e, 0);
1583#endif
1584 return rc;
1585}
1586
1590static int maildir_path_canon(char *buf, size_t buflen)
1591{
1592 mutt_path_canon(buf, buflen, HomeDir, true);
1593 return 0;
1594}
1595
1599static int maildir_path_parent(char *buf, size_t buflen)
1600{
1601 if (mutt_path_parent(buf))
1602 return 0;
1603
1604 if (buf[0] == '~')
1605 mutt_path_canon(buf, buflen, HomeDir, true);
1606
1607 if (mutt_path_parent(buf))
1608 return 0;
1609
1610 return -1;
1611}
1612
1616static int maildir_path_pretty(char *buf, size_t buflen, const char *folder)
1617{
1618 if (mutt_path_abbr_folder(buf, folder))
1619 return 0;
1620
1621 if (mutt_path_pretty(buf, buflen, HomeDir, false))
1622 return 0;
1623
1624 return -1;
1625}
1626
1630static enum MailboxType maildir_path_probe(const char *path, const struct stat *st)
1631{
1632 if (!st || !S_ISDIR(st->st_mode))
1633 return MUTT_UNKNOWN;
1634
1635 char sub[PATH_MAX] = { 0 };
1636 struct stat stsub = { 0 };
1637 char *subs[] = { "cur", "new" };
1638 for (size_t i = 0; i < mutt_array_size(subs); ++i)
1639 {
1640 snprintf(sub, sizeof(sub), "%s/%s", path, subs[i]);
1641 if ((stat(sub, &stsub) == 0) && S_ISDIR(stsub.st_mode))
1642 return MUTT_MAILDIR;
1643 }
1644
1645 return MUTT_UNKNOWN;
1646}
1647
1652 // clang-format off
1653 .type = MUTT_MAILDIR,
1654 .name = "maildir",
1655 .is_local = true,
1656 .ac_owns_path = maildir_ac_owns_path,
1657 .ac_add = maildir_ac_add,
1658 .mbox_open = maildir_mbox_open,
1659 .mbox_open_append = maildir_mbox_open_append,
1660 .mbox_check = maildir_mbox_check,
1661 .mbox_check_stats = maildir_mbox_check_stats,
1662 .mbox_sync = maildir_mbox_sync,
1663 .mbox_close = maildir_mbox_close,
1664 .msg_open = maildir_msg_open,
1665 .msg_open_new = maildir_msg_open_new,
1666 .msg_commit = maildir_msg_commit,
1667 .msg_close = maildir_msg_close,
1668 .msg_padding_size = NULL,
1669 .msg_save_hcache = maildir_msg_save_hcache,
1670 .tags_edit = NULL,
1671 .tags_commit = NULL,
1672 .path_probe = maildir_path_probe,
1673 .path_canon = maildir_path_canon,
1674 .path_pretty = maildir_path_pretty,
1675 .path_parent = maildir_path_parent,
1676 .path_is_empty = maildir_check_empty,
1677 // clang-format on
1678};
#define ARRAY_SORT(head, fn)
Sort an array.
Definition: array.h:277
#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
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:365
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:168
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:485
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:78
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
Convenience wrapper for the config headers.
char * HomeDir
User's home directory.
Definition: globals.c:38
int mutt_copy_message(FILE *fp_out, struct Email *e, struct Message *msg, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Copy a message from a Mailbox.
Definition: copy.c:875
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.
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:432
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.
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:1598
int mutt_file_safe_rename(const char *src, const char *target)
NFS-safe renaming of files.
Definition: file.c:343
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:149
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:952
long mutt_file_get_size_fp(FILE *fp)
Get the size of a file.
Definition: file.c:1556
DIR * mutt_file_opendir(const char *path, enum MuttOpenDirMode mode)
Open a directory.
Definition: file.c:614
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:1638
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition: file.c:165
@ 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:39
SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: globals.c:58
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
#define mutt_perror(...)
Definition: logging.h:88
static bool maildir_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Implements MxOps::ac_add() -.
Definition: maildir.c:1089
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:1081
struct MxOps MxMaildirOps
Maildir Mailbox - Implements MxOps -.
Definition: maildir.c:1651
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:1348
static enum MxStatus maildir_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check() -.
Definition: maildir.c:1171
static enum MxStatus maildir_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close() -.
Definition: maildir.c:1450
static bool maildir_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
Definition: maildir.c:1110
static enum MxOpenReturns maildir_mbox_open(struct Mailbox *m)
Open a Mailbox - Implements MxOps::mbox_open() -.
Definition: maildir.c:1097
static enum MxStatus maildir_mbox_sync(struct Mailbox *m)
Save changes to the Mailbox - Implements MxOps::mbox_sync() -.
Definition: maildir.c:1377
static int maildir_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition: maildir.c:1565
static int maildir_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: maildir.c:1555
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:1490
static bool maildir_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
Open an email message in a Mailbox - Implements MxOps::msg_open() -.
Definition: maildir.c:1458
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:1573
static int maildir_path_canon(char *buf, size_t buflen)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition: maildir.c:1590
static int maildir_path_parent(char *buf, size_t buflen)
Find the parent of a Mailbox path - Implements MxOps::path_parent() -.
Definition: maildir.c:1599
static int maildir_path_pretty(char *buf, size_t buflen, const char *folder)
Abbreviate a Mailbox path - Implements MxOps::path_pretty() -.
Definition: maildir.c:1616
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:1630
static int maildir_cmp_inode(const void *a, const void *b)
Compare two Maildirs by inode number - Implements sort_t -.
Definition: maildir.c:495
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:108
Header cache multiplexor.
int mutt_hcache_store(struct HeaderCache *hc, const char *key, size_t keylen, struct Email *e, uint32_t uidvalidity)
Multiplexor for StoreOps::store.
Definition: hcache.c:610
void mutt_hcache_close(struct HeaderCache *hc)
Multiplexor for StoreOps::close.
Definition: hcache.c:483
struct HeaderCache * mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer)
Multiplexor for StoreOps::open.
Definition: hcache.c:373
int mutt_hcache_delete_record(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:689
static struct Email * restore(const unsigned char *d)
Restore an Email from data retrieved from the cache.
Definition: hcache.c:143
struct HCacheEntry mutt_hcache_fetch(struct HeaderCache *hc, const char *key, size_t keylen, uint32_t uidvalidity)
Multiplexor for StoreOps::fetch.
Definition: hcache.c:507
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:222
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition: mailbox.h:176
@ NT_MAILBOX_INVALID
Email list was changed.
Definition: mailbox.h:175
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:209
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:122
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:58
struct MaildirMboxData * maildir_mdata_new(void)
Create a new MaildirMboxData object.
Definition: mdata.c:47
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, struct MdEmailArray *mda)
Copy the Maildir list to the Mailbox.
Definition: shared.c:75
static size_t maildir_hcache_keylen(const char *fn)
Calculate the length of the Maildir path.
Definition: maildir.c:585
static void maildir_delayed_parsing(struct Mailbox *m, struct MdEmailArray *mda, struct Progress *progress)
This function does the second parsing pass.
Definition: maildir.c:598
static int ch_compare(const void *a, const void *b)
qsort() callback to sort characters
Definition: maildir.c:175
static int maildir_rewrite_message(struct Mailbox *m, int msgno)
Sync a message in an MH folder.
Definition: maildir.c:330
static int maildir_read_dir(struct Mailbox *m, const char *subdir)
Read a Maildir style mailbox.
Definition: maildir.c:674
static int maildir_sync_message(struct Mailbox *m, int msgno)
Sync an email to a Maildir folder.
Definition: maildir.c:387
FILE * maildir_open_find_message(const char *folder, const char *msg, char **newname)
Find a message by name.
Definition: maildir.c:998
#define MMC_CUR_DIR
'cur' directory changed
Definition: maildir.c:75
struct Email * maildir_parse_message(enum MailboxType type, const char *fname, bool is_old, struct Email *e)
Actually parse a maildir message.
Definition: maildir.c:931
static void maildir_canon_filename(struct Buffer *dest, const char *src)
Generate the canonical filename for a Maildir folder.
Definition: maildir.c:728
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:758
static void maildir_update_mtime(struct Mailbox *m)
Update our record of the Maildir modification time.
Definition: maildir.c:477
#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:86
static int maildir_parse_dir(struct Mailbox *m, struct MdEmailArray *mda, const char *subdir, struct Progress *progress)
Read a Maildir mailbox.
Definition: maildir.c:513
#define MMC_NEW_DIR
'new' directory changed
Definition: maildir.c:74
int maildir_check_empty(const char *path)
Is the mailbox empty.
Definition: maildir.c:1045
void maildir_gen_flags(char *dest, size_t destlen, struct Email *e)
Generate the Maildir flags for an email.
Definition: maildir.c:186
static int maildir_commit_message(struct Mailbox *m, struct Message *msg, struct Email *e)
Commit a message to a maildir folder.
Definition: maildir.c:237
struct Email * maildir_parse_stream(enum MailboxType type, FILE *fp, const char *fname, bool is_old, struct Email *e)
Parse a Maildir message.
Definition: maildir.c:884
bool maildir_sync_mailbox_message(struct Mailbox *m, int msgno, struct HeaderCache *hc)
Save changes to the mailbox.
Definition: maildir.c:951
void maildir_parse_flags(struct Email *e, const char *path)
Parse Maildir file flags.
Definition: maildir.c:812
struct MdEmail * maildir_entry_new(void)
Create a new Maildir entry.
Definition: mdemail.c:39
Maildir Email helper.
#define FREE(x)
Definition: memory.h:43
#define mutt_array_size(x)
Definition: memory.h:36
bool MonitorContextChanged
true after the current mailbox has changed
Definition: monitor.c:50
Monitor files for changes.
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool mutt_path_abbr_folder(char *buf, const char *folder)
Create a folder abbreviation.
Definition: path.c:490
bool mutt_path_pretty(char *buf, size_t buflen, const char *homedir, bool is_dir)
Tidy a filesystem path.
Definition: path.c:186
bool mutt_path_canon(char *buf, size_t buflen, const char *homedir, bool is_dir)
Create the canonical version of a path.
Definition: path.c:285
bool mutt_path_parent(char *buf)
Find the parent of a path.
Definition: path.c:458
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:807
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:496
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
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:652
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:326
#define PATH_MAX
Definition: mutt.h:41
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1200
struct Message * mx_msg_open(struct Mailbox *m, int msgno)
Return a stream pointer for a message.
Definition: mx.c:1154
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1062
API for mailboxes.
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition: mx.h:42
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition: mxapi.h:60
#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:66
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mxapi.h:63
MxOpenReturns
Return values for mbox_open()
Definition: mxapi.h:97
@ MX_OPEN_ERROR
Open failed with an error.
Definition: mxapi.h:99
@ MX_OPEN_OK
Open succeeded.
Definition: mxapi.h:98
#define MUTT_MAILBOX_CHECK_FORCE_STATS
Ignore MailboxType and calculate statistics.
Definition: mxapi.h:76
#define MUTT_APPENDNEW
Set in mx_open_mailbox_append if the mailbox doesn't exist.
Definition: mxapi.h:70
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_snc(), and mbox_close()
Definition: mxapi.h:84
@ MX_STATUS_ERROR
An error occurred.
Definition: mxapi.h:85
@ MX_STATUS_OK
No changes.
Definition: mxapi.h:86
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition: mxapi.h:90
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition: mxapi.h:89
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition: mxapi.h:87
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:1743
Notmuch-specific Mailbox data.
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition: parse.c:1162
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
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:86
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:118
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: random.c:129
GUI display the mailboxes in a side panel.
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 active
Message is not to be removed.
Definition: email.h:74
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
int msgno
Number displayed to the user.
Definition: email.h:110
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:99
uint32_t uidvalidity
IMAP-specific UIDVALIDITY.
Definition: lib.h:100
struct Email * email
Retrieved email.
Definition: lib.h:102
A Hash Table.
Definition: hash.h:96
Header cache structure.
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:141
bool changed
Mailbox has been modified.
Definition: mailbox.h:110
bool has_new
Mailbox has new mail.
Definition: mailbox.h:85
struct timespec mtime
Time Mailbox was last changed.
Definition: mailbox.h:104
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:132
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:105
bool verbose
Display status messages?
Definition: mailbox.h:114
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:37
struct timespec mtime_cur
Timestamp of the 'cur' dir.
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: mxapi.h:43
FILE * fp
pointer to the message data
Definition: mxapi.h:44
char * path
path to temp file
Definition: mxapi.h:45
char * committed_path
the final path generated by mx_msg_commit()
Definition: mxapi.h:46
time_t received
Time at which this message was received.
Definition: mxapi.h:55
Definition: mxapi.h:112
enum MailboxType type
Mailbox type, e.g. MUTT_IMAP.
Definition: mxapi.h:113
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39