Andreas Krennmair:
[apps/madmutt.git] / imap / message.c
index 41d5950..016487b 100644 (file)
@@ -39,6 +39,12 @@ static int msg_has_flag (LIST* flag_list, const char* flag);
 static int msg_parse_fetch (IMAP_HEADER* h, char* s);
 static char* msg_parse_flags (IMAP_HEADER* h, char* s);
 
+#if USE_HCACHE
+static int msg_fetch_header_fetch (CONTEXT* ctx, IMAP_HEADER* h, char* buf,
+  FILE* fp);
+static size_t imap_hcache_keylen (const char *fn);
+#endif /* USE_HCACHE */
+
 /* imap_read_headers:
  * Changed to read many headers instead of just one. It will return the
  * msgno of the last message read. It will return a value other than
@@ -55,24 +61,37 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend)
   IMAP_HEADER h;
   int rc, mfhrc, oldmsgcount;
   int fetchlast = 0;
-  const char *want_headers = "DATE FROM SUBJECT TO CC MESSAGE-ID REFERENCES CONTENT-TYPE IN-REPLY-TO REPLY-TO LINES X-LABEL";
+  const char *want_headers = "DATE FROM SUBJECT TO CC MESSAGE-ID REFERENCES CONTENT-TYPE CONTENT-DESCRIPTION IN-REPLY-TO REPLY-TO LINES LIST-POST X-LABEL";
+
+#if USE_HCACHE
+  void *hc   = NULL;
+  unsigned long long *uid_validity = NULL;
+  char uid_buf[64];
+#endif /* USE_HCACHE */
 
   ctx = idata->ctx;
 
+#if USE_HCACHE
+  hc = mutt_hcache_open (HeaderCache, ctx->path);
+#endif /* USE_HCACHE */
+
   if (mutt_bit_isset (idata->capabilities,IMAP4REV1))
   {
-    snprintf (hdrreq, sizeof (hdrreq), "BODY.PEEK[HEADER.FIELDS (%s)]", 
-      want_headers); 
+    snprintf (hdrreq, sizeof (hdrreq), "BODY.PEEK[HEADER.FIELDS (%s%s%s)]", 
+             want_headers, ImapHeaders ? " " : "", ImapHeaders ? ImapHeaders : ""); 
   } 
   else if (mutt_bit_isset (idata->capabilities,IMAP4))
   {
-    snprintf (hdrreq, sizeof (hdrreq), "RFC822.HEADER.LINES (%s)", 
-      want_headers);
+    snprintf (hdrreq, sizeof (hdrreq), "RFC822.HEADER.LINES (%s%s%s)", 
+      want_headers, ImapHeaders ? " " : "", ImapHeaders ? ImapHeaders : "");
   }
   else
   {    /* Unable to fetch headers for lower versions */
     mutt_error _("Unable to fetch headers from this IMAP server version.");
     mutt_sleep (2);    /* pause a moment to let the user see the error */
+#if USE_HCACHE
+    mutt_hcache_close (hc);
+#endif /* USE_HCACHE */
     return -1;
   }
 
@@ -83,6 +102,9 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend)
   {
     mutt_error (_("Could not create temporary file %s"), tempfile);
     mutt_sleep (2);
+#if USE_HCACHE
+    mutt_hcache_close (hc);
+#endif /* USE_HCACHE */
     return -1;
   }
   unlink (tempfile);
@@ -95,14 +117,96 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend)
   idata->reopen &= ~IMAP_NEWMAIL_PENDING;
   idata->newMailCount = 0;
 
