mail-index-sync-keywords.c revision 41bb0aa8e357876bc9a1916a37c9e3e78e5f8185
1337N/A/* Copyright (C) 2004 Timo Sirainen */
1337N/A
1337N/A#include "lib.h"
1337N/A#include "array.h"
1337N/A#include "buffer.h"
0N/A#include "mail-index-view-private.h"
0N/A#include "mail-index-sync-private.h"
0N/A#include "mail-transaction-log.h"
1298N/A
1298N/Astatic int
1298N/Akeyword_lookup(struct mail_index_sync_map_ctx *ctx,
1298N/A const char *keyword_name, unsigned int *idx_r)
1298N/A{
1298N/A struct mail_index_map *map = ctx->view->map;
1298N/A const unsigned int *idx_map;
1298N/A unsigned int i, count, keyword_idx;
1298N/A
1298N/A if (!ctx->keywords_read) {
1298N/A if (mail_index_map_read_keywords(ctx->view->index, map) < 0)
1298N/A return -1;
1298N/A ctx->keywords_read = TRUE;
1298N/A }
1298N/A if (mail_index_keyword_lookup(ctx->view->index, keyword_name,
0N/A FALSE, &keyword_idx) &&
0N/A array_is_created(&map->keyword_idx_map)) {
0N/A /* FIXME: slow. maybe create index -> file mapping as well */
1298N/A idx_map = array_get(&map->keyword_idx_map, &count);
0N/A for (i = 0; i < count; i++) {
1298N/A if (idx_map[i] == keyword_idx) {
1298N/A *idx_r = i;
1298N/A return 1;
0N/A }
1298N/A }
1298N/A }
1298N/A
1298N/A *idx_r = (unsigned int)-1;
1298N/A return 0;
1073N/A}
1298N/A
0N/Astatic buffer_t *
1298N/Akeywords_get_header_buf(struct mail_index_map *map,
1298N/A const struct mail_index_ext *ext,
0N/A unsigned int new_count, unsigned int *keywords_count_r,
0N/A size_t *rec_offset_r, size_t *name_offset_root_r,
115N/A size_t *name_offset_r)
115N/A{
1298N/A buffer_t *buf;
1298N/A const struct mail_index_keyword_header *kw_hdr;
1298N/A const struct mail_index_keyword_header_rec *kw_rec;
1298N/A const char *name;
1298N/A struct mail_index_keyword_header new_kw_hdr;
1337N/A uint32_t offset;
1298N/A
0N/A kw_hdr = CONST_PTR_OFFSET(map->hdr_base, ext->hdr_offset);
1298N/A kw_rec = (const void *)(kw_hdr + 1);
1298N/A name = (const char *)(kw_rec + kw_hdr->keywords_count);
1298N/A
1298N/A if (kw_hdr->keywords_count == 0)
1298N/A return NULL;
1298N/A
1298N/A new_kw_hdr = *kw_hdr;
0N/A new_kw_hdr.keywords_count += new_count;
1298N/A *keywords_count_r = new_kw_hdr.keywords_count;
1298N/A
1298N/A offset = kw_rec[kw_hdr->keywords_count-1].name_offset;
1298N/A offset += strlen(name + offset) + 1;
1298N/A
1298N/A buf = buffer_create_dynamic(pool_datastack_create(), 512);
1298N/A buffer_append(buf, &new_kw_hdr, sizeof(new_kw_hdr));
1298N/A buffer_append(buf, kw_rec, sizeof(*kw_rec) * kw_hdr->keywords_count);
1298N/A *rec_offset_r = buf->used;
1298N/A buffer_write(buf, buf->used + sizeof(*kw_rec) * new_count,
1298N/A name, offset);
1298N/A *name_offset_root_r = buf->used;
1298N/A *name_offset_r = offset;
1298N/A return buf;
1298N/A}
1298N/A
1298N/Astatic int keywords_ext_register(struct mail_index_sync_map_ctx *ctx,
1298N/A uint32_t ext_id, uint32_t reset_id,
1298N/A uint32_t hdr_size, uint32_t keywords_count)
1298N/A{
1298N/A buffer_t *ext_intro_buf;
0N/A struct mail_transaction_ext_intro *u;
1298N/A
1298N/A ext_intro_buf =
1298N/A buffer_create_static_hard(pool_datastack_create(),
1298N/A sizeof(*u) + sizeof("keywords")-1);
1298N/A
1298N/A u = buffer_append_space_unsafe(ext_intro_buf, sizeof(*u));
1298N/A u->ext_id = ext_id;
1298N/A u->reset_id = reset_id;
1298N/A u->hdr_size = hdr_size;
1298N/A u->record_size = (keywords_count + CHAR_BIT - 1) / CHAR_BIT;
1298N/A if ((u->record_size % 4) != 0) {
1298N/A /* since we aren't properly aligned anyway,
1298N/A reserve one extra byte for future */
1298N/A u->record_size++;
1298N/A }
1298N/A u->record_align = 1;
1298N/A
1298N/A if (ext_id == (uint32_t)-1) {
1298N/A u->name_size = strlen("keywords");
1298N/A buffer_append(ext_intro_buf, "keywords", u->name_size);
1298N/A }
1298N/A
1298N/A return mail_index_sync_ext_intro(ctx, u);
1298N/A}
1298N/A
1298N/Astatic int
975N/Akeywords_header_add(struct mail_index_sync_map_ctx *ctx,
975N/A const char *keyword_name, unsigned int *keyword_idx_r)
1298N/A{
1298N/A struct mail_index_map *map = ctx->view->map;
1298N/A const struct mail_index_ext *ext = NULL;
0N/A struct mail_index_keyword_header *kw_hdr;
0N/A struct mail_index_keyword_header_rec kw_rec;
1298N/A uint32_t ext_id;
975N/A buffer_t *buf = NULL;
975N/A size_t keyword_len, rec_offset, name_offset, name_offset_root;
1298N/A unsigned int keywords_count;
1298N/A int ret;
1298N/A
1298N/A ext_id = mail_index_map_lookup_ext(map, "keywords");
1298N/A if (ext_id != (uint32_t)-1) {
975N/A /* update existing header */
1298N/A ext = array_idx(&map->extensions, ext_id);
1298N/A buf = keywords_get_header_buf(map, ext, 1, &keywords_count,
1298N/A &rec_offset, &name_offset_root,
979N/A &name_offset);
1298N/A }
975N/A
1298N/A if (buf == NULL) {
1298N/A /* create new / replace broken header */
975N/A buf = buffer_create_dynamic(pool_datastack_create(), 512);
975N/A kw_hdr = buffer_append_space_unsafe(buf, sizeof(*kw_hdr));
975N/A kw_hdr->keywords_count = 1;
1298N/A
975N/A keywords_count = kw_hdr->keywords_count;
975N/A rec_offset = buf->used;
975N/A name_offset_root = rec_offset +
975N/A kw_hdr->keywords_count * sizeof(kw_rec);
1343N/A name_offset = 0;
975N/A }
1343N/A
975N/A /* add the keyword */
975N/A memset(&kw_rec, 0, sizeof(kw_rec));
975N/A kw_rec.name_offset = name_offset;
975N/A
975N/A keyword_len = strlen(keyword_name) + 1;
975N/A buffer_write(buf, rec_offset, &kw_rec, sizeof(kw_rec));
1298N/A buffer_write(buf, name_offset_root, keyword_name, keyword_len);
1298N/A
975N/A rec_offset += sizeof(kw_rec);
1298N/A kw_rec.name_offset += keyword_len;
1298N/A name_offset_root += keyword_len;
1298N/A
1298N/A if ((buf->used % 4) != 0)
1298N/A buffer_append_zero(buf, 4 - (buf->used % 4));
1298N/A
1298N/A if (ext == NULL || buf->used > ext->hdr_size ||
975N/A (uint32_t)ext->record_size * CHAR_BIT < keywords_count) {
975N/A /* if we need to grow the buffer, add some padding */
975N/A buffer_append_zero(buf, 128);
1298N/A
975N/A ret = keywords_ext_register(ctx, ext_id,
975N/A ext == NULL ? 0 : ext->reset_id,
975N/A buf->used, keywords_count);
1298N/A if (ret <= 0)
975N/A return ret;
975N/A
975N/A /* map may have changed */
1298N/A map = ctx->view->map;
975N/A
1301N/A if (ext == NULL) {
975N/A ext_id = mail_index_map_lookup_ext(map, "keywords");
1301N/A i_assert(ext_id != (uint32_t)-1);
1301N/A }
1301N/A ext = array_idx(&map->extensions, ext_id);
975N/A
975N/A i_assert(ext->hdr_size == buf->used);
975N/A }
975N/A
1298N/A buffer_copy(map->hdr_copy_buf, ext->hdr_offset,
975N/A buf, 0, buf->used);
975N/A map->hdr_base = map->hdr_copy_buf->data;
975N/A
975N/A *keyword_idx_r = keywords_count - 1;
1298N/A ctx->keywords_read = FALSE;
1298N/A return 1;
975N/A}
1298N/A
1298N/Astatic int
1298N/Akeywords_update_records(struct mail_index_view *view,
1298N/A const struct mail_index_ext *ext,
1298N/A unsigned int keyword_idx,
975N/A enum modify_type type,
975N/A uint32_t uid1, uint32_t uid2)
975N/A{
975N/A struct mail_index_record *rec;
975N/A unsigned char *data, data_mask;
1298N/A unsigned int data_offset;
1298N/A uint32_t seq1, seq2;
975N/A
1298N/A i_assert(keyword_idx != (unsigned int)-1);
975N/A
975N/A if (mail_index_lookup_uid_range(view, uid1, uid2, &seq1, &seq2) < 0)
975N/A return -1;
975N/A
975N/A if (seq1 == 0)
1314N/A return 1;
1298N/A
975N/A if (view->map->write_seq_first == 0 ||
1314N/A view->map->write_seq_first > seq1)
1314N/A view->map->write_seq_first = seq1;
1314N/A if (view->map->write_seq_last < seq2)
1314N/A view->map->write_seq_last = seq2;
975N/A
1298N/A data_offset = keyword_idx / CHAR_BIT;
1298N/A data_mask = 1 << (keyword_idx % CHAR_BIT);
1298N/A
975N/A i_assert(data_offset < ext->record_size);
1298N/A data_offset += ext->record_offset;
975N/A
975N/A switch (type) {
1298N/A case MODIFY_ADD:
1298N/A for (seq1--; seq1 < seq2; seq1++) {
1314N/A rec = MAIL_INDEX_MAP_IDX(view->map, seq1);
1298N/A data = PTR_OFFSET(rec, data_offset);
1298N/A *data |= data_mask;
975N/A }
984N/A break;
984N/A case MODIFY_REMOVE:
984N/A data_mask = ~data_mask;
1298N/A for (seq1--; seq1 < seq2; seq1++) {
1298N/A rec = MAIL_INDEX_MAP_IDX(view->map, seq1);
984N/A data = PTR_OFFSET(rec, data_offset);
1298N/A *data &= data_mask;
1298N/A }
1298N/A break;
975N/A default:
1311N/A i_unreached();
1314N/A }
1311N/A return 1;
1311N/A}
1314N/A
984N/Aint mail_index_sync_keywords(struct mail_index_sync_map_ctx *ctx,
984N/A const struct mail_transaction_header *hdr,
975N/A const struct mail_transaction_keyword_update *rec)
1298N/A{
1298N/A const char *keyword_name;
0N/A const struct mail_index_ext *ext;
1298N/A const uint32_t *uid, *end;
1298N/A uint32_t seqset_offset, ext_id;
1298N/A unsigned int keyword_idx;
1298N/A int ret;
1298N/A
299N/A seqset_offset = sizeof(*rec) + rec->name_size;
911N/A if ((seqset_offset % 4) != 0)
299N/A seqset_offset += 4 - (seqset_offset % 4);
911N/A
0N/A if (seqset_offset > hdr->size) {
0N/A mail_transaction_log_view_set_corrupted(ctx->view->log_view,
0N/A "Keyword header ended unexpectedly");
0N/A return -1;
0N/A }
1298N/A
0N/A uid = CONST_PTR_OFFSET(rec, seqset_offset);
0N/A end = CONST_PTR_OFFSET(rec, hdr->size);
0N/A
1298N/A if (uid == end) {
1298N/A mail_transaction_log_view_set_corrupted(ctx->view->log_view,
1298N/A "Keyword sequence list empty");
0N/A return -1;
0N/A }
0N/A
0N/A keyword_name = t_strndup(rec + 1, rec->name_size);
0N/A if (keyword_lookup(ctx, keyword_name, &keyword_idx) < 0)
1298N/A return -1;
0N/A if (keyword_idx == (unsigned int)-1) {
0N/A ret = keywords_header_add(ctx, keyword_name, &keyword_idx);
1299N/A if (ret <= 0)
1299N/A return ret;
1290N/A }
1299N/A
1299N/A ext_id = mail_index_map_lookup_ext(ctx->view->map, "keywords");
1299N/A if (ext_id == (uint32_t)-1) {
0N/A /* nothing to do */
1343N/A i_assert(rec->modify_type == MODIFY_REMOVE);
1343N/A return 1;
1343N/A }
1343N/A
1343N/A ext = array_idx(&ctx->view->map->extensions, ext_id);
1343N/A if (ext->record_size == 0) {
1343N/A /* nothing to do */
1343N/A i_assert(rec->modify_type == MODIFY_REMOVE);
1343N/A return 1;
1343N/A }
1343N/A
1343N/A if (!ctx->keywords_read) {
1343N/A if (mail_index_map_read_keywords(ctx->view->index,
1343N/A ctx->view->map) < 0)
1343N/A return -1;
1343N/A ctx->keywords_read = TRUE;
1298N/A }
1343N/A
1343N/A while (uid+2 <= end) {
1343N/A if (uid[0] > uid[1] || uid[0] == 0) {
1343N/A mail_transaction_log_view_set_corrupted(
1343N/A ctx->view->log_view,
1343N/A "Keyword record UIDs are broken");
1343N/A return -1;
0N/A }
1343N/A
1343N/A ret = keywords_update_records(ctx->view, ext, keyword_idx,
1343N/A rec->modify_type,
1343N/A uid[0], uid[1]);
1343N/A if (ret <= 0)
1343N/A return ret;
1343N/A
1343N/A uid += 2;
1343N/A }
1343N/A
1343N/A return 1;
0N/A}
0N/A
979N/A
979N/Aint
0N/Amail_index_sync_keywords_reset(struct mail_index_sync_map_ctx *ctx,
0N/A const struct mail_transaction_header *hdr,
0N/A const struct mail_transaction_keyword_reset *r)
979N/A{
979N/A struct mail_index_map *map = ctx->view->map;
979N/A struct mail_index_record *rec;
0N/A const struct mail_index_ext *ext;
979N/A const struct mail_transaction_keyword_reset *end;
0N/A uint32_t ext_id, seq1, seq2;
979N/A
0N/A ext_id = mail_index_map_lookup_ext(map, "keywords");
116N/A if (ext_id == (uint32_t)-1) {
116N/A /* nothing to do */
979N/A return 1;
116N/A }
979N/A
979N/A ext = array_idx(&map->extensions, ext_id);
116N/A end = CONST_PTR_OFFSET(r, hdr->size);
979N/A for (; r != end; r++) {
979N/A if (mail_index_lookup_uid_range(ctx->view, r->uid1, r->uid2,
979N/A &seq1, &seq2) < 0)
116N/A return -1;
979N/A if (seq1 == 0)
979N/A continue;
979N/A
979N/A if (map->write_seq_first == 0 || map->write_seq_first > seq1)
979N/A map->write_seq_first = seq1;
116N/A if (map->write_seq_last < seq2)
979N/A map->write_seq_last = seq2;
116N/A
979N/A for (seq1--; seq1 < seq2; seq1++) {
116N/A rec = MAIL_INDEX_MAP_IDX(map, seq1);
979N/A memset(PTR_OFFSET(rec, ext->record_offset),
979N/A 0, ext->record_size);
979N/A }
979N/A }
979N/A return 1;
116N/A}
979N/A