Here's my modified patch against CVS. I suppose some code could be added to statuscache_lookup() to read both v.2 and v.3. There isn't any reason to invalidate all v.2 caches just because they don't have highestmodseq

--
Kenneth Murchison
Systems Programmer
Project Cyrus Developer/Maintainer
Carnegie Mellon University
Index: doc/changes.html
===================================================================
RCS file: /afs/andrew/system/cvs/src/cyrus/doc/changes.html,v
retrieving revision 1.152
diff -u -r1.152 changes.html
--- doc/changes.html	17 Jan 2008 18:51:39 -0000	1.152
+++ doc/changes.html	18 Jan 2008 04:16:11 -0000
@@ -10,6 +10,10 @@
 
 <h1>Changes to the Cyrus IMAP Server since 2.3.11</h1>
 <ul>
+<li>Added <tt>statuscache.db</tt> to cache IMAP STATUS data which
+significantly reduces the amount of I/O necessary when neither the
+mailbox nor \Seen state has changed
+<i>-- courtesy of Fastmail.fm</i>.</li>
 <li>Added option to <tt>unexpunge</tt> to restore messages by time
 interval
 <i>-- courtesy of David Carter</i>.</li>
Index: imap/Makefile.in
===================================================================
RCS file: /afs/andrew/system/cvs/src/cyrus/imap/Makefile.in,v
retrieving revision 1.191
diff -u -r1.191 Makefile.in
--- imap/Makefile.in	18 Oct 2007 18:48:02 -0000	1.191
+++ imap/Makefile.in	18 Jan 2008 04:16:11 -0000
@@ -101,7 +101,8 @@
 	convert_code.o duplicate.o saslclient.o saslserver.o signals.o \
 	annotate.o search_engines.o squat.o squat_internal.o mbdump.o \
 	imapparse.o telemetry.o user.o notify.o protocol.o idle.o quota_db.o \
-	sync_log.o $(SEEN) mboxkey.o backend.o tls.o message_guid.o
+	sync_log.o $(SEEN) mboxkey.o backend.o tls.o message_guid.o \
+	statuscache_db.o
 
 IMAPDOBJS=pushstats.o imapd.o proxy.o imap_proxy.o index.o version.o
 
Index: imap/ctl_cyrusdb.c
===================================================================
RCS file: /afs/andrew/system/cvs/src/cyrus/imap/ctl_cyrusdb.c,v
retrieving revision 1.30
diff -u -r1.30 ctl_cyrusdb.c
--- imap/ctl_cyrusdb.c	4 Jan 2008 12:24:05 -0000	1.30
+++ imap/ctl_cyrusdb.c	18 Jan 2008 04:16:11 -0000
@@ -80,6 +80,7 @@
 #include "libcyr_cfg.h"
 #include "mboxlist.h"
 #include "seen.h"
+#include "statuscache.h"
 #include "tls.h"
 #include "util.h"
 #include "xmalloc.h"
@@ -99,7 +100,8 @@
     { FNAME_ANNOTATIONS,	&config_annotation_db,	1 },
     { FNAME_DELIVERDB,		&config_duplicate_db,	0 },
     { FNAME_TLSSESSIONS,	&config_tlscache_db,	0 },
-    { FNAME_PTSDB,              &config_ptscache_db,    0 },
+    { FNAME_PTSDB,		&config_ptscache_db,	0 },
+    { FNAME_STATUSCACHEDB,	&config_statuscache_db,	0 },
     { NULL,			NULL,			0 }
 };
 
Index: imap/global.c
===================================================================
RCS file: /afs/andrew/system/cvs/src/cyrus/imap/global.c,v
retrieving revision 1.24
diff -u -r1.24 global.c
--- imap/global.c	28 Sep 2007 02:27:46 -0000	1.24
+++ imap/global.c	18 Jan 2008 04:16:11 -0000
@@ -95,6 +95,7 @@
 struct cyrusdb_backend *config_duplicate_db;
 struct cyrusdb_backend *config_tlscache_db;
 struct cyrusdb_backend *config_ptscache_db;
