Re: Another cache bug!

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On Tue, 31 Mar 2009 15:51 -0700, "David R Bosso" <dbosso+lists.cyrus@xxxxxxxxxxxxx> wrote:
> --On December 10, 2008 10:36:56 AM +1100 Bron Gondwana
> <brong@xxxxxxxxxxx> 
> wrote:
> 
> > Wow, this is the thanks I get for doing sanity checks on files, find more
> > bugs!
> >
> > This one is due to delayed expunge, plain and simple.  Cyrus decides what
> > cache records to copy during an IMAP COPY command by reading the cache
> > offsets for msgno and msgno+1 (or the end of the cache file if it's the
> > last msgno).
> >
> > Obviously if some intervening messages have already been deleted from the
> > cyrus.index then it will be copying all those cache records as well to the
> > destination folder.  Oops.
> >
> > The attached patch reworks mailbox_cache_size into two functions, the
> > second being mailbox_cache_size_detail that takes memory locations for
> > the cache mmap rather than a mailbox object (because imapd doesn't update
> > the locations in the object correctly according to my testing, *sigh*.
> > Gotta love global variables)
> >
> > It then uses mailbox_cache_size_detail to calculate the ACTUAL record
> > length for this single cache item rather than blindly copying everything
> > up to the next index record's pointer.
> >
> > Also note: in the event of cache corruption, mailbox_cache_size_detail
> > returns zero bytes, which correctly makes append_copy re-parse the
> > message file.  It's all shiny :)
> >
> > Wes/Ken - please apply to CVS :)
> >
> > Thanks,
> >
> > Bron.
> > --
> >   Bron Gondwana
> >   brong@xxxxxxxxxxx
> 
> Going through my list of 2.3.13 patches to put together a local 2.3.14 
> build, I ran across this one.  It doesn't appear to have made it into 
> 2.3.14.  Oversight, or is there something wrong with it?

It's being reworked!  In particular, I'm working on delayed cache file loading.
There's also stuff about cache version number and locking all sort of floating
around...

For the immediate fix, I would recommend the attached patch.  It applies
directly on top of CVS.  Not so sure if it depends on anything that's not in
2.3.14... I don't _think_ so.

NOTE - this patch actually touches quite a lot of code.  This is the only bit
that's actually in production at FM at the moment - the delayed loading is
still being tested.

Still - this puts ALL cache accesses through a single, simple API :)

-- 
  Bron Gondwana
  brong@xxxxxxxxxxx

diff --git a/imap/index.c b/imap/index.c
index 4efdaa2..41316ee 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);
@@ -1909,7 +1909,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;
@@ -1918,7 +1918,6 @@ static int index_fetchsection(const char *resp,
     unsigned offset = 0;
     char *decbuf = NULL;
 
-    cacheitem += CACHE_ITEM_SIZE_SKIP;
     p = section;
 
     /* Special-case BODY[] */
@@ -1943,7 +1942,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) {
@@ -1970,34 +1969,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);
@@ -2044,7 +2043,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;
@@ -2061,7 +2060,6 @@ static void index_fetchfsection(const char *msg_base,
 	return;
     }
 
-    cacheitem += CACHE_ITEM_SIZE_SKIP;
     p = fsection->section;
 
     while (*p != 'H') {
@@ -2073,31 +2071,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);
@@ -2286,24 +2284,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);
@@ -2473,7 +2471,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];
@@ -2543,28 +2541,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) {
@@ -2627,17 +2622,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_SECTION].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,
@@ -2656,20 +2650,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;
@@ -2680,20 +2671,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;
@@ -2704,15 +2691,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 */
@@ -2741,7 +2725,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;
@@ -2753,14 +2738,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) {
@@ -2779,7 +2762,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;
 	    }
@@ -2809,33 +2792,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 */
@@ -3099,8 +3082,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;
 
@@ -3145,8 +3127,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;
@@ -3159,8 +3141,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]) {
@@ -3184,63 +3165,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;
 	}
     }
@@ -3271,18 +3228,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) {
@@ -3305,7 +3260,7 @@ index_searchmsg(char *substr,
 		struct mapfile *msgfile,
 		int format,
 		int skipheader,
-		const char *cacheitem)
+		const char *cachestr)
 {
     int partsleft = 1;
     int subparts;
@@ -3316,10 +3271,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;
 
@@ -3327,10 +3281,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))) {
@@ -3340,13 +3294,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) {
@@ -3355,7 +3309,7 @@ index_searchmsg(char *substr,
 					   format == MAILBOX_FORMAT_NETNEWS,
 					   len, charset, encoding)) return 1;
 		}
-		cacheitem += 5*4;
+		cachestr += 5*4;
 	    }
 	}
     }
@@ -3400,19 +3354,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;
@@ -3420,7 +3370,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;
@@ -3428,6 +3378,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);
@@ -3442,7 +3393,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;
@@ -3458,19 +3409,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) {
@@ -3484,13 +3434,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) {
@@ -3499,7 +3449,7 @@ static void index_getsearchtextmsg(struct mailbox* mailbox,
                                       format == MAILBOX_FORMAT_NETNEWS,
                                       len, charset, encoding);
 		}
-		cacheitem += 5*4;
+		cachestr += 5*4;
 	    }
 	}
     }
@@ -3514,38 +3464,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_SECTION].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,
@@ -3647,8 +3587,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];
@@ -3683,17 +3622,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++;
 	    }
 
@@ -3703,8 +3634,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,
@@ -3715,7 +3646,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],
@@ -3723,16 +3654,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 */
@@ -3747,8 +3678,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;
 	    }
 	}
@@ -5119,14 +5050,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
      *
@@ -5134,7 +5064,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;
@@ -5213,37 +5143,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));
@@ -5300,7 +5226,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;
 
@@ -5313,21 +5239,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 b182f81..a52ef96 100644
--- a/imap/mailbox.c
+++ b/imap/mailbox.c
@@ -297,12 +297,52 @@ 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) {
+	syslog(LOG_ERR, "IOERROR: cache offset greater than mapped 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) {
+	    syslog(LOG_ERR, "IOERROR: cache offset greater than mapped size");
+	    return 0;
+	}
+    }
+
+    /* 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 +350,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 +2040,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..201965d 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_index(&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..f3e5752 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_index(&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 a904fd9..897c534 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_index(&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);
     }
 }
 
----
Cyrus Home Page: http://cyrusimap.web.cmu.edu/
Cyrus Wiki/FAQ: http://cyrusimap.web.cmu.edu/twiki
List Archives/Info: http://asg.web.cmu.edu/cyrus/mailing-list.html

[Index of Archives]     [Cyrus SASL]     [Squirrel Mail]     [Asterisk PBX]     [Video For Linux]     [Photo]     [Yosemite News]     [gtk]     [KDE]     [Gimp on Windows]     [Steve's Art]

  Powered by Linux