I'm sick of that interface.  It blows.  Random memory jumps
trusting stuff in an mmaped file leads to reading from pretty
much anywhere.  At least we don't try to write!

Still, segfaults galore.

This doesn't change the fact that index.c is a copy-paster's
enterprisy paradise, but at least it gives named access to
all the bits of the cache record!

The basic concept is this:

struct cacheitem contains a length and a string pointer
typedef cacherecord is 10 items (per NUMCACHEITEMS constant)

ALL access goes through cache_parserecord() in one way or
another (note: this is going to come in real handy if I
ever resurrect checksummed cache records!)

cache_parserecord fills a 'cacherecord' datastructure, and
it returns the length of the full cacherecord (handy, you
can just pass a null cacherecord pointer and get the size).
If it returns a length of zero then the cache is bogus, so
cacherecord is undefined.

With that said - anyone feel like glancing through this code
and seeing that it makes sense?  I've only compile tested it
so far, not even run up a test box...

(also available as the "cacherewrite" branch on github)

Bron.
diff --git a/imap/index.c b/imap/index.c
index a8346ea..85a67c0 100644
--- a/imap/index.c
+++ b/imap/index.c
@@ -127,12 +127,12 @@ void index_fetchmsg(const char *msg_base, unsigned long msg_size,
 static int index_fetchsection(const char *resp,
 			      const char *msg_base, unsigned long msg_size,
 			      int format, char *section,
-			      const char *cacheitem, unsigned size,
+			      const char *cachestr, unsigned size,
 			      unsigned start_octet, unsigned octet_count);
 static void index_fetchfsection(const char *msg_base,
 				unsigned long msg_size,
 				int format, struct fieldlist *fsection,
-				const char *cacheitem,
+				const char *cachestr,
 				unsigned start_octet, unsigned octet_count);
 static char *index_readheader(const char *msg_base, unsigned long msg_size,
 			      int format, unsigned offset, unsigned size);
@@ -156,7 +156,7 @@ static int index_search_evaluate(struct mailbox *mailbox,
 				 unsigned msgno, struct mapfile *msgfile);
 static int index_searchmsg(char *substr, comp_pat *pat,
 			   struct mapfile *msgfile, int format,
-			   int skipheader, const char *cacheitem);
+			   int skipheader, const char *cachestr);
 static int index_searchheader(char *name, char *substr, comp_pat *pat,
 			      struct mapfile *msgfile, int format,
 			      int size);