+struct cyrusdb_backend *config_statuscache_db;
 
 /* Called before a cyrus application starts (but after command line parameters
  * are read) */
@@ -190,6 +191,8 @@
 	    cyrusdb_fromname(config_getstring(IMAPOPT_TLSCACHE_DB));
 	config_ptscache_db =
 	    cyrusdb_fromname(config_getstring(IMAPOPT_PTSCACHE_DB));
+	config_statuscache_db =
+	    cyrusdb_fromname(config_getstring(IMAPOPT_STATUSCACHE_DB));
 
 	/* configure libcyrus as needed */
 	libcyrus_config_setstring(CYRUSOPT_CONFIG_DIR, config_dir);
Index: imap/global.h
===================================================================
RCS file: /afs/andrew/system/cvs/src/cyrus/imap/global.h,v
retrieving revision 1.9
diff -u -r1.9 global.h
--- imap/global.h	27 Mar 2007 19:53:08 -0000	1.9
+++ imap/global.h	18 Jan 2008 04:16:11 -0000
@@ -154,5 +154,6 @@
 extern struct cyrusdb_backend *config_duplicate_db;
 extern struct cyrusdb_backend *config_tlscache_db;
 extern struct cyrusdb_backend *config_ptscache_db;
+extern struct cyrusdb_backend *config_statuscache_db;
 
 #endif /* INCLUDED_GLOBAL_H */
Index: imap/imapd.c
===================================================================
RCS file: /afs/andrew/system/cvs/src/cyrus/imap/imapd.c,v
retrieving revision 1.539
diff -u -r1.539 imapd.c
--- imap/imapd.c	17 Jan 2008 13:07:40 -0000	1.539
+++ imap/imapd.c	18 Jan 2008 04:16:12 -0000
@@ -95,6 +95,7 @@
 #include "mkgmtime.h"
 #include "mupdate-client.h"
 #include "quota.h"
+#include "statuscache.h"
 #include "sync_log.h"
 #include "telemetry.h"
 #include "tls.h"
@@ -685,6 +686,10 @@
 	annotatemore_init(0, NULL, NULL);
     annotatemore_open(NULL);
 
+    if (config_getswitch(IMAPOPT_STATUSCACHE)) {
+	statuscache_open(NULL);
+    }
+
     /* Create a protgroup for input from the client and selected backend */
     protin = protgroup_new(2);
 
@@ -866,6 +871,11 @@
     annotatemore_close();
     annotatemore_done();
 
+    if (config_getswitch(IMAPOPT_STATUSCACHE)) {
+	statuscache_close();
+	statuscache_done();
+    }
+
     if (imapd_in) {
 	/* Flush the incoming buffer */
 	prot_NONBLOCK(imapd_in);
@@ -6747,21 +6757,19 @@
 void cmd_status(char *tag, char *name)
 {
     int c;
-    int statusitems = 0;
+    unsigned statusitems = 0;
     static struct buf arg;
-    struct mailbox mailbox;
     char mailboxname[MAX_MAILBOX_NAME+1];
     int mbtype;
-    char *server;
+    char *server, *acl;
     int r = 0;
-    int doclose = 0;
 
     r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name,
 					       imapd_userid, mailboxname);
 
     if (!r) {
 	r = mlookup(tag, name, mailboxname, &mbtype, NULL, NULL,
-		    &server, NULL, NULL);
+		    &server, &acl, NULL);
     }
     if (r == IMAP_MAILBOX_MOVED) {
 	/* Eat the argument */
@@ -6864,24 +6872,18 @@
     }
 
     if (!r) {
-	r = mailbox_open_header(mailboxname, imapd_authstate, &mailbox);
-    }
+	int myrights = cyrus_acl_myrights(imapd_authstate, acl);
 
