Rocco Rutte:
[apps/madmutt.git] / mh.c
diff --git a/mh.c b/mh.c
index 56265b5..ae978e9 100644 (file)
--- a/mh.c
+++ b/mh.c
  * mailboxes.
  */
 
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
 #include "mutt.h"
 #include "mailbox.h"
 #include "mx.h"
@@ -30,6 +34,7 @@
 #include "sort.h"
 
 #include <sys/stat.h>
+#include <sys/types.h>
 #include <dirent.h>
 #include <limits.h>
 #include <unistd.h>
@@ -47,6 +52,7 @@ struct maildir
   HEADER *h;
   char *canon_fname;
   unsigned header_parsed:1;
+  ino_t inode;
   struct maildir *next;
 };
 
@@ -190,7 +196,8 @@ static int mh_mkstemp (CONTEXT * dest, FILE ** fp, char **tgt)
   {
     snprintf (path, _POSIX_PATH_MAX, "%s/.mutt-%s-%d-%d",
              dest->path, NONULL (Hostname), (int) getpid (), Counter++);
-    if ((fd = open (path, O_WRONLY | O_EXCL | O_CREAT, 0600)) == -1)
+    umask(Umask);
+    if ((fd = open (path, O_WRONLY | O_EXCL | O_CREAT, 0666)) == -1)
     {
       if (errno != EEXIST)
       {
@@ -626,7 +633,7 @@ static HEADER *maildir_parse_message (int magic, const char *fname,
 
 static int maildir_parse_entry (CONTEXT * ctx, struct maildir ***last,
                                const char *subdir, const char *fname,
-                               int *count, int is_old)
+                               int *count, int is_old, ino_t inode)
 {
   struct maildir *entry;
   HEADER *h = NULL;
@@ -666,6 +673,7 @@ static int maildir_parse_entry (CONTEXT * ctx, struct maildir ***last,
     entry = safe_calloc (sizeof (struct maildir), 1);
     entry->h = h;
     entry->header_parsed = (ctx->magic == M_MH);
+    entry->inode = inode;
     **last = entry;
     *last = &entry->next;
 
@@ -723,7 +731,8 @@ static int maildir_parse_dir (CONTEXT * ctx, struct maildir ***last,
     dprint (2,
            (debugfile, "%s:%d: parsing %s\n", __FILE__, __LINE__,
             de->d_name));
-    maildir_parse_entry (ctx, last, subdir, de->d_name, count, is_old);
+    maildir_parse_entry (ctx, last, subdir, de->d_name, count, is_old, 
+                        de->d_ino);
   }
 
   closedir (dirp);
@@ -779,29 +788,167 @@ static int maildir_move_to_context (CONTEXT * ctx, struct maildir **md)
   return r;
 }
 
+/*
+ * Merge two maildir lists according to the inode numbers.
+ */
+static struct maildir*  maildir_merge_inode (struct maildir *left,
+                                            struct maildir *right)
+{
+  struct maildir* head;
+  struct maildir* tail;
+
+  if (left && right) 
+  {
+    if (left->inode < right->inode)
+    {
+      head = left;
+      left = left->next;
+    }
+    else 
+    {
+      head = right;
+      right = right->next;
+    }
+  } 
+  else 
+  {
+    if (left) 
+      return left;
+    else 
+      return right;
+  }
+    
+  tail = head;
+
+  while (left && right) 
+  {
+    if (left->inode < right->inode) 
+    {
+      tail->next = left;
+      left = left->next;
+    } 
+    else 
+    {
+      tail->next = right;
+      right = right->next;
+    }
+    tail = tail->next;
+  }
+
+  if (left) 
+  {
+    tail->next = left;
+  }
+  else
+  {
+    tail->next = right;
+  }
+
+  return head;
+}
+
+/*
+ * Sort maildir list according to inode.
+ */
+static struct maildir* maildir_sort_inode(struct maildir* list)
+{
+  struct maildir* left = list;
+  struct maildir* right = list;
+
+  if (!list || !list->next) 
+  {
+    return list;
+  }
+
+  list = list->next;
+  while (list && list->next) 
+  {
+    right = right->next;
+    list = list->next->next;
+  }
+
+  list = right;
+  right = right->next;
+  list->next = 0;
+
+  left = maildir_sort_inode(left);
+  right = maildir_sort_inode(right);
+  return maildir_merge_inode(left, right);
+}
+
+#if USE_HCACHE
+static size_t maildir_hcache_keylen (const char *fn)
+{
+  const char * p = strchr (fn, ':');
+  return p ? (size_t) (p - fn) : mutt_strlen(fn);
+}
+#endif
 
 /* 
  * This function does the second parsing pass for a maildir-style
  * folder.
  */
-
 void maildir_delayed_parsing (CONTEXT * ctx, struct maildir *md)
 {
   struct maildir *p;
   char fn[_POSIX_PATH_MAX];
+  int count;
 
-  for (p = md; p; p = p->next)
-    if (p && p->h && !p->header_parsed)
-    {
-      snprintf (fn, sizeof (fn), "%s/%s", ctx->path, p->h->path);
-      if (maildir_parse_message (ctx->magic, fn, p->h->old, p->h))
-       p->header_parsed = 1;
-      else
-       mutt_free_header (&p->h);
-    }
-}
+#if USE_HCACHE
+  void *hc = NULL;
+  void *data;
+  struct timeval *when = NULL;
+  struct stat lastchanged;
+  int ret;
+
+  hc = mutt_hcache_open (HeaderCache, ctx->path);
+#endif
+
+  for (p = md, count = 0; p; p = p->next, count++)
+  {
+    if (! (p && p->h && !p->header_parsed))
+       continue;
 
+#if USE_HCACHE
+    data      = mutt_hcache_fetch (hc, p->h->path + 3, &maildir_hcache_keylen);
+    when      = (struct timeval *) data;
+#endif
+
+    if (!ctx->quiet && ReadInc && ((count % ReadInc) == 0 || count == 1))
+      mutt_message (_("Reading %s... %d"), ctx->path, count);
+    snprintf (fn, sizeof (fn), "%s/%s", ctx->path, p->h->path);
 
+#if USE_HCACHE
+    if (option(OPTHCACHEVERIFY)) {
+      ret = stat(fn, &lastchanged);
+    } else {
+      lastchanged.st_mtime = 0;
+      ret = 0;
+    }
+
+    if (data != NULL && !ret && lastchanged.st_mtime <= when->tv_sec)
+    {
+      p->h = mutt_hcache_restore ((unsigned char *)data, &p->h);
+      maildir_parse_flags (p->h, fn);
+    } else
+#endif
+    if (maildir_parse_message (ctx->magic, fn, p->h->old, p->h))
+    {
+      p->header_parsed = 1;
+      maildir_parse_flags (p->h, fn);
+#if USE_HCACHE
+      mutt_hcache_store (hc, p->h->path + 3, p->h, 0, &maildir_hcache_keylen);
+#endif
+    } else
+      mutt_free_header (&p->h);
+#if USE_HCACHE
+    FREE(&data);
+#endif
+  }
+#if USE_HCACHE
+  mutt_hcache_close (hc);
+#endif
+}
 
 /* Read a MH/maildir style mailbox.
  *
@@ -835,6 +982,8 @@ int mh_read_dir (CONTEXT * ctx, const char *subdir)
     mhs_free_sequences (&mhs);
   }
 
+  md = maildir_sort_inode(md);
+
   if (ctx->magic == M_MAILDIR)
     maildir_delayed_parsing (ctx, md);
 
@@ -930,14 +1079,15 @@ int maildir_open_new_message (MESSAGE * msg, CONTEXT * dest, HEADER * hdr)
 
   FOREVER
   {
-    snprintf (path, _POSIX_PATH_MAX, "%s/tmp/%s.%ld.%d_%d.%s%s",
-             dest->path, subdir, time (NULL), getpid (), Counter++,
-             NONULL (Hostname), suffix);
+    snprintf (path, _POSIX_PATH_MAX, "%s/tmp/%s.%ld.%u_%d.%s%s",
+             dest->path, subdir, (long) time (NULL), (unsigned int)getpid (),
+             Counter++, NONULL (Hostname), suffix);
 
     dprint (2, (debugfile, "maildir_open_new_message (): Trying %s.\n",
                path));
 
-    if ((fd = open (path, O_WRONLY | O_EXCL | O_CREAT, 0600)) == -1)
+    umask(Umask);
+    if ((fd = open (path, O_WRONLY | O_EXCL | O_CREAT, 0666)) == -1)
     {
       if (errno != EEXIST)
       {
@@ -1011,8 +1161,9 @@ int maildir_commit_message (CONTEXT * ctx, MESSAGE * msg, HEADER * hdr)
   /* construct a new file name. */
   FOREVER
   {
-    snprintf (path, _POSIX_PATH_MAX, "%s/%ld.%d_%d.%s%s", subdir,
-             time (NULL), getpid (), Counter++, NONULL (Hostname), suffix);
+    snprintf (path, _POSIX_PATH_MAX, "%s/%ld.%u_%d.%s%s", subdir,
+             (long) time (NULL), (unsigned int)getpid (), Counter++, 
+             NONULL (Hostname), suffix);
     snprintf (full, _POSIX_PATH_MAX, "%s/%s", ctx->path, path);
 
     dprint (2, (debugfile, "maildir_commit_message (): renaming %s to %s.\n",
@@ -1220,7 +1371,7 @@ static int mh_sync_message (CONTEXT * ctx, int msgno)
 {
   HEADER *h = ctx->hdrs[msgno];
 
-  if (h->attach_del)
+  if (h->attach_del || h->refs_changed || h->irt_changed)
     if (mh_rewrite_message (ctx, msgno) != 0)
       return -1;
 
@@ -1231,9 +1382,9 @@ static int maildir_sync_message (CONTEXT * ctx, int msgno)
 {
   HEADER *h = ctx->hdrs[msgno];
 
-  if (h->attach_del)
+  if (h->attach_del || h->refs_changed || h->irt_changed)
   {
-    /* when doing attachment deletion, fall back to the MH case. */
+    /* when doing attachment deletion/rethreading, fall back to the MH case. */
     if (mh_rewrite_message (ctx, msgno) != 0)
       return (-1);
   }
@@ -1293,6 +1444,9 @@ int mh_sync_mailbox (CONTEXT * ctx, int *index_hint)
 {
   char path[_POSIX_PATH_MAX], tmp[_POSIX_PATH_MAX];
   int i, j;
+#if USE_HCACHE
+  void *hc = NULL;
+#endif /* USE_HCACHE */
 
   if (ctx->magic == M_MH)
     i = mh_check_mailbox (ctx, index_hint);
@@ -1302,6 +1456,11 @@ int mh_sync_mailbox (CONTEXT * ctx, int *index_hint)
   if (i != 0)
     return i;
 
+#if USE_HCACHE
+  if (ctx->magic == M_MAILDIR)
+    hc = mutt_hcache_open(HeaderCache, ctx->path);
+#endif /* USE_HCACHE */
+
   for (i = 0; i < ctx->msgcount; i++)
   {
     if (ctx->hdrs[i]->deleted
@@ -1310,7 +1469,13 @@ int mh_sync_mailbox (CONTEXT * ctx, int *index_hint)
       snprintf (path, sizeof (path), "%s/%s", ctx->path, ctx->hdrs[i]->path);
       if (ctx->magic == M_MAILDIR
          || (option (OPTMHPURGE) && ctx->magic == M_MH))
+      {
+#if USE_HCACHE
+        if (ctx->magic == M_MAILDIR)
+          mutt_hcache_delete (hc, ctx->hdrs[i]->path + 3, &maildir_hcache_keylen);
+#endif /* USE_HCACHE */
        unlink (path);
+      }
       else if (ctx->magic == M_MH)
       {
        /* MH just moves files out of the way when you delete them */
@@ -1332,16 +1497,21 @@ int mh_sync_mailbox (CONTEXT * ctx, int *index_hint)
       if (ctx->magic == M_MAILDIR)
       {
        if (maildir_sync_message (ctx, i) == -1)
-         return -1;
+         goto err;
       }
       else
       {
        if (mh_sync_message (ctx, i) == -1)
-         return -1;
+         goto err;
       }
     }
   }
 
+#if USE_HCACHE
+  if (ctx->magic == M_MAILDIR)
+    mutt_hcache_close (hc);
+#endif /* USE_HCACHE */
+
   if (ctx->magic == M_MH)
     mh_update_sequences (ctx);
 
@@ -1362,6 +1532,13 @@ int mh_sync_mailbox (CONTEXT * ctx, int *index_hint)
   }
 
   return 0;
+
+err:
+#if USE_HCACHE
+  if (ctx->magic == M_MAILDIR)
+    mutt_hcache_close (hc);
+#endif /* USE_HCACHE */
+  return -1;
 }
 
 static char *maildir_canon_filename (char *dest, const char *src, size_t l)