NeoMutt  2021-02-05-666-ge300cd
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 <stdint.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <sys/stat.h>
46 #include <unistd.h>
47 #include <utime.h>
48 #include "private.h"
49 #include "mutt/lib.h"
50 #include "config/lib.h"
51 #include "email/lib.h"
52 #include "core/lib.h"
53 #include "lib.h"
54 #include "progress/lib.h"
55 #include "copy.h"
56 #include "edata.h"
57 #include "mdata.h"
58 #include "mdemail.h"
59 #include "monitor.h"
60 #include "mutt_globals.h"
61 #include "mx.h"
62 #ifdef USE_HCACHE
63 #include "hcache/lib.h"
64 #endif
65 #ifdef USE_NOTMUCH
66 #include "notmuch/lib.h"
67 #endif
68 
69 struct Progress;
70 
71 // Flags for maildir_mbox_check()
72 #define MMC_NO_DIRS 0
73 #define MMC_NEW_DIR (1 << 0)
74 #define MMC_CUR_DIR (1 << 1)
75 
76 
85 static void maildir_check_dir(struct Mailbox *m, const char *dir_name,
86  bool check_new, bool check_stats)
87 {
88  DIR *dirp = NULL;
89  struct dirent *de = NULL;
90  char *p = NULL;
91  struct stat sb;
92 
93  struct Buffer *path = mutt_buffer_pool_get();
94  struct Buffer *msgpath = mutt_buffer_pool_get();
95  mutt_buffer_printf(path, "%s/%s", mailbox_path(m), dir_name);
96 
97  /* when $mail_check_recent is set, if the new/ directory hasn't been modified since
98  * the user last exited the m, then we know there is no recent mail. */
99  const bool c_mail_check_recent =
100  cs_subset_bool(NeoMutt->sub, "mail_check_recent");
101  if (check_new && c_mail_check_recent)
102  {
103  if ((stat(mutt_buffer_string(path), &sb) == 0) &&
105  {
106  check_new = false;
107  }
108  }
109 
110  if (!(check_new || check_stats))
111  goto cleanup;
112 
113  dirp = opendir(mutt_buffer_string(path));
114  if (!dirp)
115  {
116  m->type = MUTT_UNKNOWN;
117  goto cleanup;
118  }
119 
120  while ((de = readdir(dirp)))
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), &sb) == 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(dirp);
161 
162 cleanup:
164  mutt_buffer_pool_release(&msgpath);
165 }
166 
175 static int ch_compare(const void *a, const void *b)
176 {
177  return (int) (*((const char *) a) - *((const char *) b));
178 }
179 
186 void 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];
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 
237 int maildir_commit_message(struct Mailbox *m, struct Message *msg, struct Email *e)
238 {
239  char subdir[4];
240  char suffix[16];
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_epoch(), 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)
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(
292  _("maildir_commit_message(): unable to set time on file"));
293  rc = -1;
294  goto cleanup;
295  }
296  }
297 
298 #ifdef USE_NOTMUCH
299  if (m->type == MUTT_NOTMUCH)
300  nm_update_filename(m, e->path, mutt_buffer_string(full), e);
301 #endif
302  if (e)
305  FREE(&msg->path);
306 
307  goto cleanup;
308  }
309  else if (errno != EEXIST)
310  {
312  rc = -1;
313  goto cleanup;
314  }
315  }
316 
317 cleanup:
320 
321  return rc;
322 }
323 
331 int maildir_rewrite_message(struct Mailbox *m, int msgno)
332 {
333  if (!m || !m->emails || (msgno >= m->msg_count))
334  return -1;
335 
336  struct Email *e = m->emails[msgno];
337  if (!e)
338  return -1;
339 
340  bool restore = true;
341 
342  long old_body_offset = e->body->offset;
343  long old_body_length = e->body->length;
344  long old_hdr_lines = e->lines;
345 
346  struct Message *dest = mx_msg_open_new(m, e, MUTT_MSG_NO_FLAGS);
347  if (!dest)
348  return -1;
349 
350  int rc = mutt_copy_message(dest->fp, m, e, dest, MUTT_CM_UPDATE,
351  CH_UPDATE | CH_UPDATE_LEN, 0);
352  if (rc == 0)
353  {
354  char oldpath[PATH_MAX];
355  char partpath[PATH_MAX];
356  snprintf(oldpath, sizeof(oldpath), "%s/%s", mailbox_path(m), e->path);
357  mutt_str_copy(partpath, e->path, sizeof(partpath));
358 
359  rc = maildir_commit_message(m, dest, e);
360 
361  if (rc == 0)
362  {
363  unlink(oldpath);
364  restore = false;
365  }
366  }
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 
376  mutt_body_free(&e->body->parts);
377  return rc;
378 }
379 
387 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];
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 
446  if (mutt_str_equal(mutt_buffer_string(fullpath), mutt_buffer_string(oldpath)))
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  }
461  mutt_str_replace(&e->path, mutt_buffer_string(partpath));
462  }
463 
464 cleanup:
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 
478 {
479  char buf[PATH_MAX];
480  struct stat st;
482 
483  snprintf(buf, sizeof(buf), "%s/%s", mailbox_path(m), "cur");
484  if (stat(buf, &st) == 0)
486  snprintf(buf, sizeof(buf), "%s/%s", mailbox_path(m), "new");
487 
488  if (stat(buf, &st) == 0)
490 }
491 
495 static 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 
513 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  const bool c_mark_old = cs_subset_bool(NeoMutt->sub, "mark_old");
526  is_old = c_mark_old ? mutt_str_equal("cur", subdir) : false;
527 
528  DIR *dirp = opendir(mutt_buffer_string(buf));
529  if (!dirp)
530  {
531  rc = -1;
532  goto cleanup;
533  }
534 
535  while (((de = readdir(dirp))) && !SigInt)
536  {
537  if (*de->d_name == '.')
538  continue;
539 
540  /* FOO - really ignore the return value? */
541  mutt_debug(LL_DEBUG2, "queueing %s\n", de->d_name);
542 
543  e = email_new();
544  e->edata = maildir_edata_new();
546 
547  e->old = is_old;
548  maildir_parse_flags(e, de->d_name);
549 
550  if (m->verbose && progress)
551  progress_update(progress, ARRAY_SIZE(mda) + 1, -1);
552 
553  mutt_buffer_printf(buf, "%s/%s", subdir, de->d_name);
554  e->path = mutt_buffer_strdup(buf);
555 
556  entry = maildir_entry_new();
557  entry->email = e;
558  entry->inode = de->d_ino;
559  ARRAY_ADD(mda, entry);
560  }
561 
562  closedir(dirp);
563 
564  if (SigInt)
565  {
566  SigInt = false;
567  return -2; /* action aborted */
568  }
569 
571 
572 cleanup:
574 
575  return rc;
576 }
577 
585 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 
597 void maildir_delayed_parsing(struct Mailbox *m, struct MdEmailArray *mda,
598  struct Progress *progress)
599 {
600  char fn[PATH_MAX];
601 
602 #ifdef USE_HCACHE
603  const char *const c_header_cache =
604  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 lastchanged = { 0 };
623  int rc = 0;
624  const bool c_maildir_header_cache_verify =
625  cs_subset_bool(NeoMutt->sub, "maildir_header_cache_verify");
626  if (c_maildir_header_cache_verify)
627  {
628  rc = stat(fn, &lastchanged);
629  }
630 
631  const char *key = md->email->path + 3;
632  size_t keylen = maildir_hcache_keylen(key);
633  struct HCacheEntry hce = mutt_hcache_fetch(hc, key, keylen, 0);
634 
635  if (hce.email && (rc == 0) && (lastchanged.st_mtime <= hce.uidvalidity))
636  {
637  hce.email->edata = maildir_edata_new();
639  hce.email->old = md->email->old;
640  hce.email->path = mutt_str_dup(md->email->path);
641  email_free(&md->email);
642  md->email = hce.email;
643  maildir_parse_flags(md->email, fn);
644  }
645  else
646 #endif
647  {
648  if (maildir_parse_message(m->type, fn, md->email->old, md->email))
649  {
650  md->header_parsed = true;
651 #ifdef USE_HCACHE
652  key = md->email->path + 3;
653  keylen = maildir_hcache_keylen(key);
654  mutt_hcache_store(hc, key, keylen, md->email, 0);
655 #endif
656  }
657  else
658  email_free(&md->email);
659  }
660  }
661 #ifdef USE_HCACHE
662  mutt_hcache_close(hc);
663 #endif
664 }
665 
673 int maildir_read_dir(struct Mailbox *m, const char *subdir)
674 {
675  if (!m)
676  return -1;
677 
678  struct Progress *progress = NULL;
679 
680  if (m->verbose)
681  {
682  char msg[PATH_MAX];
683  snprintf(msg, sizeof(msg), _("Scanning %s..."), mailbox_path(m));
684  progress = progress_new(msg, MUTT_PROGRESS_READ, 0);
685  }
686 
688  if (!mdata)
689  {
690  mdata = maildir_mdata_new();
691  m->mdata = mdata;
693  }
694 
695  struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
696  int rc = maildir_parse_dir(m, &mda, subdir, progress);
697  progress_free(&progress);
698  if (rc < 0)
699  return -1;
700 
701  if (m->verbose)
702  {
703  char msg[PATH_MAX];
704  snprintf(msg, sizeof(msg), _("Reading %s..."), mailbox_path(m));
705  progress = progress_new(msg, MUTT_PROGRESS_READ, ARRAY_SIZE(&mda));
706  }
707  maildir_delayed_parsing(m, &mda, progress);
708  progress_free(&progress);
709 
710  maildir_move_to_mailbox(m, &mda);
711 
712  if (!mdata->mh_umask)
713  mdata->mh_umask = mh_umask(m);
714 
715  return 0;
716 }
717 
727 void maildir_canon_filename(struct Buffer *dest, const char *src)
728 {
729  if (!dest || !src)
730  return;
731 
732  char *t = strrchr(src, '/');
733  if (t)
734  src = t + 1;
735 
736  mutt_buffer_strcpy(dest, src);
737  char *u = strpbrk(dest->data, ",:");
738  if (u)
739  {
740  *u = '\0';
741  dest->dptr = u;
742  }
743 }
744 
757 static FILE *maildir_open_find_message_dir(const char *folder, const char *unique,
758  const char *subfolder, char **newname)
759 {
760  struct Buffer *dir = mutt_buffer_pool_get();
761  struct Buffer *tunique = mutt_buffer_pool_get();
762  struct Buffer *fname = mutt_buffer_pool_get();
763 
764  struct dirent *de = NULL;
765 
766  FILE *fp = NULL;
767  int oe = ENOENT;
768 
769  mutt_buffer_printf(dir, "%s/%s", folder, subfolder);
770 
771  DIR *dp = opendir(mutt_buffer_string(dir));
772  if (!dp)
773  {
774  errno = ENOENT;
775  goto cleanup;
776  }
777 
778  while ((de = readdir(dp)))
779  {
780  maildir_canon_filename(tunique, de->d_name);
781 
782  if (mutt_str_equal(mutt_buffer_string(tunique), unique))
783  {
784  mutt_buffer_printf(fname, "%s/%s/%s", folder, subfolder, de->d_name);
785  fp = fopen(mutt_buffer_string(fname), "r");
786  oe = errno;
787  break;
788  }
789  }
790 
791  closedir(dp);
792 
793  if (newname && fp)
794  *newname = mutt_buffer_strdup(fname);
795 
796  errno = oe;
797 
798 cleanup:
800  mutt_buffer_pool_release(&tunique);
801  mutt_buffer_pool_release(&fname);
802 
803  return fp;
804 }
805 
811 void maildir_parse_flags(struct Email *e, const char *path)
812 {
813  char *q = NULL;
814 
815  e->flagged = false;
816  e->read = false;
817  e->replied = false;
818 
820 
821  char *p = strrchr(path, ':');
822  if (p && mutt_str_startswith(p + 1, "2,"))
823  {
824  p += 3;
825 
826  mutt_str_replace(&edata->maildir_flags, p);
827  q = edata->maildir_flags;
828 
829  while (*p)
830  {
831  switch (*p)
832  {
833  case 'F':
834  e->flagged = true;
835  break;
836 
837  case 'R': /* replied */
838  e->replied = true;
839  break;
840 
841  case 'S': /* seen */
842  e->read = true;
843  break;
844 
845  case 'T': /* trashed */
846  {
847  const bool c_flag_safe = cs_subset_bool(NeoMutt->sub, "flag_safe");
848  if (!e->flagged || !c_flag_safe)
849  {
850  e->trash = true;
851  e->deleted = true;
852  }
853  break;
854  }
855 
856  default:
857  *q++ = *p;
858  break;
859  }
860  p++;
861  }
862  }
863 
864  if (q == edata->maildir_flags)
865  FREE(&edata->maildir_flags);
866  else if (q)
867  *q = '\0';
868 }
869 
882 struct Email *maildir_parse_stream(enum MailboxType type, FILE *fp,
883  const char *fname, bool is_old, struct Email *e)
884 {
885  if (!e)
886  {
887  e = email_new();
888  e->edata = maildir_edata_new();
890  }
891  e->env = mutt_rfc822_read_header(fp, e, false, false);
892 
893  struct stat st;
894  fstat(fileno(fp), &st);
895 
896  if (!e->received)
897  e->received = e->date_sent;
898 
899  /* always update the length since we have fresh information available. */
900  e->body->length = st.st_size - e->body->offset;
901 
902  e->index = -1;
903 
904  if (type == MUTT_MAILDIR)
905  {
906  /* maildir stores its flags in the filename, so ignore the
907  * flags in the header of the message */
908 
909  e->old = is_old;
910  maildir_parse_flags(e, fname);
911  }
912  return e;
913 }
914 
926 struct Email *maildir_parse_message(enum MailboxType type, const char *fname,
927  bool is_old, struct Email *e)
928 {
929  FILE *fp = fopen(fname, "r");
930  if (!fp)
931  return NULL;
932 
933  e = maildir_parse_stream(type, fp, fname, is_old, e);
934  mutt_file_fclose(&fp);
935  return e;
936 }
937 
946 bool maildir_sync_mailbox_message(struct Mailbox *m, int msgno, struct HeaderCache *hc)
947 {
948  struct Email *e = m->emails[msgno];
949  if (!e)
950  return false;
951 
952  const bool c_maildir_trash = cs_subset_bool(NeoMutt->sub, "maildir_trash");
953  if (e->deleted && !c_maildir_trash)
954  {
955  char path[PATH_MAX];
956  snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), e->path);
957 #ifdef USE_HCACHE
958  if (hc)
959  {
960  const char *key = e->path + 3;
961  size_t keylen = maildir_hcache_keylen(key);
962  mutt_hcache_delete_record(hc, key, keylen);
963  }
964 #endif
965  unlink(path);
966  }
967  else if (e->changed || e->attach_del ||
968  ((c_maildir_trash || e->trash) && (e->deleted != e->trash)))
969  {
970  if (maildir_sync_message(m, msgno) == -1)
971  return false;
972  }
973 
974 #ifdef USE_HCACHE
975  if (hc && e->changed)
976  {
977  const char *key = e->path + 3;
978  size_t keylen = maildir_hcache_keylen(key);
979  mutt_hcache_store(hc, key, keylen, e, 0);
980  }
981 #endif
982 
983  return true;
984 }
985 
993 FILE *maildir_open_find_message(const char *folder, const char *msg, char **newname)
994 {
995  static unsigned int new_hits = 0, cur_hits = 0; /* simple dynamic optimization */
996 
997  struct Buffer *unique = mutt_buffer_pool_get();
998  maildir_canon_filename(unique, msg);
999 
1000  FILE *fp = maildir_open_find_message_dir(folder, mutt_buffer_string(unique),
1001  (new_hits > cur_hits) ? "new" : "cur", newname);
1002  if (fp || (errno != ENOENT))
1003  {
1004  if ((new_hits < UINT_MAX) && (cur_hits < UINT_MAX))
1005  {
1006  new_hits += ((new_hits > cur_hits) ? 1 : 0);
1007  cur_hits += ((new_hits > cur_hits) ? 0 : 1);
1008  }
1009 
1010  goto cleanup;
1011  }
1013  (new_hits > cur_hits) ? "cur" : "new", newname);
1014  if (fp || (errno != ENOENT))
1015  {
1016  if ((new_hits < UINT_MAX) && (cur_hits < UINT_MAX))
1017  {
1018  new_hits += ((new_hits > cur_hits) ? 0 : 1);
1019  cur_hits += ((new_hits > cur_hits) ? 1 : 0);
1020  }
1021 
1022  goto cleanup;
1023  }
1024 
1025  fp = NULL;
1026 
1027 cleanup:
1028  mutt_buffer_pool_release(&unique);
1029 
1030  return fp;
1031 }
1032 
1040 int maildir_check_empty(const char *path)
1041 {
1042  DIR *dp = NULL;
1043  struct dirent *de = NULL;
1044  int rc = 1; /* assume empty until we find a message */
1045  char realpath[PATH_MAX];
1046  int iter = 0;
1047 
1048  /* Strategy here is to look for any file not beginning with a period */
1049 
1050  do
1051  {
1052  /* we do "cur" on the first iteration since it's more likely that we'll
1053  * find old messages without having to scan both subdirs */
1054  snprintf(realpath, sizeof(realpath), "%s/%s", path, (iter == 0) ? "cur" : "new");
1055  dp = opendir(realpath);
1056  if (!dp)
1057  return -1;
1058  while ((de = readdir(dp)))
1059  {
1060  if (*de->d_name != '.')
1061  {
1062  rc = 0;
1063  break;
1064  }
1065  }
1066  closedir(dp);
1067  iter++;
1068  } while (rc && iter < 2);
1069 
1070  return rc;
1071 }
1072 
1076 bool maildir_ac_owns_path(struct Account *a, const char *path)
1077 {
1078  return true;
1079 }
1080 
1084 bool maildir_ac_add(struct Account *a, struct Mailbox *m)
1085 {
1086  return true;
1087 }
1088 
1093 {
1094  /* maildir looks sort of like MH, except that there are two subdirectories
1095  * of the main folder path from which to read messages */
1096  if ((maildir_read_dir(m, "new") == -1) || (maildir_read_dir(m, "cur") == -1))
1097  return MX_OPEN_ERROR;
1098 
1099  return MX_OPEN_OK;
1100 }
1101 
1106 {
1107  if (!(flags & (MUTT_APPEND | MUTT_APPENDNEW | MUTT_NEWFOLDER)))
1108  {
1109  return true;
1110  }
1111 
1112  errno = 0;
1113  if ((mutt_file_mkdir(mailbox_path(m), S_IRWXU) != 0) && (errno != EEXIST))
1114  {
1116  return false;
1117  }
1118 
1119  char tmp[PATH_MAX];
1120  snprintf(tmp, sizeof(tmp), "%s/cur", mailbox_path(m));
1121  errno = 0;
1122  if ((mkdir(tmp, S_IRWXU) != 0) && (errno != EEXIST))
1123  {
1124  mutt_perror(tmp);
1125  rmdir(mailbox_path(m));
1126  return false;
1127  }
1128 
1129  snprintf(tmp, sizeof(tmp), "%s/new", mailbox_path(m));
1130  errno = 0;
1131  if ((mkdir(tmp, S_IRWXU) != 0) && (errno != EEXIST))
1132  {
1133  mutt_perror(tmp);
1134  snprintf(tmp, sizeof(tmp), "%s/cur", mailbox_path(m));
1135  rmdir(tmp);
1136  rmdir(mailbox_path(m));
1137  return false;
1138  }
1139 
1140  snprintf(tmp, sizeof(tmp), "%s/tmp", mailbox_path(m));
1141  errno = 0;
1142  if ((mkdir(tmp, S_IRWXU) != 0) && (errno != EEXIST))
1143  {
1144  mutt_perror(tmp);
1145  snprintf(tmp, sizeof(tmp), "%s/cur", mailbox_path(m));
1146  rmdir(tmp);
1147  snprintf(tmp, sizeof(tmp), "%s/new", mailbox_path(m));
1148  rmdir(tmp);
1149  rmdir(mailbox_path(m));
1150  return false;
1151  }
1152 
1153  return true;
1154 }
1155 
1167 {
1168  struct stat st_new; /* status of the "new" subdirectory */
1169  struct stat st_cur; /* status of the "cur" subdirectory */
1170  int changed = MMC_NO_DIRS; /* which subdirectories have changed */
1171  bool occult = false; /* messages were removed from the mailbox */
1172  int num_new = 0; /* number of new messages added to the mailbox */
1173  bool flags_changed = false; /* message flags were changed in the mailbox */
1174  struct HashTable *fnames = NULL; /* hash table for quickly looking up the base filename
1175  for a maildir message */
1177 
1178  /* XXX seems like this check belongs in mx_mbox_check() rather than here. */
1179  const bool c_check_new = cs_subset_bool(NeoMutt->sub, "check_new");
1180  if (!c_check_new)
1181  return MX_STATUS_OK;
1182 
1183  struct Buffer *buf = mutt_buffer_pool_get();
1184  mutt_buffer_printf(buf, "%s/new", mailbox_path(m));
1185  if (stat(mutt_buffer_string(buf), &st_new) == -1)
1186  {
1188  return MX_STATUS_ERROR;
1189  }
1190 
1191  mutt_buffer_printf(buf, "%s/cur", mailbox_path(m));
1192  if (stat(mutt_buffer_string(buf), &st_cur) == -1)
1193  {
1195  return MX_STATUS_ERROR;
1196  }
1197 
1198  /* determine which subdirectories need to be scanned */
1199  if (mutt_file_stat_timespec_compare(&st_new, MUTT_STAT_MTIME, &m->mtime) > 0)
1200  changed = MMC_NEW_DIR;
1201  if (mutt_file_stat_timespec_compare(&st_cur, MUTT_STAT_MTIME, &mdata->mtime_cur) > 0)
1202  changed |= MMC_CUR_DIR;
1203 
1204  if (changed == MMC_NO_DIRS)
1205  {
1207  return MX_STATUS_OK; /* nothing to do */
1208  }
1209 
1210  /* Update the modification times on the mailbox.
1211  *
1212  * The monitor code notices changes in the open mailbox too quickly.
1213  * In practice, this sometimes leads to all the new messages not being
1214  * noticed during the SAME group of mtime stat updates. To work around
1215  * the problem, don't update the stat times for a monitor caused check. */
1216 #ifdef USE_INOTIFY
1218  MonitorContextChanged = false;
1219  else
1220 #endif
1221  {
1224  }
1225 
1226  /* do a fast scan of just the filenames in
1227  * the subdirectories that have changed. */
1228  struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
1229  if (changed & MMC_NEW_DIR)
1230  maildir_parse_dir(m, &mda, "new", NULL);
1231  if (changed & MMC_CUR_DIR)
1232  maildir_parse_dir(m, &mda, "cur", NULL);
1233 
1234  /* we create a hash table keyed off the canonical (sans flags) filename
1235  * of each message we scanned. This is used in the loop over the
1236  * existing messages below to do some correlation. */
1237  fnames = mutt_hash_new(ARRAY_SIZE(&mda), MUTT_HASH_NO_FLAGS);
1238 
1239  struct MdEmail *md = NULL;
1240  struct MdEmail **mdp = NULL;
1241  ARRAY_FOREACH(mdp, &mda)
1242  {
1243  md = *mdp;
1244  maildir_canon_filename(buf, md->email->path);
1245  md->canon_fname = mutt_buffer_strdup(buf);
1246  mutt_hash_insert(fnames, md->canon_fname, md);
1247  }
1248 
1249  /* check for modifications and adjust flags */
1250  for (int i = 0; i < m->msg_count; i++)
1251  {
1252  struct Email *e = m->emails[i];
1253  if (!e)
1254  break;
1255 
1256  e->active = false;
1257  maildir_canon_filename(buf, e->path);
1258  md = mutt_hash_find(fnames, mutt_buffer_string(buf));
1259  if (md && md->email)
1260  {
1261  /* message already exists, merge flags */
1262  e->active = true;
1263 
1264  /* check to see if the message has moved to a different
1265  * subdirectory. If so, update the associated filename. */
1266  if (!mutt_str_equal(e->path, md->email->path))
1267  mutt_str_replace(&e->path, md->email->path);
1268 
1269  /* if the user hasn't modified the flags on this message, update
1270  * the flags we just detected. */
1271  if (!e->changed)
1272  if (maildir_update_flags(m, e, md->email))
1273  flags_changed = true;
1274 
1275  if (e->deleted == e->trash)
1276  {
1277  if (e->deleted != md->email->deleted)
1278  {
1279  e->deleted = md->email->deleted;
1280  flags_changed = true;
1281  }
1282  }
1283  e->trash = md->email->trash;
1284 
1285  /* this is a duplicate of an existing email, so remove it */
1286  email_free(&md->email);
1287  }
1288  /* This message was not in the list of messages we just scanned.
1289  * Check to see if we have enough information to know if the
1290  * message has disappeared out from underneath us. */
1291  else if (((changed & MMC_NEW_DIR) && mutt_strn_equal(e->path, "new/", 4)) ||
1292  ((changed & MMC_CUR_DIR) && mutt_strn_equal(e->path, "cur/", 4)))
1293  {
1294  /* This message disappeared, so we need to simulate a "reopen"
1295  * event. We know it disappeared because we just scanned the
1296  * subdirectory it used to reside in. */
1297  occult = true;
1298  e->deleted = true;
1299  e->purge = true;
1300  }
1301  else
1302  {
1303  /* This message resides in a subdirectory which was not
1304  * modified, so we assume that it is still present and
1305  * unchanged. */
1306  e->active = true;
1307  }
1308  }
1309 
1310  /* destroy the file name hash */
1311  mutt_hash_free(&fnames);
1312 
1313  /* If we didn't just get new mail, update the tables. */
1314  if (occult)
1316 
1317  /* do any delayed parsing we need to do. */
1318  maildir_delayed_parsing(m, &mda, NULL);
1319 
1320  /* Incorporate new messages */
1321  num_new = maildir_move_to_mailbox(m, &mda);
1322  if (num_new > 0)
1323  {
1325  m->changed = true;
1326  }
1327 
1329 
1330  ARRAY_FREE(&mda);
1331  if (occult)
1332  return MX_STATUS_REOPENED;
1333  if (num_new > 0)
1334  return MX_STATUS_NEW_MAIL;
1335  if (flags_changed)
1336  return MX_STATUS_FLAGS;
1337  return MX_STATUS_OK;
1338 }
1339 
1343 static enum MxStatus maildir_mbox_check_stats(struct Mailbox *m, uint8_t flags)
1344 {
1345  bool check_stats = flags;
1346  bool check_new = true;
1347 
1348  if (check_stats)
1349  {
1350  m->msg_count = 0;
1351  m->msg_unread = 0;
1352  m->msg_flagged = 0;
1353  m->msg_new = 0;
1354  }
1355 
1356  maildir_check_dir(m, "new", check_new, check_stats);
1357 
1358  const bool c_maildir_check_cur =
1359  cs_subset_bool(NeoMutt->sub, "maildir_check_cur");
1360  check_new = !m->has_new && c_maildir_check_cur;
1361  if (check_new || check_stats)
1362  maildir_check_dir(m, "cur", check_new, check_stats);
1363 
1364  return m->msg_new ? MX_STATUS_NEW_MAIL : MX_STATUS_OK;
1365 }
1366 
1374 {
1375  enum MxStatus check = maildir_mbox_check(m);
1376  if (check == MX_STATUS_ERROR)
1377  return check;
1378 
1379  struct HeaderCache *hc = NULL;
1380 #ifdef USE_HCACHE
1381  const char *const c_header_cache =
1382  cs_subset_path(NeoMutt->sub, "header_cache");
1383  if (m->type == MUTT_MAILDIR)
1384  hc = mutt_hcache_open(c_header_cache, mailbox_path(m), NULL);
1385 #endif
1386 
1387  struct Progress *progress = NULL;
1388  if (m->verbose)
1389  {
1390  char msg[PATH_MAX];
1391  snprintf(msg, sizeof(msg), _("Writing %s..."), mailbox_path(m));
1392  progress = progress_new(msg, MUTT_PROGRESS_WRITE, m->msg_count);
1393  }
1394 
1395  for (int i = 0; i < m->msg_count; i++)
1396  {
1397  if (m->verbose)
1398  progress_update(progress, i, -1);
1399 
1400  if (!maildir_sync_mailbox_message(m, i, hc))
1401  {
1402  progress_free(&progress);
1403  goto err;
1404  }
1405  }
1406  progress_free(&progress);
1407 
1408 #ifdef USE_HCACHE
1409  if (m->type == MUTT_MAILDIR)
1410  mutt_hcache_close(hc);
1411 #endif
1412 
1413  /* XXX race condition? */
1414 
1416 
1417  /* adjust indices */
1418 
1419  if (m->msg_deleted)
1420  {
1421  for (int i = 0, j = 0; i < m->msg_count; i++)
1422  {
1423  struct Email *e = m->emails[i];
1424  if (!e)
1425  break;
1426 
1427  const bool c_maildir_trash =
1428  cs_subset_bool(NeoMutt->sub, "maildir_trash");
1429  if (!e->deleted || c_maildir_trash)
1430  e->index = j++;
1431  }
1432  }
1433 
1434  return check;
1435 
1436 err:
1437 #ifdef USE_HCACHE
1438  if (m->type == MUTT_MAILDIR)
1439  mutt_hcache_close(hc);
1440 #endif
1441  return MX_STATUS_ERROR;
1442 }
1443 
1449 {
1450  return MX_STATUS_OK;
1451 }
1452 
1456 static bool maildir_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
1457 {
1458  struct Email *e = m->emails[msgno];
1459  if (!e)
1460  return false;
1461 
1462  char path[PATH_MAX];
1463 
1464  snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), e->path);
1465 
1466  msg->fp = fopen(path, "r");
1467  if (!msg->fp && (errno == ENOENT))
1468  msg->fp = maildir_open_find_message(mailbox_path(m), e->path, NULL);
1469 
1470  if (!msg->fp)
1471  {
1472  mutt_perror(path);
1473  mutt_debug(LL_DEBUG1, "fopen: %s: %s (errno %d)\n", path, strerror(errno), errno);
1474  return false;
1475  }
1476 
1477  return true;
1478 }
1479 
1488 bool maildir_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
1489 {
1490  int fd;
1491  char path[PATH_MAX];
1492  char suffix[16];
1493  char subdir[16];
1494 
1495  if (e)
1496  {
1497  struct Email tmp = *e;
1498  tmp.deleted = false;
1499  tmp.edata = NULL;
1500  maildir_gen_flags(suffix, sizeof(suffix), &tmp);
1501  }
1502  else
1503  *suffix = '\0';
1504 
1505  if (e && (e->read || e->old))
1506  mutt_str_copy(subdir, "cur", sizeof(subdir));
1507  else
1508  mutt_str_copy(subdir, "new", sizeof(subdir));
1509 
1510  mode_t omask = umask(mh_umask(m));
1511  while (true)
1512  {
1513  snprintf(path, sizeof(path), "%s/tmp/%s.%lld.R%" PRIu64 ".%s%s",
1514  mailbox_path(m), subdir, (long long) mutt_date_epoch(),
1515  mutt_rand64(), NONULL(ShortHostname), suffix);
1516 
1517  mutt_debug(LL_DEBUG2, "Trying %s\n", path);
1518 
1519  fd = open(path, O_WRONLY | O_EXCL | O_CREAT, 0666);
1520  if (fd == -1)
1521  {
1522  if (errno != EEXIST)
1523  {
1524  umask(omask);
1525  mutt_perror(path);
1526  return false;
1527  }
1528  }
1529  else
1530  {
1531  mutt_debug(LL_DEBUG2, "Success\n");
1532  msg->path = mutt_str_dup(path);
1533  break;
1534  }
1535  }
1536  umask(omask);
1537 
1538  msg->fp = fdopen(fd, "w");
1539  if (!msg->fp)
1540  {
1541  FREE(&msg->path);
1542  close(fd);
1543  unlink(path);
1544  return false;
1545  }
1546 
1547  return true;
1548 }
1549 
1553 static int maildir_msg_commit(struct Mailbox *m, struct Message *msg)
1554 {
1555  return maildir_commit_message(m, msg, NULL);
1556 }
1557 
1563 int maildir_msg_close(struct Mailbox *m, struct Message *msg)
1564 {
1565  return mutt_file_fclose(&msg->fp);
1566 }
1567 
1571 static int maildir_msg_save_hcache(struct Mailbox *m, struct Email *e)
1572 {
1573  int rc = 0;
1574 #ifdef USE_HCACHE
1575  const char *const c_header_cache =
1576  cs_subset_path(NeoMutt->sub, "header_cache");
1577  struct HeaderCache *hc = mutt_hcache_open(c_header_cache, mailbox_path(m), NULL);
1578  char *key = e->path + 3;
1579  int keylen = maildir_hcache_keylen(key);
1580  rc = mutt_hcache_store(hc, key, keylen, e, 0);
1581  mutt_hcache_close(hc);
1582 #endif
1583  return rc;
1584 }
1585 
1589 int maildir_path_canon(char *buf, size_t buflen)
1590 {
1591  mutt_path_canon(buf, buflen, HomeDir, true);
1592  return 0;
1593 }
1594 
1598 int maildir_path_parent(char *buf, size_t buflen)
1599 {
1600  if (mutt_path_parent(buf, buflen))
1601  return 0;
1602 
1603  if (buf[0] == '~')
1604  mutt_path_canon(buf, buflen, HomeDir, true);
1605 
1606  if (mutt_path_parent(buf, buflen))
1607  return 0;
1608 
1609  return -1;
1610 }
1611 
1615 int maildir_path_pretty(char *buf, size_t buflen, const char *folder)
1616 {
1617  if (mutt_path_abbr_folder(buf, buflen, folder))
1618  return 0;
1619 
1620  if (mutt_path_pretty(buf, buflen, HomeDir, false))
1621  return 0;
1622 
1623  return -1;
1624 }
1625 
1629 static enum MailboxType maildir_path_probe(const char *path, const struct stat *st)
1630 {
1631  if (!st || !S_ISDIR(st->st_mode))
1632  return MUTT_UNKNOWN;
1633 
1634  char cur[PATH_MAX];
1635  snprintf(cur, sizeof(cur), "%s/cur", path);
1636 
1637  struct stat stc;
1638  if ((stat(cur, &stc) == 0) && S_ISDIR(stc.st_mode))
1639  return MUTT_MAILDIR;
1640 
1641  return MUTT_UNKNOWN;
1642 }
1643 
1644 // clang-format off
1648 struct MxOps MxMaildirOps = {
1649  .type = MUTT_MAILDIR,
1650  .name = "maildir",
1651  .is_local = true,
1652  .ac_owns_path = maildir_ac_owns_path,
1653  .ac_add = maildir_ac_add,
1654  .mbox_open = maildir_mbox_open,
1655  .mbox_open_append = maildir_mbox_open_append,
1656  .mbox_check = maildir_mbox_check,
1657  .mbox_check_stats = maildir_mbox_check_stats,
1658  .mbox_sync = maildir_mbox_sync,
1659  .mbox_close = maildir_mbox_close,
1660  .msg_open = maildir_msg_open,
1661  .msg_open_new = maildir_msg_open_new,
1662  .msg_commit = maildir_msg_commit,
1663  .msg_close = maildir_msg_close,
1664  .msg_padding_size = NULL,
1665  .msg_save_hcache = maildir_msg_save_hcache,
1666  .tags_edit = NULL,
1667  .tags_commit = NULL,
1668  .path_probe = maildir_path_probe,
1669  .path_canon = maildir_path_canon,
1670  .path_pretty = maildir_path_pretty,
1671  .path_parent = maildir_path_parent,
1672  .path_is_empty = maildir_check_empty,
1673 };
1674 // clang-format on
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:354
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition: mx.h:41
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:215
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:327
void maildir_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free()
Definition: mdata.c:37
int maildir_commit_message(struct Mailbox *m, struct Message *msg, struct Email *e)
Commit a message to a maildir folder.
Definition: maildir.c:237
int lines
How many lines in the body of this message?
Definition: email.h:85
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
#define NONULL(x)
Definition: string2.h:37
void maildir_canon_filename(struct Buffer *dest, const char *src)
Generate the canonical filename for a Maildir folder.
Definition: maildir.c:727
bool maildir_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Implements MxOps::ac_add() -.
Definition: maildir.c:1084
int msg_count
Total number of messages.
Definition: mailbox.h:91
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
The envelope/body of an email.
Definition: email.h:37
A Hash Table.
Definition: hash.h:87
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:44
File/dir&#39;s ctime - creation time.
Definition: file.h:64
bool maildir_update_flags(struct Mailbox *m, struct Email *e_old, struct Email *e_new)
Update the mailbox flags.
Definition: shared.c:122
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:1571
Wrapper for Email retrieved from the header cache.
Definition: lib.h:95
struct Body * body
List of MIME parts.
Definition: email.h:91
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:96
int msg_unread
Number of unread messages.
Definition: mailbox.h:92
enum MxStatus maildir_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check() -This function handles arrival of new mail and re...
Definition: maildir.c:1166
Structs that make up an email.
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer&#39;s string.
Definition: buffer.c:432
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition: file.c:169
bool mutt_path_parent(char *buf, size_t buflen)
Find the parent of a path.
Definition: path.c:459
void maildir_parse_flags(struct Email *e, const char *path)
Parse Maildir file flags.
Definition: maildir.c:811
void maildir_update_mtime(struct Mailbox *m)
Update our record of the Maildir modification time.
Definition: maildir.c:477
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:449
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:93
WHERE SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: mutt_globals.h:67
Maildir-specific Mailbox data.
#define MUTT_CM_UPDATE
Update structs on sync.
Definition: copy.h:40
void mutt_file_get_stat_timespec(struct timespec *dest, struct stat *sb, enum MuttStatType type)
Read the stat() time into a time value.
Definition: file.c:1540
int maildir_move_to_mailbox(struct Mailbox *m, struct MdEmailArray *mda)
Copy the Maildir list to the Mailbox.
Definition: shared.c:75
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:198
int maildir_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition: maildir.c:1563
enum MxStatus maildir_mbox_sync(struct Mailbox *m)
Save changes to the Mailbox - Implements MxOps::mbox_sync() -.
Definition: maildir.c:1373
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:49
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:206
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
uint32_t uidvalidity
IMAP-specific UIDVALIDITY.
Definition: lib.h:97
A group of associated Mailboxes.
Definition: account.h:36
header cache structure
Definition: lib.h:84
struct timespec mtime
Time Mailbox was last changed.
Definition: mailbox.h:107
String manipulation buffer.
Definition: buffer.h:33
Nondestructive flags change (IMAP)
Definition: mxapi.h:82
New mail received in Mailbox.
Definition: mxapi.h:79
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:47
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() -Open a new (temporary) message in...
Definition: maildir.c:1488
Progress tracks elements, according to $read_inc
Definition: lib.h:46
bool changed
Email has been edited.
Definition: email.h:48
#define CH_UPDATE
Update the status and x-status fields?
Definition: copy.h:52
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:882
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mxapi.h:62
char * canon_fname
Definition: mdemail.h:36
#define MUTT_APPENDNEW
Set in mx_open_mailbox_append if the mailbox doesn&#39;t exist.
Definition: mxapi.h:69
Email list was changed.
Definition: mailbox.h:180
MxOpenReturns
Return values for mbox_open()
Definition: mxapi.h:88
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
#define MMC_NEW_DIR
&#39;new&#39; directory changed
Definition: maildir.c:73
static int maildir_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: maildir.c:1553
struct HeaderCache * mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer)
Multiplexor for StoreOps::open.
Definition: hcache.c:332
#define mutt_perror(...)
Definition: logging.h:89
Container for Accounts, Notifications.
Definition: neomutt.h:36
size_t maildir_hcache_keylen(const char *fn)
Calculate the length of the Maildir path.
Definition: maildir.c:585
A Progress Bar.
Definition: progress.c:47
Open succeeded.
Definition: mxapi.h:90
Mailbox was reopened.
Definition: mxapi.h:81
Convenience wrapper for the config headers.
Maildir-specific Email data -.
Definition: edata.h:33
bool mutt_path_pretty(char *buf, size_t buflen, const char *homedir, bool is_dir)
Tidy a filesystem path.
Definition: path.c:186
char * HomeDir
User&#39;s home directory.
Definition: mutt_globals.h:45
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:88
struct Email * email_new(void)
Create a new Email.
Definition: email.c:78
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
void(* mdata_free)(void **ptr)
Free the private data attached to the Mailbox.
Definition: mailbox.h:146
int nm_update_filename(struct Mailbox *m, const char *old_file, const char *new_file, struct Email *e)
Change the filename.
Definition: notmuch.c:1747
bool has_new
Mailbox has new mail.
Definition: mailbox.h:88
int maildir_rewrite_message(struct Mailbox *m, int msgno)
Sync a message in an MH folder.
Definition: maildir.c:331
Progress bar.
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:1343
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1186
struct timespec last_visited
Time of last exit from this mailbox.
Definition: mailbox.h:108
bool read
Email is read.
Definition: email.h:51
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:1456
A Maildir Email helper.
Definition: mdemail.h:33
FILE * maildir_open_find_message(const char *folder, const char *msg, char **newname)
Find a new.
Definition: maildir.c:993
Header cache multiplexor.
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1054
Log at debug level 2.
Definition: logging.h:41
API for mailboxes.
bool old
Email is seen, but unread.
Definition: email.h:50
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:877
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
int maildir_check_empty(const char *path)
Is the mailbox empty.
Definition: maildir.c:1040
struct Envelope * env
Envelope information.
Definition: email.h:90
Convenience wrapper for the core headers.
int maildir_path_parent(char *buf, size_t buflen)
Find the parent of a Mailbox path - Implements MxOps::path_parent() -.
Definition: maildir.c:1598
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:54
File/dir&#39;s mtime - last modified time.
Definition: file.h:63
struct MdEmail * maildir_entry_new(void)
Create a new Maildir entry.
Definition: mdemail.c:39
void * mdata
Driver specific data.
Definition: mailbox.h:136
mode_t mh_umask
Definition: mdata.h:37
&#39;Maildir&#39; Mailbox type
Definition: mailbox.h:51
enum MailboxType type
Mailbox type, e.g. MUTT_IMAP.
Definition: mxapi.h:105
int maildir_read_dir(struct Mailbox *m, const char *subdir)
Read a Maildir style mailbox.
Definition: maildir.c:673
bool trash
Message is marked as trashed on disk (used by the maildir_trash option)
Definition: email.h:60
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:926
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:82
Email list needs resorting.
Definition: mailbox.h:181
Maildir/MH private types.
struct Email * email
Definition: mdemail.h:35
bool active
Message is not to be removed.
Definition: email.h:59
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:85
Notmuch virtual mailbox type.
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
A local copy of an email.
Definition: mxapi.h:41
int maildir_sync_message(struct Mailbox *m, int msgno)
Sync an email to a Maildir folder.
Definition: maildir.c:387
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: random.c:130
A mailbox.
Definition: mailbox.h:81
#define PATH_MAX
Definition: mutt.h:40
void maildir_gen_flags(char *dest, size_t destlen, struct Email *e)
Generate the Maildir flags for an email.
Definition: maildir.c:186
No changes.
Definition: mxapi.h:78
char * dptr
Current read/write position.
Definition: buffer.h:36
WHERE char * ShortHostname
Short version of the hostname.
Definition: mutt_globals.h:46
void maildir_delayed_parsing(struct Mailbox *m, struct MdEmailArray *mda, struct Progress *progress)
This function does the second parsing pass.
Definition: maildir.c:597
char * data
Pointer to data.
Definition: buffer.h:35
struct MaildirEmailData * maildir_edata_new(void)
Create a new MaildirEmailData object.
Definition: edata.c:53
void(* edata_free)(void **ptr)
Free the private data attached to the Email.
Definition: email.h:121
int mutt_hcache_delete_record(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:637
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:54
static int maildir_cmp_inode(const void *a, const void *b)
Compare two Maildirs by inode number - Implements sort_t.
Definition: maildir.c:495
char msg[1024]
Message to display.
Definition: progress.c:50
bool verbose
Display status messages?
Definition: mailbox.h:118
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:1076
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition: mxapi.h:59
mode_t mh_umask(struct Mailbox *m)
Create a umask from the mailbox directory.
Definition: shared.c:52
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:556
#define MUTT_NEWFOLDER
Create a new folder - same as MUTT_APPEND,.
Definition: mxapi.h:65
bool purge
Skip trash folder when deleting.
Definition: email.h:46
#define CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition: copy.h:62
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
static int ch_compare(const void *a, const void *b)
qsort callback to sort characters
Definition: maildir.c:175
static enum MxOpenReturns maildir_mbox_open(struct Mailbox *m)
Open a Mailbox - Implements MxOps::mbox_open() -.
Definition: maildir.c:1092
enum MxStatus maildir_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close() -.
Definition: maildir.c:1448
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:194
bool mutt_path_abbr_folder(char *buf, size_t buflen, const char *folder)
Create a folder abbreviation.
Definition: path.c:492
int maildir_path_canon(char *buf, size_t buflen)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition: maildir.c:1589
int mutt_file_stat_timespec_compare(struct stat *sba, enum MuttStatType type, struct timespec *b)
Compare stat info with a time value.
Definition: file.c:1580
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
void progress_free(struct Progress **ptr)
Free a Progress Bar.
Definition: progress.c:228
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:757
void progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:175
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
Maildir Email helper.
&#39;Notmuch&#39; (virtual) Mailbox type
Definition: mailbox.h:54
void mutt_hcache_close(struct HeaderCache *hc)
Multiplexor for StoreOps::close.
Definition: hcache.c:435
char * committed_path
the final path generated by mx_msg_commit()
Definition: mxapi.h:45
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:664
ino_t inode
Definition: mdemail.h:38
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
Duplicate the structure of an entire email.
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
Maildir-specific Email data.
Log at debug level 1.
Definition: logging.h:40
bool flagged
Marked important?
Definition: email.h:43
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:749
#define MMC_NO_DIRS
No directories changed.
Definition: maildir.c:72
int msg_new
Number of new messages.
Definition: mailbox.h:95
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:593
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
Progress tracks elements, according to $write_inc
Definition: lib.h:47
bool deleted
Email is deleted.
Definition: email.h:45
void * edata
Driver-specific data.
Definition: email.h:111
struct Progress * progress_new(const char *msg, enum ProgressType type, size_t size)
Create a new Progress Bar.
Definition: progress.c:246
bool replied
Email has been replied to.
Definition: email.h:54
struct timespec mtime_cur
Definition: mdata.h:36
bool MonitorContextChanged
true after the current mailbox has changed
Definition: monitor.c:52
char * path
Path of Email (for local Mailboxes)
Definition: email.h:92
FILE * fp
pointer to the message data
Definition: mxapi.h:43
int index
The absolute (unsorted) message number.
Definition: email.h:86
int mutt_copy_message(FILE *fp_out, struct Mailbox *m, struct Email *e, struct Message *msg, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Copy a message from a Mailbox.
Definition: copy.c:853
#define FREE(x)
Definition: memory.h:40
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:152
char * folder
Definition: lib.h:86
bool maildir_sync_mailbox_message(struct Mailbox *m, int msgno, struct HeaderCache *hc)
Save changes to the mailbox.
Definition: maildir.c:946
Monitor files for changes.
time_t received
the time at which this message was received
Definition: mxapi.h:54
#define ARRAY_SORT(head, fn)
Sort an array.
Definition: array.h:271
struct HCacheEntry mutt_hcache_fetch(struct HeaderCache *hc, const char *key, size_t keylen, uint32_t uidvalidity)
Multiplexor for StoreOps::fetch.
Definition: hcache.c:461
struct MaildirMboxData * maildir_mdata_new(void)
Create a new MaildirMboxData object.
Definition: mdata.c:47
bool changed
Mailbox has been modified.
Definition: mailbox.h:114
Hundreds of global variables to back the user variables.
void maildir_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: edata.c:38
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
struct MaildirEmailData * maildir_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:64
struct Email * email
Retrieved email.
Definition: lib.h:99
int maildir_path_pretty(char *buf, size_t buflen, const char *folder)
Abbreviate a Mailbox path - Implements MxOps::path_pretty() -.
Definition: maildir.c:1615
int maildir_parse_dir(struct Mailbox *m, struct MdEmailArray *mda, const char *subdir, struct Progress *progress)
Read a Maildir mailbox.
Definition: maildir.c:513
Convenience wrapper for the library headers.
Open failed with an error.
Definition: mxapi.h:91
#define MMC_CUR_DIR
&#39;cur&#39; directory changed
Definition: maildir.c:74
bool header_parsed
Definition: mdemail.h:37
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:1629
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
parses an RFC822 header
Definition: parse.c:1124
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition: hash.c:251
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:100
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:208
static bool maildir_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
Definition: maildir.c:1105
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_snc(), and mbox_close() ...
Definition: mxapi.h:75
Maildir-specific Mailbox data -.
Definition: mdata.h:34
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:83
#define ARRAY_SIZE(head)
The number of elements stored.
Definition: array.h:83
char * path
path to temp file
Definition: mxapi.h:44
char * maildir_flags
Unknown Maildir flags.
Definition: edata.h:35
Definition: mxapi.h:103
int msgno
Number displayed to the user.
Definition: email.h:87
static struct Email * restore(const unsigned char *d)
Restore an Email from data retrieved from the cache.
Definition: hcache.c:143
An error occurred.
Definition: mxapi.h:77
int mutt_file_safe_rename(const char *src, const char *target)
NFS-safe renaming of files.
Definition: file.c:354
struct MaildirMboxData * maildir_mdata_get(struct Mailbox *m)
Get the private data for this Mailbox.
Definition: mdata.c:58