-    if (!r) {
-	doclose = 1;
-	r = mailbox_open_index(&mailbox);
-    }
-    if (!r && !(mailbox.myrights & ACL_READ)) {
-	r = (imapd_userisadmin || (mailbox.myrights & ACL_LOOKUP)) ?
-	  IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
+	if (!(myrights & ACL_READ)) {
+	    r = (imapd_userisadmin || (myrights & ACL_LOOKUP)) ?
+		IMAP_PERMISSION_DENIED : IMAP_MAILBOX_NONEXISTENT;
+	}
     }
 
     if (!r) {
-	r = index_status(&mailbox, name, statusitems);
+	r = index_status(mailboxname, name, statusitems);
     }
 
-    if (doclose) mailbox_close(&mailbox);
-    
     if (r) {
 	prot_printf(imapd_out, "%s NO %s\r\n", tag, error_message(r));
 	return;
Index: imap/imapd.h
===================================================================
RCS file: /afs/andrew/system/cvs/src/cyrus/imap/imapd.h,v
retrieving revision 1.69
diff -u -r1.69 imapd.h
--- imap/imapd.h	17 Jan 2008 13:07:40 -0000	1.69
+++ imap/imapd.h	18 Jan 2008 04:16:12 -0000
@@ -272,8 +272,7 @@
 			struct searchargs *searchargs, int usinguid);
 extern int index_copy(struct mailbox *mailbox, char *sequence,
 		      int usinguid, char *name, char **copyuidp, int nolink);
-extern int index_status(struct mailbox *mailbox, char *name,
-			   int statusitems);
+extern int index_status(char *mboxname, char *name, unsigned statusitems);
 
 extern int index_getuids(struct mailbox *mailbox, unsigned lowuid);
 extern int index_getstate(struct mailbox *mailbox);
Index: imap/index.c
===================================================================
RCS file: /afs/andrew/system/cvs/src/cyrus/imap/index.c,v
retrieving revision 1.241
diff -u -r1.241 index.c
--- imap/index.c	17 Jan 2008 13:07:40 -0000	1.241
+++ imap/index.c	18 Jan 2008 04:16:12 -0000
@@ -69,10 +69,12 @@
 #include "lsort.h"
 #include "mailbox.h"
 #include "map.h"
+#include "mboxlist.h"
 #include "message.h"
 #include "parseaddr.h"
 #include "search_engines.h"
 #include "seen.h"
+#include "statuscache.h"
 #include "strhash.h"
 #include "stristr.h"
 #include "util.h"
@@ -1550,53 +1552,107 @@
 /*
  * Performs a STATUS command
  */