+#if USE_HCACHE
+  snprintf (buf, sizeof (buf),
+    "FETCH %d:%d (UID FLAGS)", msgbegin + 1, msgend + 1);
+  fetchlast = msgend + 1;
+
+  imap_cmd_start (idata, buf);
+
+  for (msgno = msgbegin; msgno <= msgend ; msgno++)
+  {
+    if (ReadInc && (!msgno || ((msgno+1) % ReadInc == 0)))
+      mutt_message (_("Evaluating cache... [%d/%d]"), msgno + 1,
+        msgend + 1);
+
+    rewind (fp);
+    memset (&h, 0, sizeof (h));
+    h.data = safe_calloc (1, sizeof (IMAP_HEADER_DATA));
+    do
+    {
+      mfhrc = 0;
+
+      rc = imap_cmd_step (idata);
+      if (rc != IMAP_CMD_CONTINUE)
+       break;
+
+      if ((mfhrc = msg_fetch_header_fetch (idata->ctx, &h, idata->cmd.buf, fp)) == -1)
+       continue;
+      else if (mfhrc < 0)
+       break;
+
+      /* make sure we don't get remnants from older larger message headers */
+      fputs ("\n\n", fp);
+
+      sprintf(uid_buf, "/%u", h.data->uid); /* XXX --tg 21:41 04-07-11 */
+      uid_validity = (unsigned long long *) mutt_hcache_fetch (hc, uid_buf, &imap_hcache_keylen);
+
+      if (uid_validity != NULL
+      && *uid_validity == idata->uid_validity) {
+             ctx->hdrs[msgno] = mutt_hcache_restore((unsigned char *) uid_validity, 0);
+             ctx->hdrs[msgno]->index = h.sid - 1;
+             if (h.sid != ctx->msgcount + 1)
+                     dprint (1, (debugfile, "imap_read_headers: msgcount and sequence ID are inconsistent!"));
+             /* messages which have not been expunged are ACTIVE (borrowed from mh 
+              * folders) */
+             ctx->hdrs[msgno]->active = 1;
+             ctx->hdrs[msgno]->read = h.read;
+             ctx->hdrs[msgno]->old = h.old;
+             ctx->hdrs[msgno]->deleted = h.deleted;
+             ctx->hdrs[msgno]->flagged = h.flagged;
+             ctx->hdrs[msgno]->replied = h.replied;
+             ctx->hdrs[msgno]->changed = h.changed;
+             /*  ctx->hdrs[msgno]->received is restored from mutt_hcache_restore */
+             ctx->hdrs[msgno]->data = (void *) (h.data);
+
+             ctx->msgcount++;
+      }
+      rewind (fp);
+
+      FREE(&uid_validity);
+
+    }
+    while ((rc != IMAP_CMD_OK) && ((mfhrc == -1) ||
+      ((msgno + 1) >= fetchlast)));
+
+    if ((mfhrc < -1) || ((rc != IMAP_CMD_CONTINUE) && (rc != IMAP_CMD_OK)))
+    {
+      imap_free_header_data ((void**) &h.data);
+      fclose (fp);
+  mutt_hcache_close (hc);
+      return -1;
+    }
+  }
+
+  fetchlast = msgbegin;
+#endif /* USE_HCACHE */
+
   for (msgno = msgbegin; msgno <= msgend ; msgno++)
   {
     if (ReadInc && (!msgno || ((msgno+1) % ReadInc == 0)))
       mutt_message (_("Fetching message headers... [%d/%d]"), msgno + 1,
         msgend + 1);
 
+    if (ctx->hdrs[msgno])
+      continue;
+
     if (msgno + 1 > fetchlast)
     {
+      fetchlast = msgno + 1;
+      while((fetchlast <= msgend) && (! ctx->hdrs[fetchlast]))
+        fetchlast++;
+
       /*
        * Make one request for everything. This makes fetching headers an
        * order of magnitude faster if you have a large mailbox.
@@ -112,11 +216,9 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend)
        */
       snprintf (buf, sizeof (buf),
         "FETCH %d:%d (UID FLAGS INTERNALDATE RFC822.SIZE %s)", msgno + 1,
-        msgend + 1, hdrreq);
+        fetchlast, hdrreq);
 
       imap_cmd_start (idata, buf);
-
-      fetchlast = msgend + 1;
     }
 
     /* freshen fp, h */
@@ -170,6 +272,11 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend)
       /* content built as a side-effect of mutt_read_rfc822_header */
       ctx->hdrs[msgno]->content->length = h.content_length;
 
+#if USE_HCACHE
+      sprintf(uid_buf, "/%u", h.data->uid);
+      mutt_hcache_store(hc, uid_buf, ctx->hdrs[msgno], idata->uid_validity, &imap_hcache_keylen);
+#endif /* USE_HCACHE */
+
       ctx->msgcount++;
     }
     while ((rc != IMAP_CMD_OK) && ((mfhrc == -1) ||
@@ -179,7 +286,9 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend)
     {
       imap_free_header_data ((void**) &h.data);
       fclose (fp);
-
+#if USE_HCACHE
+  mutt_hcache_close (hc);
+#endif /* USE_HCACHE */
       return -1;
     }
        
@@ -194,6 +303,10 @@ int imap_read_headers (IMAP_DATA* idata, int msgbegin, int msgend)
     }
   }
 
+#if USE_HCACHE
+  mutt_hcache_close (hc);
+#endif /* USE_HCACHE */
+
   fclose(fp);
 
   if (ctx->msgcount > oldmsgcount)
@@ -263,7 +376,7 @@ int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno)
    * command handler */
   h->active = 0;
   
