dovecot: Created a new extensible version 3 dovecot-uidlist file.

dovecot at dovecot.org dovecot at dovecot.org
Thu Jul 12 03:21:47 EEST 2007


details:   http://hg.dovecot.org/dovecot/rev/760f910004d9
changeset: 5944:760f910004d9
user:      Timo Sirainen <tss at iki.fi>
date:      Tue Jul 10 22:30:51 2007 +0300
description:
Created a new extensible version 3 dovecot-uidlist file.

diffstat:

2 files changed, 225 insertions(+), 50 deletions(-)
src/lib-storage/index/maildir/maildir-uidlist.c |  256 ++++++++++++++++++-----
src/lib-storage/index/maildir/maildir-uidlist.h |   19 +

diffs (truncated from 445 to 300 lines):

diff -r 6c3c54dc67fc -r 760f910004d9 src/lib-storage/index/maildir/maildir-uidlist.c
--- a/src/lib-storage/index/maildir/maildir-uidlist.c	Wed Jul 11 01:41:49 2007 +0300
+++ b/src/lib-storage/index/maildir/maildir-uidlist.c	Tue Jul 10 22:30:51 2007 +0300
@@ -1,7 +1,30 @@
 /* Copyright (C) 2003 Timo Sirainen */
 
+/*
+   Version 1 format has been used for most versions of Dovecot up to v1.0.x.
+   It's also compatible with Courier IMAP's courierimapuiddb file.
+   The format is:
+
+   header: 1 <uid validity> <next uid>
+   entry: <uid> <filename>
+
+   --
+
+   Version 2 format was written by a few development Dovecot versions, but
+   v1.0.x still parses the format. The format has <flags> field after <uid>.
+
+   --
+
+   Version 3 format is an extensible format used by Dovecot v1.1 and later.
+   It's also parsed by v1.0.2 (and later). The format is:
+
+   header: 3 [<key><value> ...]
+   entry: <uid> [<key><value> ...] :<filename>
+
+   See enum maildir_uidlist_*_ext_key for used keys.
+*/
+
 #include "lib.h"
-#include "ioloop.h"
 #include "array.h"
 #include "hash.h"
 #include "istream.h"
@@ -10,7 +33,6 @@
 #include "file-dotlock.h"
 #include "close-keep-errno.h"
 #include "nfs-workarounds.h"
-#include "write-full.h"
 #include "maildir-storage.h"
 #include "maildir-sync.h"
 #include "maildir-filename.h"
@@ -36,6 +58,7 @@ struct maildir_uidlist_rec {
 	uint32_t uid;
 	uint32_t flags;
 	char *filename;
+	char *extensions; /* <data>\0[<data>\0 ...]\0 */
 };
 ARRAY_DEFINE_TYPE(maildir_uidlist_rec_p, struct maildir_uidlist_rec *);
 
@@ -63,6 +86,7 @@ struct maildir_uidlist {
 	unsigned int read_records_count;
 	uint32_t first_recent_uid;
 	uoff_t last_read_offset;
+	string_t *hdr_extensions;
 
 	unsigned int recreate:1;
 	unsigned int initial_read:1;
@@ -93,6 +117,9 @@ struct maildir_uidlist_iter_ctx {
 	unsigned int change_counter;
 	uint32_t prev_uid;
 };
+
+static bool maildir_uidlist_iter_next_rec(struct maildir_uidlist_iter_ctx *ctx,
+					  struct maildir_uidlist_rec **rec_r);
 
 static int maildir_uidlist_lock_timeout(struct maildir_uidlist *uidlist,
 					bool nonblock)
@@ -180,6 +207,7 @@ struct maildir_uidlist *maildir_uidlist_
 				     maildir_filename_base_hash,
 				     maildir_filename_base_cmp);
 	uidlist->next_uid = 1;
+	uidlist->hdr_extensions = str_new(default_pool, 128);
 
 	uidlist->dotlock_settings.use_io_notify = TRUE;
 	uidlist->dotlock_settings.use_excl_lock =