-int
-index_status(mailbox, name, statusitems)
-struct mailbox *mailbox;
-char *name;
-int statusitems;
+int index_status(char *mboxname, char *name, unsigned statusitems)
 {
     int r;
-    struct seen *status_seendb;
-    time_t last_read, last_change = 0;
-    unsigned last_uid;
-    char *last_seenuids;
+    struct statuscache_data scdata;
+    struct mailbox mailbox;
+    int doclose = 0;
     int num_recent = 0;
     int num_unseen = 0;
     int sepchar;
     static struct seq_set seq_set = { NULL, 0, 0, 0 , NULL};
 
-    if (mailbox->exists != 0 &&
-	(statusitems &
-	 (STATUS_RECENT | STATUS_UNSEEN))) {
-	r = seen_open(mailbox,
-		      (mailbox->options & OPT_IMAP_SHAREDSEEN) ? "anyone" :
+    /* Check status cache if possible */
+    if (config_getswitch(IMAPOPT_STATUSCACHE)) {
+	/* Do actual lookup of cache item. */
+	r = statuscache_lookup(mboxname, imapd_userid, &scdata);
+
+	/* We need to find everything we're looking for in the cache,
+	   otherwise we fallback to the regular method */
+	if (!r && (scdata.statusitems & statusitems) != statusitems) {
+	    r = IMAP_NO_NOSUCHMSG;
+	}
+
+	if (!r) {
+	    /* If index file has changed, fallback to regular method */
+	    char *path, *mpath;
+	    struct stat index;
+
+	    /* Get path to mailbox */
+	    r = mboxlist_detail(mboxname, NULL, &path, &mpath, NULL, NULL, NULL);
+	    if (!r) r = mailbox_stat(path, mpath, NULL, &index, NULL);
+
+	    if (!r &&
+		(index.st_mtime != scdata.message_mtime ||
+		 index.st_ino   != scdata.message_ino ||
+		 index.st_size  != scdata.message_size)) {
+		r = IMAP_NO_NOSUCHMSG;
+	    }
+
+	    /* Seen/recent status uses "push" invalidation events from
+	     * seen_db.c.   This avoids needing to open cyrus.header to get
+	     * the mailbox uniqueid to open the seen db and get the
+	     * unseen_mtime and recentuid.
+	     */
+	}
+
+	if (!r) {
+	    syslog(LOG_DEBUG, "statuscache, '%s', '%s', '0x%02x', 'yes'",
+		   mboxname, imapd_userid, statusitems);
+	    goto statusdone;
+	}
+
+	syslog(LOG_DEBUG, "statuscache, '%s', '%s', '0x%02x', 'no'",
+	       mboxname, imapd_userid, statusitems);
+    }
+
+    /* Missing or invalid cache entry */
+    r = mailbox_open_header(mboxname, imapd_authstate, &mailbox);
+
+    if (!r) {
+	doclose = 1;
+	r = mailbox_open_index(&mailbox);
+    }
+
+    if (!r && mailbox.exists != 0 &&
+	(statusitems & (STATUS_RECENT | STATUS_UNSEEN))) {
+	/* Read \Seen state */
+	struct seen *status_seendb;
+	time_t last_read, last_change = 0;
+	unsigned last_uid;
+	char *last_seenuids;
+
+	r = seen_open(&mailbox,
+		      (mailbox.options & OPT_IMAP_SHAREDSEEN) ? "anyone" :
 		      imapd_userid,
 		      SEEN_CREATE, &status_seendb);
-	if (r) return r;
 
-	r = seen_lockread(status_seendb, &last_read, &last_uid,
-			  &last_change, &last_seenuids);
-	seen_close(status_seendb);
-	if (r) return r;
+	if (!r) {
+	    r = seen_lockread(status_seendb, &last_read, &last_uid,
+			      &last_change, &last_seenuids);
+	    seen_close(status_seendb);
+	}
 
-	if (statusitems & (STATUS_RECENT | STATUS_UNSEEN)) {
+	if (!r) {
 	    const char *base;
 	    unsigned long len = 0;
 	    unsigned msg, uid;
 
-	    map_refresh(mailbox->index_fd, 0, &base, &len,
-			mailbox->start_offset +
-			mailbox->exists * mailbox->record_size,
-			"index", mailbox->name);
+	    map_refresh(mailbox.index_fd, 0, &base, &len,
+			mailbox.start_offset +
+			mailbox.exists * mailbox.record_size,
+			"index", mailbox.name);
 
 	    seq_set.len = seq_set.mark = 0;
 	    index_parse_sequence(last_seenuids, 0, &seq_set);
 
-	    for (msg = 0; msg < mailbox->exists; msg++) {
-		uid = ntohl(*((bit32 *)(base + mailbox->start_offset +
-					msg * mailbox->record_size +
+	    for (msg = 0; msg < mailbox.exists; msg++) {
+		uid = ntohl(*((bit32 *)(base + mailbox.start_offset +
+					msg * mailbox.record_size +
 					OFFSET_UID)));
+		/* Always calculate num_recent,
+		   even if only need num_unseen... for caching below */
 		if (uid > last_uid) num_recent++;
 		if ((statusitems & STATUS_UNSEEN) &&
 		    !index_insequence(uid, &seq_set, 1)) num_unseen++;
@@ -1609,36 +1665,57 @@
 	}
     }
 
+    if (!r) {
+	/* We always have message count, uidnext,
+	   uidvalidity, and highestmodseq for cache */
+	unsigned c_statusitems = statusitems | STATUS_MESSAGES |
+	    STATUS_UIDNEXT | STATUS_UIDVALIDITY | STATUS_HIGHESTMODSEQ;
+
+	/* If we calculated num_unseen, we implicitly calculated num_recent */
+	if (c_statusitems & STATUS_UNSEEN) c_statusitems |= STATUS_RECENT;
+
+	statuscache_fill(&scdata, &mailbox,
+			 c_statusitems, num_recent, num_unseen);
+    }
+
+    if (doclose) mailbox_close(&mailbox);
+    if (r) return r;
+
+    /* Upate the statuscache entry */
+    if (config_getswitch(IMAPOPT_STATUSCACHE)) {
+	statuscache_update(mboxname, imapd_userid, &scdata);
+    }
+
+  statusdone:
     prot_printf(imapd_out, "* STATUS ");
     printastring(name);
     prot_printf(imapd_out, " ");
     sepchar = '(';
 
     if (statusitems & STATUS_MESSAGES) {
-	prot_printf(imapd_out, "%cMESSAGES %lu", sepchar, mailbox->exists);
+	prot_printf(imapd_out, "%cMESSAGES %lu", sepchar, scdata.c_messages);
 	sepchar = ' ';
     }
     if (statusitems & STATUS_RECENT) {
-	prot_printf(imapd_out, "%cRECENT %u", sepchar, num_recent);
+	prot_printf(imapd_out, "%cRECENT %u", sepchar, scdata.c_recent);
 	sepchar = ' ';
     }
     if (statusitems & STATUS_UIDNEXT) {
-	prot_printf(imapd_out, "%cUIDNEXT %lu", sepchar, mailbox->last_uid+1);
+	prot_printf(imapd_out, "%cUIDNEXT %lu", sepchar, scdata.c_uidnext);
 	sepchar = ' ';
     }
     if (statusitems & STATUS_UIDVALIDITY) {
 	prot_printf(imapd_out, "%cUIDVALIDITY %lu", sepchar,
-		    mailbox->uidvalidity);
+		    scdata.c_uidvalidity);
 	sepchar = ' ';
     }
     if (statusitems & STATUS_UNSEEN) {
-	prot_printf(imapd_out, "%cUNSEEN %u", sepchar, num_unseen);
+	prot_printf(imapd_out, "%cUNSEEN %u", sepchar, scdata.c_unseen);
 	sepchar = ' ';
     }
     if (statusitems & STATUS_HIGHESTMODSEQ) {
 	prot_printf(imapd_out, "%cHIGHESTMODSEQ " MODSEQ_FMT, sepchar,
-		    (mailbox->options & OPT_IMAP_CONDSTORE) ?
-		    mailbox->highestmodseq : 0);
+		    scdata.c_highestmodseq);
 	sepchar = ' ';
     }
     prot_printf(imapd_out, ")\r\n");
Index: imap/seen_db.c
===================================================================
RCS file: /afs/andrew/system/cvs/src/cyrus/imap/seen_db.c,v
retrieving revision 1.57
diff -u -r1.57 seen_db.c
--- imap/seen_db.c	17 Jan 2008 13:25:31 -0000	1.57
+++ imap/seen_db.c	18 Jan 2008 04:16:12 -0000
@@ -68,6 +68,7 @@
 #include "xstrlcat.h"
 #include "mailbox.h"
 #include "imap_err.h"
+#include "statuscache.h"
 #include "seen.h"
 
 #define FNAME_SEENSUFFIX ".seen" /* per user seen state extension */
@@ -80,6 +81,7 @@
 
 struct seen {
     char *user;			/* what user is this for? */
+    const char *mboxname;	/* what mailbox name? */
     const char *uniqueid;	/* what mailbox? */
     const char *path;		/* where is this mailbox? */
     struct db *db;
@@ -147,6 +149,7 @@
     /* if this is the db we've already opened, return it */
     if (seendb && !strcmp(seendb->user, user)) {
 	abortcurrent(seendb);
+	seendb->mboxname = mailbox->name;
 	seendb->uniqueid = mailbox->uniqueid;
 	seendb->path = mailbox->path;
 	*seendbptr = seendb;
@@ -185,6 +188,7 @@
     free(fname);
 
     seendb->tid = NULL;
+    seendb->mboxname = mailbox->name;
     seendb->uniqueid = mailbox->uniqueid;
     seendb->path = mailbox->path;
     seendb->user = xstrdup(user);
@@ -386,6 +390,9 @@
 	break;
     }
 
+    /* Something changed, kill our status cache for this mailbox */
+    statuscache_invalidate(seendb->mboxname, seendb->user);
+
     free(data);
     return r;
 }
@@ -408,6 +415,7 @@
 	seendb->tid = NULL;
     }
 
+    seendb->mboxname = NULL;
     seendb->uniqueid = NULL;
     seendb->path = NULL;
 
Index: imap/statuscache.h
===================================================================
RCS file: imap/statuscache.h
diff -N imap/statuscache.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ imap/statuscache.h	18 Jan 2008 04:16:12 -0000
@@ -0,0 +1,93 @@
+/* statuscache.h -- Status caching routines
+ *
+ * Copyright (c) 1998-2008 Carnegie Mellon University.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any other legal
+ *    details, please contact  
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      [EMAIL PROTECTED]
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id$
+ */
+
+#ifndef STATUSCACHE_H
+#define STATUSCACHE_H
+
+#include "mailbox.h"
+
+/* name of the statuscache database */
+#define FNAME_STATUSCACHEDB "/statuscache.db"
+#define STATUSCACHE_VERSION 3
+
+/* open the statuscache db */
+void statuscache_open(char *name);
+
+struct statuscache_data {
+    unsigned version;
+    unsigned statusitems;
+
+    time_t message_mtime;
+    ino_t message_ino;
+    off_t message_size;
+
+    unsigned long c_messages;
+    unsigned c_recent;
+    unsigned long c_uidnext;
+    unsigned long c_uidvalidity;
+    unsigned c_unseen;
+    modseq_t c_highestmodseq;
+};
+
+/* fill a statuscache entry */
+int statuscache_fill(struct statuscache_data *scdata, struct mailbox *mailbox,
+		     int statusitems, int num_recent, int num_unseen);
+
+/* lookup a single statuscache entry and return result */
+int statuscache_lookup(const char *mboxname, const char *userid,
+		       struct statuscache_data *scdata);
+
+/* update a statuscache entry */
+int statuscache_update(const char *mboxname, const char *userid,
+		      struct statuscache_data *scdata);
+
+/* invalidate (delete) a statuscache entry */
+int statuscache_invalidate(const char *mboxname, const char *userid);
+
+/* close the database */
+void statuscache_close(void);
+
+/* done with database stuff */
+void statuscache_done(void);
+
+#endif /* STATUSCACHE_H */
Index: imap/statuscache_db.c
===================================================================
RCS file: imap/statuscache_db.c
diff -N imap/statuscache_db.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ imap/statuscache_db.c	18 Jan 2008 04:16:12 -0000
@@ -0,0 +1,216 @@
+/* statuscache_db.c -- Status caching routines
+ *
+ * Copyright (c) 1998-2008 Carnegie Mellon University.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The name "Carnegie Mellon University" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For permission or any other legal
+ *    details, please contact  
+ *      Office of Technology Transfer
+ *      Carnegie Mellon University
+ *      5000 Forbes Avenue
+ *      Pittsburgh, PA  15213-3890
+ *      (412) 268-4387, fax: (412) 268-7395
+ *      [EMAIL PROTECTED]
+ *
+ * 4. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by Computing Services
+ *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Id$
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <syslog.h>
+
+#include "assert.h"
+#include "cyrusdb.h"
+#include "exitcodes.h"
+#include "imapd.h"
+#include "global.h"
+#include "imap_err.h"
+#include "mboxlist.h"
+#include "seen.h"
+#include "util.h"
+#include "xmalloc.h"
+
+#include "statuscache.h"
+
+#define DB config_statuscache_db
+
+struct db *statuscachedb;
+static int statuscache_dbopen = 0;
+
+void statuscache_open(char *fname)
+{
+    int ret;
+    char *tofree = NULL;
+
+    /* create db file name */
+    if (!fname) {
+	fname = xmalloc(strlen(config_dir)+sizeof(FNAME_STATUSCACHEDB));
+	tofree = fname;
+	strcpy(fname, config_dir);
+	strcat(fname, FNAME_STATUSCACHEDB);
+    }
+
+    ret = DB->open(fname, CYRUSDB_CREATE, &statuscachedb);
+    if (ret != 0) {
+	syslog(LOG_ERR, "DBERROR: opening %s: %s", fname,
+	       cyrusdb_strerror(ret));
+	fatal("can't read statuscache file", EC_TEMPFAIL);
+    }    
+
+    if (tofree) free(tofree);
+
+    statuscache_dbopen = 1;
+}
+
+void statuscache_close(void)
+{
+    int r;
+
+    if (statuscache_dbopen) {
+	r = DB->close(statuscachedb);
+	if (r) {
+	    syslog(LOG_ERR, "DBERROR: error closing statuscache: %s",
+		   cyrusdb_strerror(r));
+	}
+	statuscache_dbopen = 0;
+    }
+}
+
+int statuscache_fill(struct statuscache_data *scdata, struct mailbox *mailbox,
+		     int statusitems, int num_recent, int num_unseen)
+{
+    assert(scdata);
+    assert(mailbox);
+
+    scdata->version = STATUSCACHE_VERSION;
+    scdata->statusitems = statusitems;
+
+    scdata->message_mtime = mailbox->index_mtime;
+    scdata->message_ino = mailbox->index_ino;
+    scdata->message_size = mailbox->index_size;
+
+    scdata->c_messages = mailbox->exists;
+    scdata->c_recent = num_recent;
+    scdata->c_uidnext = mailbox->last_uid+1;
+    scdata->c_uidvalidity = mailbox->uidvalidity;
+    scdata->c_unseen = num_unseen;
+    scdata->c_highestmodseq =
+	(mailbox->options & OPT_IMAP_CONDSTORE) ? mailbox->highestmodseq : 0;
+
+    return 0;
+}
+
+void statuscache_done(void)
+{
+    /* DB->done() handled by cyrus_done() */
+}
+
+static char *statuscache_buildkey(const char *mailboxname, const char *userid)
+{
+    static char key[MAX_MAILBOX_NAME*2+7];
+
+    /* Build lookup key */
+    strcpy(key, "sc..");
+    strcat(key, mailboxname);
+    strcat(key, "..");
+    strcat(key, userid);
+
+    return key;
+}
+
+int statuscache_lookup(const char *mboxname, const char *userid,
+		       struct statuscache_data *scdata)
+{
+    int datalen, r;
+    const char *data = NULL;
+    char *key = statuscache_buildkey(mboxname, userid);
+
+    /* Check if in database */
+    do {
+	r = DB->fetch(statuscachedb, key, strlen(key), &data, &datalen, NULL);
+    } while (r == CYRUSDB_AGAIN);
+
+    /* Copy data into struct if found and right length */
+    if (!r && data && datalen == sizeof(struct statuscache_data)) {
+	if (((struct statuscache_data *)data)->version != STATUSCACHE_VERSION)
+	    return IMAP_NO_NOSUCHMSG;
+	memcpy((void *)scdata, (void *)data, datalen);
+	return 0;
+    } else if (r == CYRUSDB_NOTFOUND) {
+	return IMAP_NO_NOSUCHMSG;
+    }
+
+    return IMAP_NO_NOSUCHMSG;
+}
+
+int statuscache_update(const char *mboxname, const char *userid,
+		       struct statuscache_data *scdata)
+{
+    int r;
+    char * key = statuscache_buildkey(mboxname, userid);
+
+    /* Store raw struct in database */
+    r = DB->store(statuscachedb, key, strlen(key),
+		  (void *)scdata, sizeof(struct statuscache_data), NULL);
+    if (r != CYRUSDB_OK) {
+	syslog(LOG_ERR, "DBERROR: error updating database: %s", 
+	       cyrusdb_strerror(r));
+    }
+    return 0; 
+}
+
+int statuscache_invalidate(const char *mboxname, const char *userid)
+{
+    int r;
+    char * key = statuscache_buildkey(mboxname, userid);
+
+    /* Don't access DB if it hasn't been opened */
+    if (!statuscachedb) return 0;
+
+    /* Delete db entry */
+    r = DB->delete(statuscachedb, key, strlen(key), NULL, 1);
+    if (r != CYRUSDB_OK) {
+	syslog(LOG_ERR, "DBERROR: error deleting from database: %s", 
+	       cyrusdb_strerror(r));
+    }
+    return 0; 
+}
+
Index: imap/sync_server.c
===================================================================
RCS file: /afs/andrew/system/cvs/src/cyrus/imap/sync_server.c,v
retrieving revision 1.23
diff -u -r1.23 sync_server.c
--- imap/sync_server.c	17 Jan 2008 13:25:31 -0000	1.23
+++ imap/sync_server.c	18 Jan 2008 04:16:12 -0000
@@ -89,6 +89,7 @@
 #include "retry.h"
 #include "seen.h"
 #include "spool.h"
+#include "statuscache.h"
 #include "telemetry.h"
 #include "tls.h"
 #include "user.h"
@@ -333,6 +334,11 @@
     annotatemore_init(0, NULL, NULL);
     annotatemore_open(NULL);
 
+    /* Open the statuscache so we can invalidate seen states */
+    if (config_getswitch(IMAPOPT_STATUSCACHE)) {
+	statuscache_open(NULL);
+    }
+
     return 0;
 }
 
@@ -495,6 +501,11 @@
 void shut_down(int code)
 {
     proc_cleanup();
+
+    if (config_getswitch(IMAPOPT_STATUSCACHE)) {
+	statuscache_close();
+	statuscache_done();
+    }
 
     seen_done();
     mboxlist_close();
Index: lib/imapoptions
===================================================================
RCS file: /afs/andrew/system/cvs/src/cyrus/lib/imapoptions,v
retrieving revision 1.47
diff -u -r1.47 imapoptions
--- lib/imapoptions	9 Jan 2008 19:10:56 -0000	1.47
+++ lib/imapoptions	18 Jan 2008 04:16:12 -0000
@@ -953,6 +953,12 @@
 { "subscription_db", "flat", STRINGLIST("flat", "berkeley", "berkeley-hash", "skiplist")}
 /* The cyrusdb backend to use for the subscriptions list. */
 
+{ "statuscache", 0, SWITCH }
+/* Enable/disable the imap status cache. */
+
+{ "statuscache_db", "berkeley-nosync", STRINGLIST("berkeley", "berkeley-nosync", "berkeley-hash", "berkeley-hash-nosync", "skiplist") }
+/* The cyrusdb backend to use for the imap status cache. */
+
 { "sync_authname", NULL, STRING }
 /* The authentication name to use when authenticating to a sync server. */
 

Reply via email to