NeoMutt  2021-02-05-89-gabe350
Teaching an old dog new tricks
DOXYGEN
maildir.c
Go to the documentation of this file.
1 
32 #include "config.h"
33 #include <dirent.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <inttypes.h>
37 #include <limits.h>
38 #include <stdbool.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44 #include <utime.h>
45 #include "private.h"
46 #include "mutt/lib.h"
47 #include "email/lib.h"
48 #include "core/lib.h"
49 #include "maildir/lib.h"
50 #include "copy.h"
51 #include "edata.h"
52 #include "mdata.h"
53 #include "mdemail.h"
54 #include "monitor.h"
55 #include "mutt_globals.h"
56 #include "mx.h"
57 #include "progress.h"
58 #ifdef USE_HCACHE
59 #include "hcache/lib.h"
60 #endif
61 #ifdef USE_NOTMUCH
62 #include "notmuch/lib.h"
63 #endif
64 
65 // Flags for maildir_mbox_check()
66 #define MMC_NO_DIRS 0
67 #define MMC_NEW_DIR (1 << 0)
68 #define MMC_CUR_DIR (1 << 1)
69 
70 
79 static void maildir_check_dir(struct Mailbox *m, const char *dir_name,
80  bool check_new, bool check_stats)
81 {
82  DIR *dirp = NULL;
83  struct dirent *de = NULL;
84  char *p = NULL;
85  struct stat sb;
86 
87  struct Buffer *path = mutt_buffer_pool_get();
88  struct Buffer *msgpath = mutt_buffer_pool_get();
89  mutt_buffer_printf(path, "%s/%s", mailbox_path(m), dir_name);
90 
91  /* when $mail_check_recent is set, if the new/ directory hasn't been modified since
92  * the user last exited the m, then we know there is no recent mail. */
93  if (check_new && C_MailCheckRecent)
94  {
95  if ((stat(mutt_buffer_string(path), &sb) == 0) &&
97  {
98  check_new = false;
99  }
100  }
101 
102  if (!(check_new || check_stats))
103  goto cleanup;
104 
105  dirp = opendir(mutt_buffer_string(path));
106  if (!dirp)
107  {
108  m->type = MUTT_UNKNOWN;
109  goto cleanup;
110  }
111 
112  while ((de = readdir(dirp)))
113  {
114  if (*de->d_name == '.')
115  continue;
116 
117  p = strstr(de->d_name, ":2,");
118  if (p && strchr(p + 3, 'T'))
119  continue;
120 
121  if (check_stats)
122  {
123  m->msg_count++;
124  if (p && strchr(p + 3, 'F'))
125  m->msg_flagged++;
126  }
127  if (!p || !strchr(p + 3, 'S'))
128  {
129  if (check_stats)
130  m->msg_unread++;
131  if (check_new)
132  {
133  if (C_MailCheckRecent)
134  {
135  mutt_buffer_printf(msgpath, "%s/%s", mutt_buffer_string(path), de->d_name);
136  /* ensure this message was received since leaving this m */
137  if ((stat(mutt_buffer_string(msgpath), &sb) == 0) &&
139  {
140  continue;
141  }
142  }
143  m->has_new = true;
144  check_new = false;
145  m->msg_new++;
146  if (!check_stats)
147  break;
148  }
149  }
150  }
151 
152  closedir(dirp);
153 
154 cleanup:
156  mutt_buffer_pool_release(&msgpath);
157 }
158 
167 static int ch_compare(const void *a, const void *b)
168 {
169  return (int) (*((const char *) a) - *((const char *) b));
170 }
171 
178 void maildir_gen_flags(char *dest, size_t destlen, struct Email *e)
179 {
180  *dest = '\0';
181 
182  const char *flags = NULL;
183 
185  if (edata)
186  flags = edata->maildir_flags;
187 
188  /* The maildir specification requires that all files in the cur
189  * subdirectory have the :unique string appended, regardless of whether
190  * or not there are any flags. If .old is set, we know that this message
191  * will end up in the cur directory, so we include it in the following
192  * test even though there is no associated flag. */
193 
194  if (e->flagged || e->replied || e->read || e->deleted || e->old || flags)
195  {
196  char tmp[1024];
197  snprintf(tmp, sizeof(tmp), "%s%s%s%s%s", e->flagged ? "F" : "", e->replied ? "R" : "",
198  e->read ? "S" : "", e->deleted ? "T" : "", NONULL(flags));
199  if (flags)
200  qsort(tmp, strlen(tmp), 1, ch_compare);
201  snprintf(dest, destlen, ":2,%s", tmp);
202  }
203 }
204 
229 int maildir_commit_message(struct Mailbox *m, struct Message *msg, struct Email *e)
230 {
231  char subdir[4];
232  char suffix[16];
233  int rc = 0;
234 
235  if (mutt_file_fsync_close(&msg->fp))
236  {
237  mutt_perror(_("Could not flush message to disk"));
238  return -1;
239  }
240 
241  /* extract the subdir */
242  char *s = strrchr(msg->path, '/') + 1;
243  mutt_str_copy(subdir, s, 4);
244 
245  /* extract the flags */
246  s = strchr(s, ':');
247  if (s)
248  mutt_str_copy(suffix, s, sizeof(suffix));
249  else
250  suffix[0] = '\0';
251 
252  /* construct a new file name. */
253  struct Buffer *path = mutt_buffer_pool_get();
254  struct Buffer *full = mutt_buffer_pool_get();
255  while (true)
256  {
257  mutt_buffer_printf(path, "%s/%lld.R%" PRIu64 ".%s%s", subdir,
258  (long long) mutt_date_epoch(), mutt_rand64(),
259  NONULL(ShortHostname), suffix);
260  mutt_buffer_printf(full, "%s/%s", mailbox_path(m), mutt_buffer_string(path));
261 
262  mutt_debug(LL_DEBUG2, "renaming %s to %s\n", msg->path, mutt_buffer_string(full));
263 
264  if (mutt_file_safe_rename(msg->path, mutt_buffer_string(full)) == 0)
265  {
266  /* Adjust the mtime on the file to match the time at which this
267  * message was received. Currently this is only set when copying
268  * messages between mailboxes, so we test to ensure that it is
269  * actually set. */
270  if (msg->received)
271  {
272  struct utimbuf ut;
273  int rc_utime;
274 
275  ut.actime = msg->received;
276  ut.modtime = msg->received;
277  do
278  {
279  rc_utime = utime(mutt_buffer_string(full), &ut);
280  } while ((rc_utime == -1) && (errno == EINTR));
281  if (rc_utime == -1)
282  {
283  mutt_perror(
284  _("maildir_commit_message(): unable to set time on file"));
285  rc = -1;
286  goto cleanup;
287  }
288  }
289 
290 #ifdef USE_NOTMUCH
291  if (m->type == MUTT_NOTMUCH)
292  nm_update_filename(m, e->path, mutt_buffer_string(full), e);
293 #endif
294  if (e)
297  FREE(&msg->path);
298 
299  goto cleanup;
300  }
301  else if (errno != EEXIST)
302  {
304  rc = -1;
305  goto cleanup;
306  }
307  }
308 
309 cleanup:
312 
313  return rc;
314 }
315 
323 int maildir_rewrite_message(struct Mailbox *m, int msgno)
324 {
325  if (!m || !m->emails || (msgno >= m->msg_count))
326  return -1;
327 
328  struct Email *e = m->emails[msgno];
329  if (!e)
330  return -1;
331 
332  bool restore = true;
333 
334  long old_body_offset = e->body->offset;
335  long old_body_length = e->body->length;
336  long old_hdr_lines = e->lines;
337 
338  struct Message *dest = mx_msg_open_new(m, e, MUTT_MSG_NO_FLAGS);
339  if (!dest)
340  return -1;
341 
342  int rc = mutt_copy_message(dest->fp, m, e, MUTT_CM_UPDATE, CH_UPDATE | CH_UPDATE_LEN, 0);
343  if (rc == 0)
344  {
345  char oldpath[PATH_MAX];
346  char partpath[PATH_MAX];
347  snprintf(oldpath, sizeof(oldpath), "%s/%s", mailbox_path(m), e->path);
348  mutt_str_copy(partpath, e->path, sizeof(partpath));
349 
350  rc = maildir_commit_message(m, dest, e);
351  mx_msg_close(m, &dest);
352 
353  if (rc == 0)
354  {
355  unlink(oldpath);
356  restore = false;
357  }
358  }
359  else
360  mx_msg_close(m, &dest);
361 
362  if ((rc == -1) && restore)
363  {
364  e->body->offset = old_body_offset;
365  e->body->length = old_body_length;
366  e->lines = old_hdr_lines;
367  }
368 
369  mutt_body_free(&e->body->parts);
370  return rc;
371 }
372 
380 int maildir_sync_message(struct Mailbox *m, int msgno)
381 {
382  if (!m || !m->emails || (msgno >= m->msg_count))
383  return -1;
384 
385  struct Email *e = m->emails[msgno];
386  if (!e)
387  return -1;
388 
389  struct Buffer *newpath = NULL;
390  struct Buffer *partpath = NULL;
391  struct Buffer *fullpath = NULL;
392  struct Buffer *oldpath = NULL;
393  char suffix[16];
394  int rc = 0;
395 
396  /* TODO: why the e->env check? */
397  if (e->attach_del || (e->env && e->env->changed))
398  {
399  /* when doing attachment deletion/rethreading, fall back to the MH case. */
400  if (maildir_rewrite_message(m, msgno) != 0)
401  return -1;
402  /* TODO: why the env check? */
403  if (e->env)
404  e->env->changed = 0;
405  }
406  else
407  {
408  /* we just have to rename the file. */
409 
410  char *p = strrchr(e->path, '/');
411  if (!p)
412  {
413  mutt_debug(LL_DEBUG1, "%s: unable to find subdir!\n", e->path);
414  return -1;
415  }
416  p++;
417  newpath = mutt_buffer_pool_get();
418  partpath = mutt_buffer_pool_get();
419  fullpath = mutt_buffer_pool_get();
420  oldpath = mutt_buffer_pool_get();
421 
422  mutt_buffer_strcpy(newpath, p);
423 
424  /* kill the previous flags */
425  p = strchr(newpath->data, ':');
426  if (p)
427  {
428  *p = '\0';
429  newpath->dptr = p; /* fix buffer up, just to be safe */
430  }
431 
432  maildir_gen_flags(suffix, sizeof(suffix), e);
433 
434  mutt_buffer_printf(partpath, "%s/%s%s", (e->read || e->old) ? "cur" : "new",
435  mutt_buffer_string(newpath), suffix);
436  mutt_buffer_printf(fullpath, "%s/%s", mailbox_path(m), mutt_buffer_string(partpath));
437  mutt_buffer_printf(oldpath, "%s/%s", mailbox_path(m), e->path);
438 
439  if (mutt_str_equal(mutt_buffer_string(fullpath), mutt_buffer_string(oldpath)))
440  {
441  /* message hasn't really changed */
442  goto cleanup;
443  }
444 
445  /* record that the message is possibly marked as trashed on disk */
446  e->trash = e->deleted;
447 
448  if (rename(mutt_buffer_string(oldpath), mutt_buffer_string(fullpath)) != 0)
449  {
450  mutt_perror("rename");
451  rc = -1;
452  goto cleanup;
453  }
454  mutt_str_replace(&e->path, mutt_buffer_string(partpath));
455  }
456 
457 cleanup:
458  mutt_buffer_pool_release(&newpath);
459  mutt_buffer_pool_release(&partpath);
460  mutt_buffer_pool_release(&fullpath);
461  mutt_buffer_pool_release(&oldpath);
462 
463  return rc;
464 }
465 
471 {
472  char buf[PATH_MAX];
473  struct stat st;
475 
476  snprintf(buf, sizeof(buf), "%s/%s", mailbox_path(m), "cur");
477  if (stat(buf, &st) == 0)
479  snprintf(buf, sizeof(buf), "%s/%s", mailbox_path(m), "new");
480 
481  if (stat(buf, &st) == 0)
483 }
484 
488 static int maildir_cmp_inode(const void *a, const void *b)
489 {
490  const struct MdEmail *ma = *(struct MdEmail **) a;
491  const struct MdEmail *mb = *(struct MdEmail **) b;
492 
493  return ma->inode - mb->inode;
494 }
495 
506 int maildir_parse_dir(struct Mailbox *m, struct MdEmailArray *mda,
507  const char *subdir, struct Progress *progress)
508 {
509  struct dirent *de = NULL;
510  int rc = 0;
511  bool is_old = false;
512  struct MdEmail *entry = NULL;
513  struct Email *e = NULL;
514 
515  struct Buffer *buf = mutt_buffer_pool_get();
516 
517  mutt_buffer_printf(buf, "%s/%s", mailbox_path(m), subdir);
518  is_old = C_MarkOld ? mutt_str_equal("cur", subdir) : false;
519 
520  DIR *dirp = opendir(mutt_buffer_string(buf));
521  if (!dirp)
522  {
523  rc = -1;
524  goto cleanup;
525  }
526 
527  while (((de = readdir(dirp))) && (SigInt != 1))
528  {
529  if (*de->d_name == '.')
530  continue;
531 
532  /* FOO - really ignore the return value? */
533  mutt_debug(LL_DEBUG2, "queueing %s\n", de->d_name);
534 
535  e = email_new();
536  e->edata = maildir_edata_new();
538 
539  e->old = is_old;
540  maildir_parse_flags(e, de->d_name);
541 
542  if (m->verbose && progress)
543  mutt_progress_update(progress, ARRAY_SIZE(mda) + 1, -1);
544 
545  mutt_buffer_printf(buf, "%s/%s", subdir, de->d_name);
546  e->path = mutt_buffer_strdup(buf);
547 
548  entry = maildir_entry_new();
549  entry->email = e;
550  entry->inode = de->d_ino;
551  ARRAY_ADD(mda, entry);
552  }
553 
554  closedir(dirp);
555 
556  if (SigInt == 1)
557  {
558  SigInt = 0;
559  return -2; /* action aborted */
560  }
561 
563 
564 cleanup:
566 
567  return rc;
568 }
569 
577 size_t maildir_hcache_keylen(const char *fn)
578 {
579  const char *p = strrchr(fn, ':');
580  return p ? (size_t)(p - fn) : mutt_str_len(fn);
581 }
582 
589 void maildir_delayed_parsing(struct Mailbox *m, struct MdEmailArray *mda,
590  struct Progress *progress)
591 {
592  char fn[PATH_MAX];
593 
594 #ifdef USE_HCACHE
595  struct HeaderCache *hc = mutt_hcache_open(C_HeaderCache, mailbox_path(m), NULL);
596 #endif
597 
598  struct MdEmail *md = NULL;
599  struct MdEmail **mdp = NULL;
600  ARRAY_FOREACH(mdp, mda)
601  {
602  md = *mdp;
603  if (!md || !md->email || md->header_parsed)
604  continue;
605 
606  if (m->verbose && progress)
607  mutt_progress_update(progress, ARRAY_FOREACH_IDX, -1);
608 
609  snprintf(fn, sizeof(fn), "%s/%s", mailbox_path(m), md->email->path);
610 
611 #ifdef USE_HCACHE
612  struct stat lastchanged = { 0 };
613  int rc = 0;
615  {
616  rc = stat(fn, &lastchanged);
617  }
618 
619  const char *key = md->email->path + 3;
620  size_t keylen = maildir_hcache_keylen(key);
621  struct HCacheEntry hce = mutt_hcache_fetch(hc, key, keylen, 0);
622 
623  if (hce.email && (rc == 0) && (lastchanged.st_mtime <= hce.uidvalidity))
624  {
625  hce.email->edata = maildir_edata_new();
627  hce.email->old = md->email->old;
628  hce.email->path = mutt_str_dup(md->email->path);
629  email_free(&md->email);
630  md->email = hce.email;
631  maildir_parse_flags(md->email, fn);
632  }
633  else
634 #endif
635  {
636  if (maildir_parse_message(m->type, fn, md->email->old, md->email))
637  {
638  md->header_parsed = true;
639 #ifdef USE_HCACHE
640  key = md->email->path + 3;
641  keylen = maildir_hcache_keylen(key);
642  mutt_hcache_store(hc, key, keylen, md->email, 0);
643 #endif
644  }
645  else
646  email_free(&md->email);
647  }
648  }
649 #ifdef USE_HCACHE
650  mutt_hcache_close(hc);
651 #endif
652 }
653 
661 int maildir_read_dir(struct Mailbox *m, const char *subdir)
662 {
663  if (!m)
664  return -1;
665 
666  struct Progress progress;
667 
668  if (m->verbose)
669  {
670  char msg[PATH_MAX];
671  snprintf(msg, sizeof(msg), _("Scanning %s..."), mailbox_path(m));
672  mutt_progress_init(&progress, msg, MUTT_PROGRESS_READ, 0);
673  }
674 
676  if (!mdata)
677  {
679  m->mdata = mdata;
681  }
682 
683  struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
684  if (maildir_parse_dir(m, &mda, subdir, &progress) < 0)
685  return -1;
686 
687  if (m->verbose)
688  {
689  char msg[PATH_MAX];
690  snprintf(msg, sizeof(msg), _("Reading %s..."), mailbox_path(m));
691  mutt_progress_init(&progress, msg, MUTT_PROGRESS_READ, ARRAY_SIZE(&mda));
692  }
693  maildir_delayed_parsing(m, &mda, &progress);
694 
695  maildir_move_to_mailbox(m, &mda);
696 
697  if (!mdata->mh_umask)
698  mdata->mh_umask = mh_umask(m);
699 
700  return 0;
701 }
702 
712 void maildir_canon_filename(struct Buffer *dest, const char *src)
713 {
714  if (!dest || !src)
715  return;
716 
717  char *t = strrchr(src, '/');
718  if (t)
719  src = t + 1;
720 
721  mutt_buffer_strcpy(dest, src);
722  char *u = strpbrk(dest->data, ",:");
723  if (u)
724  {
725  *u = '\0';
726  dest->dptr = u;
727  }
728 }
729 
742 static FILE *maildir_open_find_message_dir(const char *folder, const char *unique,
743  const char *subfolder, char **newname)
744 {
745  struct Buffer *dir = mutt_buffer_pool_get();
746  struct Buffer *tunique = mutt_buffer_pool_get();
747  struct Buffer *fname = mutt_buffer_pool_get();
748 
749  struct dirent *de = NULL;
750 
751  FILE *fp = NULL;
752  int oe = ENOENT;
753 
754  mutt_buffer_printf(dir, "%s/%s", folder, subfolder);
755 
756  DIR *dp = opendir(mutt_buffer_string(dir));
757  if (!dp)
758  {
759  errno = ENOENT;
760  goto cleanup;
761  }
762 
763  while ((de = readdir(dp)))
764  {
765  maildir_canon_filename(tunique, de->d_name);
766 
767  if (mutt_str_equal(mutt_buffer_string(tunique), unique))
768  {
769  mutt_buffer_printf(fname, "%s/%s/%s", folder, subfolder, de->d_name);
770  fp = fopen(mutt_buffer_string(fname), "r");
771  oe = errno;
772  break;
773  }
774  }
775 
776  closedir(dp);
777 
778  if (newname && fp)
779  *newname = mutt_buffer_strdup(fname);
780 
781  errno = oe;
782 
783 cleanup:
785  mutt_buffer_pool_release(&tunique);
786  mutt_buffer_pool_release(&fname);
787 
788  return fp;
789 }
790 
796 void maildir_parse_flags(struct Email *e, const char *path)
797 {
798  char *q = NULL;
799 
800  e->flagged = false;
801  e->read = false;
802  e->replied = false;
803 
805 
806  char *p = strrchr(path, ':');
807  if (p && mutt_str_startswith(p + 1, "2,"))
808  {
809  p += 3;
810 
811  mutt_str_replace(&edata->maildir_flags, p);
812  q = edata->maildir_flags;
813 
814  while (*p)
815  {
816  switch (*p)
817  {
818  case 'F':
819  e->flagged = true;
820  break;
821 
822  case 'R': /* replied */
823  e->replied = true;
824  break;
825 
826  case 'S': /* seen */
827  e->read = true;
828  break;
829 
830  case 'T': /* trashed */
831  if (!e->flagged || !C_FlagSafe)
832  {
833  e->trash = true;
834  e->deleted = true;
835  }
836  break;
837 
838  default:
839  *q++ = *p;
840  break;
841  }
842  p++;
843  }
844  }
845 
846  if (q == edata->maildir_flags)
847  FREE(&edata->maildir_flags);
848  else if (q)
849  *q = '\0';
850 }
851 
864 struct Email *maildir_parse_stream(enum MailboxType type, FILE *fp,
865  const char *fname, bool is_old, struct Email *e)
866 {
867  if (!e)
868  {
869  e = email_new();
870  e->edata = maildir_edata_new();
872  }
873  e->env = mutt_rfc822_read_header(fp, e, false, false);
874 
875  struct stat st;
876  fstat(fileno(fp), &st);
877 
878  if (!e->received)
879  e->received = e->date_sent;
880 
881  /* always update the length since we have fresh information available. */
882  e->body->length = st.st_size - e->body->offset;
883 
884  e->index = -1;
885 
886  if (type == MUTT_MAILDIR)
887  {
888  /* maildir stores its flags in the filename, so ignore the
889  * flags in the header of the message */
890 
891  e->old = is_old;
892  maildir_parse_flags(e, fname);
893  }
894  return e;
895 }
896 
908 struct Email *maildir_parse_message(enum MailboxType type, const char *fname,
909  bool is_old, struct Email *e)
910 {
911  FILE *fp = fopen(fname, "r");
912  if (!fp)
913  return NULL;
914 
915  e = maildir_parse_stream(type, fp, fname, is_old, e);
916  mutt_file_fclose(&fp);
917  return e;
918 }
919 
928 bool maildir_sync_mailbox_message(struct Mailbox *m, int msgno, struct HeaderCache *hc)
929 {
930  struct Email *e = m->emails[msgno];
931  if (!e)
932  return false;
933 
934  if (e->deleted && !C_MaildirTrash)
935  {
936  char path[PATH_MAX];
937  snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), e->path);
938 #ifdef USE_HCACHE
939  if (hc)
940  {
941  const char *key = e->path + 3;
942  size_t keylen = maildir_hcache_keylen(key);
943  mutt_hcache_delete_record(hc, key, keylen);
944  }
945 #endif
946  unlink(path);
947  }
948  else if (e->changed || e->attach_del ||
949  ((C_MaildirTrash || e->trash) && (e->deleted != e->trash)))
950  {
951  if (maildir_sync_message(m, msgno) == -1)
952  return false;
953  }
954 
955 #ifdef USE_HCACHE
956  if (hc && e->changed)
957  {
958  const char *key = e->path + 3;
959  size_t keylen = maildir_hcache_keylen(key);
960  mutt_hcache_store(hc, key, keylen, e, 0);
961  }
962 #endif
963 
964  return true;
965 }
966 
974 FILE *maildir_open_find_message(const char *folder, const char *msg, char **newname)
975 {
976  static unsigned int new_hits = 0, cur_hits = 0; /* simple dynamic optimization */
977 
978  struct Buffer *unique = mutt_buffer_pool_get();
979  maildir_canon_filename(unique, msg);
980 
981  FILE *fp = maildir_open_find_message_dir(folder, mutt_buffer_string(unique),
982  (new_hits > cur_hits) ? "new" : "cur", newname);
983  if (fp || (errno != ENOENT))
984  {
985  if ((new_hits < UINT_MAX) && (cur_hits < UINT_MAX))
986  {
987  new_hits += ((new_hits > cur_hits) ? 1 : 0);
988  cur_hits += ((new_hits > cur_hits) ? 0 : 1);
989  }
990 
991  goto cleanup;
992  }
994  (new_hits > cur_hits) ? "cur" : "new", newname);
995  if (fp || (errno != ENOENT))
996  {
997  if ((new_hits < UINT_MAX) && (cur_hits < UINT_MAX))
998  {
999  new_hits += ((new_hits > cur_hits) ? 0 : 1);
1000  cur_hits += ((new_hits > cur_hits) ? 1 : 0);
1001  }
1002 
1003  goto cleanup;
1004  }
1005 
1006  fp = NULL;
1007 
1008 cleanup:
1009  mutt_buffer_pool_release(&unique);
1010 
1011  return fp;
1012 }
1013 
1021 int maildir_check_empty(const char *path)
1022 {
1023  DIR *dp = NULL;
1024  struct dirent *de = NULL;
1025  int rc = 1; /* assume empty until we find a message */
1026  char realpath[PATH_MAX];
1027  int iter = 0;
1028 
1029  /* Strategy here is to look for any file not beginning with a period */
1030 
1031  do
1032  {
1033  /* we do "cur" on the first iteration since it's more likely that we'll
1034  * find old messages without having to scan both subdirs */
1035  snprintf(realpath, sizeof(realpath), "%s/%s", path, (iter == 0) ? "cur" : "new");
1036  dp = opendir(realpath);
1037  if (!dp)
1038  return -1;
1039  while ((de = readdir(dp)))
1040  {
1041  if (*de->d_name != '.')
1042  {
1043  rc = 0;
1044  break;
1045  }
1046  }
1047  closedir(dp);
1048  iter++;
1049  } while (rc && iter < 2);
1050 
1051  return rc;
1052 }
1053 
1057 bool maildir_ac_owns_path(struct Account *a, const char *path)
1058 {
1059  return true;
1060 }
1061 
1065 bool maildir_ac_add(struct Account *a, struct Mailbox *m)
1066 {
1067  return true;
1068 }
1069 
1074 {
1075  /* maildir looks sort of like MH, except that there are two subdirectories
1076  * of the main folder path from which to read messages */
1077  if ((maildir_read_dir(m, "new") == -1) || (maildir_read_dir(m, "cur") == -1))
1078  return MX_OPEN_ERROR;
1079 
1080  return MX_OPEN_OK;
1081 }
1082 
1087 {
1088  if (!(flags & (MUTT_APPEND | MUTT_APPENDNEW | MUTT_NEWFOLDER)))
1089  {
1090  return true;
1091  }
1092 
1093  errno = 0;
1094  if ((mutt_file_mkdir(mailbox_path(m), S_IRWXU) != 0) && (errno != EEXIST))
1095  {
1097  return false;
1098  }
1099 
1100  char tmp[PATH_MAX];
1101  snprintf(tmp, sizeof(tmp), "%s/cur", mailbox_path(m));
1102  errno = 0;
1103  if ((mkdir(tmp, S_IRWXU) != 0) && (errno != EEXIST))
1104  {
1105  mutt_perror(tmp);
1106  rmdir(mailbox_path(m));
1107  return false;
1108  }
1109 
1110  snprintf(tmp, sizeof(tmp), "%s/new", mailbox_path(m));
1111  errno = 0;
1112  if ((mkdir(tmp, S_IRWXU) != 0) && (errno != EEXIST))
1113  {
1114  mutt_perror(tmp);
1115  snprintf(tmp, sizeof(tmp), "%s/cur", mailbox_path(m));
1116  rmdir(tmp);
1117  rmdir(mailbox_path(m));
1118  return false;
1119  }
1120 
1121  snprintf(tmp, sizeof(tmp), "%s/tmp", mailbox_path(m));
1122  errno = 0;
1123  if ((mkdir(tmp, S_IRWXU) != 0) && (errno != EEXIST))
1124  {
1125  mutt_perror(tmp);
1126  snprintf(tmp, sizeof(tmp), "%s/cur", mailbox_path(m));
1127  rmdir(tmp);
1128  snprintf(tmp, sizeof(tmp), "%s/new", mailbox_path(m));
1129  rmdir(tmp);
1130  rmdir(mailbox_path(m));
1131  return false;
1132  }
1133 
1134  return true;
1135 }
1136 
1148 {
1149  struct stat st_new; /* status of the "new" subdirectory */
1150  struct stat st_cur; /* status of the "cur" subdirectory */
1151  int changed = MMC_NO_DIRS; /* which subdirectories have changed */
1152  bool occult = false; /* messages were removed from the mailbox */
1153  int num_new = 0; /* number of new messages added to the mailbox */
1154  bool flags_changed = false; /* message flags were changed in the mailbox */
1155  struct HashTable *fnames = NULL; /* hash table for quickly looking up the base filename
1156  for a maildir message */
1158 
1159  /* XXX seems like this check belongs in mx_mbox_check() rather than here. */
1160  if (!C_CheckNew)
1161  return MX_STATUS_OK;
1162 
1163  struct Buffer *buf = mutt_buffer_pool_get();
1164  mutt_buffer_printf(buf, "%s/new", mailbox_path(m));
1165  if (stat(mutt_buffer_string(buf), &st_new) == -1)
1166  {
1168  return MX_STATUS_ERROR;
1169  }
1170 
1171  mutt_buffer_printf(buf, "%s/cur", mailbox_path(m));
1172  if (stat(mutt_buffer_string(buf), &st_cur) == -1)
1173  {
1175  return MX_STATUS_ERROR;
1176  }
1177 
1178  /* determine which subdirectories need to be scanned */
1179  if (mutt_file_stat_timespec_compare(&st_new, MUTT_STAT_MTIME, &m->mtime) > 0)
1180  changed = MMC_NEW_DIR;
1181  if (mutt_file_stat_timespec_compare(&st_cur, MUTT_STAT_MTIME, &mdata->mtime_cur) > 0)
1182  changed |= MMC_CUR_DIR;
1183 
1184  if (changed == MMC_NO_DIRS)
1185  {
1187  return MX_STATUS_OK; /* nothing to do */
1188  }
1189 
1190  /* Update the modification times on the mailbox.
1191  *
1192  * The monitor code notices changes in the open mailbox too quickly.
1193  * In practice, this sometimes leads to all the new messages not being
1194  * noticed during the SAME group of mtime stat updates. To work around
1195  * the problem, don't update the stat times for a monitor caused check. */
1196 #ifdef USE_INOTIFY
1198  MonitorContextChanged = false;
1199  else
1200 #endif
1201  {
1204  }
1205 
1206  /* do a fast scan of just the filenames in
1207  * the subdirectories that have changed. */
1208  struct MdEmailArray mda = ARRAY_HEAD_INITIALIZER;
1209  if (changed & MMC_NEW_DIR)
1210  maildir_parse_dir(m, &mda, "new", NULL);
1211  if (changed & MMC_CUR_DIR)
1212  maildir_parse_dir(m, &mda, "cur", NULL);
1213 
1214  /* we create a hash table keyed off the canonical (sans flags) filename
1215  * of each message we scanned. This is used in the loop over the
1216  * existing messages below to do some correlation. */
1217  fnames = mutt_hash_new(ARRAY_SIZE(&mda), MUTT_HASH_NO_FLAGS);
1218 
1219  struct MdEmail *md = NULL;
1220  struct MdEmail **mdp = NULL;
1221  ARRAY_FOREACH(mdp, &mda)
1222  {
1223  md = *mdp;
1224  maildir_canon_filename(buf, md->email->path);
1225  md->canon_fname = mutt_buffer_strdup(buf);
1226  mutt_hash_insert(fnames, md->canon_fname, md);
1227  }
1228 
1229  /* check for modifications and adjust flags */
1230  for (int i = 0; i < m->msg_count; i++)
1231  {
1232  struct Email *e = m->emails[i];
1233  if (!e)
1234  break;
1235 
1236  e->active = false;
1237  maildir_canon_filename(buf, e->path);
1238  md = mutt_hash_find(fnames, mutt_buffer_string(buf));
1239  if (md && md->email)
1240  {
1241  /* message already exists, merge flags */
1242  e->active = true;
1243 
1244  /* check to see if the message has moved to a different
1245  * subdirectory. If so, update the associated filename. */
1246  if (!mutt_str_equal(e->path, md->email->path))
1247  mutt_str_replace(&e->path, md->email->path);
1248 
1249  /* if the user hasn't modified the flags on this message, update
1250  * the flags we just detected. */
1251  if (!e->changed)
1252  if (maildir_update_flags(m, e, md->email))
1253  flags_changed = true;
1254 
1255  if (e->deleted == e->trash)
1256  {
1257  if (e->deleted != md->email->deleted)
1258  {
1259  e->deleted = md->email->deleted;
1260  flags_changed = true;
1261  }
1262  }
1263  e->trash = md->email->trash;
1264 
1265  /* this is a duplicate of an existing email, so remove it */
1266  email_free(&md->email);
1267  }
1268  /* This message was not in the list of messages we just scanned.
1269  * Check to see if we have enough information to know if the
1270  * message has disappeared out from underneath us. */
1271  else if (((changed & MMC_NEW_DIR) && mutt_strn_equal(e->path, "new/", 4)) ||
1272  ((changed & MMC_CUR_DIR) && mutt_strn_equal(e->path, "cur/", 4)))
1273  {
1274  /* This message disappeared, so we need to simulate a "reopen"
1275  * event. We know it disappeared because we just scanned the
1276  * subdirectory it used to reside in. */
1277  occult = true;
1278  e->deleted = true;
1279  e->purge = true;
1280  }
1281  else
1282  {
1283  /* This message resides in a subdirectory which was not
1284  * modified, so we assume that it is still present and
1285  * unchanged. */
1286  e->active = true;
1287  }
1288  }
1289 
1290  /* destroy the file name hash */
1291  mutt_hash_free(&fnames);
1292 
1293  /* If we didn't just get new mail, update the tables. */
1294  if (occult)
1296 
1297  /* do any delayed parsing we need to do. */
1298  maildir_delayed_parsing(m, &mda, NULL);
1299 
1300  /* Incorporate new messages */
1301  num_new = maildir_move_to_mailbox(m, &mda);
1302  if (num_new > 0)
1303  {
1305  m->changed = true;
1306  }
1307 
1309 
1310  ARRAY_FREE(&mda);
1311  if (occult)
1312  return MX_STATUS_REOPENED;
1313  if (num_new > 0)
1314  return MX_STATUS_NEW_MAIL;
1315  if (flags_changed)
1316  return MX_STATUS_FLAGS;
1317  return MX_STATUS_OK;
1318 }
1319 
1323 static enum MxStatus maildir_mbox_check_stats(struct Mailbox *m, uint8_t flags)
1324 {
1325  bool check_stats = flags;
1326  bool check_new = true;
1327 
1328  if (check_stats)
1329  {
1330  m->msg_count = 0;
1331  m->msg_unread = 0;
1332  m->msg_flagged = 0;
1333  m->msg_new = 0;
1334  }
1335 
1336  maildir_check_dir(m, "new", check_new, check_stats);
1337 
1338  check_new = !m->has_new && C_MaildirCheckCur;
1339  if (check_new || check_stats)
1340  maildir_check_dir(m, "cur", check_new, check_stats);
1341 
1342  return m->msg_new ? MX_STATUS_NEW_MAIL : MX_STATUS_OK;
1343 }
1344 
1352 {
1353  enum MxStatus check = maildir_mbox_check(m);
1354  if (check == MX_STATUS_ERROR)
1355  return check;
1356 
1357  struct HeaderCache *hc = NULL;
1358 #ifdef USE_HCACHE
1359  if (m->type == MUTT_MAILDIR)
1361 #endif
1362 
1363  struct Progress progress;
1364  if (m->verbose)
1365  {
1366  char msg[PATH_MAX];
1367  snprintf(msg, sizeof(msg), _("Writing %s..."), mailbox_path(m));
1369  }
1370 
1371  for (int i = 0; i < m->msg_count; i++)
1372  {
1373  if (m->verbose)
1374  mutt_progress_update(&progress, i, -1);
1375 
1376  if (!maildir_sync_mailbox_message(m, i, hc))
1377  goto err;
1378  }
1379 
1380 #ifdef USE_HCACHE
1381  if (m->type == MUTT_MAILDIR)
1382  mutt_hcache_close(hc);
1383 #endif
1384 
1385  /* XXX race condition? */
1386 
1388 
1389  /* adjust indices */
1390 
1391  if (m->msg_deleted)
1392  {
1393  for (int i = 0, j = 0; i < m->msg_count; i++)
1394  {
1395  struct Email *e = m->emails[i];
1396  if (!e)
1397  break;
1398 
1399  if (!e->deleted || C_MaildirTrash)
1400  e->index = j++;
1401  }
1402  }
1403 
1404  return check;
1405 
1406 err:
1407 #ifdef USE_HCACHE
1408  if (m->type == MUTT_MAILDIR)
1409  mutt_hcache_close(hc);
1410 #endif
1411  return MX_STATUS_ERROR;
1412 }
1413 
1419 {
1420  return MX_STATUS_OK;
1421 }
1422 
1426 static bool maildir_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
1427 {
1428  struct Email *e = m->emails[msgno];
1429  if (!e)
1430  return false;
1431 
1432  char path[PATH_MAX];
1433 
1434  snprintf(path, sizeof(path), "%s/%s", mailbox_path(m), e->path);
1435 
1436  msg->fp = fopen(path, "r");
1437  if (!msg->fp && (errno == ENOENT))
1438  msg->fp = maildir_open_find_message(mailbox_path(m), e->path, NULL);
1439 
1440  if (!msg->fp)
1441  {
1442  mutt_perror(path);
1443  mutt_debug(LL_DEBUG1, "fopen: %s: %s (errno %d)\n", path, strerror(errno), errno);
1444  return false;
1445  }
1446 
1447  return true;
1448 }
1449 
1458 bool maildir_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
1459 {
1460  int fd;
1461  char path[PATH_MAX];
1462  char suffix[16];
1463  char subdir[16];
1464 
1465  if (e)
1466  {
1467  struct Email tmp = *e;
1468  tmp.deleted = false;
1469  tmp.edata = NULL;
1470  maildir_gen_flags(suffix, sizeof(suffix), &tmp);
1471  }
1472  else
1473  *suffix = '\0';
1474 
1475  if (e && (e->read || e->old))
1476  mutt_str_copy(subdir, "cur", sizeof(subdir));
1477  else
1478  mutt_str_copy(subdir, "new", sizeof(subdir));
1479 
1480  mode_t omask = umask(mh_umask(m));
1481  while (true)
1482  {
1483  snprintf(path, sizeof(path), "%s/tmp/%s.%lld.R%" PRIu64 ".%s%s",
1484  mailbox_path(m), subdir, (long long) mutt_date_epoch(),
1485  mutt_rand64(), NONULL(ShortHostname), suffix);
1486 
1487  mutt_debug(LL_DEBUG2, "Trying %s\n", path);
1488 
1489  fd = open(path, O_WRONLY | O_EXCL | O_CREAT, 0666);
1490  if (fd == -1)
1491  {
1492  if (errno != EEXIST)
1493  {
1494  umask(omask);
1495  mutt_perror(path);
1496  return false;
1497  }
1498  }
1499  else
1500  {
1501  mutt_debug(LL_DEBUG2, "Success\n");
1502  msg->path = mutt_str_dup(path);
1503  break;
1504  }
1505  }
1506  umask(omask);
1507 
1508  msg->fp = fdopen(fd, "w");
1509  if (!msg->fp)
1510  {
1511  FREE(&msg->path);
1512  close(fd);
1513  unlink(path);
1514  return false;
1515  }
1516 
1517  return true;
1518 }
1519 
1523 static int maildir_msg_commit(struct Mailbox *m, struct Message *msg)
1524 {
1525  return maildir_commit_message(m, msg, NULL);
1526 }
1527 
1533 int maildir_msg_close(struct Mailbox *m, struct Message *msg)
1534 {
1535  return mutt_file_fclose(&msg->fp);
1536 }
1537 
1541 static int maildir_msg_save_hcache(struct Mailbox *m, struct Email *e)
1542 {
1543  int rc = 0;
1544 #ifdef USE_HCACHE
1545  struct HeaderCache *hc = mutt_hcache_open(C_HeaderCache, mailbox_path(m), NULL);
1546  char *key = e->path + 3;
1547  int keylen = maildir_hcache_keylen(key);
1548  rc = mutt_hcache_store(hc, key, keylen, e, 0);
1549  mutt_hcache_close(hc);
1550 #endif
1551  return rc;
1552 }
1553 
1557 int maildir_path_canon(char *buf, size_t buflen)
1558 {
1559  mutt_path_canon(buf, buflen, HomeDir, true);
1560  return 0;
1561 }
1562 
1566 int maildir_path_parent(char *buf, size_t buflen)
1567 {
1568  if (mutt_path_parent(buf, buflen))
1569  return 0;
1570 
1571  if (buf[0] == '~')
1572  mutt_path_canon(buf, buflen, HomeDir, true);
1573 
1574  if (mutt_path_parent(buf, buflen))
1575  return 0;
1576 
1577  return -1;
1578 }
1579 
1583 int maildir_path_pretty(char *buf, size_t buflen, const char *folder)
1584 {
1585  if (mutt_path_abbr_folder(buf, buflen, folder))
1586  return 0;
1587 
1588  if (mutt_path_pretty(buf, buflen, HomeDir, false))
1589  return 0;
1590 
1591  return -1;
1592 }
1593 
1597 static enum MailboxType maildir_path_probe(const char *path, const struct stat *st)
1598 {
1599  if (!st || !S_ISDIR(st->st_mode))
1600  return MUTT_UNKNOWN;
1601 
1602  char cur[PATH_MAX];
1603  snprintf(cur, sizeof(cur), "%s/cur", path);
1604 
1605  struct stat stc;
1606  if ((stat(cur, &stc) == 0) && S_ISDIR(stc.st_mode))
1607  return MUTT_MAILDIR;
1608 
1609  return MUTT_UNKNOWN;
1610 }
1611 
1612 // clang-format off
1617  .type = MUTT_MAILDIR,
1618  .name = "maildir",
1619  .is_local = true,
1620  .ac_owns_path = maildir_ac_owns_path,
1621  .ac_add = maildir_ac_add,
1622  .mbox_open = maildir_mbox_open,
1623  .mbox_open_append = maildir_mbox_open_append,
1624  .mbox_check = maildir_mbox_check,
1625  .mbox_check_stats = maildir_mbox_check_stats,
1626  .mbox_sync = maildir_mbox_sync,
1627  .mbox_close = maildir_mbox_close,
1628  .msg_open = maildir_msg_open,
1629  .msg_open_new = maildir_msg_open_new,
1630  .msg_commit = maildir_msg_commit,
1631  .msg_close = maildir_msg_close,
1632  .msg_padding_size = NULL,
1633  .msg_save_hcache = maildir_msg_save_hcache,
1634  .tags_edit = NULL,
1635  .tags_commit = NULL,
1636  .path_probe = maildir_path_probe,
1637  .path_canon = maildir_path_canon,
1638  .path_pretty = maildir_path_pretty,
1639  .path_parent = maildir_path_parent,
1640  .path_is_empty = maildir_check_empty,
1641 };
1642 // clang-format on
Mailbox::mdata_free
void(* mdata_free)(void **ptr)
Free the private data attached to the Mailbox.
Definition: mailbox.h:142
MaildirMboxData::mh_umask
mode_t mh_umask
Definition: mdata.h:37
Email::date_sent
time_t date_sent
Time when the message was sent (UTC)
Definition: email.h:82
Email::msgno
int msgno
Number displayed to the user.
Definition: email.h:87
maildir_mbox_open_append
static bool maildir_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append()
Definition: maildir.c:1086
maildir_mbox_close
enum MxStatus maildir_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close()
Definition: maildir.c:1418
mutt_hcache_open
struct HeaderCache * mutt_hcache_open(const char *path, const char *folder, hcache_namer_t namer)
Multiplexor for StoreOps::open.
Definition: hcache.c:322
SigInt
WHERE SIG_ATOMIC_VOLATILE_T SigInt
true after SIGINT is received
Definition: mutt_globals.h:74
lib.h
MUTT_MSG_NO_FLAGS
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition: mx.h:64
maildir_parse_flags
void maildir_parse_flags(struct Email *e, const char *path)
Parse Maildir file flags.
Definition: maildir.c:796
mutt_hash_new
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition: hash.c:251
MxStatus
MxStatus
Return values from mx_mbox_check(), mx_mbox_sync(), and mx_mbox_close()
Definition: mx.h:71
MxOps::type
enum MailboxType type
Mailbox type, e.g. MUTT_IMAP.
Definition: mx.h:118
maildir_parse_stream
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:864
_
#define _(a)
Definition: message.h:28
NONULL
#define NONULL(x)
Definition: string2.h:37
Mailbox
A mailbox.
Definition: mailbox.h:81
maildir_path_canon
int maildir_path_canon(char *buf, size_t buflen)
Canonicalise a Mailbox path - Implements MxOps::path_canon()
Definition: maildir.c:1557
Email::lines
int lines
How many lines in the body of this message?
Definition: email.h:85
ARRAY_ADD
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:152
restore
static struct Email * restore(const unsigned char *d)
Restore an Email from data retrieved from the cache.
Definition: hcache.c:146
Mailbox::emails
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
mutt_strn_equal
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
MMC_NEW_DIR
#define MMC_NEW_DIR
'new' directory changed
Definition: maildir.c:67
NT_MAILBOX_INVALID
@ NT_MAILBOX_INVALID
Email list was changed.
Definition: mailbox.h:173
Buffer
String manipulation buffer.
Definition: buffer.h:33
mutt_path_abbr_folder
bool mutt_path_abbr_folder(char *buf, size_t buflen, const char *folder)
Create a folder abbreviation.
Definition: path.c:492
Body::offset
LOFF_T offset
offset where the actual data begins
Definition: body.h:44
Mailbox::msg_deleted
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:96
maildir_hcache_keylen
size_t maildir_hcache_keylen(const char *fn)
Calculate the length of the Maildir path.
Definition: maildir.c:577
mutt_file_fclose
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
MdEmail::canon_fname
char * canon_fname
Definition: mdemail.h:36
MdEmail::email
struct Email * email
Definition: mdemail.h:35
MUTT_STAT_MTIME
@ MUTT_STAT_MTIME
File/dir's mtime - last modified time.
Definition: file.h:64
MxOpenReturns
MxOpenReturns
Return values for mbox_open()
Definition: mx.h:84
MUTT_PROGRESS_WRITE
@ MUTT_PROGRESS_WRITE
Progress tracks elements, according to $write_inc
Definition: progress.h:43
maildir_commit_message
int maildir_commit_message(struct Mailbox *m, struct Message *msg, struct Email *e)
Commit a message to a maildir folder.
Definition: maildir.c:229
MaildirMboxData
Maildir-specific Mailbox data -.
Definition: mdata.h:34
mdemail.h
MxOps
The Mailbox API.
Definition: mx.h:116
maildir_mdata_get
struct MaildirMboxData * maildir_mdata_get(struct Mailbox *m)
Get the private data for this Mailbox.
Definition: mdata.c:61
monitor.h
maildir_open_find_message
FILE * maildir_open_find_message(const char *folder, const char *msg, char **newname)
Find a new.
Definition: maildir.c:974
maildir_ac_owns_path
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:1057
MaildirMboxData::mtime_cur
struct timespec mtime_cur
Definition: mdata.h:36
lib.h
MUTT_NEWFOLDER
#define MUTT_NEWFOLDER
Create a new folder - same as MUTT_APPEND,.
Definition: mx.h:56
mutt_str_dup
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
Message::received
time_t received
the time at which this message was received
Definition: mx.h:107
mutt_globals.h
LL_DEBUG1
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
ARRAY_SORT
#define ARRAY_SORT(head, fn)
Sort an array.
Definition: array.h:271
Mailbox::has_new
bool has_new
Mailbox has new mail.
Definition: mailbox.h:88
FREE
#define FREE(x)
Definition: memory.h:40
mutt_perror
#define mutt_perror(...)
Definition: logging.h:85
maildir_mdata_free
void maildir_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free()
Definition: mdata.c:37
MX_OPEN_OK
@ MX_OPEN_OK
Open succeeded.
Definition: mx.h:86
mutt_path_canon
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
maildir_update_mtime
void maildir_update_mtime(struct Mailbox *m)
Update our record of the Maildir modification time.
Definition: maildir.c:470
Buffer::dptr
char * dptr
Current read/write position.
Definition: buffer.h:36
maildir_mdata_new
struct MaildirMboxData * maildir_mdata_new(void)
Create a new MaildirMboxData object.
Definition: mdata.c:50
Email::path
char * path
Path of Email (for local Mailboxes)
Definition: email.h:92
Email::edata_free
void(* edata_free)(void **ptr)
Free the private data attached to the Email.
Definition: email.h:117
maildir_msg_open_new
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:1458
PATH_MAX
#define PATH_MAX
Definition: mutt.h:44
ARRAY_SIZE
#define ARRAY_SIZE(head)
The number of elements stored.
Definition: array.h:83
copy.h
mutt_buffer_pool_release
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
C_MaildirHeaderCacheVerify
bool C_MaildirHeaderCacheVerify
Config: (hcache) Check for maildir changes when opening mailbox.
Definition: config.c:40
email_new
struct Email * email_new(void)
Create a new Email.
Definition: email.c:72
mutt_body_free
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
MUTT_STAT_CTIME
@ MUTT_STAT_CTIME
File/dir's ctime - creation time.
Definition: file.h:65
MX_STATUS_REOPENED
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition: mx.h:77
maildir_path_pretty
int maildir_path_pretty(char *buf, size_t buflen, const char *folder)
Abbreviate a Mailbox path - Implements MxOps::path_pretty()
Definition: maildir.c:1583
mutt_hash_find
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
mutt_str_equal
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:871
MX_STATUS_NEW_MAIL
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition: mx.h:75
MdEmail::inode
ino_t inode
Definition: mdemail.h:38
Email::old
bool old
Email is seen, but unread.
Definition: email.h:50
Email::received
time_t received
Time when the message was placed in the mailbox.
Definition: email.h:83
Message::committed_path
char * committed_path
the final path generated by mx_msg_commit()
Definition: mx.h:98
mutt_progress_init
void mutt_progress_init(struct Progress *progress, const char *msg, enum ProgressType type, size_t size)
Set up a progress bar.
Definition: progress.c:153
maildir_rewrite_message
int maildir_rewrite_message(struct Mailbox *m, int msgno)
Sync a message in an MH folder.
Definition: maildir.c:323
Email::active
bool active
Message is not to be removed.
Definition: email.h:59
mutt_buffer_pool_get
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
maildir_read_dir
int maildir_read_dir(struct Mailbox *m, const char *subdir)
Read a Maildir style mailbox.
Definition: maildir.c:661
edata.h
maildir_update_flags
bool maildir_update_flags(struct Mailbox *m, struct Email *e_old, struct Email *e_new)
Update the mailbox flags.
Definition: shared.c:124
Mailbox::type
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
Mailbox::mdata
void * mdata
Driver specific data.
Definition: mailbox.h:136
Envelope::changed
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:88
ARRAY_FOREACH
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:206
HeaderCache::folder
char * folder
Definition: lib.h:87
maildir_msg_close
int maildir_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close()
Definition: maildir.c:1533
lib.h
Account
A group of associated Mailboxes.
Definition: account.h:36
maildir_mbox_check
enum MxStatus maildir_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check()
Definition: maildir.c:1147
MaildirEmailData
Maildir-specific Email data -.
Definition: edata.h:33
Message::path
char * path
path to temp file
Definition: mx.h:97
ARRAY_HEAD_INITIALIZER
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:54
Progress
A progress bar.
Definition: progress.h:50
maildir_msg_commit
static int maildir_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit()
Definition: maildir.c:1523
Body::length
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
Mailbox::changed
bool changed
Mailbox has been modified.
Definition: mailbox.h:114
maildir_sync_message
int maildir_sync_message(struct Mailbox *m, int msgno)
Sync an email to a Maildir folder.
Definition: maildir.c:380
lib.h
Mailbox::msg_count
int msg_count
Total number of messages.
Definition: mailbox.h:91
maildir_msg_open
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:1426
Mailbox::mtime
struct timespec mtime
Time Mailbox was last changed.
Definition: mailbox.h:107
MUTT_UNKNOWN
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition: mailbox.h:47
C_CheckNew
bool C_CheckNew
Config: (maildir,mh) Check for new mail while the mailbox is open.
Definition: config.c:37
mutt_hash_insert
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
MUTT_APPENDNEW
#define MUTT_APPENDNEW
Set in mx_open_mailbox_append if the mailbox doesn't exist.
Definition: mx.h:60
maildir_delayed_parsing
void maildir_delayed_parsing(struct Mailbox *m, struct MdEmailArray *mda, struct Progress *progress)
This function does the second parsing pass.
Definition: maildir.c:589
MUTT_NOTMUCH
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:54
ARRAY_FREE
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:198
Body::parts
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:54
MUTT_PROGRESS_READ
@ MUTT_PROGRESS_READ
Progress tracks elements, according to $read_inc
Definition: progress.h:42
maildir_cmp_inode
static int maildir_cmp_inode(const void *a, const void *b)
Compare two Maildirs by inode number - Implements sort_t.
Definition: maildir.c:488
HCacheEntry::email
struct Email * email
Retrieved email.
Definition: lib.h:100
Email::env
struct Envelope * env
Envelope information.
Definition: email.h:90
maildir_check_empty
int maildir_check_empty(const char *path)
Is the mailbox empty.
Definition: maildir.c:1021
maildir_mbox_sync
enum MxStatus maildir_mbox_sync(struct Mailbox *m)
Save changes to the Mailbox - Implements MxOps::mbox_sync()
Definition: maildir.c:1351
mutt_rand64
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: random.c:129
maildir_check_dir
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:79
mutt_debug
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
MdEmail
A Maildir Email helper.
Definition: mdemail.h:33
Email::purge
bool purge
Skip trash folder when deleting.
Definition: email.h:46
C_FlagSafe
WHERE bool C_FlagSafe
Config: Protect flagged messages from deletion.
Definition: mutt_globals.h:146
CH_UPDATE
#define CH_UPDATE
Update the status and x-status fields?
Definition: copy.h:51
HCacheEntry
Wrapper for Email retrieved from the header cache.
Definition: lib.h:96
Mailbox::msg_flagged
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:93
MX_STATUS_ERROR
@ MX_STATUS_ERROR
An error occurred.
Definition: mx.h:73
Message::fp
FILE * fp
pointer to the message data
Definition: mx.h:96
Email::flagged
bool flagged
Marked important?
Definition: email.h:43
mutt_str_len
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:631
mutt_buffer_string
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
nm_update_filename
int nm_update_filename(struct Mailbox *m, const char *old_file, const char *new_file, struct Email *e)
Change the filename.
Definition: notmuch.c:1827
Mailbox::verbose
bool verbose
Display status messages?
Definition: mailbox.h:118
maildir_entry_new
struct MdEmail * maildir_entry_new(void)
Create a new Maildir entry.
Definition: mdemail.c:41
maildir_gen_flags
void maildir_gen_flags(char *dest, size_t destlen, struct Email *e)
Generate the Maildir flags for an email.
Definition: maildir.c:178
MxMaildirOps
struct MxOps MxMaildirOps
Maildir Mailbox - Implements MxOps.
Definition: maildir.c:1616
mutt_date_epoch
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:416
Email::trash
bool trash
Message is marked as trashed on disk (used by the maildir_trash option)
Definition: email.h:60
mutt_str_replace
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
maildir_parse_message
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:908
maildir_msg_save_hcache
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:1541
lib.h
mutt_hcache_store
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:527
Email::deleted
bool deleted
Email is deleted.
Definition: email.h:45
mutt_file_mkdir
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:875
lib.h
MUTT_HASH_NO_FLAGS
#define MUTT_HASH_NO_FLAGS
No flags are set.
Definition: hash.h:97
HeaderCache
header cache structure
Definition: lib.h:85
mutt_path_parent
bool mutt_path_parent(char *buf, size_t buflen)
Find the parent of a path.
Definition: path.c:459
OpenMailboxFlags
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition: mx.h:50
HomeDir
char * HomeDir
User's home directory.
Definition: mutt_globals.h:49
C_MailCheckRecent
WHERE bool C_MailCheckRecent
Config: Notify the user about new mail since the last time the mailbox was opened.
Definition: mutt_globals.h:150
Progress::msg
char msg[1024]
Definition: progress.h:52
Email::edata
void * edata
Driver-specific data.
Definition: email.h:111
MdEmail::header_parsed
bool header_parsed
Definition: mdemail.h:37
ShortHostname
WHERE char * ShortHostname
Short version of the hostname.
Definition: mutt_globals.h:50
Mailbox::last_visited
struct timespec last_visited
Time of last exit from this mailbox.
Definition: mailbox.h:108
mutt_file_safe_rename
int mutt_file_safe_rename(const char *src, const char *target)
NFS-safe renaming of files.
Definition: file.c:354
MMC_CUR_DIR
#define MMC_CUR_DIR
'cur' directory changed
Definition: maildir.c:68
progress.h
maildir_mbox_open
static enum MxOpenReturns maildir_mbox_open(struct Mailbox *m)
Open a Mailbox - Implements MxOps::mbox_open()
Definition: maildir.c:1073
mutt_hcache_delete_record
int mutt_hcache_delete_record(struct HeaderCache *hc, const char *key, size_t keylen)
Multiplexor for StoreOps::delete_record.
Definition: hcache.c:604
maildir_path_probe
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:1597
MUTT_CM_UPDATE
#define MUTT_CM_UPDATE
Update structs on sync.
Definition: copy.h:39
C_HeaderCache
char * C_HeaderCache
Config: (hcache) Directory/file for the header cache database.
Definition: config.c:40
mailbox_changed
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:186
C_MaildirTrash
bool C_MaildirTrash
Config: Use the maildir 'trashed' flag, rather than deleting.
Definition: config.c:42
ch_compare
static int ch_compare(const void *a, const void *b)
qsort callback to sort characters
Definition: maildir.c:167
mutt_file_get_stat_timespec
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:1537
MailboxType
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
MMC_NO_DIRS
#define MMC_NO_DIRS
No directories changed.
Definition: maildir.c:66
C_MarkOld
bool C_MarkOld
Config: Mark new emails as old when leaving the mailbox.
Definition: globals.c:36
Email::index
int index
The absolute (unsorted) message number.
Definition: email.h:86
mdata.h
mutt_rfc822_read_header
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
parses an RFC822 header
Definition: parse.c:1112
email_free
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:43
mutt_progress_update
void mutt_progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:212
Mailbox::msg_unread
int msg_unread
Number of unread messages.
Definition: mailbox.h:92
maildir_edata_get
struct MaildirEmailData * maildir_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:64
maildir_open_find_message_dir
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:742
maildir_parse_dir
int maildir_parse_dir(struct Mailbox *m, struct MdEmailArray *mda, const char *subdir, struct Progress *progress)
Read a Maildir mailbox.
Definition: maildir.c:506
MUTT_MAILDIR
@ MUTT_MAILDIR
'Maildir' Mailbox type
Definition: mailbox.h:51
mutt_buffer_strdup
char * mutt_buffer_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:432
MX_STATUS_FLAGS
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition: mx.h:78
private.h
Email::attach_del
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:49
MUTT_APPEND
#define MUTT_APPEND
Open mailbox for appending messages.
Definition: mx.h:53
mailbox_path
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:206
mx.h
mutt_path_pretty
bool mutt_path_pretty(char *buf, size_t buflen, const char *homedir, bool is_dir)
Tidy a filesystem path.
Definition: path.c:186
HCacheEntry::uidvalidity
uint32_t uidvalidity
IMAP-specific UIDVALIDITY.
Definition: lib.h:98
mx_msg_close
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1204
maildir_ac_add
bool maildir_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Implements MxOps::ac_add()
Definition: maildir.c:1065
mutt_hcache_close
void mutt_hcache_close(struct HeaderCache *hc)
Multiplexor for StoreOps::close.
Definition: hcache.c:419
Email::replied
bool replied
Email has been replied to.
Definition: email.h:54
Buffer::data
char * data
Pointer to data.
Definition: buffer.h:35
Email
The envelope/body of an email.
Definition: email.h:37
HashTable
A Hash Table.
Definition: hash.h:84
mutt_str_startswith
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
mx_msg_open_new
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1072
Mailbox::msg_new
int msg_new
Number of new messages.
Definition: mailbox.h:95
MX_OPEN_ERROR
@ MX_OPEN_ERROR
Open failed with an error.
Definition: mx.h:87
mutt_buffer_printf
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
mutt_file_stat_timespec_compare
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:1577
mutt_copy_message
int mutt_copy_message(FILE *fp_out, struct Mailbox *m, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Copy a message from a Mailbox.
Definition: copy.c:835
mh_umask
mode_t mh_umask(struct Mailbox *m)
Create a umask from the mailbox directory.
Definition: shared.c:54
mutt_file_fsync_close
int mutt_file_fsync_close(FILE **fp)
Flush the data, before closing a file (and NULL the pointer)
Definition: file.c:169
MonitorContextChanged
bool MonitorContextChanged
true after the current mailbox has changed
Definition: monitor.c:52
NT_MAILBOX_RESORT
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition: mailbox.h:174
CH_UPDATE_LEN
#define CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition: copy.h:61
Email::read
bool read
Email is read.
Definition: email.h:51
maildir_mbox_check_stats
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:1323
mutt_buffer_strcpy
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
maildir_edata_free
void maildir_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: edata.c:38
C_MaildirCheckCur
bool C_MaildirCheckCur
Config: Check both 'new' and 'cur' directories for new mail.
Definition: config.c:38
LL_DEBUG2
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
maildir_edata_new
struct MaildirEmailData * maildir_edata_new(void)
Create a new MaildirEmailData object.
Definition: edata.c:53
mutt_hash_free
void mutt_hash_free(struct HashTable **ptr)
Free a hash table.
Definition: hash.c:447
Email::changed
bool changed
Email has been edited.
Definition: email.h:48
Message
A local copy of an email.
Definition: mx.h:94
maildir_sync_mailbox_message
bool maildir_sync_mailbox_message(struct Mailbox *m, int msgno, struct HeaderCache *hc)
Save changes to the mailbox.
Definition: maildir.c:928
MX_STATUS_OK
@ MX_STATUS_OK
No changes.
Definition: mx.h:74
Email::body
struct Body * body
List of MIME parts.
Definition: email.h:91
maildir_move_to_mailbox
int maildir_move_to_mailbox(struct Mailbox *m, struct MdEmailArray *mda)
Copy the Maildir list to the Mailbox.
Definition: shared.c:77
maildir_canon_filename
void maildir_canon_filename(struct Buffer *dest, const char *src)
Generate the canonical filename for a Maildir folder.
Definition: maildir.c:712
mutt_hcache_fetch
struct HCacheEntry mutt_hcache_fetch(struct HeaderCache *hc, const char *key, size_t keylen, uint32_t uidvalidity)
Multiplexor for StoreOps::fetch.
Definition: hcache.c:438
mutt_str_copy
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:716
maildir_path_parent
int maildir_path_parent(char *buf, size_t buflen)
Find the parent of a Mailbox path - Implements MxOps::path_parent()
Definition: maildir.c:1566