mail-cache-lookup.c revision 6d8082441e480cf481bfb3f935d17fc419d789c6
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2003-2013 Dovecot authors, see the included COPYING file */
fcfb528483369975066c6adf1c55c16e6fb6e91fTimo Sirainenint mail_cache_get_record(struct mail_cache *cache, uint32_t offset,
8a4851655777e484d70b77bed6a7a8fac5c0c98cTimo Sirainen /* records are always 32-bit aligned */
8a4851655777e484d70b77bed6a7a8fac5c0c98cTimo Sirainen mail_cache_set_corrupted(cache, "invalid record offset");
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen /* we don't know yet how large the record is, so just guess */
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen if (mail_cache_map(cache, offset, sizeof(*rec) + CACHE_PREFETCH,
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. */
9bd08aa09ea0cbd7b221aae9fc0534eb762d3de6Timo Sirainen if ((ret = mail_cache_map(cache, offset, rec->size, &data)) < 0)
9bd08aa09ea0cbd7b221aae9fc0534eb762d3de6Timo Sirainen mail_cache_set_corrupted(cache, "record points outside file");
6a7f64562ddd0dd2fec755ec4e9c9afde8e85cf1Timo Sirainenuint32_t mail_cache_lookup_cur_offset(struct mail_index_view *view,
6a7f64562ddd0dd2fec755ec4e9c9afde8e85cf1Timo Sirainen struct mail_cache *cache = mail_index_view_get_index(view)->cache;
03860f6dd70abfa2551a846e77a5c41cb40dc141Timo Sirainen mail_index_lookup_ext_full(view, seq, cache->ext_id, &map, &data, NULL);
6a7f64562ddd0dd2fec755ec4e9c9afde8e85cf1Timo Sirainen /* no cache offsets */
6a7f64562ddd0dd2fec755ec4e9c9afde8e85cf1Timo Sirainen if (!mail_index_ext_get_reset_id(view, map, cache->ext_id, reset_id_r))
6a7f64562ddd0dd2fec755ec4e9c9afde8e85cf1Timo Sirainenmail_cache_lookup_offset(struct mail_cache *cache, struct mail_index_view *view,
6a7f64562ddd0dd2fec755ec4e9c9afde8e85cf1Timo Sirainen offset = mail_cache_lookup_cur_offset(view, seq, &reset_id);
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. */
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen if (++i == 2 || reset_id < cache->hdr->file_seq)
72368bc3058d36912317ffe55e6017205f4fa036Timo Sirainen /* we're probably compressing */
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen /* error / we already have the latest file open */
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainenbool mail_cache_track_loops(struct mail_cache_loop_track *loop_track,
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen /* looping happens only in rare error conditions, so it's enough if we
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen just catch it eventually. we do this by checking if we've seen
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen more record data than possible in the accessed file area. */
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen /* first call */
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen (loop_track->max_offset - loop_track->min_offset);
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;
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen memset(&view->loop_track, 0, sizeof(view->loop_track));
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainenmail_cache_lookup_iter_transaction(struct mail_cache_lookup_iterate_ctx *ctx)
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen ctx->rec = mail_cache_transaction_lookup_rec(ctx->view->transaction,
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen ctx->remap_counter = ctx->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. */
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen /* check data still in memory */
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen /* check data already written to cache file */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen mail_cache_lookup_offset(view->cache, view->trans_view,
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen ctx->remap_counter = view->cache->remap_counter;
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen memset(&view->loop_track, 0, sizeof(view->loop_track));
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* look up the next record */
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen if (mail_cache_get_record(view->cache, ctx->offset, &ctx->rec) < 0)
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen if (mail_cache_track_loops(&view->loop_track, ctx->offset,
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen "record list is circular");
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
5e4f94015f5bbc9eefb1f2cb7af81ed50dcd2b39Timo Sirainen out its size. don't do this if we're compressing. */
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 */
f65602dff8a85170176ddaa790db6df56006d132Timo Sirainen return (field < view->cached_exists_buf->used &&
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen data[field] == view->cached_exists_value) ? 1 : 0;
58816241cbaf79e3f8dd7d831b7c6f02c6c38ee6Timo Sirainenbool mail_cache_field_exists_any(struct mail_cache_view *view, uint32_t seq)
58816241cbaf79e3f8dd7d831b7c6f02c6c38ee6Timo Sirainen return mail_cache_lookup_cur_offset(view->view, seq, &reset_id) != 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,
0beb5d6c661ee68130a954ed0f31a34c19195fb7Timo Sirainen ret = mail_cache_field_exists(view, seq, field_idx);
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen mail_cache_decision_state_update(view, seq, field_idx);
0beb5d6c661ee68130a954ed0f31a34c19195fb7Timo Sirainen /* the field should exist */
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);
6d8082441e480cf481bfb3f935d17fc419d789c6Timo Sirainen const unsigned char *data;
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainenstatic void header_lines_save(struct header_lookup_context *ctx,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* data = { line_nums[], 0, "headers" } */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; data_size >= sizeof(uint32_t); i++) {
6d8082441e480cf481bfb3f935d17fc419d789c6Timo Sirainen hdr_data = p_new(ctx->pool, struct header_lookup_data, 1);
6d8082441e480cf481bfb3f935d17fc419d789c6Timo Sirainen hdr_data->data = data_dup = data_size == 0 ? NULL :
6d8082441e480cf481bfb3f935d17fc419d789c6Timo Sirainen memcpy(data_dup, CONST_PTR_OFFSET(field->data, pos), data_size);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; i < lines_count; i++) {
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainenstatic int header_lookup_line_cmp(const struct header_lookup_line *l1,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainenmail_cache_lookup_headers_real(struct mail_cache_view *view, string_t *dest,
2a21435425c50514fa4d0708110a4ed7f0d85661Timo Sirainen (void)mail_cache_open_and_verify(view->cache);
0beb5d6c661ee68130a954ed0f31a34c19195fb7Timo Sirainen /* update the decision state regardless of whether the fields
0beb5d6c661ee68130a954ed0f31a34c19195fb7Timo Sirainen actually exist or not. */
0beb5d6c661ee68130a954ed0f31a34c19195fb7Timo Sirainen for (i = 0; i < fields_count; i++)
0beb5d6c661ee68130a954ed0f31a34c19195fb7Timo Sirainen mail_cache_decision_state_update(view, seq, field_idxs[i]);
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++) {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo 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 */
6d8082441e480cf481bfb3f935d17fc419d789c6Timo Sirainen ctx.pool = *pool_r = pool_alloconly_create("mail cache headers", 1024);
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++) {
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. */
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen array_sort(&ctx.lines, header_lookup_line_cmp);
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen lines = array_get_modifiable(&ctx.lines, &count);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* then start filling dest buffer from the headers */
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen for (i = 0; i < count; i++) {
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. */
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainenint mail_cache_lookup_headers(struct mail_cache_view *view, string_t *dest,