bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "lib.h"
6157a322f2ac1ea1332d9003ecb0b11466aa8fe7Timo Sirainen#include "ioloop.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "buffer.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "hash.h"
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen#include "file-cache.h"
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen#include "read-full.h"
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen#include "write-full.h"
c4cfee078c4a185b5ba8f0c55f51275b7e885b2cTimo Sirainen#include "mmap-util.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "mail-cache-private.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include <stddef.h>
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen#define CACHE_FIELD_IS_NEWLY_WANTED(cache, field_idx) \
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen ((cache)->field_file_map[field_idx] == (uint32_t)-1 && \
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen (cache)->fields[field_idx].used)
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainenstatic bool field_has_fixed_size(enum mail_cache_field_type type)
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen{
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen switch (type) {
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen case MAIL_CACHE_FIELD_FIXED_SIZE:
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen case MAIL_CACHE_FIELD_BITMASK:
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen return TRUE;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen case MAIL_CACHE_FIELD_VARIABLE_SIZE:
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen case MAIL_CACHE_FIELD_STRING:
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen case MAIL_CACHE_FIELD_HEADER:
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen return FALSE;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen case MAIL_CACHE_FIELD_COUNT:
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen break;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen }
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen i_unreached();
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen return FALSE;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen}
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainenstatic bool field_decision_is_valid(enum mail_cache_decision_type type)
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen{
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen switch (type & ~MAIL_CACHE_DECISION_FORCED) {
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen case MAIL_CACHE_DECISION_NO:
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen case MAIL_CACHE_DECISION_TEMP:
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen case MAIL_CACHE_DECISION_YES:
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen return TRUE;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen default:
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen return FALSE;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen }
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen}
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainenstatic int field_type_verify(struct mail_cache *cache, unsigned int idx,
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen enum mail_cache_field_type type, unsigned int size)
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen{
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen const struct mail_cache_field *field = &cache->fields[idx].field;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen if (field->type != type) {
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen mail_cache_set_corrupted(cache,
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen "registered field %s type changed", field->name);
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen return -1;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen }
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen if (field->field_size != size && field_has_fixed_size(type)) {
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen mail_cache_set_corrupted(cache,
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen "registered field %s size changed", field->name);
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen return -1;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen }
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen return 0;
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen}
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainenstatic void
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainenmail_cache_field_update(struct mail_cache *cache,
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen const struct mail_cache_field *newfield)
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen{
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen struct mail_cache_field_private *orig;
c2d7c8a808e78ed256346fb8529a565dc68e92ccTimo Sirainen bool initial_registering;
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen i_assert(newfield->type < MAIL_CACHE_FIELD_COUNT);
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen
c2d7c8a808e78ed256346fb8529a565dc68e92ccTimo Sirainen /* are we still doing the initial cache field registering for
c2d7c8a808e78ed256346fb8529a565dc68e92ccTimo Sirainen internal fields and for mail_*cache_fields settings? */
c2d7c8a808e78ed256346fb8529a565dc68e92ccTimo Sirainen initial_registering = cache->file_fields_count == 0;
c2d7c8a808e78ed256346fb8529a565dc68e92ccTimo Sirainen
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen orig = &cache->fields[newfield->idx];
a16d9a651aaa36a308f1aaae87e73e143fdff887Timo Sirainen if ((newfield->decision & MAIL_CACHE_DECISION_FORCED) != 0 ||
c2d7c8a808e78ed256346fb8529a565dc68e92ccTimo Sirainen ((orig->field.decision & MAIL_CACHE_DECISION_FORCED) == 0 &&
c2d7c8a808e78ed256346fb8529a565dc68e92ccTimo Sirainen newfield->decision > orig->field.decision)) {
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen orig->field.decision = newfield->decision;
c2d7c8a808e78ed256346fb8529a565dc68e92ccTimo Sirainen if (!initial_registering)
c2d7c8a808e78ed256346fb8529a565dc68e92ccTimo Sirainen orig->decision_dirty = TRUE;
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen }
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen if (orig->field.last_used < newfield->last_used) {
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen orig->field.last_used = newfield->last_used;
c2d7c8a808e78ed256346fb8529a565dc68e92ccTimo Sirainen if (!initial_registering)
c2d7c8a808e78ed256346fb8529a565dc68e92ccTimo Sirainen orig->decision_dirty = TRUE;
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen }
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen if (orig->decision_dirty)
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen cache->field_header_write_pending = TRUE;
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen (void)field_type_verify(cache, newfield->idx,
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen newfield->type, newfield->field_size);
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen}
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenvoid mail_cache_register_fields(struct mail_cache *cache,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct mail_cache_field *fields,
9d3ccd79130199ffdb19a688027d49bf20a4aaaaTimo Sirainen unsigned int fields_count)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
8d6cb44a0161d88743756733f83c4fb278485987Timo Sirainen char *name;
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen void *value;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen unsigned int new_idx;
f27c7e633e8eaf386f048fc78b99c68e344f6360Timo Sirainen unsigned int i, j, registered_count;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen new_idx = cache->fields_count;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; i < fields_count; i++) {
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen if (hash_table_lookup_full(cache->field_name_hash,
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen fields[i].name, &name, &value)) {
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen fields[i].idx = POINTER_CAST_TO(value, unsigned int);
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen mail_cache_field_update(cache, &fields[i]);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen continue;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen /* check if the same header is being registered in the
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen same field array */
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen for (j = 0; j < i; j++) {
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen if (strcasecmp(fields[i].name, fields[j].name) == 0) {
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen fields[i].idx = fields[j].idx;
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen break;
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen }
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen }
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen if (j == i)
be889d9b142fbb5604a922c6955bd7f6ea32f163Timo Sirainen fields[i].idx = new_idx++;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (new_idx == cache->fields_count)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* @UNSAFE */
e6d4f540bf5c3b7ef5d6e154b217a2422210048cTimo Sirainen cache->fields = i_realloc_type(cache->fields,
e6d4f540bf5c3b7ef5d6e154b217a2422210048cTimo Sirainen struct mail_cache_field_private,
e6d4f540bf5c3b7ef5d6e154b217a2422210048cTimo Sirainen cache->fields_count, new_idx);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->field_file_map =
e6d4f540bf5c3b7ef5d6e154b217a2422210048cTimo Sirainen i_realloc_type(cache->field_file_map, uint32_t,
e6d4f540bf5c3b7ef5d6e154b217a2422210048cTimo Sirainen cache->fields_count, new_idx);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
f27c7e633e8eaf386f048fc78b99c68e344f6360Timo Sirainen registered_count = cache->fields_count;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; i < fields_count; i++) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen unsigned int idx = fields[i].idx;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
f27c7e633e8eaf386f048fc78b99c68e344f6360Timo Sirainen if (idx < registered_count)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen continue;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* new index - save it */
8d6cb44a0161d88743756733f83c4fb278485987Timo Sirainen name = p_strdup(cache->field_pool, fields[i].name);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen cache->fields[idx].field = fields[i];
8d6cb44a0161d88743756733f83c4fb278485987Timo Sirainen cache->fields[idx].field.name = name;
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen cache->fields[idx].field.last_used = fields[i].last_used;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->field_file_map[idx] = (uint32_t)-1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen if (!field_has_fixed_size(cache->fields[idx].field.type))
8ae72ad7d0c69e972cfa65d1e2ce4e3e9a8b765cTimo Sirainen cache->fields[idx].field.field_size = UINT_MAX;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen hash_table_insert(cache->field_name_hash, name,
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen POINTER_CAST(idx));
f27c7e633e8eaf386f048fc78b99c68e344f6360Timo Sirainen registered_count++;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
f27c7e633e8eaf386f048fc78b99c68e344f6360Timo Sirainen i_assert(registered_count == new_idx);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->fields_count = new_idx;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenunsigned int
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenmail_cache_register_lookup(struct mail_cache *cache, const char *name)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen char *key;
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen void *value;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen if (hash_table_lookup_full(cache->field_name_hash, name, &key, &value))
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen return POINTER_CAST_TO(value, unsigned int);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen else
8ae72ad7d0c69e972cfa65d1e2ce4e3e9a8b765cTimo Sirainen return UINT_MAX;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e2e105f6182f550efa82f77d2a46b3e8a2d4da10Timo Sirainenconst struct mail_cache_field *
e2e105f6182f550efa82f77d2a46b3e8a2d4da10Timo Sirainenmail_cache_register_get_field(struct mail_cache *cache, unsigned int field_idx)
e2e105f6182f550efa82f77d2a46b3e8a2d4da10Timo Sirainen{
e2e105f6182f550efa82f77d2a46b3e8a2d4da10Timo Sirainen i_assert(field_idx < cache->fields_count);
e2e105f6182f550efa82f77d2a46b3e8a2d4da10Timo Sirainen
e2e105f6182f550efa82f77d2a46b3e8a2d4da10Timo Sirainen return &cache->fields[field_idx].field;
e2e105f6182f550efa82f77d2a46b3e8a2d4da10Timo Sirainen}
e2e105f6182f550efa82f77d2a46b3e8a2d4da10Timo Sirainen
1211f411e0f851fe144e502967d22a8c2f36dee6Aki Tuomistruct mail_cache_field *
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainenmail_cache_register_get_list(struct mail_cache *cache, pool_t pool,
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen unsigned int *count_r)
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen{
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen struct mail_cache_field *list;
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen unsigned int i;
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen
bc793bfcee945ce8871edfa298fe7235744425b6Timo Sirainen if (!cache->opened)
bc793bfcee945ce8871edfa298fe7235744425b6Timo Sirainen (void)mail_cache_open_and_verify(cache);
bc793bfcee945ce8871edfa298fe7235744425b6Timo Sirainen
32c24eab7111799d880139634144ba8aea5a666eTimo Sirainen list = cache->fields_count == 0 ? NULL :
32c24eab7111799d880139634144ba8aea5a666eTimo Sirainen p_new(pool, struct mail_cache_field, cache->fields_count);
ea4ca37ec14913354f3a0deebc0df96097eb9468Timo Sirainen for (i = 0; i < cache->fields_count; i++) {
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen list[i] = cache->fields[i].field;
ea4ca37ec14913354f3a0deebc0df96097eb9468Timo Sirainen list[i].name = p_strdup(pool, list[i].name);
ea4ca37ec14913354f3a0deebc0df96097eb9468Timo Sirainen }
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen *count_r = cache->fields_count;
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen return list;
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen}
16598a1fb870ae40d6177755a4306216e4d6a4cdTimo Sirainen
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainenstatic int
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainenmail_cache_header_fields_get_offset(struct mail_cache *cache,
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen uint32_t *offset_r,
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen const struct mail_cache_header_fields **field_hdr_r)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const struct mail_cache_header_fields *field_hdr;
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen struct mail_cache_header_fields tmp_field_hdr;
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen const void *data;
05d1312c8456f9d548470bf5662481a4cf812d70Timo Sirainen uint32_t offset = 0, next_offset, field_hdr_size;
abb83d133dd082527d500916fca66a72fbbbaa8dTimo Sirainen unsigned int next_count = 0;
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen int ret;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (MAIL_CACHE_IS_UNUSABLE(cache)) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *offset_r = 0;
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen if (field_hdr_r != NULL)
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen *field_hdr_r = NULL;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* find the latest header */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen offset = 0;
6e235046e1d8e9d89fc948f5c623676c20421a28Timo Sirainen next_offset = cache->last_field_header_offset != 0 ?
6e235046e1d8e9d89fc948f5c623676c20421a28Timo Sirainen cache->last_field_header_offset :
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen mail_index_offset_to_uint32(cache->hdr->field_header_offset);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen while (next_offset != 0) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (next_offset == offset) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen mail_cache_set_corrupted(cache,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "next_offset in field header loops");
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return -1;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
26b007545685688e8931dc05ba465df0b00c156aTimo Sirainen /* In Dovecot v2.2+ we don't try to use any holes,
26b007545685688e8931dc05ba465df0b00c156aTimo Sirainen so next_offset must always be larger than current offset.
26b007545685688e8931dc05ba465df0b00c156aTimo Sirainen also makes it easier to guarantee there aren't any loops
26b007545685688e8931dc05ba465df0b00c156aTimo Sirainen (which we don't bother doing for old files) */
26b007545685688e8931dc05ba465df0b00c156aTimo Sirainen if (next_offset < offset && cache->hdr->minor_version != 0) {
26b007545685688e8931dc05ba465df0b00c156aTimo Sirainen mail_cache_set_corrupted(cache,
26b007545685688e8931dc05ba465df0b00c156aTimo Sirainen "next_offset in field header decreases");
26b007545685688e8931dc05ba465df0b00c156aTimo Sirainen return -1;
26b007545685688e8931dc05ba465df0b00c156aTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen offset = next_offset;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
ab1e5b156d1b5480d36ed6e8e06197339d803038Timo Sirainen if (cache->mmap_base != NULL || cache->map_with_read) {
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen ret = mail_cache_map(cache, offset, sizeof(*field_hdr),
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen &data);
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen if (ret <= 0) {
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen if (ret < 0)
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen return -1;
73b251f13abfbd8e06c8c78784eb570d28fe7e40Timo Sirainen mail_cache_set_corrupted(cache,
73b251f13abfbd8e06c8c78784eb570d28fe7e40Timo Sirainen "header field next_offset points outside file");
73b251f13abfbd8e06c8c78784eb570d28fe7e40Timo Sirainen return -1;
73b251f13abfbd8e06c8c78784eb570d28fe7e40Timo Sirainen }
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen field_hdr = data;
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen } else {
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen /* if we need to follow multiple offsets to get to
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen the last one, it's faster to just pread() the file
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen instead of going through cache */
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen ret = pread_full(cache->fd, &tmp_field_hdr,
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen sizeof(tmp_field_hdr), offset);
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen if (ret < 0) {
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen mail_cache_set_syscall_error(cache, "pread()");
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen return -1;
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen }
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen if (ret == 0) {
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen mail_cache_set_corrupted(cache,
73b251f13abfbd8e06c8c78784eb570d28fe7e40Timo Sirainen "header field next_offset points outside file");
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen return -1;
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen }
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen field_hdr = &tmp_field_hdr;
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen }
c4cfee078c4a185b5ba8f0c55f51275b7e885b2cTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen next_offset =
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen mail_index_offset_to_uint32(field_hdr->next_offset);
abb83d133dd082527d500916fca66a72fbbbaa8dTimo Sirainen next_count++;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
6e235046e1d8e9d89fc948f5c623676c20421a28Timo Sirainen if (offset == 0) {
6e235046e1d8e9d89fc948f5c623676c20421a28Timo Sirainen mail_cache_set_corrupted(cache, "missing header fields");
6e235046e1d8e9d89fc948f5c623676c20421a28Timo Sirainen return -1;
6e235046e1d8e9d89fc948f5c623676c20421a28Timo Sirainen }
6e235046e1d8e9d89fc948f5c623676c20421a28Timo Sirainen cache->last_field_header_offset = offset;
6e235046e1d8e9d89fc948f5c623676c20421a28Timo Sirainen
67bbcd664bebce9a507a49c67273be4814d07c97Timo Sirainen if (next_count > cache->index->optimization_set.cache.compress_header_continue_count)
abb83d133dd082527d500916fca66a72fbbbaa8dTimo Sirainen cache->need_compress_file_seq = cache->hdr->file_seq;
abb83d133dd082527d500916fca66a72fbbbaa8dTimo Sirainen
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen if (field_hdr_r != NULL) {
05d1312c8456f9d548470bf5662481a4cf812d70Timo Sirainen /* detect corrupted size later */
05d1312c8456f9d548470bf5662481a4cf812d70Timo Sirainen field_hdr_size = I_MAX(field_hdr->size, sizeof(*field_hdr));
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen if (cache->file_cache != NULL) {
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen /* invalidate the cache fields area to make sure we
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen get the latest cache decisions/last_used fields */
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen file_cache_invalidate(cache->file_cache, offset,
05d1312c8456f9d548470bf5662481a4cf812d70Timo Sirainen field_hdr_size);
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen }
c3fcfca0df3cbc9277ba5bc147b5d492edc70986Timo Sirainen if (cache->read_buf != NULL)
b14bdce468df7d98afb3e5e8c71b74a75cad2453Timo Sirainen buffer_set_used_size(cache->read_buf, 0);
05d1312c8456f9d548470bf5662481a4cf812d70Timo Sirainen ret = mail_cache_map(cache, offset, field_hdr_size, &data);
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen if (ret < 0)
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen return -1;
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen if (ret == 0) {
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen mail_cache_set_corrupted(cache,
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen "header field size outside file");
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen return -1;
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen }
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen *field_hdr_r = data;
8b681dae1e8fa564649e703ab17398dcfaf896e4Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *offset_r = offset;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenint mail_cache_header_fields_read(struct mail_cache *cache)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen const struct mail_cache_header_fields *field_hdr;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct mail_cache_field field;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const uint32_t *last_used, *sizes;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const uint8_t *types, *decisions;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *p, *names, *end;
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen char *orig_key;
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen void *orig_value;
a04b6515c20b431294626400e173d81f3d25889bTimo Sirainen unsigned int fidx, new_fields_count;
2bfc60a0c890585f1c9d75d721dc78d86d65f6caTimo Sirainen enum mail_cache_decision_type dec;
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen time_t max_drop_time;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen uint32_t offset, i;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen if (mail_cache_header_fields_get_offset(cache, &offset, &field_hdr) < 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (offset == 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* no fields - the file is empty */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* check the fixed size of the header. name[] has to be checked
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen separately */
c5bf07c1339a288701e7566e6f90c4034ee74c24Timo Sirainen if (field_hdr->fields_count > INT_MAX / MAIL_CACHE_FIELD_NAMES(1) ||
c5bf07c1339a288701e7566e6f90c4034ee74c24Timo Sirainen field_hdr->size < MAIL_CACHE_FIELD_NAMES(field_hdr->fields_count)) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_set_corrupted(cache, "invalid field header size");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
11768c622a8e5aaf0e5d9b3c9a872867b62b4830Timo Sirainen new_fields_count = field_hdr->fields_count;
11768c622a8e5aaf0e5d9b3c9a872867b62b4830Timo Sirainen if (new_fields_count != 0) {
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainen cache->file_field_map =
e6d4f540bf5c3b7ef5d6e154b217a2422210048cTimo Sirainen i_realloc_type(cache->file_field_map, unsigned int,
e6d4f540bf5c3b7ef5d6e154b217a2422210048cTimo Sirainen cache->file_fields_count, new_fields_count);
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainen } else {
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainen i_free_and_null(cache->file_field_map);
265e9d8457aa5ce01b7aad954632aed8c36deeb8Timo Sirainen }
11768c622a8e5aaf0e5d9b3c9a872867b62b4830Timo Sirainen cache->file_fields_count = new_fields_count;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen last_used = CONST_PTR_OFFSET(field_hdr, MAIL_CACHE_FIELD_LAST_USED());
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen sizes = CONST_PTR_OFFSET(field_hdr,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen MAIL_CACHE_FIELD_SIZE(field_hdr->fields_count));
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen types = CONST_PTR_OFFSET(field_hdr,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen MAIL_CACHE_FIELD_TYPE(field_hdr->fields_count));
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen decisions = CONST_PTR_OFFSET(field_hdr,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen MAIL_CACHE_FIELD_DECISION(field_hdr->fields_count));
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen names = CONST_PTR_OFFSET(field_hdr,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen MAIL_CACHE_FIELD_NAMES(field_hdr->fields_count));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen end = CONST_PTR_OFFSET(field_hdr, field_hdr->size);
5794def630084bab82297be9b988c624d9d66d4aTimo Sirainen i_assert(names <= end);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* clear the old mapping */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; i < cache->fields_count; i++)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->field_file_map[i] = (uint32_t)-1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
6ebcdea168735ee76e32b871c1f50f3526690447Timo Sirainen max_drop_time = cache->index->map->hdr.day_stamp == 0 ? 0 :
67bbcd664bebce9a507a49c67273be4814d07c97Timo Sirainen cache->index->map->hdr.day_stamp -
67bbcd664bebce9a507a49c67273be4814d07c97Timo Sirainen cache->index->optimization_set.cache.unaccessed_field_drop_secs;
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&field);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; i < field_hdr->fields_count; i++) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (p = names; p != end && *p != '\0'; p++) ;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (p == end || *names == '\0') {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_set_corrupted(cache,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "field header names corrupted");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen if (types[i] > MAIL_CACHE_FIELD_COUNT) {
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen mail_cache_set_corrupted(cache, "field type corrupted");
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen return -1;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen }
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen if (!field_decision_is_valid(decisions[i])) {
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen mail_cache_set_corrupted(cache,
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen "field decision type corrupted");
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen return -1;
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen }
5c0034beb9933bca2a8b7d83d11dface1ea3b7faTimo Sirainen
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen /* ignore any forced-flags in the file */
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen enum mail_cache_decision_type file_dec =
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen decisions[i] & ~MAIL_CACHE_DECISION_FORCED;
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen if (hash_table_lookup_full(cache->field_name_hash, names,
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen &orig_key, &orig_value)) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* already exists, see if decision can be updated */
a04b6515c20b431294626400e173d81f3d25889bTimo Sirainen fidx = POINTER_CAST_TO(orig_value, unsigned int);
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen enum mail_cache_decision_type cur_dec =
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen cache->fields[fidx].field.decision;
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen if ((cur_dec & MAIL_CACHE_DECISION_FORCED) != 0) {
0ee986fe3dfc5ce700d232d2f6bc1b47aef1bfe7Timo Sirainen /* Forced decision. If the decision has
0ee986fe3dfc5ce700d232d2f6bc1b47aef1bfe7Timo Sirainen changed, update the fields in the file. */
0ee986fe3dfc5ce700d232d2f6bc1b47aef1bfe7Timo Sirainen if ((cur_dec & ~MAIL_CACHE_DECISION_FORCED) != file_dec)
0ee986fe3dfc5ce700d232d2f6bc1b47aef1bfe7Timo Sirainen cache->field_header_write_pending = TRUE;
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen } else if (cache->fields[fidx].decision_dirty) {
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen /* Decisions have recently been updated
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen internally. Don't change them. */
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen } else {
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen /* Use the decision from the cache file. */
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen cache->fields[fidx].field.decision = file_dec;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
a04b6515c20b431294626400e173d81f3d25889bTimo Sirainen if (field_type_verify(cache, fidx,
9bf2dc275ec21bff3d468ab1bc4fddc8874f7d1bTimo Sirainen types[i], sizes[i]) < 0)
7bb939ef70752f2731d27b18c944ea94e5b23eb5Timo Sirainen return -1;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen } else {
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen /* field is currently unknown, so just use whatever
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen exists in the file. */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen field.name = names;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen field.type = types[i];
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen field.field_size = sizes[i];
8841d0b8b47241d238b5524bded3ee116d10de33Timo Sirainen field.decision = file_dec;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen mail_cache_register_fields(cache, &field, 1);
a04b6515c20b431294626400e173d81f3d25889bTimo Sirainen fidx = field.idx;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
a04b6515c20b431294626400e173d81f3d25889bTimo Sirainen if (cache->field_file_map[fidx] != (uint32_t)-1) {
8e574a603c2c02105b073906a7ca91904271b80eTimo Sirainen mail_cache_set_corrupted(cache,
8e574a603c2c02105b073906a7ca91904271b80eTimo Sirainen "Duplicated field in header: %s", names);
8e574a603c2c02105b073906a7ca91904271b80eTimo Sirainen return -1;
8e574a603c2c02105b073906a7ca91904271b80eTimo Sirainen }
a04b6515c20b431294626400e173d81f3d25889bTimo Sirainen cache->fields[fidx].used = TRUE;
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen
a04b6515c20b431294626400e173d81f3d25889bTimo Sirainen cache->field_file_map[fidx] = i;
2b68f50813fe2bda94f227fc0144e4823b614474Timo Sirainen cache->file_field_map[i] = fidx;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* update last_used if it's newer than ours */
7dd73e056c7d9976080393d9cd731f69111818eeTimo Sirainen if ((time_t)last_used[i] > cache->fields[fidx].field.last_used)
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen cache->fields[fidx].field.last_used = last_used[i];
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
2bfc60a0c890585f1c9d75d721dc78d86d65f6caTimo Sirainen dec = cache->fields[fidx].field.decision;
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen if (cache->fields[fidx].field.last_used < max_drop_time &&
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen cache->fields[fidx].field.last_used != 0 &&
2bfc60a0c890585f1c9d75d721dc78d86d65f6caTimo Sirainen (dec & MAIL_CACHE_DECISION_FORCED) == 0 &&
2bfc60a0c890585f1c9d75d721dc78d86d65f6caTimo Sirainen dec != MAIL_CACHE_DECISION_NO) {
9c4c535b86e9473ad97c6e9242ed84f3d9d69d0dTimo Sirainen /* time to drop this field. don't bother dropping
9c4c535b86e9473ad97c6e9242ed84f3d9d69d0dTimo Sirainen fields that have never been used. */
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen cache->need_compress_file_seq = cache->hdr->file_seq;
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen }
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen names = p + 1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainenstatic void copy_to_buf(struct mail_cache *cache, buffer_t *dest, bool add_new,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen size_t offset, size_t size)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen const void *data;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen unsigned int i, field;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen /* copy the existing fields */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen for (i = 0; i < cache->file_fields_count; i++) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen field = cache->file_field_map[i];
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen data = CONST_PTR_OFFSET(&cache->fields[field], offset);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen buffer_append(dest, data, size);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen if (!add_new)
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen return;
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen /* copy newly wanted fields */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen for (i = 0; i < cache->fields_count; i++) {
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i)) {
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen data = CONST_PTR_OFFSET(&cache->fields[i], offset);
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen buffer_append(dest, data, size);
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen}
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainenstatic void copy_to_buf_byte(struct mail_cache *cache, buffer_t *dest,
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen bool add_new, size_t offset)
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen{
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen const int *data;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen unsigned int i, field;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen uint8_t byte;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen /* copy the existing fields */
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen for (i = 0; i < cache->file_fields_count; i++) {
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen field = cache->file_field_map[i];
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen data = CONST_PTR_OFFSET(&cache->fields[field], offset);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen byte = (uint8_t)*data;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen buffer_append(dest, &byte, 1);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen }
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen if (!add_new)
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen return;
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen /* copy newly wanted fields */
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen for (i = 0; i < cache->fields_count; i++) {
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i)) {
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen data = CONST_PTR_OFFSET(&cache->fields[i], offset);
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen byte = (uint8_t)*data;
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen buffer_append(dest, &byte, 1);
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen }
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen }
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen}
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainenstatic int mail_cache_header_fields_update_locked(struct mail_cache *cache)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen buffer_t *buffer;
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen uint32_t i, offset, dec_offset;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen int ret = 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (mail_cache_header_fields_read(cache) < 0 ||
8d35582f2577c64517b2341c5d6477c7010e0a0cPhil Carmody mail_cache_header_fields_get_offset(cache, &offset, NULL) < 0)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return -1;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
c147bff818798a979d93537f72f5c1f68f5d5ba8Aki Tuomi buffer = t_buffer_create(256);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen copy_to_buf(cache, buffer, FALSE,
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen offsetof(struct mail_cache_field, last_used),
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen sizeof(uint32_t));
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen ret = mail_cache_write(cache, buffer->data, buffer->used,
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen offset + MAIL_CACHE_FIELD_LAST_USED());
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (ret == 0) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen buffer_set_used_size(buffer, 0);
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen copy_to_buf_byte(cache, buffer, FALSE,
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen offsetof(struct mail_cache_field, decision));
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen dec_offset = offset +
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen MAIL_CACHE_FIELD_DECISION(cache->file_fields_count);
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen ret = mail_cache_write(cache, buffer->data, buffer->used,
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen dec_offset);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (ret == 0) {
2c20ffcb5bb1ccdfdcd0b0ff0c7296f65b990362Timo Sirainen for (i = 0; i < cache->file_fields_count; i++)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen cache->fields[i].decision_dirty = FALSE;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen if (ret == 0)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen cache->field_header_write_pending = FALSE;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen return ret;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen}
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainenint mail_cache_header_fields_update(struct mail_cache *cache)
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen{
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen int ret;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (cache->locked) {
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen T_BEGIN {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen ret = mail_cache_header_fields_update_locked(cache);
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen } T_END;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen return ret;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen }
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen
ab70f55bb8d824ca1ed7c74196f2f502edd29cc7Timo Sirainen if (mail_cache_lock(cache) <= 0)
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen return -1;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen T_BEGIN {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen ret = mail_cache_header_fields_update_locked(cache);
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen } T_END;
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen if (mail_cache_unlock(cache) < 0)
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen ret = -1;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return ret;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenvoid mail_cache_header_fields_get(struct mail_cache *cache, buffer_t *dest)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct mail_cache_header_fields hdr;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen unsigned int field;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *name;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen uint32_t i;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch i_zero(&hdr);
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen hdr.fields_count = cache->file_fields_count;
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen for (i = 0; i < cache->fields_count; i++) {
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i))
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen hdr.fields_count++;
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen buffer_append(dest, &hdr, sizeof(hdr));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* we have to keep the field order for the existing fields. */
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen copy_to_buf(cache, dest, TRUE,
008a83e9f680f04f69789fb702232416eab2a86cTimo Sirainen offsetof(struct mail_cache_field, last_used),
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen sizeof(uint32_t));
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen copy_to_buf(cache, dest, TRUE,
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen offsetof(struct mail_cache_field, field_size),
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen sizeof(uint32_t));
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen copy_to_buf_byte(cache, dest, TRUE,
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen offsetof(struct mail_cache_field, type));
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen copy_to_buf_byte(cache, dest, TRUE,
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen offsetof(struct mail_cache_field, decision));
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen i_assert(dest->used == sizeof(hdr) +
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen (sizeof(uint32_t)*2 + 2) * hdr.fields_count);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen /* add existing fields' names */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; i < cache->file_fields_count; i++) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen field = cache->file_field_map[i];
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen name = cache->fields[field].field.name;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen buffer_append(dest, name, strlen(name)+1);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen /* add newly wanted fields' names */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; i < cache->fields_count; i++) {
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i)) {
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen name = cache->fields[i].field.name;
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen buffer_append(dest, name, strlen(name)+1);
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen hdr.size = dest->used;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen buffer_write(dest, 0, &hdr, sizeof(hdr));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if ((hdr.size & 3) != 0)
d5ac54ef50db16b50689b5c8b7bb64d344190832Timo Sirainen buffer_append_zero(dest, 4 - (hdr.size & 3));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenint mail_cache_header_fields_get_next_offset(struct mail_cache *cache,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen uint32_t *offset_r)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
8d35582f2577c64517b2341c5d6477c7010e0a0cPhil Carmody if (mail_cache_header_fields_get_offset(cache, offset_r, NULL) < 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (*offset_r == 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *offset_r = offsetof(struct mail_cache_header,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen field_header_offset);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen } else {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *offset_r += offsetof(struct mail_cache_header_fields,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen next_offset);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}