Mercurial > dovecot > original-hg > dovecot-1.2
changeset 5944:760f910004d9 HEAD
Created a new extensible version 3 dovecot-uidlist file.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 10 Jul 2007 22:30:51 +0300 |
parents | 6c3c54dc67fc |
children | ab487f1de905 |
files | src/lib-storage/index/maildir/maildir-uidlist.c src/lib-storage/index/maildir/maildir-uidlist.h |
diffstat | 2 files changed, 217 insertions(+), 42 deletions(-) [+] |
line wrap: on
line diff
--- 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 @@ 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 @@ 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; @@ -94,6 +118,9 @@ 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 @@ 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 @@ 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 @@ 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; + struct mail_storage *storage = &uidlist->mbox->storage->storage; + struct maildir_uidlist_rec *rec; + uint32_t uid; - uid = flags = 0; + uid = 0; while (*line >= '0' && *line <= '9') { uid = uid*10 + (*line - '0'); line++; @@ -240,12 +306,12 @@ 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 @@ } 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 @@ 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(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 @@ 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 @@ 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) { enum maildir_uidlist_rec_flag flags; @@ -628,10 +768,9 @@ struct mail_storage *storage = &uidlist->mbox->storage->storage; struct maildir_uidlist_iter_ctx *iter; struct ostream *output; + struct maildir_uidlist_rec *rec; string_t *str; - uint32_t uid; - enum maildir_uidlist_rec_flag flags; - const char *filename; + const char *p; int ret; i_assert(fd != -1); @@ -641,18 +780,15 @@ if (output->offset == 0) { i_assert(first_idx == 0); - uidlist->version = 1; - if (uidlist->uid_validity == 0) { - /* Get UIDVALIDITY from index */ - const struct mail_index_header *hdr; + uidlist->version = 3; - hdr = mail_index_get_header(uidlist->mbox->ibox.view); - uidlist->uid_validity = hdr->uid_validity; - i_assert(uidlist->uid_validity != 0); + str_printfa(str, "%u V%u N%u", uidlist->version, + uidlist->uid_validity, uidlist->next_uid); + if (str_len(uidlist->hdr_extensions) > 0) { + str_append_c(str, ' '); + str_append_str(str, uidlist->hdr_extensions); } - - str_printfa(str, "%u %u %u\n", uidlist->version, - uidlist->uid_validity, uidlist->next_uid); + str_append_c(str, '\n'); o_stream_send(output, str_data(str), str_len(str)); } else { i_assert(first_idx != 0); @@ -661,9 +797,17 @@ iter = maildir_uidlist_iter_init(uidlist->mbox->uidlist); iter->next += first_idx; - while (maildir_uidlist_iter_next(iter, &uid, &flags, &filename)) { + while (maildir_uidlist_iter_next_rec(iter, &rec)) { str_truncate(str, 0); - str_printfa(str, "%u %s\n", uid, filename); + str_printfa(str, "%u", rec->uid); + if (rec->extensions != NULL) { + for (p = rec->extensions; *p != '\0'; ) { + str_append_c(str, ' '); + str_append(str, p); + p += strlen(p) + 1; + } + } + str_printfa(str, " :%s\n", rec->filename); o_stream_send(output, str_data(str), str_len(str)); } maildir_uidlist_iter_deinit(iter); @@ -1150,10 +1294,8 @@ ctx->next += idx; } -bool maildir_uidlist_iter_next(struct maildir_uidlist_iter_ctx *ctx, - uint32_t *uid_r, - enum maildir_uidlist_rec_flag *flags_r, - const char **filename_r) +static bool maildir_uidlist_iter_next_rec(struct maildir_uidlist_iter_ctx *ctx, + struct maildir_uidlist_rec **rec_r) { struct maildir_uidlist_rec *rec; @@ -1169,6 +1311,20 @@ ctx->prev_uid = rec->uid; ctx->next++; + *rec_r = rec; + return TRUE; +} + +bool maildir_uidlist_iter_next(struct maildir_uidlist_iter_ctx *ctx, + uint32_t *uid_r, + enum maildir_uidlist_rec_flag *flags_r, + const char **filename_r) +{ + struct maildir_uidlist_rec *rec; + + if (!maildir_uidlist_iter_next_rec(ctx, &rec)) + return FALSE; + *uid_r = rec->uid; *flags_r = rec->flags; *filename_r = rec->filename;
--- a/src/lib-storage/index/maildir/maildir-uidlist.h Wed Jul 11 01:41:49 2007 +0300 +++ b/src/lib-storage/index/maildir/maildir-uidlist.h Tue Jul 10 22:30:51 2007 +0300 @@ -17,6 +17,21 @@ MAILDIR_UIDLIST_REC_FLAG_RACING = 0x10 }; +enum maildir_uidlist_hdr_ext_key { + MAILDIR_UIDLIST_HDR_EXT_UID_VALIDITY = 'V', + MAILDIR_UIDLIST_HDR_EXT_NEXT_UID = 'N', + /* POP3 UIDL format unless overridden by records */ + MAILDIR_UIDLIST_HDR_EXT_POP3_UIDL_FORMAT = 'P' +}; + +enum maildir_uidlist_rec_ext_key { + /* Virtual message size. If filename also contains ,W=<vsize> this + isn't written to uidlist. */ + MAILDIR_UIDLIST_REC_EXT_VSIZE = 'W', + /* POP3 UIDL overriding the default format */ + MAILDIR_UIDLIST_REC_EXT_POP3_UIDL = 'P' +}; + int maildir_uidlist_lock(struct maildir_uidlist *uidlist); int maildir_uidlist_try_lock(struct maildir_uidlist *uidlist); int maildir_uidlist_lock_touch(struct maildir_uidlist *uidlist); @@ -33,6 +48,10 @@ const char * maildir_uidlist_lookup(struct maildir_uidlist *uidlist, uint32_t uid, enum maildir_uidlist_rec_flag *flags_r); +/* Returns extension's value or NULL if it doesn't exist. */ +const char * +maildir_uidlist_lookup_ext(struct maildir_uidlist *uidlist, uint32_t uid, + enum maildir_uidlist_rec_ext_key key); /* Returns TRUE if mail with given UID is recent. */ bool maildir_uidlist_is_recent(struct maildir_uidlist *uidlist, uint32_t uid); /* Returns number of recent messages. */