]> granicus.if.org Git - mutt/commitdiff
Add $imap_cachedir option. If set, mutt will keep copies of IMAP messages
authorBrendan Cully <brendan@kublai.com>
Thu, 22 Dec 2005 17:46:40 +0000 (17:46 +0000)
committerBrendan Cully <brendan@kublai.com>
Thu, 22 Dec 2005 17:46:40 +0000 (17:46 +0000)
under this directory.

globals.h
imap/imap.c
imap/imap_private.h
imap/message.c
imap/message.h
init.h

index ae3f822785759c40d5ccfdc5633f4582ad049764..f7c798c3a0e5a9d2cf420ccf95b843c6993480ad 100644 (file)
--- a/globals.h
+++ b/globals.h
@@ -55,6 +55,7 @@ WHERE char *Homedir;
 WHERE char *Hostname;
 #ifdef USE_IMAP
 WHERE char *ImapAuthenticators INITVAL (NULL);
+WHERE char *ImapCachedir;
 WHERE char *ImapDelimChars INITVAL (NULL);
 WHERE char *ImapHeaders;
 WHERE char *ImapHomeNamespace INITVAL (NULL);
index 4b729fa4ee540d93ece92fb5410f3a791488fd59..e122224415a04e7dcc26b62a13eab366fa6caf74 100644 (file)
@@ -258,6 +258,7 @@ void imap_expunge_mailbox (IMAP_DATA* idata)
 
       h->active = 0;
 
+      imap_cache_del (idata, h);
 #if USE_HCACHE
       if (hc)
       {
@@ -1134,13 +1135,14 @@ int imap_sync_mailbox (CONTEXT* ctx, int expunge, int* index_hint)
   /* save messages with real (non-flag) changes */
   for (n = 0; n < ctx->msgcount; n++)
   {
+    imap_cache_del (idata, ctx->hdrs[n]);
 #if USE_HCACHE
     if (hc && ctx->hdrs[n]->deleted)
     {
       sprintf (uidbuf, "/%u", HEADER_DATA(ctx->hdrs[n])->uid);
       mutt_hcache_delete (hc, uidbuf, imap_hcache_keylen);
     }
-#endif    
+#endif
     if (ctx->hdrs[n]->active && ctx->hdrs[n]->changed)
     {
       /* if the message has been rethreaded or attachments have been deleted
index 11da87989e05137c381af7dfab743f386914268d..705180f19c1c7d7592a693e553fab0e963b37dcd 100644 (file)
@@ -264,6 +264,7 @@ void imap_add_keywords (char* s, HEADER* keywords, LIST* mailbox_flags, size_t s
 void imap_free_header_data (void** data);
 int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend);
 char* imap_set_flags (IMAP_DATA* idata, HEADER* h, char* s);
+int imap_cache_del (IMAP_DATA* idata, HEADER* h);
 
 /* util.c */
 int imap_continue (const char* msg, const char* resp);
index feee17017584c0e20fd1bf21f0e651afece0edc1..5420c803301feffc167c62a61eb08f95e9c6db29 100644 (file)
@@ -40,6 +40,9 @@
 #include "hcache.h"
 #endif
 
+static FILE* msg_cache_get (IMAP_DATA* idata, HEADER* h);
+static FILE* msg_cache_put (IMAP_DATA* idata, HEADER* h);
+
 static void flush_buffer(char* buf, size_t* len, CONNECTION* conn);
 static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf,
   FILE* fp);
@@ -356,6 +359,15 @@ int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno)
   idata = (IMAP_DATA*) ctx->data;
   h = ctx->hdrs[msgno];
 
+  if ((msg->fp = msg_cache_get (idata, h)))
+  {
+    if (HEADER_DATA(h)->parsed)
+      return 0;
+    else
+      goto parsemsg;
+  }
+
+  /* we still do some caching even if imap_cachedir is unset */
   /* see if we already have the message in our cache */
   cacheno = HEADER_DATA(h)->uid % IMAP_CACHE_LEN;
   cache = &idata->cache[cacheno];
@@ -376,13 +388,16 @@ int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno)
   if (!isendwin())
     mutt_message _("Fetching message...");
 
-  cache->uid = HEADER_DATA(h)->uid;
-  mutt_mktemp (path);
-  cache->path = safe_strdup (path);
-  if (!(msg->fp = safe_fopen (path, "w+")))
+  if (!(msg->fp = msg_cache_put (idata, h)))
   {
-    FREE (&cache->path);
-    return -1;
+    cache->uid = HEADER_DATA(h)->uid;
+    mutt_mktemp (path);
+    cache->path = safe_strdup (path);
+    if (!(msg->fp = safe_fopen (path, "w+")))
+    {
+      FREE (&cache->path);
+      return -1;
+    }
   }
 
   /* mark this header as currently inactive so the command handler won't
@@ -469,7 +484,8 @@ int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno)
 
   if (!fetched || !imap_code (idata->buf))
     goto bail;
-    
+
+parsemsg:
   /* Update the header information.  Previously, we only downloaded a
    * portion of the headers, those required for the main display.
    */
