mail-cache-lookup.c revision 73a44af966c790560a72a0447f37bb35b1031a83
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
fcfb528483369975066c6adf1c55c16e6fb6e91fTimo Sirainenint mail_cache_get_record(struct mail_cache *cache, uint32_t offset,
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen /* we don't know yet how large the record is, so just guess */
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen if (mail_cache_map(cache, offset, sizeof(*rec) + CACHE_PREFETCH) < 0)
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen if (offset + sizeof(*rec) > cache->mmap_length) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_cache_set_corrupted(cache, "record points outside file");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_cache_set_corrupted(cache, "invalid record size");
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen /* larger than we guessed. map the rest of the record. */
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen if (mail_cache_map(cache, offset, rec->size) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_cache_set_corrupted(cache, "record points outside file");
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainenmail_cache_lookup_offset(struct mail_cache *cache, struct mail_index_view *view,
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (mail_index_lookup_ext_full(view, seq, cache->ext_id,
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen if (data == NULL || *((const uint32_t *)data) == 0) {
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen /* nothing in cache (for this record) */
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen if (!mail_index_map_get_ext_idx(map, cache->ext_id, &idx))
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen /* reset_id must match file_seq or the offset is for a different cache
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen file. if this happens, try if reopening the cache helps. if not,
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen it was probably for an old cache file that's already lost by now. */
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen while (cache->hdr->file_seq != ext->reset_id) {
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen if (++i == 2)
72368bc3058d36912317ffe55e6017205f4fa036Timo Sirainen /* we're probably compressing */
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen /* error / we already have the latest file open */
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainenbool mail_cache_track_loops(ARRAY_TYPE(uint32_t) *array, uint32_t offset)
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen unsigned int i, count;
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen for (i = 0; i < count; i++) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainenvoid mail_cache_lookup_iter_init(struct mail_cache_view *view, uint32_t seq,
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen struct mail_cache_lookup_iterate_ctx *ctx = ctx_r;
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen (void)mail_cache_open_and_verify(view->cache);
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* look up the first offset */
73a44af966c790560a72a0447f37bb35b1031a83Timo Sirainen ret = mail_cache_lookup_offset(view->cache, view->view, seq,
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen ctx->remap_counter = view->cache->remap_counter;
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainenmail_cache_lookup_iter_next_record(struct mail_cache_lookup_iterate_ctx *ctx)
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* end of this record list. check newly appended data. */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen mail_cache_lookup_offset(view->cache, view->trans_view,
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen ctx->remap_counter = view->cache->remap_counter;
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* look up the next record */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen if (mail_cache_track_loops(&view->looping_offsets, ctx->offset)) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen "record list is circular");
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen if (mail_cache_get_record(view->cache, ctx->offset, &ctx->rec) < 0)
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen ctx->remap_counter = view->cache->remap_counter;
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainenint mail_cache_lookup_iter_next(struct mail_cache_lookup_iterate_ctx *ctx,
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen i_assert(ctx->remap_counter == cache->remap_counter);
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen if (ctx->pos + sizeof(uint32_t) > ctx->rec_size) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen "record has invalid size");
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen if ((ret = mail_cache_lookup_iter_next_record(ctx)) <= 0)
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* return the next field */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen file_field = *((const uint32_t *)CONST_PTR_OFFSET(ctx->rec, ctx->pos));
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* new field, have to re-read fields header to figure
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen out its size */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen "field index too large (%u >= %u)",
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* field reading might have re-mmaped the file and
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen caused rec pointer to break. need to get it again. */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen if (mail_cache_get_record(cache, ctx->offset, &ctx->rec) < 0)
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen field_idx = cache->file_field_map[file_field];
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen data_size = cache->fields[field_idx].field.field_size;
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen ctx->pos + sizeof(uint32_t) <= ctx->rec->size) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* variable size field. get its size from the file. */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen "record continues outside its allocated size");
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen field_r->data = CONST_PTR_OFFSET(ctx->rec, ctx->pos);
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* each record begins from 32bit aligned position */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen ctx->pos += (data_size + sizeof(uint32_t)-1) & ~(sizeof(uint32_t)-1);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainenstatic int mail_cache_seq(struct mail_cache_view *view, uint32_t seq)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* wrapped, we'll have to clear the buffer */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen mail_cache_lookup_iter_init(view, seq, &iter);
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen while ((ret = mail_cache_lookup_iter_next(&iter, &field)) > 0) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen buffer_write(view->cached_exists_buf, field.field_idx,
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainenmail_cache_file_has_field(struct mail_cache *cache, unsigned int field)
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen return cache->field_file_map[field] != (uint32_t)-1;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainenint mail_cache_field_exists(struct mail_cache_view *view, uint32_t seq,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen unsigned int field)
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen (void)mail_cache_open_and_verify(view->cache);
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen if (!mail_cache_file_has_field(view->cache, field))
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen /* FIXME: we should discard the cache if view has been synced */
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen return (field <= view->cached_exists_buf->used &&
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen data[field] == view->cached_exists_value) ? 1 : 0;
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainenmail_cache_field_get_decision(struct mail_cache *cache, unsigned int field_idx)
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen return cache->fields[field_idx].field.decision;
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainenmail_cache_lookup_bitmask(struct mail_cache_lookup_iterate_ctx *iter,
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen unsigned int field_idx, unsigned int field_size,
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen const unsigned char *src;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen unsigned char *dest;
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen unsigned int i;
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* make sure all bits are cleared first */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen while ((ret = mail_cache_lookup_iter_next(iter, &field)) > 0) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* merge all bits */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen dest = buffer_get_space_unsafe(dest_buf, 0, field.size);
1171f0abf442638bac1827bb24a0b6b8eb682a82Timo Sirainenint mail_cache_lookup_field(struct mail_cache_view *view, buffer_t *dest_buf,
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen if ((ret = mail_cache_field_exists(view, seq, field_idx)) <= 0)
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* the field should exist */
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen mail_cache_decision_state_update(view, seq, field_idx);
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen mail_cache_lookup_iter_init(view, seq, &iter);
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen field_def = &view->cache->fields[field_idx].field;
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen if (field_def->type == MAIL_CACHE_FIELD_BITMASK) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen return mail_cache_lookup_bitmask(&iter, field_idx,
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* return the first one that's found. if there are multiple
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen they're all identical. */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen while ((ret = mail_cache_lookup_iter_next(&iter, &field)) > 0) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen buffer_append(dest_buf, field.data, field.size);
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen ARRAY_DEFINE(lines, struct header_lookup_line);
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainenstatic void header_lines_save(struct header_lookup_context *ctx,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen unsigned int i, lines_count;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* data = { line_nums[], 0, "headers" } */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; data_size >= sizeof(uint32_t); i++) {
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen hdr_data = t_new(struct header_lookup_data, 1);
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen hdr_data->offset = (const char *)&lines[lines_count+1] -
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; i < lines_count; i++) {
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainenstatic int header_lookup_line_cmp(const void *p1, const void *p2)
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen const struct header_lookup_line *l1 = p1, *l2 = p2;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenint mail_cache_lookup_headers(struct mail_cache_view *view, string_t *dest,
2a21435425c50514fa4d0708110a4ed7f0d85661Timo Sirainen (void)mail_cache_open_and_verify(view->cache);
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen /* mark all the fields we want to find. */
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen buf = buffer_create_dynamic(pool_datastack_create(), 32);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; i < fields_count; i++) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen if (!mail_cache_file_has_field(cache, field_idxs[i])) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen field_state = buffer_get_modifiable_data(buf, NULL);
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen /* lookup the fields */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen mail_cache_lookup_iter_init(view, seq, &iter);
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen while ((ret = mail_cache_lookup_iter_next(&iter, &field)) > 0) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen field_state[field.field_idx] != HDR_FIELD_STATE_WANT) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* a) don't want it, b) duplicate */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen field_state[field.field_idx] = HDR_FIELD_STATE_SEEN;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* check that all fields were found */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen for (i = 0; i <= max_field; i++) {
b1f58fea9e1813b48735a021043f1cea2b495c3bTimo Sirainen for (i = 0; i < fields_count; i++)
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen mail_cache_decision_state_update(view, seq, field_idxs[i]);
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen /* we need to return headers in the order they existed originally.
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen we can do this by sorting the messages by their line numbers. */
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen lines = array_get_modifiable(&ctx.lines, &count);
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen qsort(lines, count, sizeof(*lines), header_lookup_line_cmp);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* then start filling dest buffer from the headers */
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen for (i = 0; i < count; i++) {
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen start = CONST_PTR_OFFSET(cache->data, lines[i].data->offset);
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen /* find the end of the (multiline) header */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (*p == '\n' &&
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (p+1 == end || (p[1] != ' ' && p[1] != '\t'))) {
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen /* if there are more lines for this header, the following lines
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen continue after this one. so skip this line. */