-  snprintf (buf, sizeof (buf), "UID FETCH %d %s", HEADER_DATA(h)->uid,
+  snprintf (buf, sizeof (buf), "UID FETCH %u %s", HEADER_DATA(h)->uid,
            (mutt_bit_isset (idata->capabilities, IMAP4REV1) ?
             (option (OPTIMAPPEEK) ? "BODY.PEEK[]" : "BODY[]") :
             "RFC822"));
@@ -439,7 +552,7 @@ int imap_append_message (CONTEXT *ctx, MESSAGE *msg)
   rewind (fp);
   
   imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
-  snprintf (buf, sizeof (buf), "APPEND %s (\\Seen) {%d}", mbox, len);
+  snprintf (buf, sizeof (buf), "APPEND %s (\\Seen) {%lu}", mbox, len);
 
   imap_cmd_start (idata, buf);
 
@@ -665,8 +778,8 @@ void imap_add_keywords (char* s, HEADER* h, LIST* mailbox_flags, size_t slen)
   {
     if (msg_has_flag (mailbox_flags, keywords->data))
     {
-      strncat (s, keywords->data, slen);
-      strncat (s, " ", slen);
+      safe_strcat (s, slen, keywords->data);
+      safe_strcat (s, slen, " ");
     }
     keywords = keywords->next;
   }
@@ -726,6 +839,7 @@ char* imap_set_flags (IMAP_DATA* idata, HEADER* h, char* s)
   return s;
 }
 
+
 /* msg_fetch_header: import IMAP FETCH response into an IMAP_HEADER.
  *   Expects string beginning with * n FETCH.
  *   Returns:
@@ -762,19 +876,20 @@ static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf, FILE* fp)
   if (msg_parse_fetch (h, buf) != -2)
     return rc;
   
-  if (imap_get_literal_count (buf, &bytes) < 0)
-    return rc;
-  imap_read_literal (fp, idata, bytes);
-
-  /* we may have other fields of the FETCH _after_ the literal
-   * (eg Domino puts FLAGS here). Nothing wrong with that, either.
-   * This all has to go - we should accept literals and nonliterals
-   * interchangeably at any time. */
-  if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
-    return -2;
+  if (imap_get_literal_count (buf, &bytes) == 0)
+  {
+    imap_read_literal (fp, idata, bytes);
+
+    /* we may have other fields of the FETCH _after_ the literal
+     * (eg Domino puts FLAGS here). Nothing wrong with that, either.
+     * This all has to go - we should accept literals and nonliterals
+     * interchangeably at any time. */
+    if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
+      return rc;
   
-  if (msg_parse_fetch (h, idata->cmd.buf) == -1)
-    return rc;
+    if (msg_parse_fetch (h, idata->cmd.buf) == -1)
+      return rc;
+  }
 
   rc = 0; /* success */
   
@@ -785,6 +900,56 @@ static int msg_fetch_header (CONTEXT* ctx, IMAP_HEADER* h, char* buf, FILE* fp)
   return rc;
 }
 
+#if USE_HCACHE
+static size_t imap_hcache_keylen (const char *fn)
+{
+  return mutt_strlen(fn);
+}
+
+/* msg_fetch_header: import IMAP FETCH response into an IMAP_HEADER.
+ *   Expects string beginning with * n FETCH.
+ *   Returns:
+ *      0 on success
+ *     -1 if the string is not a fetch response
+ *     -2 if the string is a corrupt fetch response */
+static int msg_fetch_header_fetch (CONTEXT* ctx, IMAP_HEADER* h, char* buf, FILE* fp)
+{
+  IMAP_DATA* idata;
+  long bytes;
+  int rc = -1; /* default now is that string isn't FETCH response*/
+
+  idata = (IMAP_DATA*) ctx->data;
+
+  if (buf[0] != '*')
+    return rc;
+
+  /* skip to message number */
+  buf = imap_next_word (buf);
+  h->sid = atoi (buf);
+
+  /* find FETCH tag */
+  buf = imap_next_word (buf);
+  if (ascii_strncasecmp ("FETCH", buf, 5))
+    return rc;
+
+  rc = -2; /* we've got a FETCH response, for better or worse */
+  if (!(buf = strchr (buf, '(')))
+    return rc;
+  buf++;
+
+  if (msg_parse_fetch (h, buf) < 0) {
+         return -2;
+  }
+
+  if (!(buf = strchr (buf, ')')))
+    return rc;
+  buf++;
+
+  return 0;
+}
+#endif /* USE_HCACHE */
+
+
 /* msg_has_flag: do a caseless comparison of the flag against a flag list,
  *   return 1 if found or flag list has '\*', 0 otherwise */
 static int msg_has_flag (LIST* flag_list, const char* flag)