@@ -508,11 +524,13 @@ int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno)
 
   mutt_clear_error();
   rewind (msg->fp);
+  HEADER_DATA(h)->parsed = 1;
 
   return 0;
 
 bail:
   safe_fclose (&msg->fp);
+  imap_cache_del (idata, h);
   if (cache->path)
   {
     unlink (cache->path);
@@ -813,6 +831,102 @@ int imap_copy_messages (CONTEXT* ctx, HEADER* h, char* dest, int delete)
   return -1;
 }
 
+/* create file system path for idata/h */
+static int msg_cache_path (IMAP_DATA* idata, HEADER* h, char* buf, size_t len)
+{
+  ACCOUNT* account;
+  char* s, *p;
+  int slen;
+
+  if (!ImapCachedir)
+    return -1;
+
+  account = &idata->conn->account;
+
+  snprintf (buf, len, "%s/", ImapCachedir);
+  slen = mutt_strlen (buf);
+  if (account->flags & M_ACCT_USER)
+    snprintf (buf + slen, len - slen, "%s@", account->user);
+  safe_strcat (buf, len, account->host);
+  if (account->flags & M_ACCT_PORT)
+  {
+    slen = mutt_strlen (buf);
+    snprintf (buf + slen, len - slen, ":%hu", account->port);
+  }
+  safe_strcat (buf, len, "/");
+
+  slen = len - mutt_strlen (buf) - 2;
+  p = idata->mailbox;
+  for (s = buf + mutt_strlen (buf); *p && slen; slen--)
+  {
+    if (*p == idata->delim)
+    {
+      *s = '/';
+      /* simple way to avoid collisions with UIDs */
+      if (*(p + 1) >= '0' && *(p + 1) <= '9')
+      {
+        slen--;
+        if (slen)
+          *++s = '_';
+      }
+    }
+    else
+      *s = *p;
+    *p++;
+    *s++;
+  }
+  *s = '\0';
+
+  slen = mutt_strlen (buf);
+  snprintf (buf + slen, len - slen, "/%u", HEADER_DATA(h)->uid);
+
+  return 0;
+}
+
+static FILE* msg_cache_get (IMAP_DATA* idata, HEADER* h)
+{
+  char path[_POSIX_PATH_MAX];
+
+  if (msg_cache_path (idata, h, path, sizeof (path)) < 0)
+    return NULL;
+
+  return fopen (path, "r");
+}
+
+static FILE* msg_cache_put (IMAP_DATA* idata, HEADER* h)
+{
+  char path[_POSIX_PATH_MAX];
+  FILE* fp;
+  char* s;
+  struct stat sb;
+
+  if (msg_cache_path (idata, h, path, sizeof (path)) < 0)
+    return NULL;
+
+  s = strchr (path + 1, '/');
+  while (!(fp = safe_fopen (path, "w+")) && errno == ENOENT && s)
+  {
+    /* create missing path components */
+    *s = '\0';
+    if (stat (path, &sb) < 0 && (errno != ENOENT || mkdir (path, 0777) < 0))
+      return NULL;
+    *s = '/';
+    s = strchr (s + 1, '/');
+  }
+
+  return fp;
+}
+
+int imap_cache_del (IMAP_DATA* idata, HEADER* h)
+{
+  char path[_POSIX_PATH_MAX];
+  
+  if (msg_cache_path (idata, h, path, sizeof (path)) < 0)
+    return -1;
+
+  return unlink (path);
+}
+
 /* imap_add_keywords: concatenate custom IMAP tags to list, if they
  *   appear in the folder flags list. Why wouldn't they? */
 void imap_add_keywords (char* s, HEADER* h, LIST* mailbox_flags, size_t slen)
index fc763848ceb599f53c14f3a341c0c88572d1fddd..6f45d9e8dabd5097b6ab3d9d48e61f3f1a835c63 100644 (file)
@@ -34,6 +34,8 @@ typedef struct imap_header_data
   unsigned int replied : 1;
   unsigned int changed : 1;
 
+  unsigned int parsed : 1;
+
   unsigned int uid;    /* 32-bit Message UID */
   LIST *keywords;
 } IMAP_HEADER_DATA;
diff --git a/init.h b/init.h
index e1d6d78f5bee49b3c2e33145c89a489312af61f0..f8be4cfeac8d24a6f3186e7aedfa11d4a5f25c07 100644 (file)
--- a/init.h
+++ b/init.h
@@ -818,6 +818,13 @@ struct option_t MuttVars[] = {
   ** the previous methods are unavailable. If a method is available but
   ** authentication fails, mutt will not connect to the IMAP server.
   */
+  { "imap_cachedir", DT_PATH, R_NONE, UL &ImapCachedir, 0 },
+  /*
+   ** .pp
+   ** Set this to a directory and mutt will cache copies of messages from
+   ** your IMAP servers here. You are free to remove entries at any time
+   ** if space becomes an issue.
+   */
   { "imap_check_subscribed",  DT_BOOL, R_NONE, OPTIMAPCHECKSUBSCRIBED, 0 },
   /*
    ** .pp