static int dbox_get_cached_metadata(struct dbox_mail *mail, enum dbox_metadata_key key, enum index_cache_field cache_field, const char **value_r) { struct index_mail *imail = &mail->imail; struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(imail->mail.mail.box); const char *value; string_t *str; uint32_t order; str = str_new(imail->mail.data_pool, 64); if (mail_cache_lookup_field(imail->mail.mail.transaction->cache_view, str, imail->mail.mail.seq, ibox->cache_fields[cache_field].idx) > 0) { if (cache_field == MAIL_CACHE_POP3_ORDER) { i_assert(str_len(str) == sizeof(order)); memcpy(&order, str_data(str), sizeof(order)); str_truncate(str, 0); if (order != 0) str_printfa(str, "%u", order); else { /* order=0 means it doesn't exist. we don't want to return "0" though, because then the mails get ordered to beginning, while nonexistent are supposed to be ordered at the end. */ } } *value_r = str_c(str); return 0; } if (dbox_mail_metadata_get(mail, key, &value) < 0) return -1; if (value == NULL) value = ""; if (cache_field != MAIL_CACHE_POP3_ORDER) { index_mail_cache_add_idx(imail, ibox->cache_fields[cache_field].idx, value, strlen(value)+1); } else { if (str_to_uint(value, &order) < 0) order = 0; index_mail_cache_add_idx(imail, ibox->cache_fields[cache_field].idx, &order, sizeof(order)); } /* don't return pointer to dbox metadata directly, since it may change unexpectedly */ str_truncate(str, 0); str_append(str, value); *value_r = str_c(str); return 0; }
static int imapc_mail_get_guid(struct mail *_mail, const char **value_r) { struct index_mail *imail = (struct index_mail *)_mail; struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; const enum index_cache_field cache_idx = imail->ibox->cache_fields[MAIL_CACHE_GUID].idx; if (imapc_mail_get_cached_guid(_mail)) { *value_r = imail->data.guid; return 0; } /* GUID not in cache, fetch it */ if (mbox->guid_fetch_field_name != NULL) { if (imapc_mail_fetch(_mail, MAIL_FETCH_GUID, NULL) < 0) return -1; if (imail->data.guid == NULL) { (void)imapc_mail_failed(_mail, mbox->guid_fetch_field_name); return -1; } } else { /* use hash of message headers as the GUID */ if (imapc_mail_get_hdr_hash(imail) < 0) return -1; } index_mail_cache_add_idx(imail, cache_idx, imail->data.guid, strlen(imail->data.guid)+1); *value_r = imail->data.guid; return 0; }
static bool imapc_mail_get_cached_guid(struct mail *_mail) { struct index_mail *imail = (struct index_mail *)_mail; const enum index_cache_field cache_idx = imail->ibox->cache_fields[MAIL_CACHE_GUID].idx; string_t *str; if (imail->data.guid != NULL) { if (mail_cache_field_can_add(_mail->transaction->cache_trans, _mail->seq, cache_idx)) { /* GUID was prefetched - add to cache */ index_mail_cache_add_idx(imail, cache_idx, imail->data.guid, strlen(imail->data.guid)+1); } return TRUE; } str = str_new(imail->mail.data_pool, 64); if (mail_cache_lookup_field(_mail->transaction->cache_view, str, imail->mail.mail.seq, cache_idx) > 0) { imail->data.guid = str_c(str); return TRUE; } return FALSE; }
static void index_mail_body_parsed_cache_flags(struct index_mail *mail) { struct index_mail_data *data = &mail->data; unsigned int cache_flags_idx; uint32_t cache_flags = data->cache_flags; bool want_cached; cache_flags_idx = mail->ibox->cache_fields[MAIL_CACHE_FLAGS].idx; want_cached = mail_cache_field_want_add(mail->trans->cache_trans, data->seq, cache_flags_idx); if (data->parsed_bodystructure && imap_bodystructure_is_plain_7bit(data->parts) && (want_cached || want_plain_bodystructure_cached(mail))) { cache_flags |= MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII; /* we need message_parts cached to be able to actually use it in BODY/BODYSTRUCTURE reply */ want_cached = TRUE; data->save_message_parts = TRUE; } /* cache flags should never get unset as long as the message doesn't change, but try to handle it anyway */ cache_flags &= ~(MAIL_CACHE_FLAG_BINARY_HEADER | MAIL_CACHE_FLAG_BINARY_BODY | MAIL_CACHE_FLAG_HAS_NULS | MAIL_CACHE_FLAG_HAS_NO_NULS); if ((data->parts->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) { mail->mail.mail.has_nuls = TRUE; mail->mail.mail.has_no_nuls = FALSE; cache_flags |= MAIL_CACHE_FLAG_HAS_NULS; } else { mail->mail.mail.has_nuls = FALSE; mail->mail.mail.has_no_nuls = TRUE; cache_flags |= MAIL_CACHE_FLAG_HAS_NO_NULS; } if (data->hdr_size.virtual_size == data->hdr_size.physical_size) cache_flags |= MAIL_CACHE_FLAG_BINARY_HEADER; if (data->body_size.virtual_size == data->body_size.physical_size) cache_flags |= MAIL_CACHE_FLAG_BINARY_BODY; if (cache_flags != data->cache_flags && want_cached) { index_mail_cache_add_idx(mail, cache_flags_idx, &cache_flags, sizeof(cache_flags)); } data->cache_flags = cache_flags; }
static void index_mail_parse_finish_imap_envelope(struct index_mail *mail) { struct mail *_mail = &mail->mail.mail; const unsigned int cache_field_envelope = mail->ibox->cache_fields[MAIL_CACHE_IMAP_ENVELOPE].idx; string_t *str; str = str_new(mail->mail.data_pool, 256); imap_envelope_write_part_data(mail->data.envelope_data, str); mail->data.envelope = str_c(str); if (mail_cache_field_can_add(_mail->transaction->cache_trans, _mail->seq, cache_field_envelope)) { index_mail_cache_add_idx(mail, cache_field_envelope, str_data(str), str_len(str)); } }
static void pop3c_mail_cache_size(struct index_mail *mail) { struct mail *_mail = &mail->mail.mail; uoff_t size; unsigned int cache_idx; if (i_stream_get_size(mail->data.stream, TRUE, &size) <= 0) return; mail->data.virtual_size = size; cache_idx = mail->ibox->cache_fields[MAIL_CACHE_VIRTUAL_FULL_SIZE].idx; if (mail_cache_field_exists(_mail->transaction->cache_view, _mail->seq, cache_idx) == 0) { index_mail_cache_add_idx(mail, cache_idx, &size, sizeof(size)); /* make sure it's not cached twice */ mail->data.dont_cache_fetch_fields |= MAIL_CACHE_VIRTUAL_FULL_SIZE; } }
static int imapc_mail_get_guid(struct mail *_mail, const char **value_r) { struct index_mail *imail = (struct index_mail *)_mail; struct imapc_mailbox *mbox = (struct imapc_mailbox *)_mail->box; const enum index_cache_field cache_idx = imail->ibox->cache_fields[MAIL_CACHE_GUID].idx; string_t *str; if (imail->data.guid != NULL) { *value_r = imail->data.guid; return 0; } str = str_new(imail->mail.data_pool, 64); if (mail_cache_lookup_field(_mail->transaction->cache_view, str, imail->mail.mail.seq, cache_idx) > 0) { *value_r = str_c(str); return 0; } /* GUID not in cache, fetch it */ if (mbox->guid_fetch_field_name != NULL) { if (imapc_mail_fetch(_mail, MAIL_FETCH_GUID) < 0) return -1; if (imail->data.guid == NULL) { (void)imapc_mail_failed(_mail, mbox->guid_fetch_field_name); return -1; } } else { /* use hash of message headers as the GUID */ if (imapc_mail_get_hdr_hash(imail) < 0) return -1; } index_mail_cache_add_idx(imail, cache_idx, imail->data.guid, strlen(imail->data.guid)+1); *value_r = imail->data.guid; return 0; }
static void index_mail_body_parsed_cache_message_parts(struct index_mail *mail) { struct index_mail_data *data = &mail->data; unsigned int cache_field = mail->ibox->cache_fields[MAIL_CACHE_MESSAGE_PARTS].idx; enum mail_cache_decision_type decision; buffer_t *buffer; if (data->messageparts_saved_to_cache || mail_cache_field_exists(mail->trans->cache_view, mail->data.seq, cache_field) != 0) { /* already cached */ return; } decision = mail_cache_field_get_decision(mail->mail.mail.box->cache, cache_field); if (decision == (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED)) { /* we never want it cached */ return; } if (decision == MAIL_CACHE_DECISION_NO && !data->save_message_parts && (mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) == 0) { /* we didn't really care about the message parts themselves, just wanted to use something that depended on it */ return; } T_BEGIN { buffer = buffer_create_dynamic(pool_datastack_create(), 1024); message_part_serialize(mail->data.parts, buffer); index_mail_cache_add_idx(mail, cache_field, buffer->data, buffer->used); } T_END; data->messageparts_saved_to_cache = TRUE; }
static void index_mail_parse_header_finish(struct index_mail *mail) { struct mail *_mail = &mail->mail.mail; const struct index_mail_line *lines; const unsigned char *header, *data; const uint8_t *match; buffer_t *buf; size_t data_size; unsigned int i, j, count, match_idx, match_count; bool noncontiguous; /* sort it first so fields are grouped together and ordered by line number */ array_sort(&mail->header_lines, header_line_cmp); lines = array_get(&mail->header_lines, &count); match = array_get(&mail->header_match, &match_count); header = buffer_get_data(mail->header_data, NULL); buf = buffer_create_dynamic(pool_datastack_create(), 256); /* go through all the header lines we found */ for (i = match_idx = 0; i < count; i = j) { /* matches and header lines are both sorted, all matches until lines[i] weren't found */ while (match_idx < lines[i].field_idx && match_idx < match_count) { if (HEADER_MATCH_USABLE(mail, match[match_idx]) && mail_cache_field_can_add(_mail->transaction->cache_trans, _mail->seq, match_idx)) { /* this header doesn't exist. remember that. */ i_assert((match[match_idx] & HEADER_MATCH_FLAG_FOUND) == 0); index_mail_cache_add_idx(mail, match_idx, "", 0); } match_idx++; } if (match_idx < match_count) { /* save index to first header line */ j = i + 1; array_idx_set(&mail->header_match_lines, match_idx, &j); match_idx++; } if (!mail_cache_field_can_add(_mail->transaction->cache_trans, _mail->seq, lines[i].field_idx)) { /* header is already cached */ j = i + 1; continue; } /* buffer contains: { uint32_t line_num[], 0, header texts } noncontiguous is just a small optimization.. */ buffer_set_used_size(buf, 0); buffer_append(buf, &lines[i].line_num, sizeof(lines[i].line_num)); noncontiguous = FALSE; for (j = i+1; j < count; j++) { if (lines[j].field_idx != lines[i].field_idx) break; if (lines[j].start_pos != lines[j-1].end_pos) noncontiguous = TRUE; buffer_append(buf, &lines[j].line_num, sizeof(lines[j].line_num)); } buffer_append_zero(buf, sizeof(uint32_t)); if (noncontiguous) { for (; i < j; i++) { buffer_append(buf, header + lines[i].start_pos, lines[i].end_pos - lines[i].start_pos); } i--; } else { buffer_append(buf, header + lines[i].start_pos, lines[j-1].end_pos - lines[i].start_pos); } data = buffer_get_data(buf, &data_size); index_mail_cache_add_idx(mail, lines[i].field_idx, data, data_size); } for (; match_idx < match_count; match_idx++) { if (HEADER_MATCH_USABLE(mail, match[match_idx]) && mail_cache_field_can_add(_mail->transaction->cache_trans, _mail->seq, match_idx)) { /* this header doesn't exist. remember that. */ i_assert((match[match_idx] & HEADER_MATCH_FLAG_FOUND) == 0); index_mail_cache_add_idx(mail, match_idx, "", 0); } } mail->data.dont_cache_field_idx = UINT_MAX; }
void index_mail_cache_add(struct index_mail *mail, enum index_cache_field field, const void *data, size_t data_size) { index_mail_cache_add_idx(mail, mail->ibox->cache_fields[field].idx, data, data_size); }