mail-index-update.c revision 54d1312e1be5a84a9f9585f9f5e1f884f1d37d0e
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (C) 2002 Timo Sirainen */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "lib.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "buffer.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "str.h"
3343a61404603b21c246783a7963b77833095f31Timo Sirainen#include "istream.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "ioloop.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "str.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "message-date.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "message-parser.h"
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen#include "message-part-serialize.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "message-size.h"
3ed2d0f6b5e67e2663d44489d9da3176823789a8Timo Sirainen#include "imap-envelope.h"
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen#include "imap-bodystructure.h"
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen#include "mail-index.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "mail-index-data.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#include "mail-index-util.h"
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenstruct mail_index_update {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen pool_t pool;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen struct mail_index *index;
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen struct mail_index_record *rec;
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen struct mail_index_data_record_header data_hdr;
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen unsigned int updated_fields;
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen void *fields[DATA_FIELD_MAX_BITS];
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen size_t field_sizes[DATA_FIELD_MAX_BITS];
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen size_t field_extra_sizes[DATA_FIELD_MAX_BITS];
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen};
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainenstruct mail_index_update *
3f190f4cbb9233a3a6830956cb5c7ae56a577b79Timo Sirainenmail_index_update_begin(struct mail_index *index, struct mail_index_record *rec)
3f190f4cbb9233a3a6830956cb5c7ae56a577b79Timo Sirainen{
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen pool_t pool;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen struct mail_index_update *update;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen struct mail_index_data_record_header *data_hdr;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen pool = pool_alloconly_create("mail_index_update", 4096);
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen update = p_new(pool, struct mail_index_update, 1);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen update->pool = pool;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen update->index = index;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen update->rec = rec;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen data_hdr = mail_index_data_lookup_header(index->data, rec);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (data_hdr != NULL)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen memcpy(&update->data_hdr, data_hdr, sizeof(*data_hdr));
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen return update;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen}
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainenstatic int mail_field_get_index(enum mail_data_field field)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen{
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen unsigned int i, mask;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen for (i = 0, mask = 1; i < DATA_FIELD_MAX_BITS; i++, mask <<= 1) {
0d16525a729011f4fced989a3da74d755ea49e6dTimo Sirainen if (field == mask)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen return i;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen }
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen return -1;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen}
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenstatic void get_data_block_sizes(struct mail_index_update *update,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen size_t *min_size, size_t *max_size,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen int *no_grown_fields)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen{
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen struct mail_index_data_record *rec;
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen enum mail_data_field field;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen unsigned int field_min_size;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen int i, field_exists;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen *min_size = *max_size = sizeof(struct mail_index_data_record_header);
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen *no_grown_fields = TRUE;
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen rec = mail_index_data_lookup(update->index->data, update->rec, 0);
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen for (i = 0, field = 1; field != DATA_FIELD_LAST; i++, field <<= 1) {
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen field_exists = rec != NULL && rec->field == field;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen if (update->fields[i] != NULL) {
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen /* value was modified - use it */
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi field_min_size = INDEX_ALIGN(update->field_sizes[i]);
d42eb03b3a4e79a2da22a1be2de59b95660af2beTimo Sirainen *min_size += SIZEOF_MAIL_INDEX_DATA + field_min_size;
573f0491a5733fe21fa062a455acb4790b4e0499Timo Sirainen *max_size += SIZEOF_MAIL_INDEX_DATA +
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen INDEX_ALIGN(update->field_sizes[i] +
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen update->field_extra_sizes[i]);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (!field_exists ||
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen rec->full_field_size < field_min_size)
f0a2d04321ba456e5c5ba821c0d1ed9e8e0e2e08Timo Sirainen *no_grown_fields = FALSE;
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen } else if (field_exists) {
8ed8c821ba8aab0b4ed0375f87d48737ef0e0d8eTimo Sirainen /* use the old value */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen *min_size += SIZEOF_MAIL_INDEX_DATA +
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen rec->full_field_size;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen *max_size += SIZEOF_MAIL_INDEX_DATA +
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen rec->full_field_size;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen }
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen if (field_exists) {
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen rec = mail_index_data_next(update->index->data,
817d027593510c3ba70ad542ce0011f5f6916d1eTimo Sirainen update->rec, rec);
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen }
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen }
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen}
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainenstatic size_t get_max_align_size(size_t base, size_t extra, size_t *max_extra)
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen{
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen size_t size;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen size = INDEX_ALIGN(base);
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen extra -= size - base;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen if (*max_extra < INDEX_ALIGN_SIZE || extra == 0)
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen return size; /* no extra / extra went into alignment */
3d4c24127f4f83259c0f81851184abc34793dbe0Timo Sirainen
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen extra = INDEX_ALIGN(extra);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen if (extra > *max_extra) {
3d4c24127f4f83259c0f81851184abc34793dbe0Timo Sirainen /* partial */
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen extra = *max_extra & ~(size_t)(INDEX_ALIGN_SIZE-1);
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen i_assert(extra <= *max_extra);
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen }
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen *max_extra -= extra;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen return size + extra;
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen}
0a601ada15c7fe82f0db895fc2068b71b3a5243cTimo Sirainen
be5773cb4d6edae8a5d9f300c3c7375cdd33826eJosef 'Jeff' Sipek/* extra_size is the amount of data in data_size which can be used for
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen field_extra_sizes */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenstatic void *create_data_block(struct mail_index_update *update,
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen size_t data_size, size_t extra_size)
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen{
c000c8eca8f24b2a0c76393ec4bbf76a505a4983Timo Sirainen struct mail_index_data_record_header *dest_hdr;
424236b2b88a5a7bbde5cf6a6b32189ca3437629Timo Sirainen struct mail_index_data_record *rec, *destrec;
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen enum mail_data_field field;
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen buffer_t *buf;
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen const void *src;
04ab375449dd97eed50ada88dd0df2abab01cfeeTimo Sirainen size_t src_size;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen size_t full_field_size;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen int i;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen i_assert(data_size <= UINT_MAX);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen buf = buffer_create_static_hard(update->pool, data_size);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen /* set header */
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen dest_hdr = buffer_append_space(buf, sizeof(*dest_hdr));
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen memcpy(dest_hdr, &update->data_hdr, sizeof(*dest_hdr));
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen dest_hdr->data_size = data_size;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen /* set fields */
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen rec = mail_index_data_lookup(update->index->data, update->rec, 0);
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen for (i = 0, field = 1; field != DATA_FIELD_LAST; i++, field <<= 1) {
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen if (update->fields[i] != NULL) {
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen /* value was modified - use it */
761c441db58493fcf10d3418b0cabadc3028cfb6Timo Sirainen full_field_size =
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen get_max_align_size(update->field_sizes[i],
6060b7c8edf8fce73470d0df6a2479b69b01c537Timo Sirainen update->field_extra_sizes[i],
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen &extra_size);
7501b9f694460101b41d1d708ebc3ec2b0400b1cTimo Sirainen src = update->fields[i];
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen src_size = update->field_sizes[i];
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen } else if (rec != NULL && rec->field == field) {
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen /* use the old value */
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen full_field_size = rec->full_field_size;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen src = rec->data;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen src_size = rec->full_field_size;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen } else {
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen /* the field doesn't exist, jump to next */
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen continue;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen }
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen i_assert((full_field_size % INDEX_ALIGN_SIZE) == 0);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen destrec = buffer_append_space(buf, SIZEOF_MAIL_INDEX_DATA +
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen full_field_size);
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen destrec->field = field;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen destrec->full_field_size = full_field_size;
1b5366b2234892f8930a29351da06b193e385150Timo Sirainen memcpy(destrec->data, src, src_size);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if (rec != NULL && rec->field == field) {
e5afebd2df1d4990f7bec2a839260ff2e6d78168Timo Sirainen rec = mail_index_data_next(update->index->data,
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen update->rec, rec);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen }
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen }
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return buffer_free_without_data(buf);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen}
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen/* Append all the data at the end of the data file and update
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen the index's data position */
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainenstatic int update_by_append(struct mail_index_update *update,
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen size_t data_size, size_t extra_size)
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen{
761c441db58493fcf10d3418b0cabadc3028cfb6Timo Sirainen void *mem;
690bafa70767e3f6e98bbfd62ad4a26be2387ea9Timo Sirainen uoff_t fpos;
6060b7c8edf8fce73470d0df6a2479b69b01c537Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen mem = create_data_block(update, data_size, extra_size);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
23bdbb7b1831785c6ba6df190f6369da882d2b9dTimo Sirainen /* append the data at the end of the data file */
3343a61404603b21c246783a7963b77833095f31Timo Sirainen fpos = mail_index_data_append(update->index->data, mem, data_size);
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen if (fpos == 0)
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen return FALSE;
3343a61404603b21c246783a7963b77833095f31Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen /* the old data is discarded */
3343a61404603b21c246783a7963b77833095f31Timo Sirainen (void)mail_index_data_delete(update->index->data, update->rec);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen /* update index file position - it's mmap()ed so it'll be written
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen into disk when index is unlocked. */
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen update->rec->data_position = fpos;
return TRUE;
}
/* Replace the whole block - assumes there's enough space to do it */
static void update_by_replace_block(struct mail_index_update *update,
size_t extra_size)
{
struct mail_index_data_record_header *data_hdr;
size_t data_size;
void *mem;
data_hdr = mail_index_data_lookup_header(update->index->data,
update->rec);
data_size = update->data_hdr.data_size;
i_assert(data_size == data_hdr->data_size);
mem = create_data_block(update, data_size, extra_size);
memcpy(data_hdr, mem, data_size);
/* clear the extra space. not really needed. */
memset((char *) data_hdr + data_size, 0,
data_hdr->data_size - data_size);
mail_index_data_mark_modified(update->index->data);
}
/* Replace the modified fields in the file - assumes there's enough
space to do it */
static void update_by_replace_fields(struct mail_index_update *update)
{
struct mail_index_data_record_header *data_hdr;
struct mail_index_data_record *rec;
size_t field_size;
int index;
/* update header */
data_hdr = mail_index_data_lookup_header(update->index->data,
update->rec);
memcpy(data_hdr, &update->data_hdr, sizeof(*data_hdr));
rec = mail_index_data_lookup(update->index->data, update->rec, 0);
while (rec != NULL) {
if (rec->field & update->updated_fields) {
/* field was changed */
index = mail_field_get_index(rec->field);
i_assert(index >= 0);
field_size = update->field_sizes[index];
i_assert(field_size <= rec->full_field_size);
memcpy(rec->data, update->fields[index], field_size);
/* clear the extra space. not really needed. */
memset(rec->data + field_size, 0,
rec->full_field_size - field_size);
}
rec = mail_index_data_next(update->index->data,
update->rec, rec);
}
mail_index_data_mark_modified(update->index->data);
}
int mail_index_update_end(struct mail_index_update *update)
{
struct mail_index_data_record_header *data_hdr;
size_t min_size, max_size, extra_size;
int no_grown_fields, failed = FALSE;
i_assert(update->index->lock_type == MAIL_LOCK_EXCLUSIVE);
if (update->updated_fields != 0) {
/* if fields don't fit to allocated data block, we have
to move it to end of file */
get_data_block_sizes(update, &min_size, &max_size,
&no_grown_fields);
extra_size = max_size - min_size;
data_hdr = mail_index_data_lookup_header(update->index->data,
update->rec);
if (no_grown_fields)
update_by_replace_fields(update);
else if (data_hdr != NULL && min_size <= data_hdr->data_size)
update_by_replace_block(update, extra_size);
else {
if (!update_by_append(update, max_size, extra_size))
failed = TRUE;
}
if (!failed) {
/* update cached fields mask */
update->rec->data_fields |= update->updated_fields;
}
}
pool_unref(update->pool);
return !failed;
}
void mail_index_update_abort(struct mail_index_update *update)
{
pool_unref(update->pool);
}
static void update_field_full(struct mail_index_update *update,
enum mail_data_field field,
const void *value, size_t size,
size_t extra_space)
{
int index;
index = mail_field_get_index(field);
i_assert(index >= 0);
update->updated_fields |= field;
update->field_sizes[index] = size;
update->field_extra_sizes[index] = extra_space;
update->fields[index] = p_malloc(update->pool, size);
memcpy(update->fields[index], value, size);
}
static void update_header_field(struct mail_index_update *update,
enum mail_data_field field,
const void *value, size_t size __attr_unused__)
{
switch (field) {
case DATA_HDR_INTERNAL_DATE:
i_assert(size == sizeof(time_t));
update->data_hdr.internal_date = *((const time_t *) value);
break;
case DATA_HDR_VIRTUAL_SIZE:
i_assert(size == sizeof(uoff_t));
update->data_hdr.virtual_size = *((const uoff_t *) value);
break;
case DATA_HDR_HEADER_SIZE:
i_assert(size == sizeof(uoff_t));
update->data_hdr.header_size = *((const uoff_t *) value);
break;
case DATA_HDR_BODY_SIZE:
i_assert(size == sizeof(uoff_t));
update->data_hdr.body_size = *((const uoff_t *) value);
break;
default:
i_unreached();
}
update->updated_fields |= field;
}
void mail_index_update_field(struct mail_index_update *update,
enum mail_data_field field,
const char *value, size_t extra_space)
{
update_field_full(update, field, value, strlen(value) + 1, extra_space);
}
void mail_index_update_field_raw(struct mail_index_update *update,
enum mail_data_field field,
const void *value, size_t size)
{
if (field >= DATA_FIELD_LAST)
update_header_field(update, field, value, size);
else
update_field_full(update, field, value, size, 0);
}
struct header_update_context {
struct mail_index_update *update;
pool_t envelope_pool;
struct message_part_envelope_data *envelope;
message_header_callback_t *header_cb;
void *context;
};
static void update_header_cb(struct message_part *part,
struct message_header_line *hdr, void *context)
{
struct header_update_context *ctx = context;
if (part != NULL && part->parent != NULL)
return;
/* see if we can do anything with this field */
if (ctx->update->index->header->cache_fields & DATA_FIELD_ENVELOPE) {
if (ctx->envelope_pool == NULL) {
ctx->envelope_pool =
pool_alloconly_create("index envelope", 2048);
}
imap_envelope_parse_header(ctx->envelope_pool,
&ctx->envelope, hdr);
}
if (ctx->header_cb != NULL)
ctx->header_cb(part, hdr, ctx->context);
}
void mail_index_update_headers(struct mail_index_update *update,
struct istream *input,
enum mail_data_field cache_fields,
message_header_callback_t *header_cb,
void *context)
{
struct header_update_context ctx;
struct message_part *part;
struct message_size hdr_size, body_size;
pool_t pool;
buffer_t *buf;
const char *value, *error;
size_t size;
uoff_t start_offset;
ctx.update = update;
ctx.envelope_pool = NULL;
ctx.envelope = NULL;
ctx.header_cb = header_cb;
ctx.context = context;
if (cache_fields == 0)
cache_fields = update->index->header->cache_fields;
if (IS_BODYSTRUCTURE_FIELD(cache_fields)) {
/* for body / bodystructure, we need need to
fully parse the message. unless it's already parsed
and cached. */
pool = pool_alloconly_create("index message parser", 2048);
value = update->index->lookup_field_raw(update->index,
update->rec,
DATA_FIELD_MESSAGEPART,
&size);
if (value == NULL)
part = NULL;
else {
part = message_part_deserialize(pool, value, size,
&error);
if (part == NULL) {
/* corrupted, rebuild it */
index_set_corrupted(update->index,
"Corrupted cached MessagePart data "
"(%s)", error);
}
}
start_offset = input->v_offset;
if (part == NULL) {
part = message_parse(pool, input,
update_header_cb, &ctx);
} else {
/* cached, construct the bodystructure using it.
also we need to parse the header.. */
i_stream_seek(input, start_offset);
message_parse_header(NULL, input, NULL,
update_header_cb, &ctx);
}
/* save sizes */
hdr_size = part->header_size;
body_size = part->body_size;
/* don't save both BODY + BODYSTRUCTURE since BODY can be
generated from BODYSTRUCTURE. FIXME: However that takes
CPU, maybe this should be configurable (I/O vs. CPU)? */
if ((cache_fields & DATA_FIELD_BODY) &&
((update->rec->data_fields | cache_fields) &
DATA_FIELD_BODYSTRUCTURE) == 0) {
t_push();
i_stream_seek(input, start_offset);
value = imap_part_get_bodystructure(pool, &part,
input, FALSE);
update->index->update_field(update, DATA_FIELD_BODY,
value, 0);
t_pop();
}
if (cache_fields & DATA_FIELD_BODYSTRUCTURE) {
t_push();
i_stream_seek(input, start_offset);
value = imap_part_get_bodystructure(pool, &part,
input, TRUE);
update->index->update_field(update,
DATA_FIELD_BODYSTRUCTURE,
value, 0);
t_pop();
}
if (cache_fields & DATA_FIELD_MESSAGEPART) {
t_push();
buf = buffer_create_dynamic(data_stack_pool, 2048,
(size_t)-1);
message_part_serialize(part, buf);
value = buffer_get_data(buf, &size);
update->index->update_field_raw(update,
DATA_FIELD_MESSAGEPART,
value, size);
t_pop();
}
pool_unref(pool);
} else {
message_parse_header(NULL, input, &hdr_size,
update_header_cb, &ctx);
body_size.physical_size = input->v_limit - input->v_offset;
if (body_size.physical_size == 0)
body_size.virtual_size = 0;
else if (update->data_hdr.virtual_size == 0)
body_size.virtual_size = (uoff_t)-1;
else {
body_size.virtual_size =
update->data_hdr.virtual_size -
hdr_size.virtual_size;
}
}
if (ctx.envelope != NULL) {
string_t *str;
t_push();
str = str_new(data_stack_pool, 2048);
imap_envelope_write_part_data(ctx.envelope, str);
update->index->update_field(update, DATA_FIELD_ENVELOPE,
str_c(str), 0);
t_pop();
pool_unref(ctx.envelope_pool);
}
/* update physical sizes */
update->index->update_field_raw(update, DATA_HDR_HEADER_SIZE,
&hdr_size.physical_size,
sizeof(hdr_size.physical_size));
update->index->update_field_raw(update, DATA_HDR_BODY_SIZE,
&body_size.physical_size,
sizeof(body_size.physical_size));
/* update virtual size if we know it */
if (body_size.virtual_size != (uoff_t)-1) {
uoff_t virtual_size;
virtual_size = hdr_size.virtual_size + body_size.virtual_size;
update->index->update_field_raw(update, DATA_HDR_VIRTUAL_SIZE,
&virtual_size,
sizeof(virtual_size));
}
/* update binary flags. */
if (hdr_size.virtual_size == hdr_size.physical_size)
update->rec->index_flags |= INDEX_MAIL_FLAG_BINARY_HEADER;
if (body_size.virtual_size == body_size.physical_size)
update->rec->index_flags |= INDEX_MAIL_FLAG_BINARY_BODY;
}