changeset 10007:c862648185b8 HEAD

util/*view binaries are now accessed via "doveadm dump". listview binary stays for now, since mailbox list indexes won't work anyway and they might get a complete redesign.
author Timo Sirainen <tss@iki.fi>
date Thu, 08 Oct 2009 20:43:25 -0400
parents a425ba46a64c
children a15182f41f30
files .hgignore src/doveadm/Makefile.am src/doveadm/doveadm-dump-index.c src/doveadm/doveadm-dump-log.c src/doveadm/doveadm-dump-mailboxlog.c src/doveadm/doveadm-dump-thread.c src/doveadm/doveadm-dump.c src/doveadm/doveadm-dump.h src/doveadm/doveadm.c src/doveadm/doveadm.h src/util/Makefile.am src/util/idxview.c src/util/logview.c src/util/mailboxlogview.c src/util/threadview.c
diffstat 15 files changed, 1399 insertions(+), 1300 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Thu Oct 08 20:10:49 2009 -0400
+++ b/.hgignore	Thu Oct 08 20:43:25 2009 -0400
@@ -79,14 +79,10 @@
 src/pop3-login/pop3-login
 src/pop3/pop3
 src/util/gdbhelper
-src/util/idxview
 src/util/imap-utf7
 src/util/listview
-src/util/logview
-src/util/mailboxlogview
 src/util/maildirlock
 src/util/rawlog
-src/util/threadview
 src/plugins/quota/rquota_xdr.c
 src/plugins/quota/rquota.h
 
--- a/src/doveadm/Makefile.am	Thu Oct 08 20:10:49 2009 -0400
+++ b/src/doveadm/Makefile.am	Thu Oct 08 20:43:25 2009 -0400
@@ -30,8 +30,14 @@
 doveadm_SOURCES = \
 	doveadm.c \
 	doveadm-auth.c \
+	doveadm-dump.c \
+	doveadm-dump-index.c \
+	doveadm-dump-log.c \
+	doveadm-dump-mailboxlog.c \
+	doveadm-dump-thread.c \
 	doveadm-mail.c \
 	doveadm-pw.c
 
 noinst_HEADERS = \
+	doveadm-dump.h \
 	doveadm-mail.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/doveadm/doveadm-dump-index.c	Thu Oct 08 20:43:25 2009 -0400
@@ -0,0 +1,531 @@
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "hex-binary.h"
+#include "file-lock.h"
+#include "mail-index-private.h"
+#include "mail-cache-private.h"
+#include "mail-cache-private.h"
+#include "mail-index-modseq.h"
+#include "doveadm-dump.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+struct maildir_index_header {
+	uint32_t new_check_time, new_mtime, new_mtime_nsecs;
+	uint32_t cur_check_time, cur_mtime, cur_mtime_nsecs;
+	uint32_t uidlist_mtime, uidlist_mtime_nsecs, uidlist_size;
+};
+struct mbox_index_header {
+	uint64_t sync_size;
+	uint32_t sync_mtime;
+	uint8_t dirty_flag;
+	uint8_t unused[3];
+	uint8_t mailbox_guid[16];
+};
+struct dbox_index_header {
+	uint32_t map_uid_validity;
+	uint32_t highest_maildir_uid;
+	uint8_t mailbox_guid[16];
+};
+struct dbox_mail_index_record {
+	uint32_t map_uid;
+	uint32_t save_date;
+};
+
+struct virtual_mail_index_record {
+	uint32_t mailbox_id;
+	uint32_t real_uid;
+};
+
+struct dbox_mail_index_map_record {
+	uint32_t file_id;
+	uint32_t offset;
+	uint32_t size;
+};
+
+static const char *unixdate2str(time_t timestamp)
+{
+	static char buf[64];
+	struct tm *tm;
+
+	tm = localtime(&timestamp);
+	strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
+	return buf;
+}
+
+static void dump_hdr(struct mail_index *index)
+{
+	const struct mail_index_header *hdr = &index->map->hdr;
+	unsigned int i;
+
+	printf("version .................. = %u.%u\n", hdr->major_version, hdr->minor_version);
+	printf("base header size ......... = %u\n", hdr->base_header_size);
+	printf("header size .............. = %u\n", hdr->header_size);
+	printf("record size .............. = %u\n", hdr->record_size);
+	printf("compat flags ............. = %u\n", hdr->compat_flags);
+	printf("index id ................. = %u (%s)\n", hdr->indexid, unixdate2str(hdr->indexid));
+	printf("flags .................... = %u\n", hdr->flags);
+	printf("uid validity ............. = %u (%s)\n", hdr->uid_validity, unixdate2str(hdr->uid_validity));
+	printf("next uid ................. = %u\n", hdr->next_uid);
+	printf("messages count ........... = %u\n", hdr->messages_count);
+	printf("seen messages count ...... = %u\n", hdr->seen_messages_count);
+	printf("deleted messages count ... = %u\n", hdr->deleted_messages_count);
+	printf("first recent uid ......... = %u\n", hdr->first_recent_uid);
+	printf("first unseen uid lowwater  = %u\n", hdr->first_unseen_uid_lowwater);
+	printf("first deleted uid lowwater = %u\n", hdr->first_deleted_uid_lowwater);
+	printf("log file seq ............. = %u\n", hdr->log_file_seq);
+	if (hdr->minor_version == 0) {
+		printf("log file int offset ...... = %u\n", hdr->log_file_tail_offset);
+		printf("log file ext offset ...... = %u\n", hdr->log_file_head_offset);
+	} else {
+		printf("log file tail offset ..... = %u\n", hdr->log_file_tail_offset);
+		printf("log file head offset ..... = %u\n", hdr->log_file_head_offset);
+	}
+	printf("sync size ................ = %llu\n", (unsigned long long)hdr->sync_size);
+	printf("sync stamp ............... = %u (%s)\n", hdr->sync_stamp, unixdate2str(hdr->sync_stamp));
+	printf("day stamp ................ = %u (%s)\n", hdr->day_stamp, unixdate2str(hdr->day_stamp));
+	for (i = 0; i < N_ELEMENTS(hdr->day_first_uid); i++)
+		printf("day first uid[%u] ......... = %u\n", i, hdr->day_first_uid[i]);
+}
+
+static void dump_extension_header(struct mail_index *index,
+				  const struct mail_index_ext *ext)
+{
+	const void *data;
+
+	if (strcmp(ext->name, MAIL_INDEX_EXT_KEYWORDS) == 0)
+		return;
+
+	data = CONST_PTR_OFFSET(index->map->hdr_base, ext->hdr_offset);
+	if (strcmp(ext->name, "maildir") == 0) {
+		const struct maildir_index_header *hdr = data;
+
+		printf("header\n");
+		printf(" - new_check_time .... = %s\n", unixdate2str(hdr->new_check_time));
+		printf(" - new_mtime ......... = %s\n", unixdate2str(hdr->new_mtime));
+		printf(" - new_mtime_nsecs ... = %u\n", hdr->new_mtime_nsecs);
+		printf(" - cur_check_time .... = %s\n", unixdate2str(hdr->cur_check_time));
+		printf(" - cur_mtime ......... = %s\n", unixdate2str(hdr->cur_mtime));
+		printf(" - cur_mtime_nsecs.... = %u\n", hdr->cur_mtime_nsecs);
+		printf(" - uidlist_mtime ..... = %s\n", unixdate2str(hdr->uidlist_mtime));
+		printf(" - uidlist_mtime_nsecs = %u\n", hdr->uidlist_mtime_nsecs);
+		printf(" - uidlist_size ...... = %u\n", hdr->uidlist_size);
+	} else if (strcmp(ext->name, "mbox") == 0) {
+		const struct mbox_index_header *hdr = data;
+
+		printf("header\n");
+		printf(" - sync_mtime . = %s\n", unixdate2str(hdr->sync_mtime));
+		printf(" - sync_size .. = %llu\n",
+		       (unsigned long long)hdr->sync_size);
+		printf(" - dirty_flag . = %d\n", hdr->dirty_flag);
+		printf(" - mailbox_guid = %s\n",
+		       binary_to_hex(hdr->mailbox_guid,
+				     sizeof(hdr->mailbox_guid)));
+	} else if (strcmp(ext->name, "dbox-hdr") == 0) {
+		const struct dbox_index_header *hdr = data;
+
+		printf("header\n");
+		printf(" - map_uid_validity .. = %u\n", hdr->map_uid_validity);
+		printf(" - highest_maildir_uid = %u\n", hdr->highest_maildir_uid);
+		printf(" - mailbox_guid ...... = %s\n",
+		       binary_to_hex(hdr->mailbox_guid,
+				     sizeof(hdr->mailbox_guid)));
+	} else if (strcmp(ext->name, "modseq") == 0) {
+		const struct mail_index_modseq_header *hdr = data;
+
+		printf("header\n");
+		printf(" - highest_modseq = %llu\n",
+		       (unsigned long long)hdr->highest_modseq);
+		printf(" - log_seq ...... = %u\n", hdr->log_seq);
+		printf(" - log_offset ... = %u\n", hdr->log_offset);
+	} else {
+		printf("header ........ = %s\n",
+		       binary_to_hex(data, ext->hdr_size));
+	}
+}
+
+static void dump_extensions(struct mail_index *index)
+{
+	const struct mail_index_ext *extensions;
+	unsigned int i, count;
+
+	if (array_is_created(&index->map->extensions))
+		extensions = array_get(&index->map->extensions, &count);
+	else
+		count = 0;
+	if (count == 0) {
+		printf("no extensions\n");
+		return;
+	}
+
+	for (i = 0; i < count; i++) {
+		const struct mail_index_ext *ext = &extensions[i];
+
+		printf("-- Extension %u --\n", i);
+		printf("name ........ = %s\n", ext->name);
+		printf("hdr_size .... = %u\n", ext->hdr_size);
+		printf("reset_id .... = %u\n", ext->reset_id);
+		printf("record_offset = %u\n", ext->record_offset);
+		printf("record_size . = %u\n", ext->record_size);
+		printf("record_align  = %u\n", ext->record_align);
+		if (ext->hdr_size > 0)
+			dump_extension_header(index, ext);
+	}
+}
+
+static void dump_keywords(struct mail_index *index)
+{
+	const unsigned int *kw_indexes;
+	const char *const *keywords;
+	unsigned int i, count;
+
+	printf("-- Keywords --\n");
+	if (!array_is_created(&index->map->keyword_idx_map))
+		return;
+
+	kw_indexes = array_get(&index->map->keyword_idx_map, &count);
+	if (count == 0)
+		return;
+
+	keywords = array_idx(&index->keywords, 0);
+	for (i = 0; i < count; i++)
+		printf("%3u = %s\n", i, keywords[kw_indexes[i]]);
+}
+
+static const char *cache_decision2str(enum mail_cache_decision_type type)
+{
+	const char *str;
+
+	switch (type & ~MAIL_CACHE_DECISION_FORCED) {
+	case MAIL_CACHE_DECISION_NO:
+		str = "no";
+		break;
+	case MAIL_CACHE_DECISION_TEMP:
+		str = "tmp";
+		break;
+	case MAIL_CACHE_DECISION_YES:
+		str = "yes";
+		break;
+	default:
+		return t_strdup_printf("0x%x", type);
+	}
+
+	if ((type & MAIL_CACHE_DECISION_FORCED) != 0)
+		str = t_strconcat(str, "!", NULL);
+	return str;
+}
+
+#define CACHE_TYPE_IS_FIXED_SIZE(type) \
+	((type) == MAIL_CACHE_FIELD_FIXED_SIZE || \
+	 (type) == MAIL_CACHE_FIELD_BITMASK)
+static const char *cache_type2str(enum mail_cache_field_type type)
+{
+	switch (type) {
+	case MAIL_CACHE_FIELD_FIXED_SIZE:
+		return "fix";
+	case MAIL_CACHE_FIELD_VARIABLE_SIZE:
+		return "var";
+	case MAIL_CACHE_FIELD_STRING:
+		return "str";
+	case MAIL_CACHE_FIELD_BITMASK:
+		return "bit";
+	case MAIL_CACHE_FIELD_HEADER:
+		return "hdr";
+	default:
+		return t_strdup_printf("0x%x", type);
+	}
+}
+
+static void dump_cache_hdr(struct mail_cache *cache)
+{
+	const struct mail_cache_header *hdr;
+	const struct mail_cache_field *fields, *field;
+	unsigned int i, count, cache_idx;
+
+	(void)mail_cache_open_and_verify(cache);
+	if (MAIL_CACHE_IS_UNUSABLE(cache)) {
+		printf("cache is unusable\n");
+		return;
+	}
+
+	hdr = cache->hdr;
+	printf("version .............. = %u\n", hdr->version);
+	printf("indexid .............. = %u (%s)\n", hdr->indexid, unixdate2str(hdr->indexid));
+	printf("file_seq ............. = %u (%s) (%d compressions)\n",
+	       hdr->file_seq, unixdate2str(hdr->file_seq),
+	       hdr->file_seq - hdr->indexid);
+	printf("continued_record_count = %u\n", hdr->continued_record_count);
+	printf("hole_offset .......... = %u\n", hdr->hole_offset);
+	printf("used_file_size ....... = %u\n", hdr->used_file_size);
+	printf("deleted_space ........ = %u\n", hdr->deleted_space);
+	printf("field_header_offset .. = %u (0x%08x nontranslated)\n",
+	       mail_index_offset_to_uint32(hdr->field_header_offset),
+	       hdr->field_header_offset);
+
+	printf("-- Cache fields --\n");
+	fields = mail_cache_register_get_list(cache, pool_datastack_create(),
+					      &count);
+	printf(
+" #  Name                                         Type Size Dec  Last used\n");
+	for (i = 0; i < cache->file_fields_count; i++) {
+		cache_idx = cache->file_field_map[i];
+		field = &fields[cache_idx];
+
+		printf("%2u: %-44s %-4s ", i, field->name,
+		       cache_type2str(field->type));
+		if (field->field_size != (uint32_t)-1 ||
+		    CACHE_TYPE_IS_FIXED_SIZE(field->type))
+			printf("%4u ", field->field_size);
+		else
+			printf("   - ");
+		printf("%-4s %.16s\n",
+		       cache_decision2str(field->decision),
+		       unixdate2str(cache->fields[cache_idx].last_used));
+	}
+}
+
+static void dump_cache(struct mail_cache_view *cache_view, unsigned int seq)
+{
+	struct mail_cache_lookup_iterate_ctx iter;
+	const struct mail_cache_record *prev_rec = NULL;
+	const struct mail_cache_field *field;
+	struct mail_cache_iterate_field iter_field;
+	const void *data;
+	unsigned int size;
+	string_t *str;
+	int ret;
+
+	str = t_str_new(512);
+	mail_cache_lookup_iter_init(cache_view, seq, &iter);
+	while ((ret = mail_cache_lookup_iter_next(&iter, &iter_field)) > 0) {
+		if (iter.rec != prev_rec) {
+			printf(" - cache offset=%u size=%u, prev_offset = %u\n",
+			       iter.offset, iter.rec->size,
+			       iter.rec->prev_offset);
+			prev_rec = iter.rec;
+		}
+
+		field = &cache_view->cache->fields[iter_field.field_idx].field;
+		data = iter_field.data;
+		size = iter_field.size;
+
+		str_truncate(str, 0);
+		str_printfa(str, "    - %s: ", field->name);
+		switch (field->type) {
+		case MAIL_CACHE_FIELD_FIXED_SIZE:
+			if (size == sizeof(uint32_t))
+				str_printfa(str, "%u ", *((const uint32_t *)data));
+			else if (size == sizeof(uint64_t))
+				str_printfa(str, "%llu ", (unsigned long long)*((const uint64_t *)data));
+		case MAIL_CACHE_FIELD_VARIABLE_SIZE:
+		case MAIL_CACHE_FIELD_BITMASK:
+			str_printfa(str, "(%s)", binary_to_hex(data, size));
+			break;
+		case MAIL_CACHE_FIELD_STRING:
+			if (size > 0)
+				str_printfa(str, "%.*s", (int)size, (const char *)data);
+			break;
+		case MAIL_CACHE_FIELD_HEADER: {
+			const uint32_t *lines = data;
+			int i;
+
+			for (i = 0;; i++) {
+				if (size < sizeof(uint32_t)) {
+					if (i == 0 && size == 0) {
+						/* header doesn't exist */
+						break;
+					}
+
+					str_append(str, "\n - BROKEN: header field doesn't end with 0 line");
+					size = 0;
+					break;
+				}
+
+				size -= sizeof(uint32_t);
+				data = CONST_PTR_OFFSET(data, sizeof(uint32_t));
+				if (lines[i] == 0)
+					break;
+
+				if (i > 0)
+					str_append(str, ", ");
+				str_printfa(str, "%u", lines[i]);
+			}
+
+			if (i == 1 && size > 0 &&
+			    ((const char *)data)[size-1] == '\n')
+				size--;
+			if (size > 0)
+				str_printfa(str, ": %.*s", (int)size, (const char *)data);
+			break;
+		}
+		case MAIL_CACHE_FIELD_COUNT:
+			i_unreached();
+			break;
+		}
+
+		printf("%s\n", str_c(str));
+	}
+	if (ret < 0)
+		printf(" - broken cache\n");
+}
+
+static const char *flags2str(enum mail_flags flags)
+{
+	string_t *str;
+
+	str = t_str_new(64);
+	str_append_c(str, '(');
+	if ((flags & MAIL_SEEN) != 0)
+		str_append(str, "Seen ");
+	if ((flags & MAIL_ANSWERED) != 0)
+		str_append(str, "Answered ");
+	if ((flags & MAIL_FLAGGED) != 0)
+		str_append(str, "Flagged ");
+	if ((flags & MAIL_DELETED) != 0)
+		str_append(str, "Deleted ");
+	if ((flags & MAIL_DRAFT) != 0)
+		str_append(str, "Draft ");
+	if (str_len(str) == 1)
+		return "";
+
+	str_truncate(str, str_len(str)-1);
+	str_append_c(str, ')');
+	return str_c(str);
+}
+
+static void dump_record(struct mail_index_view *view, unsigned int seq)
+{
+	struct mail_index *index = mail_index_view_get_index(view);
+	const struct mail_index_record *rec;
+	const struct mail_index_registered_ext *ext;
+	const void *data;
+	unsigned int i, ext_count;
+	string_t *str;
+	bool expunged;
+
+	rec = mail_index_lookup(view, seq);
+	printf("RECORD: seq=%u, uid=%u, flags=0x%02x %s\n",
+	       seq, rec->uid, rec->flags, flags2str(rec->flags));
+
+	str = t_str_new(256);
+	ext = array_get(&index->extensions, &ext_count);
+	for (i = 0; i < ext_count; i++) {
+		mail_index_lookup_ext(view, seq, i, &data, &expunged);
+		if (data == NULL || ext[i].record_size == 0)
+			continue;
+
+		str_truncate(str, 0);
+		str_printfa(str, " - ext %d %-10s: ", i, ext[i].name);
+		if (ext[i].record_size == sizeof(uint16_t) &&
+		    ext[i].record_align == sizeof(uint16_t))
+			str_printfa(str, "%10u", *((const uint16_t *)data));
+		else if (ext[i].record_size == sizeof(uint32_t) &&
+			 ext[i].record_align == sizeof(uint32_t))
+			str_printfa(str, "%10u", *((const uint32_t *)data));
+		else if (ext[i].record_size == sizeof(uint64_t) &&
+			 ext[i].record_align == sizeof(uint64_t)) {
+			uint64_t value = *((const uint64_t *)data);
+			str_printfa(str, "%10llu", (unsigned long long)value);
+		} else {
+			str_append(str, "          ");
+		}
+		str_printfa(str, " (%s)",
+			    binary_to_hex(data, ext[i].record_size));
+		printf("%s\n", str_c(str));
+		if (strcmp(ext[i].name, "virtual") == 0) {
+			const struct virtual_mail_index_record *vrec = data;
+			printf("                   : mailbox_id = %u\n", vrec->mailbox_id);
+			printf("                   : real_uid   = %u\n", vrec->real_uid);
+		} else if (strcmp(ext[i].name, "map") == 0) {
+			const struct dbox_mail_index_map_record *mrec = data;
+			printf("                   : file_id = %u\n", mrec->file_id);
+			printf("                   : offset  = %u\n", mrec->offset);
+			printf("                   : size    = %u\n", mrec->size);
+		} else if (strcmp(ext[i].name, "dbox") == 0) {
+			const struct dbox_mail_index_record *drec = data;
+			printf("                   : map_uid   = %u\n", drec->map_uid);
+			printf("                   : save_date = %u (%s)\n", drec->save_date, unixdate2str(drec->save_date));
+		}
+	}
+}
+
+static void cmd_dump_index(int argc ATTR_UNUSED, char *argv[])
+{
+	struct mail_index *index;
+	struct mail_index_view *view;
+	struct mail_cache_view *cache_view;
+	struct stat st;
+	const char *p;
+	unsigned int seq, uid = 0;
+
+	if (stat(argv[1], &st) == 0 && S_ISDIR(st.st_mode))
+		index = mail_index_alloc(argv[1], "dovecot.index");
+	else if ((p = strrchr(argv[1], '/')) != NULL)
+		index = mail_index_alloc(t_strdup_until(argv[1], p), p + 1);
+	else
+		index = mail_index_alloc(".", argv[1]);
+	if (mail_index_open(index, MAIL_INDEX_OPEN_FLAG_READONLY,
+			    FILE_LOCK_METHOD_FCNTL) <= 0)
+		i_fatal("Couldn't open index %s", argv[1]);
+	if (argv[2] != NULL)
+		uid = atoi(argv[2]);
+
+	view = mail_index_view_open(index);
+	cache_view = mail_cache_view_open(index->cache, view);
+
+	if (uid == 0) {
+		printf("-- INDEX: %s\n", index->filepath);
+		dump_hdr(index);
+		dump_extensions(index);
+		dump_keywords(index);
+
+		printf("\n-- CACHE: %s\n", index->cache->filepath);
+		dump_cache_hdr(index->cache);
+
+		printf("\n-- RECORDS: %u\n", index->map->hdr.messages_count);
+	}
+	for (seq = 1; seq <= index->map->hdr.messages_count; seq++) {
+		if (uid == 0 || mail_index_lookup(view, seq)->uid == uid) {
+			T_BEGIN {
+				dump_record(view, seq);
+				dump_cache(cache_view, seq);
+				printf("\n");
+			} T_END;
+		}
+	}
+	mail_cache_view_close(cache_view);
+	mail_index_view_close(&view);
+	mail_index_close(index);
+	mail_index_free(&index);
+}
+
+static bool test_dump_index(const char *path)
+{
+	struct mail_index *index;
+	struct stat st;
+	const char *p;
+	bool ret;
+
+	if (stat(path, &st) == 0 && S_ISDIR(st.st_mode))
+		index = mail_index_alloc(path, "dovecot.index");
+	else if ((p = strrchr(path, '/')) != NULL)
+		index = mail_index_alloc(t_strdup_until(path, p), p + 1);
+	else
+		index = mail_index_alloc(".", path);
+
+	ret = mail_index_open(index, MAIL_INDEX_OPEN_FLAG_READONLY,
+			      FILE_LOCK_METHOD_FCNTL) > 0;
+	mail_index_free(&index);
+	return ret;
+}
+
+struct doveadm_cmd_dump doveadm_cmd_dump_index = {
+	"index",
+	test_dump_index,
+	cmd_dump_index
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/doveadm/doveadm-dump-log.c	Thu Oct 08 20:43:25 2009 -0400
@@ -0,0 +1,505 @@
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "hex-binary.h"
+#include "mail-index-private.h"
+#include "mail-transaction-log.h"
+#include "doveadm-dump.h"
+
+#include <stdio.h>
+
+static struct mail_transaction_ext_intro prev_intro;
+
+static void dump_hdr(int fd, uint64_t *modseq_r)
+{
+	struct mail_transaction_log_header hdr;
+	ssize_t ret;
+
+	ret = read(fd, &hdr, sizeof(hdr));
+	if (ret != sizeof(hdr)) {
+		i_fatal("file hdr read() %"PRIuSIZE_T" != %"PRIuSIZE_T,
+			ret, sizeof(hdr));
+	}
+	if (hdr.hdr_size < sizeof(hdr)) {
+		memset(PTR_OFFSET(&hdr, hdr.hdr_size), 0,
+		       sizeof(hdr) - hdr.hdr_size);
+	}
+	lseek(fd, hdr.hdr_size, SEEK_SET);
+
+	printf("version = %u.%u\n", hdr.major_version, hdr.minor_version);
+	printf("hdr size = %u\n", hdr.hdr_size);
+	printf("index id = %u\n", hdr.indexid);
+	printf("file seq = %u\n", hdr.file_seq);
+	printf("prev file = %u/%u\n", hdr.prev_file_seq, hdr.prev_file_offset);
+	printf("create stamp = %u\n", hdr.create_stamp);
+	printf("initial modseq = %llu\n",
+	       (unsigned long long)hdr.initial_modseq);
+	printf("compat flags = %x\n", hdr.compat_flags);
+	*modseq_r = hdr.initial_modseq;
+}
+
+static bool
+mail_transaction_header_has_modseq(const struct mail_transaction_header *hdr)
+{
+	switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
+	case MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT:
+	case MAIL_TRANSACTION_EXPUNGE_GUID | MAIL_TRANSACTION_EXPUNGE_PROT:
+		if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) {
+			/* ignore expunge requests */
+			break;
+		}
+	case MAIL_TRANSACTION_APPEND:
+	case MAIL_TRANSACTION_FLAG_UPDATE:
+	case MAIL_TRANSACTION_KEYWORD_UPDATE:
+	case MAIL_TRANSACTION_KEYWORD_RESET:
+	case MAIL_TRANSACTION_UID_UPDATE:
+		/* these changes increase modseq */
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static const char *log_record_type(unsigned int type)
+{
+	const char *name;
+
+	switch (type & MAIL_TRANSACTION_TYPE_MASK) {
+	case MAIL_TRANSACTION_EXPUNGE|MAIL_TRANSACTION_EXPUNGE_PROT:
+		name = "expunge";
+		break;
+	case MAIL_TRANSACTION_EXPUNGE_GUID|MAIL_TRANSACTION_EXPUNGE_PROT:
+		name = "expunge-guid";
+		break;
+	case MAIL_TRANSACTION_APPEND:
+		name = "append";
+		break;
+	case MAIL_TRANSACTION_FLAG_UPDATE:
+		name = "flag-update";
+		break;
+	case MAIL_TRANSACTION_HEADER_UPDATE:
+		name = "header-update";
+		break;
+	case MAIL_TRANSACTION_EXT_INTRO:
+		name = "ext-intro";
+		break;
+	case MAIL_TRANSACTION_EXT_RESET:
+		name = "ext-reset";
+		break;
+	case MAIL_TRANSACTION_EXT_HDR_UPDATE:
+		name = "ext-hdr";
+		break;
+	case MAIL_TRANSACTION_EXT_REC_UPDATE:
+		name = "ext-rec";
+		break;
+	case MAIL_TRANSACTION_KEYWORD_UPDATE:
+		name = "keyword-update";
+		break;
+	case MAIL_TRANSACTION_KEYWORD_RESET:
+		name = "keyword-reset";
+		break;
+	case MAIL_TRANSACTION_EXT_ATOMIC_INC:
+		name = "ext-atomic-inc";
+		break;
+	case MAIL_TRANSACTION_UID_UPDATE:
+		name = "uid-update";
+		break;
+	case MAIL_TRANSACTION_MODSEQ_UPDATE:
+		name = "modseq-update";
+		break;
+	default:
+		name = t_strdup_printf("unknown: %x", type);
+		break;
+	}
+
+	if (type & MAIL_TRANSACTION_EXTERNAL)
+		name = t_strconcat(name, " (ext)", NULL);
+	return name;
+}
+
+static void print_data(const void *data, size_t size)
+{
+	size_t i;
+
+	for (i = 0; i < size; i++)
+		printf("%02x", ((const unsigned char *)data)[i]);
+	if (size == 4) {
+		const uint32_t *n = (const uint32_t *)data;
+
+		printf(" (dec=%u)", *n);
+	}
+}
+
+static void print_try_uint(const void *data, size_t size)
+{
+	size_t i;
+
+	switch (size) {
+	case 1: {
+		const uint8_t *n = data;
+		printf("%u", *n);
+		break;
+	}
+	case 2: {
+		const uint16_t *n = data;
+		uint32_t n16;
+
+		memcpy(&n16, n, sizeof(n16));
+		printf("%u", n16);
+		break;
+	}
+	case 4: {
+		const uint32_t *n = data;
+		uint32_t n32;
+
+		memcpy(&n32, n, sizeof(n32));
+		printf("%u", n32);
+		break;
+	}
+	case 8: {
+		const uint64_t *n = data;
+		uint64_t n64;
+
+		memcpy(&n64, n, sizeof(n64));
+		printf("%llu", (unsigned long long)n64);
+		break;
+	}
+	default:
+		for (i = 0; i < size; i++)
+			printf("%02x", ((const unsigned char *)data)[i]);
+	}
+}
+
+#define HDRF(field) { \
+	#field, offsetof(struct mail_index_header, field), \
+	sizeof(((struct mail_index_header *)0)->field) }
+
+static struct {
+	const char *name;
+	unsigned int offset, size;
+} header_fields[] = {
+	HDRF(minor_version),
+	HDRF(base_header_size),
+	HDRF(header_size),
+	HDRF(record_size),
+	HDRF(compat_flags),
+	HDRF(indexid),
+	HDRF(flags),
+	HDRF(uid_validity),
+	HDRF(next_uid),
+	HDRF(messages_count),
+	HDRF(unused_old_recent_messages_count),
+	HDRF(seen_messages_count),
+	HDRF(deleted_messages_count),
+	HDRF(first_recent_uid),
+	HDRF(first_unseen_uid_lowwater),
+	HDRF(first_deleted_uid_lowwater),
+	HDRF(log_file_seq),
+	HDRF(log_file_tail_offset),
+	HDRF(log_file_head_offset),
+	HDRF(sync_size),
+	HDRF(sync_stamp),
+	HDRF(day_stamp)
+};
+
+static void log_header_update(const struct mail_transaction_header_update *u)
+{
+	const void *data = u + 1;
+	unsigned int offset = u->offset, size = u->size;
+	unsigned int i;
+
+	while (size > 0) {
+		/* don't bother trying to handle header updates that include
+		   unknown/unexpected fields offsets/sizes */
+		for (i = 0; i < N_ELEMENTS(header_fields); i++) {
+			if (header_fields[i].offset == offset &&
+			    header_fields[i].size <= size)
+				break;
+		}
+
+		if (i == N_ELEMENTS(header_fields)) {
+			printf(" - offset = %u, size = %u: ", offset, size);
+			print_data(data, size);
+			printf("\n");
+			break;
+		}
+
+		printf(" - %s = ", header_fields[i].name);
+		print_try_uint(data, header_fields[i].size);
+		printf("\n");
+
+		data = CONST_PTR_OFFSET(data, header_fields[i].size);
+		offset += header_fields[i].size;
+		size -= header_fields[i].size;
+	}
+}
+
+static void log_record_print(const struct mail_transaction_header *hdr,
+			     const void *data, uint64_t *modseq)
+{
+	unsigned int size = hdr->size - sizeof(*hdr);
+
+	switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
+	case MAIL_TRANSACTION_EXPUNGE|MAIL_TRANSACTION_EXPUNGE_PROT: {
+		const struct mail_transaction_expunge *exp = data;
+
+		printf(" -");
+		for (; size > 0; size -= sizeof(*exp), exp++) {
+			printf(" %u-%u", exp->uid1, exp->uid2);
+		}
+		printf("\n");
+		break;
+	}
+	case MAIL_TRANSACTION_EXPUNGE_GUID|MAIL_TRANSACTION_EXPUNGE_PROT: {
+		const struct mail_transaction_expunge_guid *exp = data;
+
+		for (; size > 0; size -= sizeof(*exp), exp++) {
+			printf(" - %u (guid ", exp->uid);
+			print_data(exp->guid_128, sizeof(exp->guid_128));
+			printf(")\n");
+		}
+		break;
+	}
+	case MAIL_TRANSACTION_APPEND: {
+		const struct mail_index_record *rec = data;
+
+		printf(" - ");
+		for (; size > 0; size -= sizeof(*rec), rec++) {
+			printf("%u", rec->uid);
+			if (rec->flags != 0)
+				printf(" (flags=%x)", rec->flags);
+			printf(",");
+		}
+		printf("\n");
+		break;
+	}
+	case MAIL_TRANSACTION_FLAG_UPDATE: {
+		const struct mail_transaction_flag_update *u = data;
+
+		for (; size > 0; size -= sizeof(*u), u++) {
+			printf(" - %u-%u (flags +%x-%x)\n", u->uid1, u->uid2,
+			       u->add_flags, u->remove_flags);
+		}
+		break;
+	}
+	case MAIL_TRANSACTION_HEADER_UPDATE: {
+		const struct mail_transaction_header_update *u = data;
+
+		log_header_update(u);
+		break;
+	}
+	case MAIL_TRANSACTION_EXT_INTRO: {
+		const struct mail_transaction_ext_intro *intro = data;
+
+		prev_intro = *intro;
+		printf(" - ext_id = %u\n", intro->ext_id);
+		printf(" - reset_id = %u\n", intro->reset_id);
+		printf(" - hdr_size = %u\n", intro->hdr_size);
+		printf(" - record_size = %u\n", intro->record_size);
+		printf(" - record_align = %u\n", intro->record_align);
+		printf(" - flags = %u\n", intro->flags);
+		printf(" - name_size = %u\n", intro->name_size);
+		if (intro->name_size > 0) {
+			const char *name = (const char *)(intro+1);
+
+			printf(" - name = '%.*s'\n", intro->name_size, name);
+			if (*modseq == 0 && intro->name_size == 6 &&
+			    memcmp(name, "modseq", 6) == 0)
+				*modseq = 1;
+		}
+		break;
+	}
+	case MAIL_TRANSACTION_EXT_RESET: {
+		const struct mail_transaction_ext_reset *reset = data;
+
+		printf(" - new_reset_id = %u\n", reset->new_reset_id);
+		printf(" - preserve_data = %u\n", reset->preserve_data);
+		break;
+	}
+	case MAIL_TRANSACTION_EXT_HDR_UPDATE: {
+		const struct mail_transaction_ext_hdr_update *u = data;
+
+		printf(" - offset = %u, size = %u: ", u->offset, u->size);
+		print_data(u + 1, u->size);
+		printf("\n");
+		break;
+	}
+	case MAIL_TRANSACTION_EXT_REC_UPDATE: {
+		const struct mail_transaction_ext_rec_update *rec = data, *end;
+		size_t record_size;
+
+		end = CONST_PTR_OFFSET(data, size);
+		record_size = (sizeof(*rec) + prev_intro.record_size + 3) & ~3;
+		while (rec < end) {
+			printf(" - %u: ", rec->uid);
+			print_data(rec + 1, prev_intro.record_size);
+			printf("\n");
+			rec = CONST_PTR_OFFSET(rec, record_size);
+		}
+		break;
+	}
+	case MAIL_TRANSACTION_EXT_ATOMIC_INC: {
+		const struct mail_transaction_ext_atomic_inc *rec = data, *end;
+
+		end = CONST_PTR_OFFSET(data, size);
+		for (; rec < end; rec++) {
+			printf(" - %u: ", rec->uid);
+			if (rec->diff > 0)
+				printf("+%d\n", rec->diff);
+			else
+				printf("%d\n", rec->diff);
+		}
+		break;
+	}
+	case MAIL_TRANSACTION_KEYWORD_UPDATE: {
+		const struct mail_transaction_keyword_update *u = data;
+		const uint32_t *uid;
+		unsigned int uid_offset;
+
+		printf(" - modify=%d, name=%.*s, ",
+		       u->modify_type, u->name_size, (const char *)(u+1));
+
+		uid_offset = sizeof(*u) + u->name_size +
+			((u->name_size % 4) == 0 ? 0 : 4 - (u->name_size%4));
+		uid = (const uint32_t *)((const char *)u + uid_offset);
+		size -= uid_offset;
+
+		for (; size > 0; size -= sizeof(*uid)*2, uid += 2) {
+			printf("%u-%u,", uid[0], uid[1]);
+		}
+		printf("\n");
+		break;
+	}
+	case MAIL_TRANSACTION_KEYWORD_RESET: {
+		const struct mail_transaction_keyword_reset *u = data;
+
+		printf(" - ");
+		for (; size > 0; size -= sizeof(*u), u++) {
+			printf("%u-%u, ", u->uid1, u->uid2);
+		}
+		printf("\n");
+		break;
+	}
+	case MAIL_TRANSACTION_UID_UPDATE: {
+		const struct mail_transaction_uid_update *rec = data, *end;
+
+		end = CONST_PTR_OFFSET(data, size);
+		for (rec = data; rec < end; rec++) {
+			printf(" - old uid=%u new uid=%u\n",
+			       rec->old_uid, rec->new_uid);
+		}
+		break;
+	}
+	case MAIL_TRANSACTION_MODSEQ_UPDATE: {
+		const struct mail_transaction_modseq_update *rec = data, *end;
+
+		end = CONST_PTR_OFFSET(data, size);
+		for (rec = data; rec < end; rec++) {
+			printf(" - uid=%u modseq=%llu\n", rec->uid,
+			       ((unsigned long long)rec->modseq_high32 << 32) |
+			       rec->modseq_low32);
+		}
+		break;
+	}
+	default:
+		break;
+	}
+}
+
+static int dump_record(int fd, uint64_t *modseq)
+{
+	off_t offset;
+	ssize_t ret;
+	struct mail_transaction_header hdr;
+	unsigned int orig_size;
+
+	offset = lseek(fd, 0, SEEK_CUR);
+
+	ret = read(fd, &hdr, sizeof(hdr));
+	if (ret == 0)
+		return 0;
+
+	if (ret != sizeof(hdr)) {
+		i_fatal("rec hdr read() %"PRIuSIZE_T" != %"PRIuSIZE_T,
+			ret, sizeof(hdr));
+	}
+
+	orig_size = hdr.size;
+	hdr.size = mail_index_offset_to_uint32(hdr.size);
+	if (hdr.size == 0) {
+		printf("record: offset=%"PRIuUOFF_T", "
+		       "type=%s, size=broken (%x)\n",
+		       offset, log_record_type(hdr.type), orig_size);
+		return 0;
+	}
+
+	printf("record: offset=%"PRIuUOFF_T", type=%s, size=%u",
+	       offset, log_record_type(hdr.type), hdr.size);
+	if (*modseq > 0 && mail_transaction_header_has_modseq(&hdr)) {
+		*modseq += 1;
+		printf(", modseq=%llu", (unsigned long long)*modseq);
+	}
+	printf("\n");
+
+	if (hdr.size < 1024*1024) {
+		unsigned char *buf = t_malloc(hdr.size);
+
+		ret = read(fd, buf, hdr.size - sizeof(hdr));
+		if (ret != (ssize_t)(hdr.size - sizeof(hdr))) {
+			i_fatal("rec data read() %"PRIuSIZE_T" != %"PRIuSIZE_T,
+				ret, hdr.size - sizeof(hdr));
+		}
+		log_record_print(&hdr, buf, modseq);
+	} else {
+		lseek(fd, hdr.size - sizeof(hdr), SEEK_CUR);
+	}
+	return 1;
+}
+
+static void cmd_dump_log(int argc ATTR_UNUSED, char *argv[])
+{
+	uint64_t modseq;
+	int fd, ret;
+
+	fd = open(argv[1], O_RDONLY);
+	if (fd < 0)
+		i_fatal("open(%s) failed: %m", argv[1]);
+
+	dump_hdr(fd, &modseq);
+	do {
+		T_BEGIN {
+			ret = dump_record(fd, &modseq);
+		} T_END;
+	} while (ret > 0);
+}
+
+static bool test_dump_log(const char *path)
+{
+	struct mail_transaction_log_header hdr;
+	const char *p;
+	bool ret = FALSE;
+	int fd;
+
+	p = strrchr(path, '/');
+	if (p == NULL)
+		return FALSE;
+	p = strstr(p, ".log");
+	if (p == NULL || !(p[4] == '\0' || p[4] == '.'))
+		return FALSE;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return FALSE;
+
+	if (read(fd, &hdr, sizeof(hdr)) >= MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE &&
+	    hdr.major_version == MAIL_TRANSACTION_LOG_MAJOR_VERSION &&
+	    hdr.hdr_size >= MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE)
+		ret = TRUE;
+	(void)close(fd);
+	return ret;
+}
+
+struct doveadm_cmd_dump doveadm_cmd_dump_log = {
+	"log",
+	test_dump_log,
+	cmd_dump_log
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/doveadm/doveadm-dump-mailboxlog.c	Thu Oct 08 20:43:25 2009 -0400
@@ -0,0 +1,121 @@
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "hex-binary.h"
+#include "mailbox-log.h"
+#include "doveadm-dump.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+
+static const char *unixdate2str(time_t timestamp)
+{
+	static char buf[64];
+	struct tm *tm;
+
+	tm = localtime(&timestamp);
+	strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
+	return buf;
+}
+
+static int dump_record(int fd)
+{
+	off_t offset;
+	ssize_t ret;
+	struct mailbox_log_record rec;
+	time_t timestamp;
+
+	offset = lseek(fd, 0, SEEK_CUR);
+
+	ret = read(fd, &rec, sizeof(rec));
+	if (ret == 0)
+		return 0;
+
+	if (ret != sizeof(rec)) {
+		i_fatal("rec read() %"PRIuSIZE_T" != %"PRIuSIZE_T,
+			ret, sizeof(rec));
+	}
+
+	printf("#%"PRIuUOFF_T": ", offset);
+	switch (rec.type) {
+	case MAILBOX_LOG_RECORD_DELETE_MAILBOX:
+		printf("delete-mailbox");
+		break;
+	case MAILBOX_LOG_RECORD_DELETE_DIR:
+		printf("delete-dir");
+		break;
+	case MAILBOX_LOG_RECORD_RENAME:
+		printf("rename");
+		break;
+	case MAILBOX_LOG_RECORD_SUBSCRIBE:
+		printf("subscribe");
+		break;
+	case MAILBOX_LOG_RECORD_UNSUBSCRIBE:
+		printf("unsubscribe");
+		break;
+	}
+	printf(" %s", binary_to_hex(rec.mailbox_guid,
+				    sizeof(rec.mailbox_guid)));
+
+	timestamp = ((uint32_t)rec.timestamp[0] << 24) |
+		((uint32_t)rec.timestamp[1] << 16) |
+		((uint32_t)rec.timestamp[2] << 8) |
+		(uint32_t)rec.timestamp[3];
+	printf(" (%s)\n", unixdate2str(timestamp));
+	return 1;
+}
+
+static void cmd_dump_mailboxlog(int argc ATTR_UNUSED, char *argv[])
+{
+	int fd, ret;
+
+	fd = open(argv[1], O_RDONLY);
+	if (fd < 0)
+		i_fatal("open(%s) failed: %m", argv[1]);
+
+	do {
+		T_BEGIN {
+			ret = dump_record(fd);
+		} T_END;
+	} while (ret > 0);
+}
+
+static bool test_dump_mailboxlog(const char *path)
+{
+	const char *p;
+	int fd;
+	struct mailbox_log_record rec;
+	bool ret = FALSE;
+
+	p = strrchr(path, '.');
+	if (p == NULL || strcmp(p, ".log") != 0)
+		return FALSE;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1)
+		return FALSE;
+
+	if (read(fd, &rec, sizeof(rec)) == sizeof(rec) &&
+	    rec.padding[0] == 0 && rec.padding[1] == 0 && rec.padding[2] == 0) {
+		enum mailbox_log_record_type type = rec.type;
+		switch (type) {
+		case MAILBOX_LOG_RECORD_DELETE_MAILBOX:
+		case MAILBOX_LOG_RECORD_DELETE_DIR:
+		case MAILBOX_LOG_RECORD_RENAME:
+		case MAILBOX_LOG_RECORD_SUBSCRIBE:
+		case MAILBOX_LOG_RECORD_UNSUBSCRIBE:
+			ret = TRUE;
+			break;
+		}
+	}
+	(void)close(fd);
+	return ret;
+}
+
+struct doveadm_cmd_dump doveadm_cmd_dump_mailboxlog = {
+	"mailboxlog",
+	test_dump_mailboxlog,
+	cmd_dump_mailboxlog
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/doveadm/doveadm-dump-thread.c	Thu Oct 08 20:43:25 2009 -0400
@@ -0,0 +1,137 @@
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "mmap-util.h"
+#include "mail-index-private.h"
+#include "mail-index-strmap.h"
+#include "doveadm-dump.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+static uint32_t max_likely_index;
+
+static size_t dump_hdr(const struct mail_index_strmap_header *hdr)
+{
+	printf("version = %u\n", hdr->version);
+	printf("uid validity = %u\n", hdr->uid_validity);
+	return sizeof(*hdr);
+}
+
+static int dump_record(const uint8_t **p, const uint8_t *end, uint32_t *uid)
+{
+	uint32_t uid_diff, n, i, count, crc32, idx;
+	size_t size;
+
+	/* <uid diff> <n> <crc32>*count <str_idx>*count */
+	if (mail_index_unpack_num(p, end, &uid_diff) <  0)
+		return -1;
+	*uid += uid_diff;
+
+	if (mail_index_unpack_num(p, end, &n) <  0)
+		return -1;
+	printf(" - uid %u: n=%u\n", *uid, n);
+
+	count = n < 2 ? n + 1 : n;
+	size = sizeof(crc32)*count + sizeof(idx)*count;
+	if (*p + size > end)
+		return -1;
+	for (i = 0; i < count; i++) {
+		if (i == 0)
+			printf("   - message-id: ");
+		else if (i == 1) {
+			if (n == 1)
+				printf("   - in-reply-to: ");
+			else
+				printf("   - references[1]: ");
+		} else {
+			printf("   - references[%u]: ", i);
+		}
+		memcpy(&crc32, *p + sizeof(crc32)*i, sizeof(crc32));
+		memcpy(&idx, *p + sizeof(crc32)*count + sizeof(idx)*i, sizeof(idx));
+		printf("crc32=%08x index=%u\n", crc32, idx);
+		if (idx > max_likely_index)
+			printf(" - index probably broken\n");
+	}
+	*p += size;
+	return 0;
+}
+
+static int dump_block(const uint8_t *data, const uint8_t *end, uint32_t *uid)
+{
+	const uint8_t *p;
+	uint32_t block_size;
+
+	if (data + 4 >= end)
+		return -1;
+
+	memcpy(&block_size, data, sizeof(block_size));
+	block_size = mail_index_offset_to_uint32(block_size) >> 2;
+	printf(" - block_size=%u\n", block_size);
+	if (block_size == 0) {
+		/* finished */
+		return -1;
+	}
+	if (data + sizeof(block_size) + block_size > end) {
+		printf("   - broken!\n");
+		return -1;
+	}
+	p = data + sizeof(block_size);
+	end = p + block_size;
+
+	*uid += 1;
+	while (p != end) {
+		if (dump_record(&p, end, uid) < 0) {
+			printf(" - broken\n");
+			return -1;
+		}
+	}
+	return p - data;
+}
+
+static void cmd_dump_thread(int argc ATTR_UNUSED, char *argv[])
+{
+	unsigned int pos;
+	const void *map, *end;
+	struct stat st;
+	uint32_t uid;
+	int fd, ret;
+
+	fd = open(argv[1], O_RDONLY);
+	if (fd < 0)
+		i_fatal("open(%s) failed: %m", argv[1]);
+
+	if (fstat(fd, &st) < 0)
+		i_fatal("fstat(%s) failed: %m", argv[1]);
+	max_likely_index = (st.st_size / 8) * 2;
+
+	map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (map == MAP_FAILED)
+		i_fatal("mmap() failed: %m");
+	end = CONST_PTR_OFFSET(map, st.st_size);
+	pos = dump_hdr(map);
+	uid = 0;
+	do {
+		printf("block at offset %u:\n", pos);
+		T_BEGIN {
+			ret = dump_block(CONST_PTR_OFFSET(map, pos), end, &uid);
+			pos += ret;
+		} T_END;
+	} while (ret > 0);
+}
+
+static bool test_dump_thread(const char *path)
+{
+	const char *p;
+
+	p = strrchr(path, '.');
+	return p != NULL && strcmp(p, ".thread") == 0;
+}
+
+struct doveadm_cmd_dump doveadm_cmd_dump_thread = {
+	"thread",
+	test_dump_thread,
+	cmd_dump_thread
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/doveadm/doveadm-dump.c	Thu Oct 08 20:43:25 2009 -0400
@@ -0,0 +1,79 @@
+/* Copyright (C) 2004 Joshua Goodall */
+
+#include "lib.h"
+#include "doveadm.h"
+#include "doveadm-dump.h"
+
+#include <stdio.h>
+#include <unistd.h>
+
+static const struct doveadm_cmd_dump *dumps[] = {
+	&doveadm_cmd_dump_index,
+	&doveadm_cmd_dump_log,
+	&doveadm_cmd_dump_mailboxlog,
+	&doveadm_cmd_dump_thread
+};
+
+static const struct doveadm_cmd_dump *
+dump_find_name(const char *name)
+{
+	unsigned int i;
+
+	for (i = 0; i < N_ELEMENTS(dumps); i++) {
+		if (strcmp(dumps[i]->name, name) == 0)
+			return dumps[i];
+	}
+	return NULL;
+}
+
+static const struct doveadm_cmd_dump *
+dump_find_test(const char *path)
+{
+	unsigned int i;
+
+	for (i = 0; i < N_ELEMENTS(dumps); i++) {
+		if (dumps[i]->test(path))
+			return dumps[i];
+	}
+	return NULL;
+}
+
+static void cmd_dump(int argc, char *argv[])
+{
+	const struct doveadm_cmd_dump *dump;
+	const char *type = NULL;
+	int c;
+
+	while ((c = getopt(argc, argv, "t:")) > 0) {
+		switch (c) {
+		case 't':
+			type = optarg;
+			break;
+		default:
+			help(&doveadm_cmd_dump);
+		}
+	}
+	if (optind == argc)
+		help(&doveadm_cmd_dump);
+
+	optind--;
+	argc -= optind;
+	argv += optind;
+
+	dump = type != NULL ? dump_find_name(type) : dump_find_test(argv[1]);
+	if (dump == NULL) {
+		if (type != NULL)
+			i_fatal("Unknown type: %s", type);
+		else
+			i_fatal("Can't autodetect file type: %s", argv[1]);
+	} else {
+		if (type == NULL)
+			printf("Detected file type: %s\n", dump->name);
+	}
+	dump->cmd(argc, argv);
+}
+
+struct doveadm_cmd doveadm_cmd_dump = {
+	cmd_dump, "dump", "[-t <type>] <path>",
+"  <type> can be: index log mailboxlog thread\n"
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/doveadm/doveadm-dump.h	Thu Oct 08 20:43:25 2009 -0400
@@ -0,0 +1,17 @@
+#ifndef DOVEADM_DUMP_H
+#define DOVEADM_DUMP_H
+
+#include "doveadm.h"
+
+struct doveadm_cmd_dump {
+	const char *name;
+	bool (*test)(const char *path);
+	doveadm_command_t *cmd;
+};
+
+extern struct doveadm_cmd_dump doveadm_cmd_dump_index;
+extern struct doveadm_cmd_dump doveadm_cmd_dump_log;
+extern struct doveadm_cmd_dump doveadm_cmd_dump_mailboxlog;
+extern struct doveadm_cmd_dump doveadm_cmd_dump_thread;
+
+#endif
--- a/src/doveadm/doveadm.c	Thu Oct 08 20:10:49 2009 -0400
+++ b/src/doveadm/doveadm.c	Thu Oct 08 20:43:25 2009 -0400
@@ -81,6 +81,7 @@
 	doveadm_register_cmd(&doveadm_cmd_help);
 	doveadm_register_cmd(&doveadm_cmd_auth);
 	doveadm_register_cmd(&doveadm_cmd_user);
+	doveadm_register_cmd(&doveadm_cmd_dump);
 	doveadm_register_cmd(&doveadm_cmd_pw);
 
 	/* "+" is GNU extension to stop at the first non-option.
--- a/src/doveadm/doveadm.h	Thu Oct 08 20:10:49 2009 -0400
+++ b/src/doveadm/doveadm.h	Thu Oct 08 20:43:25 2009 -0400
@@ -14,6 +14,7 @@
 
 extern struct doveadm_cmd doveadm_cmd_auth;
 extern struct doveadm_cmd doveadm_cmd_user;
+extern struct doveadm_cmd doveadm_cmd_dump;
 extern struct doveadm_cmd doveadm_cmd_pw;
 
 void doveadm_register_cmd(const struct doveadm_cmd *cmd);
--- a/src/util/Makefile.am	Thu Oct 08 20:10:49 2009 -0400
+++ b/src/util/Makefile.am	Thu Oct 08 20:43:25 2009 -0400
@@ -3,13 +3,9 @@
 pkglibexec_PROGRAMS = \
 	rawlog \
 	gdbhelper \
-	idxview \
 	imap-utf7 \
 	listview \
-	logview \
-	mailboxlogview \
-	maildirlock \
-	threadview
+	maildirlock
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
@@ -33,11 +29,6 @@
 gdbhelper_SOURCES = \
 	gdbhelper.c
 
-idxview_LDADD = $(LIBDOVECOT_STORAGE) $(LIBDOVECOT)
-idxview_DEPENDENCIES = $(LIBDOVECOT_STORAGE) $(LIBDOVECOT)
-idxview_SOURCES = \
-	idxview.c
-
 imap_utf7_LDADD = $(LIBDOVECOT)
 imap_utf7_DEPENDENCIES = $(LIBDOVECOT)
 imap_utf7_SOURCES = \
@@ -48,22 +39,7 @@
 listview_SOURCES = \
 	listview.c
 
-logview_LDADD = $(LIBDOVECOT)
-logview_DEPENDENCIES = $(LIBDOVECOT)
-logview_SOURCES = \
-	logview.c
-
-mailboxlogview_LDADD = $(LIBDOVECOT)
-mailboxlogview_DEPENDENCIES = $(LIBDOVECOT)
-mailboxlogview_SOURCES = \
-	mailboxlogview.c
-
 maildirlock_LDADD = $(LIBDOVECOT)
 maildirlock_DEPENDENCIES = $(LIBDOVECOT)
 maildirlock_SOURCES = \
 	maildirlock.c
-
-threadview_LDADD = $(LIBDOVECOT)
-threadview_DEPENDENCIES = $(LIBDOVECOT)
-threadview_SOURCES = \
-	threadview.c
--- a/src/util/idxview.c	Thu Oct 08 20:10:49 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,510 +0,0 @@
-/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "array.h"
-#include "str.h"
-#include "hex-binary.h"
-#include "file-lock.h"
-#include "mail-index-private.h"
-#include "mail-cache-private.h"
-#include "mail-cache-private.h"
-#include "mail-index-modseq.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-
-struct maildir_index_header {
-	uint32_t new_check_time, new_mtime, new_mtime_nsecs;
-	uint32_t cur_check_time, cur_mtime, cur_mtime_nsecs;
-	uint32_t uidlist_mtime, uidlist_mtime_nsecs, uidlist_size;
-};
-struct mbox_index_header {
-	uint64_t sync_size;
-	uint32_t sync_mtime;
-	uint8_t dirty_flag;
-	uint8_t unused[3];
-	uint8_t mailbox_guid[16];
-};
-struct dbox_index_header {
-	uint32_t map_uid_validity;
-	uint32_t highest_maildir_uid;
-	uint8_t mailbox_guid[16];
-};
-struct dbox_mail_index_record {
-	uint32_t map_uid;
-	uint32_t save_date;
-};
-
-struct virtual_mail_index_record {
-	uint32_t mailbox_id;
-	uint32_t real_uid;
-};
-
-struct dbox_mail_index_map_record {
-	uint32_t file_id;
-	uint32_t offset;
-	uint32_t size;
-};
-
-static const char *unixdate2str(time_t timestamp)
-{
-	static char buf[64];
-	struct tm *tm;
-
-	tm = localtime(&timestamp);
-	strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
-	return buf;
-}
-
-static void dump_hdr(struct mail_index *index)
-{
-	const struct mail_index_header *hdr = &index->map->hdr;
-	unsigned int i;
-
-	printf("version .................. = %u.%u\n", hdr->major_version, hdr->minor_version);
-	printf("base header size ......... = %u\n", hdr->base_header_size);
-	printf("header size .............. = %u\n", hdr->header_size);
-	printf("record size .............. = %u\n", hdr->record_size);
-	printf("compat flags ............. = %u\n", hdr->compat_flags);
-	printf("index id ................. = %u (%s)\n", hdr->indexid, unixdate2str(hdr->indexid));
-	printf("flags .................... = %u\n", hdr->flags);
-	printf("uid validity ............. = %u (%s)\n", hdr->uid_validity, unixdate2str(hdr->uid_validity));
-	printf("next uid ................. = %u\n", hdr->next_uid);
-	printf("messages count ........... = %u\n", hdr->messages_count);
-	printf("seen messages count ...... = %u\n", hdr->seen_messages_count);
-	printf("deleted messages count ... = %u\n", hdr->deleted_messages_count);
-	printf("first recent uid ......... = %u\n", hdr->first_recent_uid);
-	printf("first unseen uid lowwater  = %u\n", hdr->first_unseen_uid_lowwater);
-	printf("first deleted uid lowwater = %u\n", hdr->first_deleted_uid_lowwater);
-	printf("log file seq ............. = %u\n", hdr->log_file_seq);
-	if (hdr->minor_version == 0) {
-		printf("log file int offset ...... = %u\n", hdr->log_file_tail_offset);
-		printf("log file ext offset ...... = %u\n", hdr->log_file_head_offset);
-	} else {
-		printf("log file tail offset ..... = %u\n", hdr->log_file_tail_offset);
-		printf("log file head offset ..... = %u\n", hdr->log_file_head_offset);
-	}
-	printf("sync size ................ = %llu\n", (unsigned long long)hdr->sync_size);
-	printf("sync stamp ............... = %u (%s)\n", hdr->sync_stamp, unixdate2str(hdr->sync_stamp));
-	printf("day stamp ................ = %u (%s)\n", hdr->day_stamp, unixdate2str(hdr->day_stamp));
-	for (i = 0; i < N_ELEMENTS(hdr->day_first_uid); i++)
-		printf("day first uid[%u] ......... = %u\n", i, hdr->day_first_uid[i]);
-}
-
-static void dump_extension_header(struct mail_index *index,
-				  const struct mail_index_ext *ext)
-{
-	const void *data;
-
-	if (strcmp(ext->name, MAIL_INDEX_EXT_KEYWORDS) == 0)
-		return;
-
-	data = CONST_PTR_OFFSET(index->map->hdr_base, ext->hdr_offset);
-	if (strcmp(ext->name, "maildir") == 0) {
-		const struct maildir_index_header *hdr = data;
-
-		printf("header\n");
-		printf(" - new_check_time .... = %s\n", unixdate2str(hdr->new_check_time));
-		printf(" - new_mtime ......... = %s\n", unixdate2str(hdr->new_mtime));
-		printf(" - new_mtime_nsecs ... = %u\n", hdr->new_mtime_nsecs);
-		printf(" - cur_check_time .... = %s\n", unixdate2str(hdr->cur_check_time));
-		printf(" - cur_mtime ......... = %s\n", unixdate2str(hdr->cur_mtime));
-		printf(" - cur_mtime_nsecs.... = %u\n", hdr->cur_mtime_nsecs);
-		printf(" - uidlist_mtime ..... = %s\n", unixdate2str(hdr->uidlist_mtime));
-		printf(" - uidlist_mtime_nsecs = %u\n", hdr->uidlist_mtime_nsecs);
-		printf(" - uidlist_size ...... = %u\n", hdr->uidlist_size);
-	} else if (strcmp(ext->name, "mbox") == 0) {
-		const struct mbox_index_header *hdr = data;
-
-		printf("header\n");
-		printf(" - sync_mtime . = %s\n", unixdate2str(hdr->sync_mtime));
-		printf(" - sync_size .. = %llu\n",
-		       (unsigned long long)hdr->sync_size);
-		printf(" - dirty_flag . = %d\n", hdr->dirty_flag);
-		printf(" - mailbox_guid = %s\n",
-		       binary_to_hex(hdr->mailbox_guid,
-				     sizeof(hdr->mailbox_guid)));
-	} else if (strcmp(ext->name, "dbox-hdr") == 0) {
-		const struct dbox_index_header *hdr = data;
-
-		printf("header\n");
-		printf(" - map_uid_validity .. = %u\n", hdr->map_uid_validity);
-		printf(" - highest_maildir_uid = %u\n", hdr->highest_maildir_uid);
-		printf(" - mailbox_guid ...... = %s\n",
-		       binary_to_hex(hdr->mailbox_guid,
-				     sizeof(hdr->mailbox_guid)));
-	} else if (strcmp(ext->name, "modseq") == 0) {
-		const struct mail_index_modseq_header *hdr = data;
-
-		printf("header\n");
-		printf(" - highest_modseq = %llu\n",
-		       (unsigned long long)hdr->highest_modseq);
-		printf(" - log_seq ...... = %u\n", hdr->log_seq);
-		printf(" - log_offset ... = %u\n", hdr->log_offset);
-	} else {
-		printf("header ........ = %s\n",
-		       binary_to_hex(data, ext->hdr_size));
-	}
-}
-
-static void dump_extensions(struct mail_index *index)
-{
-	const struct mail_index_ext *extensions;
-	unsigned int i, count;
-
-	if (array_is_created(&index->map->extensions))
-		extensions = array_get(&index->map->extensions, &count);
-	else
-		count = 0;
-	if (count == 0) {
-		printf("no extensions\n");
-		return;
-	}
-
-	for (i = 0; i < count; i++) {
-		const struct mail_index_ext *ext = &extensions[i];
-
-		printf("-- Extension %u --\n", i);
-		printf("name ........ = %s\n", ext->name);
-		printf("hdr_size .... = %u\n", ext->hdr_size);
-		printf("reset_id .... = %u\n", ext->reset_id);
-		printf("record_offset = %u\n", ext->record_offset);
-		printf("record_size . = %u\n", ext->record_size);
-		printf("record_align  = %u\n", ext->record_align);
-		if (ext->hdr_size > 0)
-			dump_extension_header(index, ext);
-	}
-}
-
-static void dump_keywords(struct mail_index *index)
-{
-	const unsigned int *kw_indexes;
-	const char *const *keywords;
-	unsigned int i, count;
-
-	printf("-- Keywords --\n");
-	if (!array_is_created(&index->map->keyword_idx_map))
-		return;
-
-	kw_indexes = array_get(&index->map->keyword_idx_map, &count);
-	if (count == 0)
-		return;
-
-	keywords = array_idx(&index->keywords, 0);
-	for (i = 0; i < count; i++)
-		printf("%3u = %s\n", i, keywords[kw_indexes[i]]);
-}
-
-static const char *cache_decision2str(enum mail_cache_decision_type type)
-{
-	const char *str;
-
-	switch (type & ~MAIL_CACHE_DECISION_FORCED) {
-	case MAIL_CACHE_DECISION_NO:
-		str = "no";
-		break;
-	case MAIL_CACHE_DECISION_TEMP:
-		str = "tmp";
-		break;
-	case MAIL_CACHE_DECISION_YES:
-		str = "yes";
-		break;
-	default:
-		return t_strdup_printf("0x%x", type);
-	}
-
-	if ((type & MAIL_CACHE_DECISION_FORCED) != 0)
-		str = t_strconcat(str, "!", NULL);
-	return str;
-}
-
-#define CACHE_TYPE_IS_FIXED_SIZE(type) \
-	((type) == MAIL_CACHE_FIELD_FIXED_SIZE || \
-	 (type) == MAIL_CACHE_FIELD_BITMASK)
-static const char *cache_type2str(enum mail_cache_field_type type)
-{
-	switch (type) {
-	case MAIL_CACHE_FIELD_FIXED_SIZE:
-		return "fix";
-	case MAIL_CACHE_FIELD_VARIABLE_SIZE:
-		return "var";
-	case MAIL_CACHE_FIELD_STRING:
-		return "str";
-	case MAIL_CACHE_FIELD_BITMASK:
-		return "bit";
-	case MAIL_CACHE_FIELD_HEADER:
-		return "hdr";
-	default:
-		return t_strdup_printf("0x%x", type);
-	}
-}
-
-static void dump_cache_hdr(struct mail_cache *cache)
-{
-	const struct mail_cache_header *hdr;
-	const struct mail_cache_field *fields, *field;
-	unsigned int i, count, cache_idx;
-
-	(void)mail_cache_open_and_verify(cache);
-	if (MAIL_CACHE_IS_UNUSABLE(cache)) {
-		printf("cache is unusable\n");
-		return;
-	}
-
-	hdr = cache->hdr;
-	printf("version .............. = %u\n", hdr->version);
-	printf("indexid .............. = %u (%s)\n", hdr->indexid, unixdate2str(hdr->indexid));
-	printf("file_seq ............. = %u (%s) (%d compressions)\n",
-	       hdr->file_seq, unixdate2str(hdr->file_seq),
-	       hdr->file_seq - hdr->indexid);
-	printf("continued_record_count = %u\n", hdr->continued_record_count);
-	printf("hole_offset .......... = %u\n", hdr->hole_offset);
-	printf("used_file_size ....... = %u\n", hdr->used_file_size);
-	printf("deleted_space ........ = %u\n", hdr->deleted_space);
-	printf("field_header_offset .. = %u (0x%08x nontranslated)\n",
-	       mail_index_offset_to_uint32(hdr->field_header_offset),
-	       hdr->field_header_offset);
-
-	printf("-- Cache fields --\n");
-	fields = mail_cache_register_get_list(cache, pool_datastack_create(),
-					      &count);
-	printf(
-" #  Name                                         Type Size Dec  Last used\n");
-	for (i = 0; i < cache->file_fields_count; i++) {
-		cache_idx = cache->file_field_map[i];
-		field = &fields[cache_idx];
-
-		printf("%2u: %-44s %-4s ", i, field->name,
-		       cache_type2str(field->type));
-		if (field->field_size != (uint32_t)-1 ||
-		    CACHE_TYPE_IS_FIXED_SIZE(field->type))
-			printf("%4u ", field->field_size);
-		else
-			printf("   - ");
-		printf("%-4s %.16s\n",
-		       cache_decision2str(field->decision),
-		       unixdate2str(cache->fields[cache_idx].last_used));
-	}
-}
-
-static void dump_cache(struct mail_cache_view *cache_view, unsigned int seq)
-{
-	struct mail_cache_lookup_iterate_ctx iter;
-	const struct mail_cache_record *prev_rec = NULL;
-	const struct mail_cache_field *field;
-	struct mail_cache_iterate_field iter_field;
-	const void *data;
-	unsigned int size;
-	string_t *str;
-	int ret;
-
-	str = t_str_new(512);
-	mail_cache_lookup_iter_init(cache_view, seq, &iter);
-	while ((ret = mail_cache_lookup_iter_next(&iter, &iter_field)) > 0) {
-		if (iter.rec != prev_rec) {
-			printf(" - cache offset=%u size=%u, prev_offset = %u\n",
-			       iter.offset, iter.rec->size,
-			       iter.rec->prev_offset);
-			prev_rec = iter.rec;
-		}
-
-		field = &cache_view->cache->fields[iter_field.field_idx].field;
-		data = iter_field.data;
-		size = iter_field.size;
-
-		str_truncate(str, 0);
-		str_printfa(str, "    - %s: ", field->name);
-		switch (field->type) {
-		case MAIL_CACHE_FIELD_FIXED_SIZE:
-			if (size == sizeof(uint32_t))
-				str_printfa(str, "%u ", *((const uint32_t *)data));
-			else if (size == sizeof(uint64_t))
-				str_printfa(str, "%llu ", (unsigned long long)*((const uint64_t *)data));
-		case MAIL_CACHE_FIELD_VARIABLE_SIZE:
-		case MAIL_CACHE_FIELD_BITMASK:
-			str_printfa(str, "(%s)", binary_to_hex(data, size));
-			break;
-		case MAIL_CACHE_FIELD_STRING:
-			if (size > 0)
-				str_printfa(str, "%.*s", (int)size, (const char *)data);
-			break;
-		case MAIL_CACHE_FIELD_HEADER: {
-			const uint32_t *lines = data;
-			int i;
-
-			for (i = 0;; i++) {
-				if (size < sizeof(uint32_t)) {
-					if (i == 0 && size == 0) {
-						/* header doesn't exist */
-						break;
-					}
-
-					str_append(str, "\n - BROKEN: header field doesn't end with 0 line");
-					size = 0;
-					break;
-				}
-
-				size -= sizeof(uint32_t);
-				data = CONST_PTR_OFFSET(data, sizeof(uint32_t));
-				if (lines[i] == 0)
-					break;
-
-				if (i > 0)
-					str_append(str, ", ");
-				str_printfa(str, "%u", lines[i]);
-			}
-
-			if (i == 1 && size > 0 &&
-			    ((const char *)data)[size-1] == '\n')
-				size--;
-			if (size > 0)
-				str_printfa(str, ": %.*s", (int)size, (const char *)data);
-			break;
-		}
-		case MAIL_CACHE_FIELD_COUNT:
-			i_unreached();
-			break;
-		}
-
-		printf("%s\n", str_c(str));
-	}
-	if (ret < 0)
-		printf(" - broken cache\n");
-}
-
-static const char *flags2str(enum mail_flags flags)
-{
-	string_t *str;
-
-	str = t_str_new(64);
-	str_append_c(str, '(');
-	if ((flags & MAIL_SEEN) != 0)
-		str_append(str, "Seen ");
-	if ((flags & MAIL_ANSWERED) != 0)
-		str_append(str, "Answered ");
-	if ((flags & MAIL_FLAGGED) != 0)
-		str_append(str, "Flagged ");
-	if ((flags & MAIL_DELETED) != 0)
-		str_append(str, "Deleted ");
-	if ((flags & MAIL_DRAFT) != 0)
-		str_append(str, "Draft ");
-	if (str_len(str) == 1)
-		return "";
-
-	str_truncate(str, str_len(str)-1);
-	str_append_c(str, ')');
-	return str_c(str);
-}
-
-static void dump_record(struct mail_index_view *view, unsigned int seq)
-{
-	struct mail_index *index = mail_index_view_get_index(view);
-	const struct mail_index_record *rec;
-	const struct mail_index_registered_ext *ext;
-	const void *data;
-	unsigned int i, ext_count;
-	string_t *str;
-	bool expunged;
-
-	rec = mail_index_lookup(view, seq);
-	printf("RECORD: seq=%u, uid=%u, flags=0x%02x %s\n",
-	       seq, rec->uid, rec->flags, flags2str(rec->flags));
-
-	str = t_str_new(256);
-	ext = array_get(&index->extensions, &ext_count);
-	for (i = 0; i < ext_count; i++) {
-		mail_index_lookup_ext(view, seq, i, &data, &expunged);
-		if (data == NULL || ext[i].record_size == 0)
-			continue;
-
-		str_truncate(str, 0);
-		str_printfa(str, " - ext %d %-10s: ", i, ext[i].name);
-		if (ext[i].record_size == sizeof(uint16_t) &&
-		    ext[i].record_align == sizeof(uint16_t))
-			str_printfa(str, "%10u", *((const uint16_t *)data));
-		else if (ext[i].record_size == sizeof(uint32_t) &&
-			 ext[i].record_align == sizeof(uint32_t))
-			str_printfa(str, "%10u", *((const uint32_t *)data));
-		else if (ext[i].record_size == sizeof(uint64_t) &&
-			 ext[i].record_align == sizeof(uint64_t)) {
-			uint64_t value = *((const uint64_t *)data);
-			str_printfa(str, "%10llu", (unsigned long long)value);
-		} else {
-			str_append(str, "          ");
-		}
-		str_printfa(str, " (%s)",
-			    binary_to_hex(data, ext[i].record_size));
-		printf("%s\n", str_c(str));
-		if (strcmp(ext[i].name, "virtual") == 0) {
-			const struct virtual_mail_index_record *vrec = data;
-			printf("                   : mailbox_id = %u\n", vrec->mailbox_id);
-			printf("                   : real_uid   = %u\n", vrec->real_uid);
-		} else if (strcmp(ext[i].name, "map") == 0) {
-			const struct dbox_mail_index_map_record *mrec = data;
-			printf("                   : file_id = %u\n", mrec->file_id);
-			printf("                   : offset  = %u\n", mrec->offset);
-			printf("                   : size    = %u\n", mrec->size);
-		} else if (strcmp(ext[i].name, "dbox") == 0) {
-			const struct dbox_mail_index_record *drec = data;
-			printf("                   : map_uid   = %u\n", drec->map_uid);
-			printf("                   : save_date = %u (%s)\n", drec->save_date, unixdate2str(drec->save_date));
-		}
-	}
-}
-
-int main(int argc, const char *argv[])
-{
-	struct mail_index *index;
-	struct mail_index_view *view;
-	struct mail_cache_view *cache_view;
-	struct stat st;
-	const char *p;
-	unsigned int seq, uid = 0;
-
-	lib_init();
-
-	if (argc < 2)
-		i_fatal("Usage: idxview <index dir> [<uid>]");
-
-	if (stat(argv[1], &st) == 0 && S_ISDIR(st.st_mode))
-		index = mail_index_alloc(argv[1], "dovecot.index");
-	else if ((p = strrchr(argv[1], '/')) != NULL)
-		index = mail_index_alloc(t_strdup_until(argv[1], p), p + 1);
-	else
-		index = mail_index_alloc(".", argv[1]);
-	if (mail_index_open(index, MAIL_INDEX_OPEN_FLAG_READONLY,
-			    FILE_LOCK_METHOD_FCNTL) <= 0)
-		i_fatal("Couldn't open index %s", argv[1]);
-	if (argv[2] != NULL)
-		uid = atoi(argv[2]);
-
-	view = mail_index_view_open(index);
-	cache_view = mail_cache_view_open(index->cache, view);
-
-	if (uid == 0) {
-		printf("-- INDEX: %s\n", index->filepath);
-		dump_hdr(index);
-		dump_extensions(index);
-		dump_keywords(index);
-
-		printf("\n-- CACHE: %s\n", index->cache->filepath);
-		dump_cache_hdr(index->cache);
-
-		printf("\n-- RECORDS: %u\n", index->map->hdr.messages_count);
-	}
-	for (seq = 1; seq <= index->map->hdr.messages_count; seq++) {
-		if (uid == 0 || mail_index_lookup(view, seq)->uid == uid) {
-			T_BEGIN {
-				dump_record(view, seq);
-				dump_cache(cache_view, seq);
-				printf("\n");
-			} T_END;
-		}
-	}
-	mail_cache_view_close(cache_view);
-	mail_index_view_close(&view);
-	mail_index_close(index);
-	mail_index_free(&index);
-	return 0;
-}
--- a/src/util/logview.c	Thu Oct 08 20:10:49 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,492 +0,0 @@
-/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "hex-binary.h"
-#include "mail-index-private.h"
-#include "mail-transaction-log.h"
-
-#include <stdio.h>
-
-static struct mail_transaction_ext_intro prev_intro;
-
-uint32_t mail_index_offset_to_uint32(uint32_t offset)
-{
-	const unsigned char *buf = (const unsigned char *) &offset;
-
-	if ((offset & 0x80808080) != 0x80808080)
-		return 0;
-
-	return (((uint32_t)buf[3] & 0x7f) << 2) |
-		(((uint32_t)buf[2] & 0x7f) << 9) |
-		(((uint32_t)buf[1] & 0x7f) << 16) |
-		(((uint32_t)buf[0] & 0x7f) << 23);
-}
-
-static void dump_hdr(int fd, uint64_t *modseq_r)
-{
-	struct mail_transaction_log_header hdr;
-	ssize_t ret;
-
-	ret = read(fd, &hdr, sizeof(hdr));
-	if (ret != sizeof(hdr)) {
-		i_fatal("file hdr read() %"PRIuSIZE_T" != %"PRIuSIZE_T,
-			ret, sizeof(hdr));
-	}
-	if (hdr.hdr_size < sizeof(hdr)) {
-		memset(PTR_OFFSET(&hdr, hdr.hdr_size), 0,
-		       sizeof(hdr) - hdr.hdr_size);
-	}
-	lseek(fd, hdr.hdr_size, SEEK_SET);
-
-	printf("version = %u.%u\n", hdr.major_version, hdr.minor_version);
-	printf("hdr size = %u\n", hdr.hdr_size);
-	printf("index id = %u\n", hdr.indexid);
-	printf("file seq = %u\n", hdr.file_seq);
-	printf("prev file = %u/%u\n", hdr.prev_file_seq, hdr.prev_file_offset);
-	printf("create stamp = %u\n", hdr.create_stamp);
-	printf("initial modseq = %llu\n",
-	       (unsigned long long)hdr.initial_modseq);
-	printf("compat flags = %x\n", hdr.compat_flags);
-	*modseq_r = hdr.initial_modseq;
-}
-
-static bool
-mail_transaction_header_has_modseq(const struct mail_transaction_header *hdr)
-{
-	switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
-	case MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT:
-	case MAIL_TRANSACTION_EXPUNGE_GUID | MAIL_TRANSACTION_EXPUNGE_PROT:
-		if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) {
-			/* ignore expunge requests */
-			break;
-		}
-	case MAIL_TRANSACTION_APPEND:
-	case MAIL_TRANSACTION_FLAG_UPDATE:
-	case MAIL_TRANSACTION_KEYWORD_UPDATE:
-	case MAIL_TRANSACTION_KEYWORD_RESET:
-	case MAIL_TRANSACTION_UID_UPDATE:
-		/* these changes increase modseq */
-		return TRUE;
-	}
-	return FALSE;
-}
-static const char *log_record_type(unsigned int type)
-{
-	const char *name;
-
-	switch (type & MAIL_TRANSACTION_TYPE_MASK) {
-	case MAIL_TRANSACTION_EXPUNGE|MAIL_TRANSACTION_EXPUNGE_PROT:
-		name = "expunge";
-		break;
-	case MAIL_TRANSACTION_EXPUNGE_GUID|MAIL_TRANSACTION_EXPUNGE_PROT:
-		name = "expunge-guid";
-		break;
-	case MAIL_TRANSACTION_APPEND:
-		name = "append";
-		break;
-	case MAIL_TRANSACTION_FLAG_UPDATE:
-		name = "flag-update";
-		break;
-	case MAIL_TRANSACTION_HEADER_UPDATE:
-		name = "header-update";
-		break;
-	case MAIL_TRANSACTION_EXT_INTRO:
-		name = "ext-intro";
-		break;
-	case MAIL_TRANSACTION_EXT_RESET:
-		name = "ext-reset";
-		break;
-	case MAIL_TRANSACTION_EXT_HDR_UPDATE:
-		name = "ext-hdr";
-		break;
-	case MAIL_TRANSACTION_EXT_REC_UPDATE:
-		name = "ext-rec";
-		break;
-	case MAIL_TRANSACTION_KEYWORD_UPDATE:
-		name = "keyword-update";
-		break;
-	case MAIL_TRANSACTION_KEYWORD_RESET:
-		name = "keyword-reset";
-		break;
-	case MAIL_TRANSACTION_EXT_ATOMIC_INC:
-		name = "ext-atomic-inc";
-		break;
-	case MAIL_TRANSACTION_UID_UPDATE:
-		name = "uid-update";
-		break;
-	case MAIL_TRANSACTION_MODSEQ_UPDATE:
-		name = "modseq-update";
-		break;
-	default:
-		name = t_strdup_printf("unknown: %x", type);
-		break;
-	}
-
-	if (type & MAIL_TRANSACTION_EXTERNAL)
-		name = t_strconcat(name, " (ext)", NULL);
-	return name;
-}
-
-static void print_data(const void *data, size_t size)
-{
-	size_t i;
-
-	for (i = 0; i < size; i++)
-		printf("%02x", ((const unsigned char *)data)[i]);
-	if (size == 4) {
-		const uint32_t *n = (const uint32_t *)data;
-
-		printf(" (dec=%u)", *n);
-	}
-}
-
-static void print_try_uint(const void *data, size_t size)
-{
-	size_t i;
-
-	switch (size) {
-	case 1: {
-		const uint8_t *n = data;
-		printf("%u", *n);
-		break;
-	}
-	case 2: {
-		const uint16_t *n = data;
-		uint32_t n16;
-
-		memcpy(&n16, n, sizeof(n16));
-		printf("%u", n16);
-		break;
-	}
-	case 4: {
-		const uint32_t *n = data;
-		uint32_t n32;
-
-		memcpy(&n32, n, sizeof(n32));
-		printf("%u", n32);
-		break;
-	}
-	case 8: {
-		const uint64_t *n = data;
-		uint64_t n64;
-
-		memcpy(&n64, n, sizeof(n64));
-		printf("%llu", (unsigned long long)n64);
-		break;
-	}
-	default:
-		for (i = 0; i < size; i++)
-			printf("%02x", ((const unsigned char *)data)[i]);
-	}
-}
-
-#define HDRF(field) { \
-	#field, offsetof(struct mail_index_header, field), \
-	sizeof(((struct mail_index_header *)0)->field) }
-
-static struct {
-	const char *name;
-	unsigned int offset, size;
-} header_fields[] = {
-	HDRF(minor_version),
-	HDRF(base_header_size),
-	HDRF(header_size),
-	HDRF(record_size),
-	HDRF(compat_flags),
-	HDRF(indexid),
-	HDRF(flags),
-	HDRF(uid_validity),
-	HDRF(next_uid),
-	HDRF(messages_count),
-	HDRF(unused_old_recent_messages_count),
-	HDRF(seen_messages_count),
-	HDRF(deleted_messages_count),
-	HDRF(first_recent_uid),
-	HDRF(first_unseen_uid_lowwater),
-	HDRF(first_deleted_uid_lowwater),
-	HDRF(log_file_seq),
-	HDRF(log_file_tail_offset),
-	HDRF(log_file_head_offset),
-	HDRF(sync_size),
-	HDRF(sync_stamp),
-	HDRF(day_stamp)
-};
-
-static void log_header_update(const struct mail_transaction_header_update *u)
-{
-	const void *data = u + 1;
-	unsigned int offset = u->offset, size = u->size;
-	unsigned int i;
-
-	while (size > 0) {
-		/* don't bother trying to handle header updates that include
-		   unknown/unexpected fields offsets/sizes */
-		for (i = 0; i < N_ELEMENTS(header_fields); i++) {
-			if (header_fields[i].offset == offset &&
-			    header_fields[i].size <= size)
-				break;
-		}
-
-		if (i == N_ELEMENTS(header_fields)) {
-			printf(" - offset = %u, size = %u: ", offset, size);
-			print_data(data, size);
-			printf("\n");
-			break;
-		}
-
-		printf(" - %s = ", header_fields[i].name);
-		print_try_uint(data, header_fields[i].size);
-		printf("\n");
-
-		data = CONST_PTR_OFFSET(data, header_fields[i].size);
-		offset += header_fields[i].size;
-		size -= header_fields[i].size;
-	}
-}
-
-static void log_record_print(const struct mail_transaction_header *hdr,
-			     const void *data, uint64_t *modseq)
-{
-	unsigned int size = hdr->size - sizeof(*hdr);
-
-	switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
-	case MAIL_TRANSACTION_EXPUNGE|MAIL_TRANSACTION_EXPUNGE_PROT: {
-		const struct mail_transaction_expunge *exp = data;
-
-		printf(" -");
-		for (; size > 0; size -= sizeof(*exp), exp++) {
-			printf(" %u-%u", exp->uid1, exp->uid2);
-		}
-		printf("\n");
-		break;
-	}
-	case MAIL_TRANSACTION_EXPUNGE_GUID|MAIL_TRANSACTION_EXPUNGE_PROT: {
-		const struct mail_transaction_expunge_guid *exp = data;
-
-		for (; size > 0; size -= sizeof(*exp), exp++) {
-			printf(" - %u (guid ", exp->uid);
-			print_data(exp->guid_128, sizeof(exp->guid_128));
-			printf(")\n");
-		}
-		break;
-	}
-	case MAIL_TRANSACTION_APPEND: {
-		const struct mail_index_record *rec = data;
-
-		printf(" - ");
-		for (; size > 0; size -= sizeof(*rec), rec++) {
-			printf("%u", rec->uid);
-			if (rec->flags != 0)
-				printf(" (flags=%x)", rec->flags);
-			printf(",");
-		}
-		printf("\n");
-		break;
-	}
-	case MAIL_TRANSACTION_FLAG_UPDATE: {
-		const struct mail_transaction_flag_update *u = data;
-
-		for (; size > 0; size -= sizeof(*u), u++) {
-			printf(" - %u-%u (flags +%x-%x)\n", u->uid1, u->uid2,
-			       u->add_flags, u->remove_flags);
-		}
-		break;
-	}
-	case MAIL_TRANSACTION_HEADER_UPDATE: {
-		const struct mail_transaction_header_update *u = data;
-
-		log_header_update(u);
-		break;
-	}
-	case MAIL_TRANSACTION_EXT_INTRO: {
-		const struct mail_transaction_ext_intro *intro = data;
-
-		prev_intro = *intro;
-		printf(" - ext_id = %u\n", intro->ext_id);
-		printf(" - reset_id = %u\n", intro->reset_id);
-		printf(" - hdr_size = %u\n", intro->hdr_size);
-		printf(" - record_size = %u\n", intro->record_size);
-		printf(" - record_align = %u\n", intro->record_align);
-		printf(" - flags = %u\n", intro->flags);
-		printf(" - name_size = %u\n", intro->name_size);
-		if (intro->name_size > 0) {
-			const char *name = (const char *)(intro+1);
-
-			printf(" - name = '%.*s'\n", intro->name_size, name);
-			if (*modseq == 0 && intro->name_size == 6 &&
-			    memcmp(name, "modseq", 6) == 0)
-				*modseq = 1;
-		}
-		break;
-	}
-	case MAIL_TRANSACTION_EXT_RESET: {
-		const struct mail_transaction_ext_reset *reset = data;
-
-		printf(" - new_reset_id = %u\n", reset->new_reset_id);
-		printf(" - preserve_data = %u\n", reset->preserve_data);
-		break;
-	}
-	case MAIL_TRANSACTION_EXT_HDR_UPDATE: {
-		const struct mail_transaction_ext_hdr_update *u = data;
-
-		printf(" - offset = %u, size = %u: ", u->offset, u->size);
-		print_data(u + 1, u->size);
-		printf("\n");
-		break;
-	}
-	case MAIL_TRANSACTION_EXT_REC_UPDATE: {
-		const struct mail_transaction_ext_rec_update *rec = data, *end;
-		size_t record_size;
-
-		end = CONST_PTR_OFFSET(data, size);
-		record_size = (sizeof(*rec) + prev_intro.record_size + 3) & ~3;
-		while (rec < end) {
-			printf(" - %u: ", rec->uid);
-			print_data(rec + 1, prev_intro.record_size);
-			printf("\n");
-			rec = CONST_PTR_OFFSET(rec, record_size);
-		}
-		break;
-	}
-	case MAIL_TRANSACTION_EXT_ATOMIC_INC: {
-		const struct mail_transaction_ext_atomic_inc *rec = data, *end;
-
-		end = CONST_PTR_OFFSET(data, size);
-		for (; rec < end; rec++) {
-			printf(" - %u: ", rec->uid);
-			if (rec->diff > 0)
-				printf("+%d\n", rec->diff);
-			else
-				printf("%d\n", rec->diff);
-		}
-		break;
-	}
-	case MAIL_TRANSACTION_KEYWORD_UPDATE: {
-		const struct mail_transaction_keyword_update *u = data;
-		const uint32_t *uid;
-		unsigned int uid_offset;
-
-		printf(" - modify=%d, name=%.*s, ",
-		       u->modify_type, u->name_size, (const char *)(u+1));
-
-		uid_offset = sizeof(*u) + u->name_size +
-			((u->name_size % 4) == 0 ? 0 : 4 - (u->name_size%4));
-		uid = (const uint32_t *)((const char *)u + uid_offset);
-		size -= uid_offset;
-
-		for (; size > 0; size -= sizeof(*uid)*2, uid += 2) {
-			printf("%u-%u,", uid[0], uid[1]);
-		}
-		printf("\n");
-		break;
-	}
-	case MAIL_TRANSACTION_KEYWORD_RESET: {
-		const struct mail_transaction_keyword_reset *u = data;
-
-		printf(" - ");
-		for (; size > 0; size -= sizeof(*u), u++) {
-			printf("%u-%u, ", u->uid1, u->uid2);
-		}
-		printf("\n");
-		break;
-	}
-	case MAIL_TRANSACTION_UID_UPDATE: {
-		const struct mail_transaction_uid_update *rec = data, *end;
-
-		end = CONST_PTR_OFFSET(data, size);
-		for (rec = data; rec < end; rec++) {
-			printf(" - old uid=%u new uid=%u\n",
-			       rec->old_uid, rec->new_uid);
-		}
-		break;
-	}
-	case MAIL_TRANSACTION_MODSEQ_UPDATE: {
-		const struct mail_transaction_modseq_update *rec = data, *end;
-
-		end = CONST_PTR_OFFSET(data, size);
-		for (rec = data; rec < end; rec++) {
-			printf(" - uid=%u modseq=%llu\n", rec->uid,
-			       ((unsigned long long)rec->modseq_high32 << 32) |
-			       rec->modseq_low32);
-		}
-		break;
-	}
-	default:
-		break;
-	}
-}
-
-static int dump_record(int fd, uint64_t *modseq)
-{
-	off_t offset;
-	ssize_t ret;
-	struct mail_transaction_header hdr;
-	unsigned int orig_size;
-
-	offset = lseek(fd, 0, SEEK_CUR);
-
-	ret = read(fd, &hdr, sizeof(hdr));
-	if (ret == 0)
-		return 0;
-
-	if (ret != sizeof(hdr)) {
-		i_fatal("rec hdr read() %"PRIuSIZE_T" != %"PRIuSIZE_T,
-			ret, sizeof(hdr));
-	}
-
-	orig_size = hdr.size;
-	hdr.size = mail_index_offset_to_uint32(hdr.size);
-	if (hdr.size == 0) {
-		printf("record: offset=%"PRIuUOFF_T", "
-		       "type=%s, size=broken (%x)\n",
-		       offset, log_record_type(hdr.type), orig_size);
-		return 0;
-	}
-
-	printf("record: offset=%"PRIuUOFF_T", type=%s, size=%u",
-	       offset, log_record_type(hdr.type), hdr.size);
-	if (*modseq > 0 && mail_transaction_header_has_modseq(&hdr)) {
-		*modseq += 1;
-		printf(", modseq=%llu", (unsigned long long)*modseq);
-	}
-	printf("\n");
-
-	if (hdr.size < 1024*1024) {
-		unsigned char *buf = t_malloc(hdr.size);
-
-		ret = read(fd, buf, hdr.size - sizeof(hdr));
-		if (ret != (ssize_t)(hdr.size - sizeof(hdr))) {
-			i_fatal("rec data read() %"PRIuSIZE_T" != %"PRIuSIZE_T,
-				ret, hdr.size - sizeof(hdr));
-		}
-		log_record_print(&hdr, buf, modseq);
-	} else {
-		lseek(fd, hdr.size - sizeof(hdr), SEEK_CUR);
-	}
-	return 1;
-}
-
-int main(int argc, const char *argv[])
-{
-	uint64_t modseq;
-	int fd, ret;
-
-	lib_init();
-
-	if (argc < 2)
-		i_fatal("Usage: logview dovecot.index.log");
-
-	fd = open(argv[1], O_RDONLY);
-	if (fd < 0) {
-		i_error("open(): %m");
-		return 1;
-	}
-
-	dump_hdr(fd, &modseq);
-	do {
-		T_BEGIN {
-			ret = dump_record(fd, &modseq);
-		} T_END;
-	} while (ret > 0);
-	return 0;
-}
--- a/src/util/mailboxlogview.c	Thu Oct 08 20:10:49 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "hex-binary.h"
-#include "mailbox-log.h"
-
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <time.h>
-
-static const char *unixdate2str(time_t timestamp)
-{
-	static char buf[64];
-	struct tm *tm;
-
-	tm = localtime(&timestamp);
-	strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
-	return buf;
-}
-
-static int dump_record(int fd)
-{
-	off_t offset;
-	ssize_t ret;
-	struct mailbox_log_record rec;
-	time_t timestamp;
-
-	offset = lseek(fd, 0, SEEK_CUR);
-
-	ret = read(fd, &rec, sizeof(rec));
-	if (ret == 0)
-		return 0;
-
-	if (ret != sizeof(rec)) {
-		i_fatal("rec read() %"PRIuSIZE_T" != %"PRIuSIZE_T,
-			ret, sizeof(rec));
-	}
-
-	printf("#%"PRIuUOFF_T": ", offset);
-	switch (rec.type) {
-	case MAILBOX_LOG_RECORD_DELETE_MAILBOX:
-		printf("delete-mailbox");
-		break;
-	case MAILBOX_LOG_RECORD_DELETE_DIR:
-		printf("delete-dir");
-		break;
-	case MAILBOX_LOG_RECORD_RENAME:
-		printf("rename");
-		break;
-	case MAILBOX_LOG_RECORD_SUBSCRIBE:
-		printf("subscribe");
-		break;
-	case MAILBOX_LOG_RECORD_UNSUBSCRIBE:
-		printf("unsubscribe");
-		break;
-	}
-	printf(" %s", binary_to_hex(rec.mailbox_guid,
-				    sizeof(rec.mailbox_guid)));
-
-	timestamp = ((uint32_t)rec.timestamp[0] << 24) |
-		((uint32_t)rec.timestamp[1] << 16) |
-		((uint32_t)rec.timestamp[2] << 8) |
-		(uint32_t)rec.timestamp[3];
-	printf(" (%s)\n", unixdate2str(timestamp));
-	return 1;
-}
-
-int main(int argc, const char *argv[])
-{
-	int fd, ret;
-
-	lib_init();
-
-	if (argc < 2)
-		i_fatal("Usage: logview dovecot.mailbox.log");
-
-	fd = open(argv[1], O_RDONLY);
-	if (fd < 0) {
-		i_error("open(): %m");
-		return 1;
-	}
-
-	do {
-		T_BEGIN {
-			ret = dump_record(fd);
-		} T_END;
-	} while (ret > 0);
-	return 0;
-}
--- a/src/util/threadview.c	Thu Oct 08 20:10:49 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,179 +0,0 @@
-/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "mmap-util.h"
-#include "mail-index-private.h"
-#include "mail-index-strmap.h"
-
-#include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
-
-static uint32_t max_likely_index;
-
-uint32_t mail_index_offset_to_uint32(uint32_t offset)
-{
-	const unsigned char *buf = (const unsigned char *) &offset;
-
-	if ((offset & 0x80808080) != 0x80808080)
-		return 0;
-
-	return (((uint32_t)buf[3] & 0x7f) << 2) |
-		(((uint32_t)buf[2] & 0x7f) << 9) |
-		(((uint32_t)buf[1] & 0x7f) << 16) |
-		(((uint32_t)buf[0] & 0x7f) << 23);
-}
-
-int mail_index_unpack_num(const uint8_t **p, const uint8_t *end,
-			  uint32_t *num_r)
-{
-	const uint8_t *c = *p;
-	uint32_t value = 0;
-	unsigned int bits = 0;
-
-	for (;;) {
-		if (unlikely(c == end)) {
-			/* we should never see EOF */
-			*num_r = 0;
-			return -1;
-		}
-
-		value |= (*c & 0x7f) << bits;
-		if (*c < 0x80)
-			break;
-
-		bits += 7;
-		c++;
-	}
-
-	if (unlikely(bits >= 32)) {
-		/* broken input */
-		*p = end;
-		*num_r = 0;
-		return -1;
-	}
-
-	*p = c + 1;
-	*num_r = value;
-	return 0;
-}
-
-static size_t dump_hdr(const struct mail_index_strmap_header *hdr)
-{
-	printf("version = %u\n", hdr->version);
-	printf("uid validity = %u\n", hdr->uid_validity);
-	return sizeof(*hdr);
-}
-
-static int dump_record(const uint8_t **p, const uint8_t *end, uint32_t *uid)
-{
-	uint32_t uid_diff, n, i, count, crc32, idx;
-	size_t size;
-
-	/* <uid diff> <n> <crc32>*count <str_idx>*count */
-	if (mail_index_unpack_num(p, end, &uid_diff) <  0)
-		return -1;
-	*uid += uid_diff;
-
-	if (mail_index_unpack_num(p, end, &n) <  0)
-		return -1;
-	printf(" - uid %u: n=%u\n", *uid, n);
-
-	count = n < 2 ? n + 1 : n;
-	size = sizeof(crc32)*count + sizeof(idx)*count;
-	if (*p + size > end)
-		return -1;
-	for (i = 0; i < count; i++) {
-		if (i == 0)
-			printf("   - message-id: ");
-		else if (i == 1) {
-			if (n == 1)
-				printf("   - in-reply-to: ");
-			else
-				printf("   - references[1]: ");
-		} else {
-			printf("   - references[%u]: ", i);
-		}
-		memcpy(&crc32, *p + sizeof(crc32)*i, sizeof(crc32));
-		memcpy(&idx, *p + sizeof(crc32)*count + sizeof(idx)*i, sizeof(idx));
-		printf("crc32=%08x index=%u\n", crc32, idx);
-		if (idx > max_likely_index)
-			printf(" - index probably broken\n");
-	}
-	*p += size;
-	return 0;
-}
-
-static int dump_block(const uint8_t *data, const uint8_t *end, uint32_t *uid)
-{
-	const uint8_t *p;
-	uint32_t block_size;
-
-	if (data + 4 >= end)
-		return -1;
-
-	memcpy(&block_size, data, sizeof(block_size));
-	block_size = mail_index_offset_to_uint32(block_size) >> 2;
-	printf(" - block_size=%u\n", block_size);
-	if (block_size == 0) {
-		/* finished */
-		return -1;
-	}
-	if (data + sizeof(block_size) + block_size > end) {
-		printf("   - broken!\n");
-		return -1;
-	}
-	p = data + sizeof(block_size);
-	end = p + block_size;
-
-	*uid += 1;
-	while (p != end) {
-		if (dump_record(&p, end, uid) < 0) {
-			printf(" - broken\n");
-			return -1;
-		}
-	}
-	return p - data;
-}
-
-int main(int argc, const char *argv[])
-{
-	unsigned int pos;
-	const void *map, *end;
-	struct stat st;
-	uint32_t uid;
-	int fd, ret;
-
-	lib_init();
-
-	if (argc < 2)
-		i_fatal("Usage: threadview dovecot.index.thread");
-
-	fd = open(argv[1], O_RDONLY);
-	if (fd < 0) {
-		i_error("open(%s) failed: %m", argv[1]);
-		return 1;
-	}
-
-	if (fstat(fd, &st) < 0) {
-		i_error("fstat(%s) failed: %m", argv[1]);
-		return 1;
-	}
-	max_likely_index = (st.st_size / 8) * 2;
-
-	map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-	if (map == MAP_FAILED)
-		i_fatal("mmap() failed: %m");
-	end = CONST_PTR_OFFSET(map, st.st_size);
-	pos = dump_hdr(map);
-	uid = 0;
-	do {
-		printf("block at offset %u:\n", pos);
-		T_BEGIN {
-			ret = dump_block(CONST_PTR_OFFSET(map, pos), end, &uid);
-			pos += ret;
-		} T_END;
-	} while (ret > 0);
-	return 0;
-}