@@ -1886,7 +1886,7 @@ struct protstream *pout;
 static int index_fetchsection(const char *resp,
 			      const char *msg_base, unsigned long msg_size,
 			      int format, char *section,
-			      const char *cacheitem, unsigned size,
+			      const char *cachestr, unsigned size,
 			      unsigned start_octet, unsigned octet_count)
 {
     char *p;
@@ -1895,7 +1895,6 @@ static int index_fetchsection(const char *resp,
     unsigned offset = 0;
     char *decbuf = NULL;
 
-    cacheitem += CACHE_ITEM_SIZE_SKIP;
     p = section;
 
     /* Special-case BODY[] */
@@ -1920,7 +1919,7 @@ static int index_fetchsection(const char *resp,
 	if (*p == '.') p++;
 
 	/* section number too large */
-	if (skip >= CACHE_ITEM_BIT32(cacheitem)) goto badpart;
+	if (skip >= CACHE_ITEM_BIT32(cachestr)) goto badpart;
 
 	/* Handle .0, .HEADER, and .TEXT */
 	if (!skip) {
@@ -1947,34 +1946,34 @@ static int index_fetchsection(const char *resp,
 
 	    /* Skip the headers for this part, along with the number of
 	     * sub parts */
-	    cacheitem +=
-		CACHE_ITEM_BIT32(cacheitem) * 5 * 4 + CACHE_ITEM_SIZE_SKIP;
+	    cachestr +=
+		CACHE_ITEM_BIT32(cachestr) * 5 * 4 + CACHE_ITEM_SIZE_SKIP;
 
 	    /* Skip to the correct part */
 	    while (--skip) {
-		if (CACHE_ITEM_BIT32(cacheitem) > 0) {
+		if (CACHE_ITEM_BIT32(cachestr) > 0) {
 		    /* Skip each part at this level */
-		    skip += CACHE_ITEM_BIT32(cacheitem)-1;
-		    cacheitem += CACHE_ITEM_BIT32(cacheitem) * 5 * 4;
+		    skip += CACHE_ITEM_BIT32(cachestr)-1;
+		    cachestr += CACHE_ITEM_BIT32(cachestr) * 5 * 4;
 		}
-		cacheitem += CACHE_ITEM_SIZE_SKIP;
+		cachestr += CACHE_ITEM_SIZE_SKIP;
 	    }
 	}
     }
 
     if (*p == 'M') fetchmime++;
 
-    cacheitem += skip * 5 * 4 + CACHE_ITEM_SIZE_SKIP + (fetchmime ? 0 : 2 * 4);
+    cachestr += skip * 5 * 4 + CACHE_ITEM_SIZE_SKIP + (fetchmime ? 0 : 2 * 4);
     
-    if (CACHE_ITEM_BIT32(cacheitem + CACHE_ITEM_SIZE_SKIP) == (bit32) -1)
+    if (CACHE_ITEM_BIT32(cachestr + CACHE_ITEM_SIZE_SKIP) == (bit32) -1)
 	goto badpart;
 
-    offset = CACHE_ITEM_BIT32(cacheitem);
-    size = CACHE_ITEM_BIT32(cacheitem + CACHE_ITEM_SIZE_SKIP);
+    offset = CACHE_ITEM_BIT32(cachestr);
+    size = CACHE_ITEM_BIT32(cachestr + CACHE_ITEM_SIZE_SKIP);
 
     if (msg_base && (p = strstr(resp, "BINARY"))) {
 	/* BINARY or BINARY.SIZE */
-	int encoding = CACHE_ITEM_BIT32(cacheitem + 2 * 4) & 0xff;
+	int encoding = CACHE_ITEM_BIT32(cachestr + 2 * 4) & 0xff;
 
 	msg_base = charset_decode_mimebody(msg_base + offset, size, encoding,
 					   &decbuf, 0, (int *) &size);
@@ -2021,7 +2020,7 @@ static void index_fetchfsection(const char *msg_base,
 				unsigned long msg_size,
 				int format,
 				struct fieldlist *fsection,
-				const char *cacheitem,
+				const char *cachestr,
 				unsigned start_octet, unsigned octet_count)
 {
     char *p;
@@ -2038,7 +2037,6 @@ static void index_fetchfsection(const char *msg_base,
 	return;
     }
 
-    cacheitem += CACHE_ITEM_SIZE_SKIP;
     p = fsection->section;
 
     while (*p != 'H') {
@@ -2050,31 +2048,31 @@ static void index_fetchfsection(const char *msg_base,
 	if (*p == '.') p++;
 
 	/* section number too large */
-	if (skip >= CACHE_ITEM_BIT32(cacheitem)) goto badpart;
+	if (skip >= CACHE_ITEM_BIT32(cachestr)) goto badpart;
 
-	cacheitem += CACHE_ITEM_BIT32(cacheitem) * 5 * 4 + CACHE_ITEM_SIZE_SKIP;
+	cachestr += CACHE_ITEM_BIT32(cachestr) * 5 * 4 + CACHE_ITEM_SIZE_SKIP;
 	while (--skip) {
-	    if (CACHE_ITEM_BIT32(cacheitem) > 0) {
-		skip += CACHE_ITEM_BIT32(cacheitem)-1;
-		cacheitem += CACHE_ITEM_BIT32(cacheitem) * 5 * 4;
+	    if (CACHE_ITEM_BIT32(cachestr) > 0) {
+		skip += CACHE_ITEM_BIT32(cachestr)-1;
+		cachestr += CACHE_ITEM_BIT32(cachestr) * 5 * 4;
 	    }
-	    cacheitem += CACHE_ITEM_SIZE_SKIP;
+	    cachestr += CACHE_ITEM_SIZE_SKIP;
 	}
     }
 
     /* leaf object */
-    if (0 == CACHE_ITEM_BIT32(cacheitem)) goto badpart;
+    if (0 == CACHE_ITEM_BIT32(cachestr)) goto badpart;
 
-    cacheitem += 4;
+    cachestr += 4;
 
-    if (CACHE_ITEM_BIT32(cacheitem+CACHE_ITEM_SIZE_SKIP) == (bit32) -1)
+    if (CACHE_ITEM_BIT32(cachestr+CACHE_ITEM_SIZE_SKIP) == (bit32) -1)
 	goto badpart;
 	
     if (p[13]) fields_not++;	/* Check for "." after "HEADER.FIELDS" */
 
     buf = index_readheader(msg_base, msg_size, format,
-			   CACHE_ITEM_BIT32(cacheitem),
-			   CACHE_ITEM_BIT32(cacheitem+CACHE_ITEM_SIZE_SKIP));
+			   CACHE_ITEM_BIT32(cachestr),
+			   CACHE_ITEM_BIT32(cachestr+CACHE_ITEM_SIZE_SKIP));
 
     if (fields_not) {
 	index_pruneheader(buf, 0, fsection->fields);
@@ -2263,24 +2261,24 @@ index_fetchcacheheader(unsigned msgno, struct strlist *headers,
 {
     static char *buf;
     static unsigned bufsize;
-    const char *cacheitem;
+    cacherecord crec;
     unsigned size;
     unsigned crlf_start = 0;
     unsigned crlf_size = 2;
 
-    cacheitem = cache_base + CACHE_OFFSET(msgno);
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip envelope */
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bodystructure */
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip body */
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip section */
-    
-    size = CACHE_ITEM_LEN(cacheitem);
+    if (!cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec)) {
+	/* bogus cache record */
+	prot_printf(imapd_out, "\"\"");
+	return;
+    }
+
+    size = crec[CACHE_HEADERS].l;
     if (bufsize < size+2) {
 	bufsize = size+100;
 	buf = xrealloc(buf, bufsize);
     }
 
-    memcpy(buf, cacheitem + CACHE_ITEM_SIZE_SKIP, size);
+    memcpy(buf, crec[CACHE_HEADERS].s, size);
     buf[size] = '\0';
 
     index_pruneheader(buf, headers, 0);
@@ -2450,7 +2448,7 @@ static int index_fetchreply(struct mailbox *mailbox,
     int started = 0;
     unsigned i;
     bit32 user_flags[MAX_USER_FLAGS/32];
-    const char *cacheitem;
+    cacherecord crec;
     struct strlist *section, *field;
     struct fieldlist *fsection;
     char respbuf[100];
@@ -2520,28 +2518,25 @@ static int index_fetchreply(struct mailbox *mailbox,
 	sepchar = ' ';
     }
     if (fetchitems & FETCH_ENVELOPE) {
-	prot_printf(imapd_out, "%cENVELOPE ", sepchar);
-	sepchar = ' ';
-	cacheitem = cache_base + CACHE_OFFSET(msgno);
-	prot_write(imapd_out, cacheitem + CACHE_ITEM_SIZE_SKIP,
-		   CACHE_ITEM_LEN(cacheitem));
+        if (cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec)) {
+	    prot_printf(imapd_out, "%cENVELOPE ", sepchar);
+	    sepchar = ' ';
+	    prot_write(imapd_out, crec[CACHE_ENVELOPE].s, crec[CACHE_ENVELOPE].l);
+	}
     }
     if (fetchitems & FETCH_BODYSTRUCTURE) {
-	prot_printf(imapd_out, "%cBODYSTRUCTURE ", sepchar);
-	sepchar = ' ';
-	cacheitem = cache_base + CACHE_OFFSET(msgno);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip envelope */
-	prot_write(imapd_out, cacheitem + CACHE_ITEM_SIZE_SKIP,
-		   CACHE_ITEM_LEN(cacheitem));
+        if (cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec)) {
+	    prot_printf(imapd_out, "%cBODYSTRUCTURE ", sepchar);
+	    sepchar = ' ';
+	    prot_write(imapd_out, crec[CACHE_BODYSTRUCTURE].s, crec[CACHE_BODYSTRUCTURE].l);
+	}
     }
     if (fetchitems & FETCH_BODY) {
-	prot_printf(imapd_out, "%cBODY ", sepchar);
-	sepchar = ' ';
-	cacheitem = cache_base + CACHE_OFFSET(msgno);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip envelope */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bodystructure */
-	prot_write(imapd_out, cacheitem + CACHE_ITEM_SIZE_SKIP,
-		   CACHE_ITEM_LEN(cacheitem));
+        if (cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec)) {
+	    prot_printf(imapd_out, "%cBODY ", sepchar);
+	    sepchar = ' ';
+	    prot_write(imapd_out, crec[CACHE_BODY].s, crec[CACHE_BODY].l);
+	}
     }
 
     if (fetchitems & FETCH_HEADER) {
@@ -2604,17 +2599,16 @@ static int index_fetchreply(struct mailbox *mailbox,
 	prot_printf(imapd_out, "%s ", fsection->trail);
 
 	if(fetchargs->cache_atleast > CACHE_VERSION(msgno)) {
-	    cacheitem = cache_base + CACHE_OFFSET(msgno);
-	    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip envelope */
-	    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bodystructure */
-	    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip body */
+	    if (cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec))
+		index_fetchfsection(msg_base, msg_size, mailbox->format, fsection,
+				    crec[CACHE_HEADERS].s,
+				    (fetchitems & FETCH_IS_PARTIAL) ?
+				      fetchargs->start_octet : oi->start_octet,
+				    (fetchitems & FETCH_IS_PARTIAL) ?
+				      fetchargs->octet_count : oi->octet_count);
+	    else
+	    	prot_printf(imapd_out, "NIL");
 	    
-	    index_fetchfsection(msg_base, msg_size, mailbox->format, fsection,
-				cacheitem,
-				(fetchitems & FETCH_IS_PARTIAL) ?
-				  fetchargs->start_octet : oi->start_octet,
-				(fetchitems & FETCH_IS_PARTIAL) ?
-				  fetchargs->octet_count : oi->octet_count);
 	}
 	else {
 	    index_fetchcacheheader(msgno, fsection->fields,
@@ -2633,20 +2627,17 @@ static int index_fetchreply(struct mailbox *mailbox,
 	snprintf(respbuf+strlen(respbuf), sizeof(respbuf)-strlen(respbuf),
 		 "%cBODY[%s ", sepchar, section->s);
 
-	cacheitem = cache_base + CACHE_OFFSET(msgno);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip envelope */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bodystructure */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip body */
-
 	oi = section->rock;
 
-	r = index_fetchsection(respbuf, msg_base, msg_size, mailbox->format,
-			       section->s, cacheitem, SIZE(msgno),
-			       (fetchitems & FETCH_IS_PARTIAL) ?
-				 fetchargs->start_octet : oi->start_octet,
-			       (fetchitems & FETCH_IS_PARTIAL) ?
-			         fetchargs->octet_count : oi->octet_count);
-	if (!r)	sepchar = ' ';
+	if (cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec)) {
+	    r = index_fetchsection(respbuf, msg_base, msg_size, mailbox->format,
+			           section->s, crec[CACHE_SECTION].s, SIZE(msgno),
+			           (fetchitems & FETCH_IS_PARTIAL) ?
+				     fetchargs->start_octet : oi->start_octet,
+			           (fetchitems & FETCH_IS_PARTIAL) ?
+			             fetchargs->octet_count : oi->octet_count);
+	    if (!r) sepchar = ' ';
+	}
     }
     for (section = fetchargs->binsections; section; section = section->next) {
 	respbuf[0] = 0;
@@ -2657,20 +2648,16 @@ static int index_fetchreply(struct mailbox *mailbox,
 	snprintf(respbuf+strlen(respbuf), sizeof(respbuf)-strlen(respbuf),
 		 "%cBINARY[%s ", sepchar, section->s);
 
-	cacheitem = cache_base + CACHE_OFFSET(msgno);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip envelope */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bodystructure */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip body */
-
-	oi = section->rock;
-
-	r = index_fetchsection(respbuf, msg_base, msg_size, mailbox->format,
-			       section->s, cacheitem, SIZE(msgno),
-			       (fetchitems & FETCH_IS_PARTIAL) ?
-				 fetchargs->start_octet : oi->start_octet,
-			       (fetchitems & FETCH_IS_PARTIAL) ?
-			         fetchargs->octet_count : oi->octet_count);
-	if (!r)	sepchar = ' ';
+	if (cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec)) {
+	    oi = section->rock;
+	    r = index_fetchsection(respbuf, msg_base, msg_size, mailbox->format,
+			           section->s, crec[CACHE_SECTION].s, SIZE(msgno),
+			           (fetchitems & FETCH_IS_PARTIAL) ?
+				     fetchargs->start_octet : oi->start_octet,
+			           (fetchitems & FETCH_IS_PARTIAL) ?
+			             fetchargs->octet_count : oi->octet_count);
+	    if (!r) sepchar = ' ';
+	}
     }
     for (section = fetchargs->sizesections; section; section = section->next) {
 	respbuf[0] = 0;
@@ -2681,15 +2668,12 @@ static int index_fetchreply(struct mailbox *mailbox,
 	snprintf(respbuf+strlen(respbuf), sizeof(respbuf)-strlen(respbuf),
 		 "%cBINARY.SIZE[%s ", sepchar, section->s);
 
-	cacheitem = cache_base + CACHE_OFFSET(msgno);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip envelope */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bodystructure */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip body */
-
-	r = index_fetchsection(respbuf, msg_base, msg_size, mailbox->format,
-			       section->s, cacheitem, SIZE(msgno),
-			       fetchargs->start_octet, fetchargs->octet_count);
-	if (!r)	sepchar = ' ';
+	if (cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec)) {
+	    r = index_fetchsection(respbuf, msg_base, msg_size, mailbox->format,
+			           section->s, crec[CACHE_SECTION].s, SIZE(msgno),
+			           fetchargs->start_octet, fetchargs->octet_count);
+	    if (!r) sepchar = ' ';
+	}
     }
     if (sepchar != '(') {
 	/* finsh the response if we have one */
@@ -2718,7 +2702,8 @@ int index_urlfetch(struct mailbox *mailbox, unsigned msgno,
 {
     const char *msg_base = 0;
     unsigned long msg_size = 0;
-    const char *cacheitem;
+    cacherecord crec;
+    const char *cachestr;
     int fetchmime = 0;
     unsigned size, offset = 0, skip = 0;
     int n, r = 0;
@@ -2730,14 +2715,12 @@ int index_urlfetch(struct mailbox *mailbox, unsigned msgno,
 	return IMAP_NO_MSGGONE;
     }
 
-    cacheitem = cache_base + CACHE_OFFSET(msgno);
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip envelope */
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bodystructure */
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip body */
-
-    size = SIZE(msgno);
+    if (!cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec)) {
+	return IMAP_NO_MSGGONE;
+    }
 
-    cacheitem += CACHE_ITEM_SIZE_SKIP;
+    size = crec[CACHE_SECTION].l;
+    cachestr = crec[CACHE_SECTION].s;
 
     /* Special-case BODY[] */
     if (!section || !*section) {
@@ -2756,7 +2739,7 @@ int index_urlfetch(struct mailbox *mailbox, unsigned msgno,
 	    if (*p == '.') p++;
 
 	    /* section number too large */
-	    if (skip >= CACHE_ITEM_BIT32(cacheitem)) {
+	    if (skip >= CACHE_ITEM_BIT32(cachestr)) {
 		r = IMAP_BADURL;
 		goto done;
 	    }
@@ -2786,33 +2769,33 @@ int index_urlfetch(struct mailbox *mailbox, unsigned msgno,
 
 		/* Skip the headers for this part, along with the number of
 		 * sub parts */
-		cacheitem +=
-		    CACHE_ITEM_BIT32(cacheitem) * 5 * 4 + CACHE_ITEM_SIZE_SKIP;
+		cachestr +=
+		    CACHE_ITEM_BIT32(cachestr) * 5 * 4 + CACHE_ITEM_SIZE_SKIP;
 
 		/* Skip to the correct part */
 		while (--skip) {
-		    if (CACHE_ITEM_BIT32(cacheitem) > 0) {
+		    if (CACHE_ITEM_BIT32(cachestr) > 0) {
 			/* Skip each part at this level */
-			skip += CACHE_ITEM_BIT32(cacheitem)-1;
-			cacheitem += CACHE_ITEM_BIT32(cacheitem) * 5 * 4;
+			skip += CACHE_ITEM_BIT32(cachestr)-1;
+			cachestr += CACHE_ITEM_BIT32(cachestr) * 5 * 4;
 		    }
-		    cacheitem += CACHE_ITEM_SIZE_SKIP;
+		    cachestr += CACHE_ITEM_SIZE_SKIP;
 		}
 	    }
 	}
 
 	if (*p == 'M') fetchmime++;
 
-	cacheitem += skip * 5 * 4 + CACHE_ITEM_SIZE_SKIP +
+	cachestr += skip * 5 * 4 + CACHE_ITEM_SIZE_SKIP +
 	    (fetchmime ? 0 : 2 * 4);
     
-	if (CACHE_ITEM_BIT32(cacheitem + CACHE_ITEM_SIZE_SKIP) == (bit32) -1) {
+	if (CACHE_ITEM_BIT32(cachestr + CACHE_ITEM_SIZE_SKIP) == (bit32) -1) {
 	    r = IMAP_BADURL;
 	    goto done;
 	}
 
-	offset = CACHE_ITEM_BIT32(cacheitem);
-	size = CACHE_ITEM_BIT32(cacheitem + CACHE_ITEM_SIZE_SKIP);
+	offset = CACHE_ITEM_BIT32(cachestr);
+	size = CACHE_ITEM_BIT32(cachestr + CACHE_ITEM_SIZE_SKIP);
     }
 
     /* Handle PARTIAL request */
@@ -3076,8 +3059,7 @@ static int index_search_evaluate(struct mailbox *mailbox,
 {
     unsigned i;
     struct strlist *l, *h;
-    const char *cacheitem;
-    int cachelen;
+    cacherecord crec;
     struct searchsub *s;
     struct seq_set *seq;
 
@@ -3122,8 +3104,8 @@ static int index_search_evaluate(struct mailbox *mailbox,
     if (searchargs->from || searchargs->to || searchargs->cc ||
 	searchargs->bcc || searchargs->subject || searchargs->messageid) {
 
-	cacheitem = cache_base + CACHE_OFFSET(msgno);
-	cachelen = CACHE_ITEM_LEN(cacheitem);
+	if (!cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec))
+	    return 0;
 
 	if (searchargs->messageid) {
 	    char *tmpenv;
@@ -3136,8 +3118,7 @@ static int index_search_evaluate(struct mailbox *mailbox,
 	    /* get a working copy; strip outer ()'s */
 	    /* +1 -> skip the leading paren */
 	    /* -2 -> don't include the size of the outer parens */
-	    tmpenv = xstrndup(cacheitem + CACHE_ITEM_SIZE_SKIP + 1,
-			      cachelen - 2);
+	    tmpenv = xstrndup(crec[CACHE_ENVELOPE].s + 1, crec[CACHE_ENVELOPE].l - 2);
 	    parse_cached_envelope(tmpenv, envtokens, VECTOR_SIZE(envtokens));
 
 	    if (!envtokens[ENV_MSGID]) {
@@ -3161,63 +3142,39 @@ static int index_search_evaluate(struct mailbox *mailbox,
 	    if (l) return 0;
 	}
 
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip envelope */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bodystructure */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip body */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip section */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip cacheheaders */
-	cachelen = CACHE_ITEM_LEN(cacheitem);
-	    
 	for (l = searchargs->from; l; l = l->next) {
-	    if (cachelen == 0 ||
+	    if (!crec[CACHE_FROM].l ||
 		!charset_searchstring(l->s, l->p,
-				      cacheitem + CACHE_ITEM_SIZE_SKIP,
-				      cachelen))
+				      crec[CACHE_FROM].s, crec[CACHE_FROM].l))
 		return 0;
 	}
 
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip from */
-	cachelen = CACHE_ITEM_LEN(cacheitem);
-
 	for (l = searchargs->to; l; l = l->next) {
-	    if (cachelen == 0 ||
+	    if (!crec[CACHE_TO].l ||
 		!charset_searchstring(l->s, l->p,
-				      cacheitem + CACHE_ITEM_SIZE_SKIP,
-				      cachelen)) 
+				      crec[CACHE_TO].s, crec[CACHE_TO].l))
 		return 0;
 	}
 
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip to */
-	cachelen = CACHE_ITEM_LEN(cacheitem);
-
 	for (l = searchargs->cc; l; l = l->next) {
-	    if (cachelen == 0 ||
+	    if (!crec[CACHE_CC].l ||
 		!charset_searchstring(l->s, l->p,
-				      cacheitem + CACHE_ITEM_SIZE_SKIP,
-				      cachelen)) 
+				      crec[CACHE_CC].s, crec[CACHE_CC].l))
 		return 0;
 	}
 
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip cc */
-	cachelen = CACHE_ITEM_LEN(cacheitem);
-
 	for (l = searchargs->bcc; l; l = l->next) {
-	    if (cachelen == 0 ||
+	    if (!crec[CACHE_BCC].l ||
 		!charset_searchstring(l->s, l->p,
-				      cacheitem + CACHE_ITEM_SIZE_SKIP,
-				      cachelen)) 
+				      crec[CACHE_BCC].s, crec[CACHE_BCC].l))
 		return 0;
 	}
 
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bcc */
-	cachelen = CACHE_ITEM_LEN(cacheitem);
-
 	for (l = searchargs->subject; l; l = l->next) {
-	    if ((cachelen == 3 &&
-		 !strcmp(cacheitem + CACHE_ITEM_SIZE_SKIP, "NIL")) ||
+	    if ((crec[CACHE_SUBJECT].l == 3 && 
+		 !strncmp(crec[CACHE_SUBJECT].s, "NIL", 3)) ||
 		!charset_searchstring(l->s, l->p,
-				      cacheitem + CACHE_ITEM_SIZE_SKIP,
-				      cachelen)) 
+				      crec[CACHE_SUBJECT].s, crec[CACHE_SUBJECT].l))
 		return 0;
 	}
     }
@@ -3248,18 +3205,16 @@ static int index_search_evaluate(struct mailbox *mailbox,
 				    HEADER_SIZE(msgno))) return 0;
 	}
 
-	cacheitem = cache_base + CACHE_OFFSET(msgno);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip envelope */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bodystructure */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip body */
+	if (!cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec))
+	    return 0;
 
 	for (l = searchargs->body; l; l = l->next) {
 	    if (!index_searchmsg(l->s, l->p, msgfile, mailbox->format, 1,
-				 cacheitem)) return 0;
+				 crec[CACHE_SECTION].s)) return 0;
 	}
 	for (l = searchargs->text; l; l = l->next) {
 	    if (!index_searchmsg(l->s, l->p, msgfile, mailbox->format, 0,
-				  cacheitem)) return 0;
+				 crec[CACHE_SECTION].s)) return 0;
 	}
     }
     else if (searchargs->header_name) {
@@ -3282,7 +3237,7 @@ index_searchmsg(char *substr,
 		struct mapfile *msgfile,
 		int format,
 		int skipheader,
-		const char *cacheitem)
+		const char *cachestr)
 {
     int partsleft = 1;
     int subparts;
@@ -3293,10 +3248,9 @@ index_searchmsg(char *substr,
     /* Won't find anything in a truncated file */
     if (msgfile->size == 0) return 0;
 
-    cacheitem += CACHE_ITEM_SIZE_SKIP;
     while (partsleft--) {
-	subparts = CACHE_ITEM_BIT32(cacheitem);
-	cacheitem += 4;
+	subparts = CACHE_ITEM_BIT32(cachestr);
+	cachestr += 4;
 	if (subparts) {
 	    partsleft += subparts-1;
 
@@ -3304,10 +3258,10 @@ index_searchmsg(char *substr,
 		skipheader = 0;	/* Only skip top-level message header */
 	    }
 	    else {
-		len = CACHE_ITEM_BIT32(cacheitem + CACHE_ITEM_SIZE_SKIP);
+		len = CACHE_ITEM_BIT32(cachestr + CACHE_ITEM_SIZE_SKIP);
 		if (len > 0) {
 		    p = index_readheader(msgfile->base, msgfile->size,
-					 format, CACHE_ITEM_BIT32(cacheitem),
+					 format, CACHE_ITEM_BIT32(cachestr),
 					 len);
 		    q = charset_decode_mimeheader(p, NULL, 0);
 		    if (charset_searchstring(substr, pat, q, strlen(q))) {
@@ -3317,13 +3271,13 @@ index_searchmsg(char *substr,
 		    free(q);
 		}
 	    }
-	    cacheitem += 5*4;
+	    cachestr += 5*4;
 
 	    while (--subparts) {
-		start = CACHE_ITEM_BIT32(cacheitem+2*4);
-		len = CACHE_ITEM_BIT32(cacheitem+3*4);
-		charset = CACHE_ITEM_BIT32(cacheitem+4*4) >> 16;
-		encoding = CACHE_ITEM_BIT32(cacheitem+4*4) & 0xff;
+		start = CACHE_ITEM_BIT32(cachestr+2*4);
+		len = CACHE_ITEM_BIT32(cachestr+3*4);
+		charset = CACHE_ITEM_BIT32(cachestr+4*4) >> 16;
+		encoding = CACHE_ITEM_BIT32(cachestr+4*4) & 0xff;
 
 		if (start < msgfile->size && len > 0 &&
 		    charset >= 0 && charset < 0xffff) {
@@ -3332,7 +3286,7 @@ index_searchmsg(char *substr,
 					   format == MAILBOX_FORMAT_NETNEWS,
 					   len, charset, encoding)) return 1;
 		}
-		cacheitem += 5*4;
+		cachestr += 5*4;
 	    }
 	}
     }
@@ -3377,19 +3331,15 @@ static int index_searchcacheheader(unsigned msgno,
     static struct strlist header;
     static char *buf;
     static unsigned bufsize;
-    const char *cacheitem;
+    cacherecord crec;
     unsigned size;
     int r;
 
-    cacheitem = cache_base + CACHE_OFFSET(msgno);
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip envelope */
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bodystructure */
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip body */
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip section */
-    
-    size = CACHE_ITEM_LEN(cacheitem);
+    if (!cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec))
+	return 0;
+
+    size = crec[CACHE_HEADERS].l;
     if (!size) return 0;	/* No cached headers, fail */
-    cacheitem += sizeof(bit32);
     
     if (bufsize < size+2) {
 	bufsize = size+100;
@@ -3397,7 +3347,7 @@ static int index_searchcacheheader(unsigned msgno,
     }
 
     /* Copy this item to the buffer */
-    memcpy(buf, cacheitem, size);
+    memcpy(buf, crec[CACHE_HEADERS].s, size);
     buf[size] = '\0';
 
     header.s = name;
@@ -3405,6 +3355,7 @@ static int index_searchcacheheader(unsigned msgno,
     index_pruneheader(buf, &header, 0);
     if (!*buf) return 0;	/* Header not present, fail */
     if (!*substr) return 1;	/* Only checking existence, succeed */
+    /* XXX - we could do this in one pass maybe? charset_search_mimeheader */
     q = charset_decode_mimeheader(strchr(buf, ':') + 1, NULL, 0);
     r = charset_searchstring(substr, pat, q, strlen(q));
     free(q);
@@ -3419,7 +3370,7 @@ static void index_getsearchtextmsg(struct mailbox* mailbox,
                                   int uid,
                                   index_search_text_receiver_t receiver,
                                   void* rock,
-                                  char const* cacheitem) {
+                                  char const* cachestr) {
   struct mapfile msgfile;
   int partsleft = 1;
   int subparts;
@@ -3435,19 +3386,18 @@ static void index_getsearchtextmsg(struct mailbox* mailbox,
 
   /* Won't find anything in a truncated file */
   if (msgfile.size > 0) {
-    cacheitem += 4;
     while (partsleft--) {
-	subparts = CACHE_ITEM_BIT32(cacheitem);
-	cacheitem += 4;
+	subparts = CACHE_ITEM_BIT32(cachestr);
+	cachestr += 4;
 	if (subparts) {
 	    partsleft += subparts-1;
 
             partcount++;
 
-            len = CACHE_ITEM_BIT32(cacheitem+4);
+            len = CACHE_ITEM_BIT32(cachestr+4);
             if (len > 0) {
               p = index_readheader(msgfile.base, msgfile.size,
-                                   format, CACHE_ITEM_BIT32(cacheitem),
+                                   format, CACHE_ITEM_BIT32(cachestr),
                                    len);
               q = charset_decode_mimeheader(p, NULL, 0);
               if (partcount == 1) {
@@ -3461,13 +3411,13 @@ static void index_getsearchtextmsg(struct mailbox* mailbox,
               }
               free(q);
             }
-	    cacheitem += 5*4;
+	    cachestr += 5*4;
 
 	    while (--subparts) {
-		start = CACHE_ITEM_BIT32(cacheitem+2*4);
-		len = CACHE_ITEM_BIT32(cacheitem+3*4);
-		charset = CACHE_ITEM_BIT32(cacheitem+4*4) >> 16;
-		encoding = CACHE_ITEM_BIT32(cacheitem+4*4) & 0xff;
+		start = CACHE_ITEM_BIT32(cachestr+2*4);
+		len = CACHE_ITEM_BIT32(cachestr+3*4);
+		charset = CACHE_ITEM_BIT32(cachestr+4*4) >> 16;
+		encoding = CACHE_ITEM_BIT32(cachestr+4*4) & 0xff;
 
 		if (start < msgfile.size && len > 0 &&
 		    charset >= 0 && charset < 0xffff) {
@@ -3476,7 +3426,7 @@ static void index_getsearchtextmsg(struct mailbox* mailbox,
                                       format == MAILBOX_FORMAT_NETNEWS,
                                       len, charset, encoding);
 		}
-		cacheitem += 5*4;
+		cachestr += 5*4;
 	    }
 	}
     }
@@ -3491,38 +3441,28 @@ static void index_getsearchtextmsg(struct mailbox* mailbox,
 void index_getsearchtext_single(struct mailbox* mailbox, unsigned msgno,
                                 index_search_text_receiver_t receiver,
                                 void* rock) {
-    const char *cacheitem;
+    cacherecord crec;
     int uid = UID(msgno);
 
-    cacheitem = cache_base + CACHE_OFFSET(msgno);
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip envelope */
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bodystructure */
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip body */
+    if (!cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec))
+	return;
 
-    index_getsearchtextmsg(mailbox, uid, receiver, rock, cacheitem);
+    index_getsearchtextmsg(mailbox, uid, receiver, rock, crec[CACHE_HEADERS].s);
     
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip section */
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip cacheheaders */
-
     receiver(uid, SEARCHINDEX_PART_FROM, SEARCHINDEX_CMD_STUFFPART,
-             cacheitem + 4, CACHE_ITEM_LEN(cacheitem), rock);
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip from */
+             crec[CACHE_FROM].s, crec[CACHE_FROM].l, rock);
 
     receiver(uid, SEARCHINDEX_PART_TO, SEARCHINDEX_CMD_STUFFPART,
-             cacheitem + 4, CACHE_ITEM_LEN(cacheitem), rock);
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip to */
+             crec[CACHE_TO].s, crec[CACHE_TO].l, rock);
 
     receiver(uid, SEARCHINDEX_PART_CC, SEARCHINDEX_CMD_STUFFPART,
-             cacheitem + 4, CACHE_ITEM_LEN(cacheitem), rock);
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip cc */
+             crec[CACHE_CC].s, crec[CACHE_CC].l, rock);
 
     receiver(uid, SEARCHINDEX_PART_BCC, SEARCHINDEX_CMD_STUFFPART,
-             cacheitem + 4, CACHE_ITEM_LEN(cacheitem), rock);
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bcc */
+             crec[CACHE_BCC].s, crec[CACHE_BCC].l, rock);
 
     receiver(uid, SEARCHINDEX_PART_SUBJECT, SEARCHINDEX_CMD_STUFFPART,
-             cacheitem + 4, CACHE_ITEM_LEN(cacheitem), rock);
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip subject */
+             crec[CACHE_SUBJECT].s, crec[CACHE_SUBJECT].l, rock);
 }
 
 void index_getsearchtext(struct mailbox* mailbox,
@@ -3624,8 +3564,7 @@ static MsgData *index_msgdata_load(unsigned *msgno_list, int n,
 				   struct sortcrit *sortcrit)
 {
     MsgData *md, *cur;
-    const char *cacheitem = NULL, *env = NULL, 
-	*headers = NULL, *from = NULL, *to = NULL, *cc = NULL, *subj = NULL;
+    cacherecord crec;
     int i, j;
     char *tmpenv;
     char *envtokens[NUMENVTOKENS];
@@ -3660,17 +3599,9 @@ static MsgData *index_msgdata_load(unsigned *msgno_list, int n,
 		!did_cache) {
 
 		/* fetch cached info */
-		env = cache_base + CACHE_OFFSET(cur->msgno);
-		cacheitem = CACHE_ITEM_NEXT(env); /* bodystructure */
-		cacheitem = CACHE_ITEM_NEXT(cacheitem); /* body */
-		cacheitem = CACHE_ITEM_NEXT(cacheitem); /* section */
-		headers = CACHE_ITEM_NEXT(cacheitem);
-		from = CACHE_ITEM_NEXT(headers);
-		to = CACHE_ITEM_NEXT(from);
-		cc = CACHE_ITEM_NEXT(to);
-		cacheitem = CACHE_ITEM_NEXT(cc); /* bcc */
-		subj = CACHE_ITEM_NEXT(cacheitem);
-
+		if (!cache_parserecord(cache_base, cache_end, CACHE_OFFSET(cur->msgno), &crec))
+		    continue; /* can't do this with a broken cache */
+		
 		did_cache++;
 	    }
 
@@ -3680,8 +3611,8 @@ static MsgData *index_msgdata_load(unsigned *msgno_list, int n,
 		/* make a working copy of envelope -- strip outer ()'s */
 		/* +1 -> skip the leading paren */
 		/* -2 -> don't include the size of the outer parens */
-		tmpenv = xstrndup(env + CACHE_ITEM_SIZE_SKIP + 1,
-				  CACHE_ITEM_LEN(env) - 2);
+		tmpenv = xstrndup(crec[CACHE_ENVELOPE].s + 1, 
+				  crec[CACHE_ENVELOPE].l - 2);
 
 		/* parse envelope into tokens */
 		parse_cached_envelope(tmpenv, envtokens,
@@ -3692,7 +3623,7 @@ static MsgData *index_msgdata_load(unsigned *msgno_list, int n,
 
 	    switch (label) {
 	    case SORT_CC:
-		cur->cc = get_localpart_addr(cc + CACHE_ITEM_SIZE_SKIP);
+		cur->cc = get_localpart_addr(crec[CACHE_CC].s);
 		break;
 	    case SORT_DATE:
 		cur->date = message_parse_date(envtokens[ENV_DATE],
@@ -3700,16 +3631,16 @@ static MsgData *index_msgdata_load(unsigned *msgno_list, int n,
 					       | PARSE_NOCREATE);
 		break;
 	    case SORT_FROM:
-		cur->from = get_localpart_addr(from + CACHE_ITEM_SIZE_SKIP);
+		cur->from = get_localpart_addr(crec[CACHE_FROM].s);
 		break;
 	    case SORT_SUBJECT:
-		cur->xsubj = index_extract_subject(subj + CACHE_ITEM_SIZE_SKIP,
-						   CACHE_ITEM_LEN(subj),
+		cur->xsubj = index_extract_subject(crec[CACHE_SUBJECT].s,
+						   crec[CACHE_SUBJECT].l,
 						   &cur->is_refwd);
 		cur->xsubj_hash = strhash(cur->xsubj);
 		break;
 	    case SORT_TO:
-		cur->to = get_localpart_addr(to + CACHE_ITEM_SIZE_SKIP);
+		cur->to = get_localpart_addr(crec[CACHE_TO].s);
 		break;
  	    case SORT_ANNOTATION:
  		/* reallocate space for the annotation values if necessary */
@@ -3724,8 +3655,8 @@ static MsgData *index_msgdata_load(unsigned *msgno_list, int n,
  		cur->nannot++;
  		break;
 	    case LOAD_IDS:
-		index_get_ids(cur, envtokens, headers + CACHE_ITEM_SIZE_SKIP,
-			      CACHE_ITEM_LEN(headers));
+		index_get_ids(cur, envtokens, crec[CACHE_HEADERS].s, 
+					      crec[CACHE_HEADERS].l);
 		break;
 	    }
 	}
@@ -5096,14 +5027,13 @@ static void index_thread_ref(unsigned *msgno_list, int nmsg, int usinguid)
 char *index_get_msgid(struct mailbox *mailbox __attribute__((unused)),
 		      unsigned msgno)
 {
-    const char *cacheitem;
-    int cachelen;
+    cacherecord crec;
     char *env;
     char *envtokens[NUMENVTOKENS];
     char *msgid;
 
-    cacheitem = cache_base + CACHE_OFFSET(msgno);
-    cachelen = CACHE_ITEM_LEN(cacheitem);
+    if (!cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec))
+	return NULL;
 
     /* get msgid out of the envelope
      *
@@ -5111,7 +5041,7 @@ char *index_get_msgid(struct mailbox *mailbox __attribute__((unused)),
      * +1 -> skip the leading paren
      * -2 -> don't include the size of the outer parens
      */
-    env = xstrndup(cacheitem + CACHE_ITEM_SIZE_SKIP + 1, cachelen - 2);
+    env = xstrndup(crec[CACHE_ENVELOPE].s + 1, crec[CACHE_ENVELOPE].l - 2);
     parse_cached_envelope(env, envtokens, VECTOR_SIZE(envtokens));
 
     msgid = envtokens[ENV_MSGID] ? xstrdup(envtokens[ENV_MSGID]) : NULL;
@@ -5190,37 +5120,33 @@ extern struct nntp_overview *index_overview(struct mailbox *mailbox,
     static struct nntp_overview over;
     static char *env = NULL, *from = NULL, *hdr = NULL;
     static int envsize = 0, fromsize = 0, hdrsize = 0;
-    const char *cacheitem;
+    cacherecord crec;
     int size;
     char *envtokens[NUMENVTOKENS];
     struct address addr = { NULL, NULL, NULL, NULL, NULL, NULL };
     static struct strlist refhdr;
 
-    cacheitem = cache_base + CACHE_OFFSET(msgno); /* envelope */
+    if (!cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec))
+	return NULL; /* upper layers can cope! */
 
     /* make a working copy of envelope; strip outer ()'s */
     /* -2 -> don't include the size of the outer parens */
     /* +1 -> leave space for NUL */
-    size = CACHE_ITEM_LEN(cacheitem) - 2 + 1;
+    size = crec[CACHE_ENVELOPE].l - 2 + 1;
     if (envsize < size) {
 	envsize = size;
 	env = xrealloc(env, envsize);
     }
     /* +1 -> skip the leading paren */
-    strlcpy(env, cacheitem + CACHE_ITEM_SIZE_SKIP + 1, size);
-
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bodystructure */
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip body */
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip section */
-    cacheitem = CACHE_ITEM_NEXT(cacheitem); /* cacheheaders */
+    strlcpy(env, crec[CACHE_ENVELOPE].s + 1, size);
 
     /* make a working copy of headers */
-    size = CACHE_ITEM_LEN(cacheitem);
+    size = crec[CACHE_HEADERS].l;
     if (hdrsize < size+2) {
 	hdrsize = size+100;
 	hdr = xrealloc(hdr, hdrsize);
     }
-    memcpy(hdr, cacheitem + CACHE_ITEM_SIZE_SKIP, size);
+    memcpy(hdr, crec[CACHE_HEADERS].s, size);
     hdr[size] = '\0';
 
     parse_cached_envelope(env, envtokens, VECTOR_SIZE(envtokens));
@@ -5277,7 +5203,7 @@ extern char *index_getheader(struct mailbox *mailbox, unsigned msgno,
     struct strlist headers = { NULL, NULL, NULL, NULL };
     static char *alloc = NULL;
     static unsigned allocsize = 0;
-    const char *cacheitem;
+    cacherecord crec;
     unsigned size;
     char *buf;
 
@@ -5290,21 +5216,16 @@ extern char *index_getheader(struct mailbox *mailbox, unsigned msgno,
     }
 
     /* see if the header is cached */
-    if (mailbox_cached_header(hdr) != BIT32_MAX) {
-	/* cached header */
-	cacheitem = cache_base + CACHE_OFFSET(msgno);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip envelope */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bodystructure */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip body */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip section */
+    if (mailbox_cached_header(hdr) != BIT32_MAX &&
+        cache_parserecord(cache_base, cache_end, CACHE_OFFSET(msgno), &crec)) {
     
-	size = CACHE_ITEM_LEN(cacheitem);
+	size = crec[CACHE_HEADERS].l;
 	if (allocsize < size+2) {
 	    allocsize = size+100;
 	    alloc = xrealloc(alloc, allocsize);
 	}
 
-	memcpy(alloc, cacheitem+CACHE_ITEM_SIZE_SKIP, size);
+	memcpy(alloc, crec[CACHE_HEADERS].s, size);
 	alloc[size] = '\0';
 
 	buf = alloc;
diff --git a/imap/index.h b/imap/index.h
index 14c32ec..f21f568 100644
--- a/imap/index.h
+++ b/imap/index.h
@@ -85,36 +85,9 @@
 #endif
 #define GUID(msgno) message_guid_import(NULL, (unsigned char *)(INDEC_OFFSET(msgno)+OFFSET_MESSAGE_GUID))
 
-/* Access assistance macros for memory-mapped cache file data */
-/* CACHE_ITEM_BIT32: Convert to host byte order */
-/* CACHE_ITEM_LEN: Get the length out */
-/* CACHE_ITEM_NEXT: Return a pointer to the next entry.  Sizes are
- * 4-byte aligned, so round up to the next 4 byte boundry */
-#define CACHE_ITEM_BIT32(ptr) (ntohl(*((bit32 *)(ptr))))
-#define CACHE_ITEM_LEN(ptr) CACHE_ITEM_BIT32(ptr)
-#define CACHE_ITEM_NEXT(ptr) ((ptr)+4+((3+CACHE_ITEM_LEN(ptr))&~3))
-
-/* Size of a bit32 to skip when jumping over cache item sizes */
-#define CACHE_ITEM_SIZE_SKIP sizeof(bit32)
-
 /* Calculate the number of entries in a vector */
 #define VECTOR_SIZE(vector) (sizeof(vector)/sizeof(vector[0]))
 
-/* Cached envelope token positions */
-enum {
-    ENV_DATE = 0,
-    ENV_SUBJECT,
-    ENV_FROM,
-    ENV_SENDER,
-    ENV_REPLYTO,
-    ENV_TO,
-    ENV_CC,
-    ENV_BCC,
-    ENV_INREPLYTO,
-    ENV_MSGID
-};
-#define NUMENVTOKENS (10)
-
 /* Special "sort criteria" to load message-id and references/in-reply-to
  * into msgdata array for threaders that need them.
  */
diff --git a/imap/mailbox.c b/imap/mailbox.c
index 39896a4..fabc9bd 100644
--- a/imap/mailbox.c
+++ b/imap/mailbox.c
@@ -297,12 +297,48 @@ unsigned mailbox_cached_header_inline(const char *text)
     return BIT32_MAX;
 }
 
-unsigned long mailbox_cache_size(struct mailbox *mailbox, unsigned msgno)
+/* returns the length of the parsed record, if it's valid */
+unsigned cache_parserecord(const char *map_base, unsigned map_size, unsigned cache_offset, cacherecord *rec)
 {
+    unsigned cache_ent, offset;
+    const char *cacheitem, *range_start, *range_end;
+
+    if (cache_offset >= map_size)
+	return 0;
+
+    /* Compute size of this record */
+    range_start = cacheitem = map_base + cache_offset;
+    range_end = map_base + map_size;
+
+    for (cache_ent = 0; cache_ent < NUMCACHEITEMS; cache_ent++) {
+	/* copy locations */
+	if (rec) {
+	    (*rec)[cache_ent].l = CACHE_ITEM_LEN(cacheitem);
+	    (*rec)[cache_ent].s = cacheitem + CACHE_ITEM_SIZE_SKIP;
+	}
+
+	/* moving on */
+	cacheitem = CACHE_ITEM_NEXT(cacheitem);
+	if (cacheitem < range_start || cacheitem >= range_end)
+	    return 0; /* clearly bogus */
+    }
+
+    /* all fit within the cache, it's gold as far as we can tell */
+    return (cacheitem - range_start);
+}
+
+unsigned mailbox_cacherecord_offset(struct mailbox *mailbox, unsigned cache_offset, cacherecord *rec)
+{
+    unsigned cache_ent, offset;
+    const char *cacheitem;
+
+    return cache_parserecord(mailbox->cache_base, mailbox->cache_size, cache_offset, rec);
+}
+
+unsigned mailbox_cacherecord_index(struct mailbox *mailbox, unsigned msgno, cacherecord *rec)
+{
+    unsigned cache_offset;
     const char *p;
-    unsigned long cache_offset;
-    unsigned int cache_ent;
-    const char *cacheitem, *cacheitembegin;
 
     assert((msgno > 0) && (msgno <= mailbox->exists));
 
@@ -310,22 +346,8 @@ unsigned long mailbox_cache_size(struct mailbox *mailbox, unsigned msgno)
          ((msgno-1) * mailbox->record_size));
     
     cache_offset = ntohl(*((bit32 *)(p+OFFSET_CACHE_OFFSET)));
-    if (cache_offset > mailbox->cache_size) {
-	return 0;
-    }
 
-    /* Compute size of this record */
-    cacheitembegin = cacheitem = mailbox->cache_base + cache_offset;
-    if (cache_offset >= mailbox->cache_size)
-	return 0;
-    for (cache_ent = 0; cache_ent < NUM_CACHE_FIELDS; cache_ent++) {
-	cacheitem = CACHE_ITEM_NEXT(cacheitem);
-	if (cacheitem < cacheitembegin ||
-	    cacheitem > cacheitembegin + mailbox->cache_size) {
-	    return 0; /* clearly bogus */
-	}
-    }
-    return (cacheitem - cacheitembegin);
+    return mailbox_cacherecord_offset(mailbox, cache_offset, rec);
 }
 
 /* function to be used for notification of mailbox changes/updates */
@@ -2014,22 +2036,14 @@ static int process_records(struct mailbox *mailbox, FILE *newindex,
 		       msgno, exists);
 		return IMAP_IOERROR;
             }
-	    for (cache_ent = 0; cache_ent < NUM_CACHE_FIELDS; cache_ent++) {
-		cacheitem = CACHE_ITEM_NEXT(cacheitem);
-		if ((cacheitem < (mailbox->cache_base + cache_offset)) || 
-		    (cacheitem > (mailbox->cache_base + mailbox->cache_size))) {
-		    syslog(LOG_ERR,
-			   "IOERROR: reading cache record for %s:"
-			   " item %d has bogus offset %d of %d for %u/%lu; mailbox needs a reconstruct",
-			   mailbox->name,
-			   cache_ent+1,
-			   (int) (cacheitem - mailbox->cache_base),
-			   (int) mailbox->cache_size,
-			   msgno, exists);
-		    return IMAP_IOERROR;
-		}
+
+	    cache_record_size = mailbox_cacherecord_offset(mailbox, cache_offset, 0);
+	    if (!cache_record_size) {
+		syslog(LOG_ERR,
+			"IOERROR: reading cache record for %s record %d; mailbox needs a reconstruct",
+			mailbox->name, msgno);
+		return IMAP_IOERROR;
 	    }
-	    cache_record_size = (cacheitem - cacheitembegin);
 	    *new_cache_total_size += cache_record_size;
 
 	    /* fwrite will automatically call write() in a sane way */
diff --git a/imap/mailbox.h b/imap/mailbox.h
index 06b43b5..7316d3a 100644
--- a/imap/mailbox.h
+++ b/imap/mailbox.h
@@ -290,10 +290,62 @@ enum {
     EXPUNGE_CLEANUP =		(1<<1)
 };
 
+/* Access assistance macros for memory-mapped cache file data */
+/* CACHE_ITEM_BIT32: Convert to host byte order */
+/* CACHE_ITEM_LEN: Get the length out */
+/* CACHE_ITEM_NEXT: Return a pointer to the next entry.  Sizes are
+ * 4-byte aligned, so round up to the next 4 byte boundry */
+#define CACHE_ITEM_BIT32(ptr) (ntohl(*((bit32 *)(ptr))))
+#define CACHE_ITEM_LEN(ptr) CACHE_ITEM_BIT32(ptr)
+#define CACHE_ITEM_NEXT(ptr) ((ptr)+4+((3+CACHE_ITEM_LEN(ptr))&~3))
+
+/* Size of a bit32 to skip when jumping over cache item sizes */
+#define CACHE_ITEM_SIZE_SKIP sizeof(bit32)
+
+/* Cache item positions */
+enum {
+    CACHE_ENVELOPE = 0,
+    CACHE_BODYSTRUCTURE,
+    CACHE_BODY,
+    CACHE_SECTION,
+    CACHE_HEADERS,
+    CACHE_FROM,
+    CACHE_TO,
+    CACHE_CC,
+    CACHE_BCC,
+    CACHE_SUBJECT
+};
+#define NUMCACHEITEMS 10
+
+struct cacheitem {
+    unsigned l;
+    const char *s;
+};
+
+/* pointers for a single cache record */
+typedef struct cacheitem cacherecord[NUMCACHEITEMS];
+
+/* Cached envelope token positions */
+enum {
+    ENV_DATE = 0,
+    ENV_SUBJECT,
+    ENV_FROM,
+    ENV_SENDER,
+    ENV_REPLYTO,
+    ENV_TO,
+    ENV_CC,
+    ENV_BCC,
+    ENV_INREPLYTO,
+    ENV_MSGID
+};
+#define NUMENVTOKENS (10)
+
 unsigned mailbox_cached_header(const char *s);
 unsigned mailbox_cached_header_inline(const char *text);
 
-unsigned long mailbox_cache_size(struct mailbox *mailbox, unsigned msgno);
+unsigned cache_parserecord(const char *map_base, unsigned map_size, unsigned cache_offset, cacherecord *rec);
+unsigned mailbox_cacherecord_offset(struct mailbox *mailbox, unsigned cache_offset, cacherecord *rec);
+unsigned mailbox_cacherecord_index(struct mailbox *mailbox, unsigned msgno, cacherecord *rec);
 
 typedef unsigned mailbox_decideproc_t(struct mailbox *mailbox, void *rock,
 				      unsigned char *indexbuf,
diff --git a/imap/make_md5.c b/imap/make_md5.c
index 61ace9a..2487161 100644
--- a/imap/make_md5.c
+++ b/imap/make_md5.c
@@ -611,7 +611,7 @@ md5_single(char *name, int matchlen __attribute__((unused)),
         }
 
         cache_offset = record.cache_offset;
-        cache_size = mailbox_cache_size(&m, msgno);
+        cache_size = mailbox_cacherecord_msgno(&m, msgno, 0);
 
         if (!md5_buffer(m.cache_base+cache_offset, cache_size, md5_cache)) {
             syslog(LOG_ERR, "IOERROR: %s failed to md5 msg cache UID %lu",
diff --git a/imap/make_sha1.c b/imap/make_sha1.c
index 5875d83..38cf81e 100644
--- a/imap/make_sha1.c
+++ b/imap/make_sha1.c
@@ -611,7 +611,7 @@ sha1_single(char *name, int matchlen __attribute__((unused)),
         }
 
         cache_offset = record.cache_offset;
-        cache_size = mailbox_cache_size(&m, msgno);
+        cache_size = mailbox_cacherecord_msgno(&m, msgno, 0);
 
         if (!sha1_buffer(m.cache_base+cache_offset, cache_size, sha1_cache)) {
             syslog(LOG_ERR, "IOERROR: %s failed to sha1 msg cache UID %lu",
diff --git a/imap/mbexamine.c b/imap/mbexamine.c
index 626463a..9e43bcf 100644
--- a/imap/mbexamine.c
+++ b/imap/mbexamine.c
@@ -218,6 +218,7 @@ int do_examine(char *name,
     struct mailbox mailbox;
     const char *index_base;
     long int start_offset, record_size;
+    cacherecord crec;
     
     signals_poll();
 
@@ -324,7 +325,6 @@ int do_examine(char *name,
     record_size = mailbox.record_size;
     
     for(i=1; i<=mailbox.exists; i++) {
-	const char *cacheitem;
 	int j;
 
 	if(wantvalue) {
@@ -363,41 +363,32 @@ int do_examine(char *name,
 	}
 	printf("\n");
 
-	cacheitem = mailbox.cache_base + CACHE_OFFSET(i);
-	
-	printf(" Envel>{%d}%.*s\n", CACHE_ITEM_LEN(cacheitem), CACHE_ITEM_LEN(cacheitem),
-	       cacheitem + CACHE_ITEM_SIZE_SKIP);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem);
-	printf("BdyStr>{%d}%.*s\n", CACHE_ITEM_LEN(cacheitem), CACHE_ITEM_LEN(cacheitem),
-	       cacheitem + CACHE_ITEM_SIZE_SKIP);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem);
-	printf("  Body>{%d}%.*s\n", CACHE_ITEM_LEN(cacheitem), CACHE_ITEM_LEN(cacheitem),
-	       cacheitem + CACHE_ITEM_SIZE_SKIP);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem);
+	if (mailbox_cacherecord_offset(&mailbox, CACHE_OFFSET(i), &crec)) {
+	    printf(" Envel>{%d}%.*s\n", crec[CACHE_ENVELOPE].l, 
+		crec[CACHE_ENVELOPE].l, crec[CACHE_ENVELOPE].s);
+	    printf("BdyStr>{%d}%.*s\n", crec[CACHE_BODYSTRUCTURE].l,
+		crec[CACHE_BODYSTRUCTURE].l, crec[CACHE_BODYSTRUCTURE].s);
+	    printf("  Body>{%d}%.*s\n", crec[CACHE_BODY].l,
+		crec[CACHE_BODY].l, crec[CACHE_BODY].s);
 
 #if 0
-	/* xxx print out machine-readable bodystructure? */
-	printf(" Sects>\n");
+	    /* xxx print out machine-readable bodystructure? */
+	    printf(" Sects>\n");
 #endif
 
-	cacheitem = CACHE_ITEM_NEXT(cacheitem);
-	printf("CacHdr>{%d}%.*s\n", CACHE_ITEM_LEN(cacheitem), CACHE_ITEM_LEN(cacheitem),
-	       cacheitem + CACHE_ITEM_SIZE_SKIP);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem);
-	printf("  From>{%d}%.*s\n", CACHE_ITEM_LEN(cacheitem), CACHE_ITEM_LEN(cacheitem),
-	       cacheitem + CACHE_ITEM_SIZE_SKIP);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem);
-	printf("    To>{%d}%.*s\n", CACHE_ITEM_LEN(cacheitem), CACHE_ITEM_LEN(cacheitem),
-	       cacheitem + CACHE_ITEM_SIZE_SKIP);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem);
-	printf("    Cc>{%d}%.*s\n", CACHE_ITEM_LEN(cacheitem), CACHE_ITEM_LEN(cacheitem),
-	       cacheitem + CACHE_ITEM_SIZE_SKIP);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem);
-	printf("   Bcc>{%d}%.*s\n", CACHE_ITEM_LEN(cacheitem), CACHE_ITEM_LEN(cacheitem),
-	       cacheitem + CACHE_ITEM_SIZE_SKIP);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem);
-	printf("Subjct>{%d}%.*s\n", CACHE_ITEM_LEN(cacheitem), CACHE_ITEM_LEN(cacheitem),
-	       cacheitem + CACHE_ITEM_SIZE_SKIP);
+	    printf("CacHdr>{%d}%.*s\n", crec[CACHE_HEADERS].l,
+		crec[CACHE_HEADERS].l, crec[CACHE_HEADERS].s);
+	    printf("  From>{%d}%.*s\n", crec[CACHE_FROM].l,
+		crec[CACHE_FROM].l, crec[CACHE_FROM].s);
+	    printf("    To>{%d}%.*s\n", crec[CACHE_TO].l,
+		crec[CACHE_TO].l, crec[CACHE_TO].s);
+	    printf("    Cc>{%d}%.*s\n", crec[CACHE_CC].l,
+		crec[CACHE_CC].l, crec[CACHE_CC].s);
+	    printf("   Bcc>{%d}%.*s\n", crec[CACHE_BCC].l,
+		crec[CACHE_BCC].l, crec[CACHE_BCC].s);
+	    printf("Subjct>{%d}%.*s\n", crec[CACHE_SUBJECT].l,
+		crec[CACHE_SUBJECT].l, crec[CACHE_SUBJECT].s);
+	}
 
 	if(flag) break;
     }
diff --git a/imap/sync_server.c b/imap/sync_server.c
index f41e2d5..e9f751d 100644
--- a/imap/sync_server.c
+++ b/imap/sync_server.c
@@ -1506,7 +1506,7 @@ static void cmd_reserve(char *mailbox_name,
             continue;
         }
 
-        cache_size = mailbox_cache_size(&m, msgno);
+        cache_size = mailbox_cacherecord_msgno(&m, msgno, 0);
         if (!cache_size) {
             syslog(LOG_ERR, "IOERROR: bogus cache record %s %d",
                 m.name, msgno);
diff --git a/imap/unexpunge.c b/imap/unexpunge.c
index 37f62cc..213fc42 100644
--- a/imap/unexpunge.c
+++ b/imap/unexpunge.c
@@ -126,7 +126,7 @@ void list_expunged(struct mailbox *mailbox,
     unsigned msgno;
     unsigned long uid, size, cache_offset;
     time_t internaldate, sentdate, last_updated;
-    const char *cacheitem;
+    cacherecord crec;
 
     for (msgno = 0; msgno < exists; msgno++) {
 	/* Jump to index record for this message */
@@ -146,22 +146,16 @@ void list_expunged(struct mailbox *mailbox,
 	printf("\tRecv: %s", ctime(&internaldate));
 	printf("\tExpg: %s", ctime(&last_updated));
 
-	cacheitem = mailbox->cache_base + cache_offset;
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip envelope */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip body structure */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip body */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip binary body */
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip cached headers */
-
-	printf("\tFrom: %s\n", cacheitem + CACHE_ITEM_SIZE_SKIP);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip from */
-	printf("\tTo  : %s\n", cacheitem + CACHE_ITEM_SIZE_SKIP);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip to */
-	printf("\tCc  : %s\n", cacheitem + CACHE_ITEM_SIZE_SKIP);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip cc */
-	printf("\tBcc : %s\n", cacheitem + CACHE_ITEM_SIZE_SKIP);
-	cacheitem = CACHE_ITEM_NEXT(cacheitem); /* skip bcc */
-	printf("\tSubj: %s\n\n", cacheitem + CACHE_ITEM_SIZE_SKIP);
+	if (!mailbox_cacherecord_offset(mailbox, cache_offset, &crec)) {
+	    printf("\tERROR: cache record missing or corrupt, not printing cache details\n\n");
+	    continue;
+	}
+
+	printf("\tFrom: %.*s\n", crec[CACHE_FROM].l, crec[CACHE_FROM].s);
+	printf("\tTo  : %.*s\n", crec[CACHE_TO].l, crec[CACHE_TO].s);
+	printf("\tCc  : %.*s\n", crec[CACHE_CC].l, crec[CACHE_CC].s);
+	printf("\tBcc : %.*s\n", crec[CACHE_BCC].l, crec[CACHE_BCC].s);
+	printf("\tSubj: %.*s\n\n", crec[CACHE_SUBJECT].l, crec[CACHE_SUBJECT].s);
     }
 }
 

Reply via email to