* Allister MacLeod <amacleod@xxxxxxxx> [08-10-2003 17:11]: > Thinking about parsing mboxes reminded me of another question I had > about the way mutt works. Would it make sense for mutt to have an > optional mode to cache mailbox info somewhere? That way e.g. IMAP > folders wouldn't have to be re-scanned every time.. mutt could talk to > the IMAP server and see that there were X new messages and Y > deletions, and just apply the delta to its cache. > > Certainly this would not be desirable behavior for all situations, but > I think that an informed user could avoid or deal with the pitfalls, > as long as they knew what they were when they turned on the option. > > Just some ideas.. I'll probably be cracking open browser.c to see if I > can figure out how to add % specs. Tudor Bosman has written an IMAP header caching patch. I've attached the 1.5.4 version of it; Tudor, Thomas, any word on the status of the patch and if it's ready for inclusion yet? Still willing to test it of course (as I'm doing quite some time now)... -- René Clerc - (rene@xxxxxxxx) - PGP: 0x9ACE0AC7 Arithmetic is being able to count up to twenty without taking off your shoes. -Mickey Mouse
diff -pruN mutt-1.5.4/globals.h mutt-1.5.4-patched/globals.h --- mutt-1.5.4/globals.h Fri Mar 7 09:19:41 2003 +++ mutt-1.5.4-patched/globals.h Wed May 14 15:11:19 2003 @@ -57,6 +57,7 @@ WHERE char *ImapDelimChars INITVAL (NULL WHERE char *ImapHomeNamespace INITVAL (NULL); WHERE char *ImapPass INITVAL (NULL); WHERE char *ImapUser INITVAL (NULL); +WHERE char *ImapHeadercache INITVAL (NULL); #endif WHERE char *Inbox; WHERE char *Ispell; diff -pruN mutt-1.5.4/imap/Makefile.am mutt-1.5.4-patched/imap/Makefile.am --- mutt-1.5.4/imap/Makefile.am Thu Jan 24 14:35:57 2002 +++ mutt-1.5.4-patched/imap/Makefile.am Wed May 14 15:11:19 2003 @@ -22,4 +22,5 @@ noinst_LIBRARIES = libimap.a noinst_HEADERS = auth.h imap_private.h message.h libimap_a_SOURCES = auth.c auth_login.c browse.c command.c imap.c imap.h \ - message.c utf7.c util.c $(AUTHENTICATORS) $(GSSSOURCES) + imap_headercache.c imap_headercache.h message.c utf7.c util.c \ + $(AUTHENTICATORS) $(GSSSOURCES) diff -pruN mutt-1.5.4/imap/imap.c mutt-1.5.4-patched/imap/imap.c --- mutt-1.5.4/imap/imap.c Thu Jan 23 23:04:28 2003 +++ mutt-1.5.4-patched/imap/imap.c Wed May 14 15:11:19 2003 @@ -29,6 +29,7 @@ #include "browser.h" #include "message.h" #include "imap_private.h" +#include "imap_headercache.h" #ifdef USE_SSL # include "mutt_ssl.h" #endif @@ -530,6 +531,13 @@ int imap_open_mailbox (CONTEXT* ctx) /* Clean up path and replace the one in the ctx */ imap_fix_path (idata, mx.mbox, buf, sizeof (buf)); + + if (idata->hcache) + { + imap_headercache_close(idata->hcache); + idata->hcache = NULL; + } + FREE(&(idata->mailbox)); idata->mailbox = safe_strdup (buf); imap_qualify_path (buf, sizeof (buf), &mx, idata->mailbox); @@ -540,6 +548,7 @@ int imap_open_mailbox (CONTEXT* ctx) idata->ctx = ctx; /* clear mailbox status */ + idata->uidvalidity = 0; idata->status = 0; memset (idata->rights, 0, (RIGHTSMAX+7)/8); idata->newMailCount = 0; @@ -585,6 +594,15 @@ int imap_open_mailbox (CONTEXT* ctx) if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL) goto fail; } + /* save UIDVALIDITY for the header cache */ + else if (ascii_strncasecmp("OK [UIDVALIDITY", pc, 14) == 0) + { + dprint(2, (debugfile, "Getting mailbox UIDVALIDITY\n")); + pc += 3; + pc = imap_next_word(pc); + + sscanf(pc, "%u", &(idata->uidvalidity)); + } else { pc = imap_next_word (pc); @@ -668,6 +686,9 @@ int imap_open_mailbox (CONTEXT* ctx) ctx->hdrs = safe_malloc (count * sizeof (HEADER *)); ctx->v2r = safe_malloc (count * sizeof (int)); ctx->msgcount = 0; + + idata->hcache = imap_headercache_open(idata); + if (count && (imap_read_headers (idata, 0, count-1) < 0)) { mutt_error _("Error opening mailbox"); @@ -677,6 +698,7 @@ int imap_open_mailbox (CONTEXT* ctx) dprint (2, (debugfile, "imap_open_mailbox: msgcount is %d\n", ctx->msgcount)); FREE (&mx.mbox); + return 0; fail: @@ -897,6 +919,7 @@ int imap_sync_mailbox (CONTEXT* ctx, int int n; int err_continue = M_NO; /* continue on error? */ int rc; + IMAP_HEADER h; idata = (IMAP_DATA*) ctx->data; @@ -936,8 +959,20 @@ int imap_sync_mailbox (CONTEXT* ctx, int /* mark these messages as unchanged so second pass ignores them. Done * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */ for (n = 0; n < ctx->msgcount; n++) - if (ctx->hdrs[n]->deleted && ctx->hdrs[n]->changed) - ctx->hdrs[n]->active = 0; + { + if (ctx->hdrs[n]->deleted) + { + if (idata->hcache) + { + h.data = HEADER_DATA(ctx->hdrs[n]); + imap_headercache_delete(idata->hcache, &h); + } + + if (ctx->hdrs[n]->changed) + ctx->hdrs[n]->active = 0; + } + } + if (imap_exec (idata, cmd.data, 0) != 0) { mutt_error (_("Expunge failed")); @@ -955,6 +990,23 @@ int imap_sync_mailbox (CONTEXT* ctx, int { ctx->hdrs[n]->changed = 0; + if (idata->hcache) + { + h.data = HEADER_DATA(ctx->hdrs[n]); + + h.read = ctx->hdrs[n]->read; + h.old = ctx->hdrs[n]->old; + h.deleted = ctx->hdrs[n]->deleted; + h.flagged = ctx->hdrs[n]->flagged; + h.replied = ctx->hdrs[n]->replied; + h.changed = ctx->hdrs[n]->changed; + h.sid = ctx->hdrs[n]->index + 1; + h.received = ctx->hdrs[n]->received; + h.content_length = ctx->hdrs[n]->content->length; + + imap_headercache_update(idata->hcache, &h); + } + mutt_message (_("Saving message status flags... [%d/%d]"), n+1, ctx->msgcount); @@ -1082,6 +1134,11 @@ void imap_close_mailbox (CONTEXT* ctx) idata->reopen &= IMAP_REOPEN_ALLOW; idata->state = IMAP_AUTHENTICATED; + if (idata->hcache) + { + imap_headercache_close(idata->hcache); + idata->hcache = NULL; + } FREE (&(idata->mailbox)); mutt_free_list (&idata->flags); } diff -pruN mutt-1.5.4/imap/imap_headercache.c mutt-1.5.4-patched/imap/imap_headercache.c --- mutt-1.5.4/imap/imap_headercache.c Thu Jan 1 01:00:00 1970 +++ mutt-1.5.4-patched/imap/imap_headercache.c Wed May 14 15:11:19 2003 @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2002 Tudor Bosman <tudorb-mutt@xxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#include "mutt.h" +#include "imap.h" +#include "imap_private.h" +#include "imap_headercache.h" +#include "mx.h" +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <dirent.h> +#include <assert.h> + +/* Delete all messages from headercache */ +static int imap_headercache_purge(IMAP_HEADERCACHE *hc) +{ + int rc = -1; + DIR *dir; + struct dirent *ent; + + dir = opendir(hc->name); + if (!dir) + { + mutt_error(_("IMAP headercache: can't purge directory %s: %s"), hc->name, + strerror(errno)); + mutt_sleep(2); + return -1; + } + + while ((ent = readdir(dir)) != NULL) + { + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + continue; + + sprintf(hc->tmpname, "%s/%s", hc->name, ent->d_name); + if (unlink(hc->tmpname) == -1) + { + mutt_error(_("IMAP headercache: can't unlink file %s: %s"), hc->tmpname, + strerror(errno)); + mutt_sleep(2); + goto bail; + } + } + + rc = 0; + +bail: + closedir(dir); + + return rc; +} + +/* Open headercache */ +IMAP_HEADERCACHE *imap_headercache_open(IMAP_DATA *idata) +{ + IMAP_HEADERCACHE *hc; + char hcdir[_POSIX_PATH_MAX + 1]; + FILE *f; + size_t len; + char *p; + + if (!ImapHeadercache || ImapHeadercache[0] == '\0') + return NULL; + + strfcpy(hcdir, ImapHeadercache, _POSIX_PATH_MAX); + mutt_expand_path(hcdir, _POSIX_PATH_MAX); + + hc = safe_malloc(sizeof(IMAP_HEADERCACHE)); + + len = strlen(hcdir) + strlen(idata->conn->account.host) + + strlen(idata->mailbox) + 5; + + hc->name = safe_malloc(len); + hc->tmpname = safe_malloc(len + NAME_MAX + 2); + + sprintf(hc->name, "%s/%s", hcdir, idata->conn->account.host); + + if (mkdir(hcdir, 0777) == -1 && errno != EEXIST) + { + mutt_error(_("Can't create IMAP headercache root directory %s: %s"), + hcdir, strerror(errno)); + mutt_sleep(2); + goto bail; + } + + if (mkdir(hc->name, 0700) == -1 && errno != EEXIST) + { + mutt_error(_("Can't create IMAP headercache server directory %s: %s"), + hc->name, strerror(errno)); + mutt_sleep(2); + goto bail; + } + + p = idata->mailbox; + while ((p = strchr(p, '/')) != NULL) + { + *p = '\0'; + sprintf(hc->name, "%s/%s/%s", hcdir, + idata->conn->account.host, idata->mailbox); + + if (mkdir(hc->name, 0700) == -1 && errno != EEXIST) + { + mutt_error(_("Can't create IMAP headercache mailbox directory %s: %s"), + hc->name, strerror(errno)); + mutt_sleep(2); + goto bail; + } + + *p = '/'; + p++; + } + + sprintf(hc->name, "%s/%s/%s", hcdir, + idata->conn->account.host, idata->mailbox); + + if (mkdir(hc->name, 0700) == -1 && errno != EEXIST) + { + mutt_error(_("Can't create IMAP headercache mailbox directory %s: %s"), + hc->name, strerror(errno)); + mutt_sleep(2); + goto bail; + } + + sprintf(hc->tmpname, "%s/uidvalidity", hc->name); + f = fopen(hc->tmpname, "r"); + + if (f) + { + fscanf(f, "%u", &hc->uidvalidity); + if (idata->uidvalidity != hc->uidvalidity) + { + fclose(f); + f = NULL; + } + } + + if (!f) + { + if (imap_headercache_purge(hc) == -1) + goto bail; + + sprintf(hc->tmpname, "%s/uidvalidity", hc->name); + f = fopen(hc->tmpname, "w"); + if (!f) + { + mutt_error(_("Can't create IMAP headercache uidvalidity file %s: %s"), + hc->tmpname, strerror(errno)); + mutt_sleep(2); + goto bail; + } + + hc->uidvalidity = idata->uidvalidity; + + fprintf(f, "%u\n", hc->uidvalidity); + fclose(f); + } + + return hc; + +bail: + safe_free((void **)&hc->tmpname); + safe_free((void **)&hc->name); + safe_free((void **)&hc); + + return NULL; +} + +/* Close headercache */ +void imap_headercache_close(IMAP_HEADERCACHE *hc) +{ + safe_free((void **)&hc->tmpname); + safe_free((void **)&hc->name); + safe_free((void **)&hc); +} + +static void imap_headercache_writehdr(FILE *f, IMAP_HEADER *h) +{ + /* Write the stuff in the header. This must have a fixed length, as it is + * overwritten in case of imap_headercache_update + */ + fprintf(f, "%1x %1x %1x %1x %1x %1x %8x %16lx %16lx %8x\n", + h->read, h->old, h->deleted, h->flagged, h->replied, h->changed, + h->sid, h->received, h->content_length, HEADER_DATA(h)->uid); +} + +/* Add message to headercache */ +int imap_headercache_add(IMAP_HEADERCACHE *hc, IMAP_HEADER *h, FILE *from, + size_t hdrsz) +{ + FILE *f; +#define BUFSIZE 4096 + char buf[BUFSIZE]; + size_t sz; + int rc = -1; + + sprintf(hc->tmpname, "%s/%u", hc->name, HEADER_DATA(h)->uid); + + f = fopen(hc->tmpname, "w"); + if (!f) + { + mutt_error(_("Can't create IMAP headercache message file %s: %s"), + hc->tmpname, strerror(errno)); + mutt_sleep(2); + goto bail; + } + + imap_headercache_writehdr(f, h); + + while ((sz = fread(buf, 1, (hdrsz < BUFSIZE ? hdrsz : BUFSIZE), from)) != 0) + { + hdrsz -= sz; + fwrite(buf, 1, sz, f); + } + + fclose(f); + + rc = 0; + +bail: + return rc; +} + +/* Update flags in headercache message */ +int imap_headercache_update(IMAP_HEADERCACHE *hc, IMAP_HEADER *h) +{ + FILE *f; + int rc = -1; + + sprintf(hc->tmpname, "%s/%u", hc->name, HEADER_DATA(h)->uid); + + f = fopen(hc->tmpname, "r+"); + if (!f) + goto bail; + + imap_headercache_writehdr(f, h); + + fclose(f); + + rc = 0; + +bail: + return rc; +} + +/* Delete message from headercache */ +int imap_headercache_delete(IMAP_HEADERCACHE *hc, IMAP_HEADER *h) +{ + int rc = -1; + + sprintf(hc->tmpname, "%s/%u", hc->name, HEADER_DATA(h)->uid); + + if (unlink(hc->tmpname) == -1) + { + mutt_error(_("Can't delete IMAP headercache message %s: %s"), + hc->tmpname, strerror(errno)); + mutt_sleep(2); + goto bail; + } + + rc = 0; + +bail: + return rc; +} + +/* Find message in headercache */ +FILE *imap_headercache_find(IMAP_HEADERCACHE *hc, IMAP_HEADER *h) +{ + FILE *f = NULL; + unsigned int flag_read, flag_old, flag_deleted, flag_flagged, flag_replied; + unsigned int flag_changed; + unsigned int uid; + unsigned long received; + unsigned long content_length; + + sprintf(hc->tmpname, "%s/%u", hc->name, HEADER_DATA(h)->uid); + + f = fopen(hc->tmpname, "r"); + if (!f) + goto bail; + + fscanf(f, "%x %x %x %x %x %x %x %lx %lx %x\n", + &flag_read, &flag_old, &flag_deleted, &flag_flagged, &flag_replied, + &flag_changed, &h->sid, &received, &content_length, &uid); + + if (uid != HEADER_DATA(h)->uid) + { + fclose(f); + f = NULL; + goto bail; + } + + h->received = received; + h->read = flag_read; + h->old = flag_old; + h->deleted = flag_deleted; + h->flagged = flag_flagged; + h->replied = flag_replied; + h->changed = flag_changed; + h->content_length = (long)content_length; + +bail: + return f; +} + +/* Close file returned by imap_headercache_find */ +void imap_headercache_done(IMAP_HEADERCACHE *hc, FILE *f) +{ + fclose(f); +} + diff -pruN mutt-1.5.4/imap/imap_headercache.h mutt-1.5.4-patched/imap/imap_headercache.h --- mutt-1.5.4/imap/imap_headercache.h Thu Jan 1 01:00:00 1970 +++ mutt-1.5.4-patched/imap/imap_headercache.h Wed May 14 15:11:19 2003 @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2002 Tudor Bosman <tudorb-mutt@xxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#ifndef _IMAP_HEADERCACHE_H +#define _IMAP_HEADERCACHE_H +#include "imap_private.h" +#include "message.h" + +typedef struct IMAP_HEADERCACHE +{ + char *name; + char *tmpname; + unsigned int uidvalidity; + int exists; +} IMAP_HEADERCACHE; + +struct IMAP_DATA; + +IMAP_HEADERCACHE *imap_headercache_open(struct IMAP_DATA *idata); + +void imap_headercache_close(IMAP_HEADERCACHE *hc); + +int imap_headercache_add(IMAP_HEADERCACHE *hc, IMAP_HEADER *h, FILE *from, + size_t hdrsz); +int imap_headercache_update(IMAP_HEADERCACHE *hc, IMAP_HEADER *h); +int imap_headercache_delete(IMAP_HEADERCACHE *hc, IMAP_HEADER *h); + +FILE *imap_headercache_find(IMAP_HEADERCACHE *hc, IMAP_HEADER *h); +void imap_headercache_done(IMAP_HEADERCACHE *hc, FILE *f); + +#endif + diff -pruN mutt-1.5.4/imap/imap_private.h mutt-1.5.4-patched/imap/imap_private.h --- mutt-1.5.4/imap/imap_private.h Thu Jan 24 14:35:57 2002 +++ mutt-1.5.4-patched/imap/imap_private.h Wed May 14 15:11:19 2003 @@ -21,6 +21,7 @@ #define _IMAP_PRIVATE_H 1 #include "imap.h" +#include "imap_headercache.h" #include "mutt_socket.h" /* -- symbols -- */ @@ -148,7 +149,7 @@ typedef struct int state; } IMAP_COMMAND; -typedef struct +typedef struct IMAP_DATA { /* This data is specific to a CONNECTION to an IMAP server */ CONNECTION *conn; @@ -174,6 +175,7 @@ typedef struct char *mailbox; unsigned short check_status; unsigned char reopen; + unsigned int uidvalidity; unsigned char rights[(RIGHTSMAX + 7)/8]; unsigned int newMailCount; IMAP_CACHE cache[IMAP_CACHE_LEN]; @@ -181,6 +183,7 @@ typedef struct /* all folder flags - system flags AND keywords */ LIST *flags; + IMAP_HEADERCACHE *hcache; } IMAP_DATA; /* I wish that were called IMAP_CONTEXT :( */ diff -pruN mutt-1.5.4/imap/message.c mutt-1.5.4-patched/imap/message.c --- mutt-1.5.4/imap/message.c Tue Dec 10 21:56:52 2002 +++ mutt-1.5.4-patched/imap/message.c Wed May 14 15:11:19 2003 @@ -25,6 +25,7 @@ #include "mutt.h" #include "mutt_curses.h" #include "imap_private.h" +#include "imap_headercache.h" #include "message.h" #include "mx.h" @@ -54,9 +55,14 @@ int imap_read_headers (IMAP_DATA* idata, int msgno; IMAP_HEADER h; int rc, mfhrc, oldmsgcount; + IMAP_HEADERCACHE *hc = NULL; + int msgbegin_hc; 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"; + msgno = msgbegin; + ctx = idata->ctx; if (mutt_bit_isset (idata->capabilities,IMAP4REV1)) @@ -87,36 +93,150 @@ int imap_read_headers (IMAP_DATA* idata, } unlink (tempfile); + oldmsgcount = ctx->msgcount; + + msgbegin_hc = msgbegin; + + hc = idata->hcache; + +restart: /* make sure context has room to hold the mailbox */ while ((msgend) >= idata->ctx->hdrmax) mx_alloc_memory (idata->ctx); - oldmsgcount = ctx->msgcount; idata->reopen &= ~IMAP_NEWMAIL_PENDING; idata->newMailCount = 0; + if (hc) + { + snprintf(buf, sizeof(buf), "FETCH %d:%d (UID)", msgbegin_hc + 1, + msgend + 1); + imap_cmd_start(idata, buf); + + for (msgno = msgbegin_hc; msgno <= msgend; msgno++) + { + if (ReadInc && (!msgno || ((msgno+1) % ReadInc == 0))) + mutt_message (_("Fetching message UIDs... [%d/%d]"), msgno + 1, + msgend + 1); + + /* XXX */ + ctx->hdrs[msgno] = NULL; + + /* XXX leaking h.data on successful exit */ + memset (&h, 0, sizeof (h)); + h.data = safe_calloc (1, sizeof (IMAP_HEADER_DATA)); + + do + { + FILE *cache_fp; + + mfhrc = 0; + + rc = imap_cmd_step (idata); + if (rc != IMAP_CMD_CONTINUE) + break; + + if ((mfhrc = msg_fetch_header (idata->ctx, &h, idata->cmd.buf, NULL)) == -1) + continue; + else if (mfhrc < 0) + break; + + cache_fp = imap_headercache_find(hc, &h); + if (cache_fp) + { + /* update context with message header */ + ctx->hdrs[msgno] = mutt_new_header (); + + ctx->hdrs[msgno]->index = h.sid - 1; + + /* 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 = h.received; + ctx->hdrs[msgno]->data = (void *) (h.data); + + /* NOTE: if Date: header is missing, mutt_read_rfc822_header depends + * on h.received being set */ + ctx->hdrs[msgno]->env = mutt_read_rfc822_header (cache_fp, ctx->hdrs[msgno], + 0, 0); + /* content built as a side-effect of mutt_read_rfc822_header */ + ctx->hdrs[msgno]->content->length = h.content_length; + + imap_headercache_done(hc, cache_fp); + } + } + while (mfhrc == -1); + + /* in case we get new mail while fetching the headers */ + if (idata->reopen & IMAP_NEWMAIL_PENDING) + { + msgbegin_hc = msgno + 1; + msgend = idata->newMailCount - 1; + goto restart; + } + /* XXX freshen... etc */ + } + } + + /* Remember where we left if we get new mail while fetching actual headers */ + msgbegin_hc = msgno; + + /* Now, either one of the following is true: + * 1. We don't have a headercache (hc == 0) + * 2. All messages found in the cache have ctx->hdrs[msgno] != NULL, and + * filled up. + */ + + /* + * Make one request for everything. This makes fetching headers an + * order of magnitude faster if you have a large mailbox. + * + * If we get more messages while doing this, we make another + * request for all the new messages. + */ + if (!hc) + { + snprintf (buf, sizeof (buf), + "FETCH %d:%d (UID FLAGS INTERNALDATE RFC822.SIZE %s)", msgbegin + 1, + msgend + 1, hdrreq); + + imap_cmd_start (idata, buf); + } + 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 (msgno + 1 > fetchlast) + /* If the message is in the cache, skip it */ + if (hc) { - /* - * Make one request for everything. This makes fetching headers an - * order of magnitude faster if you have a large mailbox. - * - * If we get more messages while doing this, we make another - * request for all the new messages. - */ - snprintf (buf, sizeof (buf), - "FETCH %d:%d (UID FLAGS INTERNALDATE RFC822.SIZE %s)", msgno + 1, - msgend + 1, hdrreq); - - imap_cmd_start (idata, buf); + if (ctx->hdrs[msgno]) + { + ctx->msgcount++; + continue; + } + else if (msgno >= fetchlast) + { + /* Find the longest "run" of messages not in the cache and fetch it in + * one go + */ + for (fetchlast = msgno + 1; + fetchlast <= msgend && !ctx->hdrs[fetchlast]; fetchlast++); + + snprintf (buf, sizeof (buf), + "FETCH %d:%d (UID FLAGS INTERNALDATE RFC822.SIZE %s)", msgno + 1, + fetchlast, hdrreq); - fetchlast = msgend + 1; + imap_cmd_start (idata, buf); + } } /* freshen fp, h */ @@ -130,6 +250,8 @@ int imap_read_headers (IMAP_DATA* idata, */ do { + size_t hdrsz; + mfhrc = 0; rc = imap_cmd_step (idata); @@ -144,12 +266,16 @@ int imap_read_headers (IMAP_DATA* idata, /* make sure we don't get remnants from older larger message headers */ fputs ("\n\n", fp); + hdrsz = (size_t)ftell(fp); + /* update context with message header */ ctx->hdrs[msgno] = mutt_new_header (); ctx->hdrs[msgno]->index = h.sid - 1; +#if 0 if (h.sid != ctx->msgcount + 1) dprint (1, (debugfile, "imap_read_headers: msgcount and sequence ID are inconsistent!")); +#endif /* messages which have not been expunged are ACTIVE (borrowed from mh * folders) */ ctx->hdrs[msgno]->active = 1; @@ -163,6 +289,13 @@ int imap_read_headers (IMAP_DATA* idata, ctx->hdrs[msgno]->data = (void *) (h.data); rewind (fp); + + if (hc) + { + imap_headercache_add(hc, &h, fp, hdrsz); + rewind(fp); + } + /* NOTE: if Date: header is missing, mutt_read_rfc822_header depends * on h.received being set */ ctx->hdrs[msgno]->env = mutt_read_rfc822_header (fp, ctx->hdrs[msgno], @@ -172,8 +305,7 @@ int imap_read_headers (IMAP_DATA* idata, ctx->msgcount++; } - while ((rc != IMAP_CMD_OK) && ((mfhrc == -1) || - ((msgno + 1) >= fetchlast))); + while (mfhrc == -1); if ((mfhrc < -1) || ((rc != IMAP_CMD_CONTINUE) && (rc != IMAP_CMD_OK))) { @@ -186,11 +318,9 @@ int imap_read_headers (IMAP_DATA* idata, /* in case we get new mail while fetching the headers */ if (idata->reopen & IMAP_NEWMAIL_PENDING) { + msgbegin = msgno + 1; msgend = idata->newMailCount - 1; - while ((msgend) >= ctx->hdrmax) - mx_alloc_memory (ctx); - idata->reopen &= ~IMAP_NEWMAIL_PENDING; - idata->newMailCount = 0; + goto restart; } } @@ -735,6 +865,7 @@ static int msg_fetch_header (CONTEXT* ct IMAP_DATA* idata; long bytes; int rc = -1; /* default now is that string isn't FETCH response*/ + int fetch_rc; idata = (IMAP_DATA*) ctx->data; @@ -757,9 +888,15 @@ static int msg_fetch_header (CONTEXT* ct /* FIXME: current implementation - call msg_parse_fetch - if it returns -2, * read header lines and call it again. Silly. */ - if (msg_parse_fetch (h, buf) != -2) + fetch_rc = msg_parse_fetch(h, buf); + if (fetch_rc == 0) + return 0; + else if (fetch_rc != -2) return rc; - + + if (!fp) + return -2; + if (imap_get_literal_count (buf, &bytes) < 0) return rc; imap_read_literal (fp, idata, bytes); diff -pruN mutt-1.5.4/imap/util.c mutt-1.5.4-patched/imap/util.c --- mutt-1.5.4/imap/util.c Wed Dec 11 12:19:42 2002 +++ mutt-1.5.4-patched/imap/util.c Wed May 14 15:11:19 2003 @@ -255,6 +255,7 @@ IMAP_DATA* imap_new_idata (void) { if (!idata) return NULL; + idata->hcache = NULL; idata->conn = NULL; idata->capstr = NULL; idata->state = IMAP_DISCONNECTED; diff -pruN mutt-1.5.4/init.h mutt-1.5.4-patched/init.h --- mutt-1.5.4/init.h Tue Mar 4 10:28:12 2003 +++ mutt-1.5.4-patched/init.h Wed May 14 15:11:19 2003 @@ -839,6 +839,11 @@ struct option_t MuttVars[] = { ** .pp ** This variable defaults to your user name on the local machine. */ + { "imap_headercache", DT_STR, R_NONE, UL &ImapHeadercache, UL 0 }, + /* + ** .pp + ** The location of the IMAP headercache directory. + */ #endif { "implicit_autoview", DT_BOOL,R_NONE, OPTIMPLICITAUTOVIEW, 0}, /*
Attachment:
pgpdrftyaSNtI.pgp
Description: PGP signature