@@ -214,6 +242,7 @@ void maildir_uidlist_deinit(struct maild
 		pool_unref(uidlist->record_pool);
 
 	array_free(&uidlist->records);
+	str_free(&uidlist->hdr_extensions);
 	i_free(uidlist->path);
 	i_free(uidlist);
 }
@@ -226,13 +255,50 @@ maildir_uidlist_mark_recent(struct maild
 		uidlist->first_recent_uid = uid;
 }
 
+static bool
+maildir_uidlist_read_extended(struct maildir_uidlist *uidlist,
+			      const char **line_p,
+			      struct maildir_uidlist_rec *rec)
+{
+	const char *start, *line = *line_p;
+	buffer_t *buf;
+
+	t_push();
+	buf = buffer_create_dynamic(pool_datastack_create(), 128);
+	while (*line != '\0' && *line != ':') {
+		/* skip over an extension field */
+		start = line;
+		while (*line != ' ' && *line != '\0') line++;
+		buffer_append(buf, start, line - start);
+		buffer_append_c(buf, '\0');
+		while (*line == ' ') line++;
+	}
+
+	if (buf->used > 0) {
+		/* save the extensions */
+		buffer_append_c(buf, '\0');
+		rec->extensions = p_malloc(uidlist->record_pool, buf->used);
+		memcpy(rec->extensions, buf->data, buf->used);
+	}
+	t_pop();
+
+	if (*line == ':')
+		line++;
+	if (*line == '\0')
+		return FALSE;
+
+	*line_p = line;
+	return TRUE;
+}
+
 static int maildir_uidlist_next(struct maildir_uidlist *uidlist,
 				const char *line)
 {
-        struct maildir_uidlist_rec *rec;
-	uint32_t uid, flags;
-
-	uid = flags = 0;
+	struct mail_storage *storage = &uidlist->mbox->storage->storage;
+	struct maildir_uidlist_rec *rec;
+	uint32_t uid;
+
+	uid = 0;
 	while (*line >= '0' && *line <= '9') {
 		uid = uid*10 + (*line - '0');
 		line++;
@@ -240,12 +306,12 @@ static int maildir_uidlist_next(struct m
 
 	if (uid == 0 || *line != ' ') {
 		/* invalid file */
-                mail_storage_set_critical(&uidlist->mbox->storage->storage,
+                mail_storage_set_critical(storage,
 			"Invalid data in file %s", uidlist->path);
 		return 0;
 	}
 	if (uid <= uidlist->prev_read_uid) {
-                mail_storage_set_critical(&uidlist->mbox->storage->storage,
+                mail_storage_set_critical(storage,
 			"UIDs not ordered in file %s (%u > %u)",
 			uidlist->path, uid, uidlist->prev_read_uid);
 		return 0;
@@ -258,12 +324,19 @@ static int maildir_uidlist_next(struct m
 	}
         uidlist->last_seen_uid = uid;
 
+	rec = p_new(uidlist->record_pool, struct maildir_uidlist_rec, 1);
+	rec->uid = uid;
+	rec->flags = MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
+
 	while (*line == ' ') line++;
 
-	if (uidlist->version == 2) {
-		/* skip flags parameter */
-		while (*line != ' ') line++;
-		while (*line == ' ') line++;
+	if (uidlist->version == 3) {
+		/* read extended fields */
+		if (!maildir_uidlist_read_extended(uidlist, &line, rec)) {
+			mail_storage_set_critical(storage,
+				"Invalid data in file %s", uidlist->path);
+			return 0;
+		}
 	}
 
 	if (hash_lookup_full(uidlist->files, line, NULL, NULL)) {
@@ -273,9 +346,6 @@ static int maildir_uidlist_next(struct m
 		return 0;
 	}
 
-	rec = p_new(uidlist->record_pool, struct maildir_uidlist_rec, 1);
-	rec->uid = uid;
-	rec->flags = MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
 	rec->filename = p_strdup(uidlist->record_pool, line);
 	hash_insert(uidlist->files, rec->filename, rec);
 	array_append(&uidlist->records, &rec, 1);
@@ -285,8 +355,11 @@ static int maildir_uidlist_read_header(s
 static int maildir_uidlist_read_header(struct maildir_uidlist *uidlist,
 				       struct istream *input)
 {
+	struct mail_storage *storage = &uidlist->mbox->storage->storage;
 	unsigned int uid_validity, next_uid;
-	const char *line;
+	string_t *ext_hdr;
+	const char *line, *value;
+	char key;
 
 	line = i_stream_read_next_line(input);
         if (line == NULL) {
@@ -294,17 +367,61 @@ static int maildir_uidlist_read_header(s
                 return input->stream_errno == 0 ? 0 : -1;
 	}
 
-	if (sscanf(line, "%u %u %u", &uidlist->version,
-		   &uid_validity, &next_uid) != 3 ||
-	    uidlist->version < 1 || uidlist->version > 2) {
-		/* broken file */
-		mail_storage_set_critical(&uidlist->mbox->storage->storage,
-			"Corrupted header in file %s (version = %u)",
-			uidlist->path, uidlist->version);
+	if (*line < '0' || *line > '9' || line[1] != ' ') {
+		mail_storage_set_critical(storage,
+			"%s: Corrupted header (invalid version number)",
+			uidlist->path);
 		return 0;
 	}
+
+	uidlist->version = *line - '0';
+	line += 2;
+
+	switch (uidlist->version) {
+	case 1:
+		if (sscanf(line, "%u %u", &uid_validity, &next_uid) != 2) {
+			mail_storage_set_critical(storage,
+				"%s: Corrupted header (version 1)",
+				uidlist->path);
+			return 0;
+		}
+		break;
+	case 3:
+		ext_hdr = uidlist->hdr_extensions;
+		str_truncate(ext_hdr, 0);
+		while (*line != '\0') {
+			t_push();
+			key = *line;
+			value = ++line;
+			while (*line != '\0' && *line != ' ') line++;
+			value = t_strdup_until(value, line);
+
+			switch (key) {
+			case MAILDIR_UIDLIST_HDR_EXT_UID_VALIDITY:
+				uid_validity = strtoul(value, NULL, 10);
+				break;
+			case MAILDIR_UIDLIST_HDR_EXT_NEXT_UID:
+				next_uid = strtoul(value, NULL, 10);
+				break;
+			default:
+				if (str_len(ext_hdr) > 0)
+					str_append_c(ext_hdr, ' ');
+				str_printfa(ext_hdr, "%c%s", key, value);
+				break;
+			}
+
+			while (*line == ' ') line++;
+			t_pop();
+		}
+		break;
+	default:
+		mail_storage_set_critical(storage, "%s: Unsupported version %u",
+					  uidlist->path, uidlist->version);
+		return 0;
+	}
+
 	if (uid_validity == 0 || next_uid == 0) {
-		mail_storage_set_critical(&uidlist->mbox->storage->storage,
+		mail_storage_set_critical(storage,
 			"%s: Broken header (uidvalidity = %u, next_uid=%u)",
 			uidlist->path, uid_validity, next_uid);
 		return 0;
@@ -554,6 +671,29 @@ maildir_uidlist_lookup(struct maildir_ui
 	return rec->filename;
 }
 
+const char *
+maildir_uidlist_lookup_ext(struct maildir_uidlist *uidlist, uint32_t uid,
+			   enum maildir_uidlist_rec_ext_key key)
+{
+	const struct maildir_uidlist_rec *rec;
+	unsigned int idx;
+	const char *p, *value;
+
+	rec = maildir_uidlist_lookup_rec(uidlist, uid, &idx);
+	if (rec == NULL || rec->extensions == NULL)
+		return NULL;
+
+	p = rec->extensions; value = NULL;
+	while (*p != '\0') {
+		/* <key><value>\0 */
+		if (*p == (char)key)
+			return p + 1;
+
+		p += strlen(p) + 1;
+	}
+	return NULL;
+}
+
 bool maildir_uidlist_is_recent(struct maildir_uidlist *uidlist, uint32_t uid)


More information about the dovecot-cvs mailing list