static void index_mailbox_expunge_unseen_recent(struct index_mailbox_sync_context *ctx) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(ctx->ctx.box); struct mail_index_view *view = ctx->ctx.box->view; const struct mail_index_header *hdr; uint32_t seq, start_uid, uid; if (!array_is_created(&ibox->recent_flags)) return; /* expunges array contained expunges for the messages that were already visible in this view, but append+expunge would be invisible. recent_flags may however contain the append UID, so we'll have to remove it separately */ hdr = mail_index_get_header(view); if (ctx->messages_count == 0) uid = 0; else if (ctx->messages_count <= hdr->messages_count) mail_index_lookup_uid(view, ctx->messages_count, &uid); else { i_assert(mail_index_view_is_inconsistent(view)); return; } for (seq = ctx->messages_count + 1; seq <= hdr->messages_count; seq++) { start_uid = uid; mail_index_lookup_uid(view, seq, &uid); if (start_uid + 1 > uid - 1) continue; ibox->recent_flags_count -= seq_range_array_remove_range(&ibox->recent_flags, start_uid + 1, uid - 1); } if (uid + 1 < hdr->next_uid) { ibox->recent_flags_count -= seq_range_array_remove_range(&ibox->recent_flags, uid + 1, hdr->next_uid - 1); } #ifdef DEBUG if (!mail_index_view_is_inconsistent(view)) { const struct seq_range *range; unsigned int i, count; range = array_get(&ibox->recent_flags, &count); for (i = 0; i < count; i++) { for (uid = range[i].seq1; uid <= range[i].seq2; uid++) { if (uid >= hdr->next_uid) break; (void)mail_index_lookup_seq(view, uid, &seq); i_assert(seq != 0); } } } #endif }
static int sync_pvt_expunges(struct index_mailbox_sync_pvt_context *ctx) { uint32_t seq_shared, seq_pvt, count_shared, count_pvt; uint32_t uid_shared, uid_pvt; count_shared = mail_index_view_get_messages_count(ctx->view_shared); count_pvt = mail_index_view_get_messages_count(ctx->view_pvt); seq_shared = seq_pvt = 1; while (seq_pvt <= count_pvt && seq_shared <= count_shared) { mail_index_lookup_uid(ctx->view_pvt, seq_pvt, &uid_pvt); mail_index_lookup_uid(ctx->view_shared, seq_shared, &uid_shared); if (uid_pvt == uid_shared) { seq_pvt++; seq_shared++; } else if (uid_pvt < uid_shared) { /* message expunged */ mail_index_expunge(ctx->trans_pvt, seq_pvt); seq_pvt++; } else { mail_storage_set_critical(ctx->box->storage, "%s: Message UID=%u unexpectedly inserted to mailbox", ctx->box->index_pvt->filepath, uid_shared); return -1; } } return 0; }
static void virtual_mail_set_seq(struct mail *mail, uint32_t seq) { struct virtual_mail *vmail = (struct virtual_mail *)mail; struct virtual_mailbox *mbox = (struct virtual_mailbox *)mail->box; struct virtual_backend_box *bbox; const struct virtual_mail_index_record *vrec; const void *data; bool expunged; mail_index_lookup_ext(mail->box->view, seq, mbox->virtual_ext_id, &data, &expunged); vrec = data; bbox = virtual_backend_box_lookup(mbox, vrec->mailbox_id); vmail->backend_mail = backend_mail_find(vmail, bbox->box); if (vmail->backend_mail == NULL) virtual_mail_set_backend_mail(mail, bbox); vmail->lost = !mail_set_uid(vmail->backend_mail, vrec->real_uid); memset(&vmail->imail.data, 0, sizeof(vmail->imail.data)); p_clear(vmail->imail.data_pool); vmail->imail.data.seq = seq; mail->seq = seq; mail_index_lookup_uid(mail->box->view, seq, &mail->uid); if (!vmail->lost) { mail->expunged = vmail->backend_mail->expunged; mail->has_nuls = vmail->backend_mail->has_nuls; mail->has_no_nuls = vmail->backend_mail->has_no_nuls; } else { mail->expunged = TRUE; mail->has_nuls = FALSE; mail->has_no_nuls = FALSE; } }
int mdbox_map_view_lookup_rec(struct mdbox_map *map, struct mail_index_view *view, uint32_t seq, struct dbox_mail_lookup_rec *rec_r) { const uint16_t *ref16_p; const void *data; memset(rec_r, 0, sizeof(*rec_r)); mail_index_lookup_uid(view, seq, &rec_r->map_uid); mail_index_lookup_ext(view, seq, map->map_ext_id, &data, NULL); if (data == NULL) { mdbox_map_set_corrupted(map, "missing map extension"); return -1; } memcpy(&rec_r->rec, data, sizeof(rec_r->rec)); mail_index_lookup_ext(view, seq, map->ref_ext_id, &data, NULL); if (data == NULL) { mdbox_map_set_corrupted(map, "missing ref extension"); return -1; } ref16_p = data; rec_r->refcount = *ref16_p; return 0; }
uint32_t mdbox_map_lookup_uid(struct mdbox_map *map, uint32_t seq) { uint32_t uid; mail_index_lookup_uid(map->view, seq, &uid); return uid; }
static unsigned int index_storage_count_pvt_unseen(struct mailbox *box) { const struct mail_index_record *pvt_rec; uint32_t shared_seq, pvt_seq, shared_count, pvt_count; uint32_t shared_uid; unsigned int unseen_count = 0; /* we can't trust private index to be up to date. we'll need to go through the shared index and for each existing mail lookup its private flags. if a mail doesn't exist in private index then its flags are 0. */ shared_count = mail_index_view_get_messages_count(box->view); pvt_count = mail_index_view_get_messages_count(box->view_pvt); shared_seq = pvt_seq = 1; while (shared_seq <= shared_count && pvt_seq <= pvt_count) { mail_index_lookup_uid(box->view, shared_seq, &shared_uid); pvt_rec = mail_index_lookup(box->view_pvt, pvt_seq); if (shared_uid == pvt_rec->uid) { if ((pvt_rec->flags & MAIL_SEEN) == 0) unseen_count++; shared_seq++; pvt_seq++; } else if (shared_uid < pvt_rec->uid) { shared_seq++; } else { pvt_seq++; } } unseen_count += (shared_count+1) - shared_seq; return unseen_count; }
static void cydir_sync_expunge(struct cydir_sync_context *ctx, uint32_t seq1, uint32_t seq2) { struct mailbox *box = &ctx->mbox->box; uint32_t uid; if (ctx->path == NULL) { ctx->path = cydir_get_path_prefix(ctx->mbox); ctx->path_dir_prefix_len = str_len(ctx->path); } for (; seq1 <= seq2; seq1++) { mail_index_lookup_uid(ctx->sync_view, seq1, &uid); str_truncate(ctx->path, ctx->path_dir_prefix_len); str_printfa(ctx->path, "%u.", uid); if (i_unlink_if_exists(str_c(ctx->path)) < 0) { /* continue anyway */ } else { if (box->v.sync_notify != NULL) { box->v.sync_notify(box, uid, MAILBOX_SYNC_TYPE_EXPUNGE); } mail_index_expunge(ctx->trans, seq1); } } }
static void cydir_sync_expunge(struct cydir_sync_context *ctx, uint32_t seq1, uint32_t seq2) { struct mailbox *box = &ctx->mbox->box; uint32_t uid; if (ctx->path == NULL) { ctx->path = cydir_get_path_prefix(ctx->mbox); ctx->path_dir_prefix_len = str_len(ctx->path); } for (; seq1 <= seq2; seq1++) { mail_index_lookup_uid(ctx->sync_view, seq1, &uid); str_truncate(ctx->path, ctx->path_dir_prefix_len); str_printfa(ctx->path, "%u.", uid); if (unlink(str_c(ctx->path)) == 0) { if (box->v.sync_notify != NULL) { box->v.sync_notify(box, uid, MAILBOX_SYNC_TYPE_EXPUNGE); } mail_index_expunge(ctx->trans, seq1); } else if (errno != ENOENT) { mail_storage_set_critical(&ctx->mbox->storage->storage, "unlink(%s) failed: %m", str_c(ctx->path)); /* continue anyway */ } } }
void mail_cache_decision_add(struct mail_cache_view *view, uint32_t seq, unsigned int field) { struct mail_cache *cache = view->cache; uint32_t uid; i_assert(field < cache->fields_count); if (MAIL_CACHE_IS_UNUSABLE(cache) || view->no_decision_updates) return; if (cache->fields[field].field.decision != MAIL_CACHE_DECISION_NO) { /* a) forced decision b) we're already caching it, so it just wasn't in cache */ return; } /* field used the first time */ cache->fields[field].field.decision = MAIL_CACHE_DECISION_TEMP; cache->fields[field].decision_dirty = TRUE; cache->field_header_write_pending = TRUE; mail_index_lookup_uid(view->view, seq, &uid); cache->fields[field].uid_highwater = uid; }
void mail_cache_decision_state_update(struct mail_cache_view *view, uint32_t seq, unsigned int field) { struct mail_cache *cache = view->cache; enum mail_cache_decision_type dec; const struct mail_index_header *hdr; uint32_t uid; i_assert(field < cache->fields_count); if (view->no_decision_updates) return; dec = cache->fields[field].field.decision; if (dec == (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED)) { /* don't update last_used */ return; } if (ioloop_time - cache->fields[field].field.last_used > 3600*24) { /* update last_used about once a day */ cache->fields[field].field.last_used = (uint32_t)ioloop_time; if (cache->field_file_map[field] != (uint32_t)-1) cache->field_header_write_pending = TRUE; } if (dec != MAIL_CACHE_DECISION_TEMP) { /* a) forced decision b) not cached, mail_cache_decision_add() will handle this c) permanently cached already, okay. */ return; } mail_index_lookup_uid(view->view, seq, &uid); hdr = mail_index_get_header(view->view); /* see if we want to change decision from TEMP to YES */ if (uid < cache->fields[field].uid_highwater || uid < hdr->day_first_uid[7]) { /* a) nonordered access within this session. if client doesn't request messages in growing order, we assume it doesn't have a permanent local cache. b) accessing message older than one week. assume it's a client with no local cache. if it was just a new client generating the local cache for the first time, we'll drop back to TEMP within few months. */ cache->fields[field].field.decision = MAIL_CACHE_DECISION_YES; cache->fields[field].decision_dirty = TRUE; if (cache->field_file_map[field] != (uint32_t)-1) cache->field_header_write_pending = TRUE; } else { cache->fields[field].uid_highwater = uid; } }
void index_mailbox_set_recent_seq(struct mailbox *box, struct mail_index_view *view, uint32_t seq1, uint32_t seq2) { uint32_t uid; for (; seq1 <= seq2; seq1++) { mail_index_lookup_uid(view, seq1, &uid); index_mailbox_set_recent_uid(box, uid); } }
static void imapc_sync_add_missing_deleted_flags(struct imapc_sync_context *ctx, uint32_t seq1, uint32_t seq2) { const struct mail_index_record *rec; uint32_t seq, uid1, uid2; const char *cmd; /* if any of them has a missing \Deleted flag, just add it to all of them. */ for (seq = seq1; seq <= seq2; seq++) { rec = mail_index_lookup(ctx->sync_view, seq); if ((rec->flags & MAIL_DELETED) == 0) break; } if (seq <= seq2) { mail_index_lookup_uid(ctx->sync_view, seq1, &uid1); mail_index_lookup_uid(ctx->sync_view, seq2, &uid2); cmd = t_strdup_printf("UID STORE %u:%u +FLAGS \\Deleted", uid1, uid2); imapc_sync_cmd(ctx, cmd); } }
static void index_mailbox_expunge_recent(struct mailbox *box, uint32_t seq1, uint32_t seq2) { struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box); uint32_t uid; if (!array_is_created(&ibox->recent_flags)) return; for (; seq1 <= seq2; seq1++) { mail_index_lookup_uid(box->view, seq1, &uid); if (seq_range_array_remove(&ibox->recent_flags, uid)) ibox->recent_flags_count--; } }
static int dbox_sync_mark_expunges(struct mdbox_sync_context *ctx) { enum mail_index_transaction_flags flags = MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL; struct mailbox *box = &ctx->mbox->box; struct mail_index_transaction *trans; struct seq_range_iter iter; unsigned int n; const void *data; uint32_t seq, uid; /* use a separate transaction here so that we can commit the changes during map transaction */ trans = mail_index_transaction_begin(ctx->sync_view, flags); seq_range_array_iter_init(&iter, &ctx->expunged_seqs); n = 0; while (seq_range_array_iter_nth(&iter, n++, &seq)) { mail_index_lookup_uid(ctx->sync_view, seq, &uid); mail_index_lookup_ext(ctx->sync_view, seq, ctx->mbox->guid_ext_id, &data, NULL); mail_index_expunge_guid(trans, seq, data); } if (mail_index_transaction_commit(&trans) < 0) return -1; if (box->v.sync_notify != NULL) { /* do notifications after commit finished successfully */ box->tmp_sync_view = ctx->sync_view; seq_range_array_iter_init(&iter, &ctx->expunged_seqs); n = 0; while (seq_range_array_iter_nth(&iter, n++, &seq)) { mail_index_lookup_uid(ctx->sync_view, seq, &uid); box->v.sync_notify(box, uid, MAILBOX_SYNC_TYPE_EXPUNGE); } box->tmp_sync_view = NULL; } return 0; }
static int mdbox_map_lookup_seq(struct mdbox_map *map, uint32_t seq, const struct mdbox_map_mail_index_record **rec_r) { const struct mdbox_map_mail_index_record *rec; const void *data; uint32_t uid; mail_index_lookup_ext(map->view, seq, map->map_ext_id, &data, NULL); rec = data; if (rec == NULL || rec->file_id == 0) { mail_index_lookup_uid(map->view, seq, &uid); mdbox_map_set_corrupted(map, "file_id=0 for map_uid=%u", uid); return -1; } *rec_r = rec; return 0; }
int mdbox_mail_lookup(struct mdbox_mailbox *mbox, struct mail_index_view *view, uint32_t seq, uint32_t *map_uid_r) { const struct mdbox_mail_index_record *dbox_rec; struct mdbox_index_header hdr; const void *data; uint32_t uid, cur_map_uid_validity; bool expunged; mail_index_lookup_ext(view, seq, mbox->ext_id, &data, &expunged); dbox_rec = data; if (dbox_rec == NULL || dbox_rec->map_uid == 0) { mail_index_lookup_uid(view, seq, &uid); mail_storage_set_critical(&mbox->storage->storage.storage, "mdbox %s: map uid lost for uid %u", mbox->box.path, uid); mdbox_storage_set_corrupted(mbox->storage); return -1; } if (mbox->map_uid_validity == 0) { if (mdbox_read_header(mbox, &hdr) < 0) { mdbox_storage_set_corrupted(mbox->storage); return -1; } mbox->map_uid_validity = hdr.map_uid_validity; } if (mdbox_map_open_or_create(mbox->storage->map) < 0) return -1; cur_map_uid_validity = mdbox_map_get_uid_validity(mbox->storage->map); if (cur_map_uid_validity != mbox->map_uid_validity) { mail_storage_set_critical(&mbox->storage->storage.storage, "mdbox %s: map uidvalidity mismatch (%u vs %u)", mbox->box.path, mbox->map_uid_validity, cur_map_uid_validity); mdbox_storage_set_corrupted(mbox->storage); return -1; } *map_uid_r = dbox_rec->map_uid; return 0; }
static int dbox_sync_verify_expunge_guid(struct mdbox_sync_context *ctx, uint32_t seq, const guid_128_t guid_128) { const void *data; uint32_t uid; mail_index_lookup_uid(ctx->sync_view, seq, &uid); mail_index_lookup_ext(ctx->sync_view, seq, ctx->mbox->guid_ext_id, &data, NULL); if (guid_128_is_empty(guid_128) || memcmp(data, guid_128, GUID_128_SIZE) == 0) return 0; mail_storage_set_critical(&ctx->mbox->storage->storage.storage, "Mailbox %s: Expunged GUID mismatch for UID %u: %s vs %s", ctx->mbox->box.vname, uid, guid_128_to_string(data), guid_128_to_string(guid_128)); mdbox_storage_set_corrupted(ctx->mbox->storage); return -1; }
static void imapc_sync_expunge_eom(struct imapc_sync_context *ctx) { struct imapc_mailbox *mbox = ctx->mbox; uint32_t lseq, uid, msg_count; if (mbox->sync_next_lseq == 0) return; /* if we haven't seen FETCH reply for some messages at the end of mailbox they've been externally expunged. */ msg_count = mail_index_view_get_messages_count(ctx->sync_view); for (lseq = mbox->sync_next_lseq; lseq <= msg_count; lseq++) { mail_index_lookup_uid(ctx->sync_view, lseq, &uid); if (uid >= mbox->sync_uid_next) { /* another process already added new messages to index that our IMAP connection hasn't seen yet */ break; } mail_index_expunge(ctx->trans, lseq); } mbox->sync_next_lseq = 0; mbox->sync_next_rseq = 0; }
array_foreach(changes, range) { for (seq = range->seq1; seq <= range->seq2; seq++) { mail_index_lookup_uid(ctx->ctx.box->view, seq, &uid); seq_range_array_add(&ctx->all_flag_update_uids, uid); } }
static int mailbox_list_index_parse_records(struct mailbox_list_index *ilist, struct mail_index_view *view, const char **error_r) { struct mailbox_list_index_node *node; const struct mail_index_record *rec; const struct mailbox_list_index_record *irec; const void *data; bool expunged; uint32_t seq, uid, count; *error_r = NULL; count = mail_index_view_get_messages_count(view); for (seq = 1; seq <= count; seq++) { node = p_new(ilist->mailbox_pool, struct mailbox_list_index_node, 1); rec = mail_index_lookup(view, seq); node->uid = rec->uid; node->flags = rec->flags; mail_index_lookup_ext(view, seq, ilist->ext_id, &data, &expunged); if (data == NULL) { *error_r = "Missing list extension data"; return -1; } irec = data; node->name_id = irec->name_id; node->name = hash_table_lookup(ilist->mailbox_names, POINTER_CAST(irec->name_id)); if (node->name == NULL) { *error_r = "name_id not in index header"; if (ilist->has_backing_store) return -1; /* generate a new name and use it */ mailbox_list_index_generate_name(ilist, node); } hash_table_insert(ilist->mailbox_hash, POINTER_CAST(node->uid), node); } /* do a second scan to create the actual mailbox tree hierarchy. this is needed because the parent_uid may be smaller or higher than the current node's uid */ for (seq = 1; seq <= count; seq++) { mail_index_lookup_uid(view, seq, &uid); mail_index_lookup_ext(view, seq, ilist->ext_id, &data, &expunged); irec = data; node = mailbox_list_index_lookup_uid(ilist, uid); i_assert(node != NULL); if (irec->parent_uid != 0) { /* node should have a parent */ node->parent = mailbox_list_index_lookup_uid(ilist, irec->parent_uid); if (node->parent != NULL) { node->next = node->parent->children; node->parent->children = node; continue; } *error_r = "parent_uid points to nonexistent record"; if (ilist->has_backing_store) return -1; /* just place it under the root */ } node->next = ilist->mailbox_tree; ilist->mailbox_tree = node; } return *error_r == NULL ? 0 : -1; }
bool mbox_sync_parse_match_mail(struct mbox_mailbox *mbox, struct mail_index_view *view, uint32_t seq) { struct mbox_sync_mail_context ctx; struct message_header_parser_ctx *hdr_ctx; struct message_header_line *hdr; struct header_func *func; struct mbox_md5_context *mbox_md5_ctx; const void *data; bool expunged; uint32_t uid; int ret; /* we only wish to be sure that this mail actually is what we expect it to be. If there's X-UID header and it matches our UID, we use it. Otherwise it could mean that the X-UID header is invalid and it's just not yet been rewritten. In that case use MD5 sum, if it exists. */ mail_index_lookup_uid(view, seq, &uid); memset(&ctx, 0, sizeof(ctx)); mbox_md5_ctx = mbox->md5_v.init(); hdr_ctx = message_parse_header_init(mbox->mbox_stream, NULL, 0); while ((ret = message_parse_header_next(hdr_ctx, &hdr)) > 0) { if (hdr->eoh) break; func = bsearch(hdr->name, header_funcs, N_ELEMENTS(header_funcs), sizeof(*header_funcs), mbox_sync_bsearch_header_func_cmp); if (func != NULL) { if (strcasecmp(hdr->name, "X-UID") == 0) { if (hdr->continues) { hdr->use_full_value = TRUE; continue; } (void)parse_x_uid(&ctx, hdr); if (ctx.mail.uid == uid) break; } } else { mbox->md5_v.more(mbox_md5_ctx, hdr); } } i_assert(ret != 0); message_parse_header_deinit(&hdr_ctx); mbox->md5_v.finish(mbox_md5_ctx, ctx.hdr_md5_sum); if (ctx.mail.uid == uid) return TRUE; /* match by MD5 sum */ mbox->mbox_save_md5 = TRUE; mail_index_lookup_ext(view, seq, mbox->md5hdr_ext_idx, &data, &expunged); return data == NULL ? 0 : memcmp(data, ctx.hdr_md5_sum, 16) == 0; }
void index_mail_set_seq(struct mail *_mail, uint32_t seq) { struct index_mail *mail = (struct index_mail *)_mail; struct index_mail_data *data = &mail->data; struct mail_cache_field *cache_fields = mail->ibox->cache_fields; struct mail_cache_view *cache_view = mail->trans->cache_view; const struct mail_index_header *hdr; struct istream *input; if (data->seq == seq) return; index_mail_reset(mail); data->seq = seq; mail->mail.mail.seq = seq; mail_index_lookup_uid(_mail->transaction->view, seq, &mail->mail.mail.uid); if (mail_index_view_is_inconsistent(_mail->transaction->view)) { mail_set_expunged(&mail->mail.mail); return; } if ((mail->wanted_fields & (MAIL_FETCH_NUL_STATE | MAIL_FETCH_IMAP_BODY | MAIL_FETCH_IMAP_BODYSTRUCTURE)) != 0) { (void)index_mail_get_fixed_field(mail, MAIL_CACHE_FLAGS, &data->cache_flags, sizeof(data->cache_flags)); mail->mail.mail.has_nuls = (data->cache_flags & MAIL_CACHE_FLAG_HAS_NULS) != 0; mail->mail.mail.has_no_nuls = (data->cache_flags & MAIL_CACHE_FLAG_HAS_NO_NULS) != 0; } /* see if wanted_fields can tell us if we need to read/parse header/body */ if ((mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) != 0) { unsigned int cache_field = cache_fields[MAIL_CACHE_MESSAGE_PARTS].idx; if (mail_cache_field_exists(cache_view, seq, cache_field) <= 0) { data->access_part |= PARSE_HDR | PARSE_BODY; data->save_message_parts = TRUE; } } if ((mail->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE) != 0) check_envelope(mail); if ((mail->wanted_fields & MAIL_FETCH_IMAP_BODY) != 0 && (data->cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) == 0) { /* we need either imap.body or imap.bodystructure */ unsigned int cache_field1 = cache_fields[MAIL_CACHE_IMAP_BODY].idx; unsigned int cache_field2 = cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx; if (mail_cache_field_exists(cache_view, seq, cache_field1) <= 0 && mail_cache_field_exists(cache_view, seq, cache_field2) <= 0) { data->access_part |= PARSE_HDR | PARSE_BODY; data->save_bodystructure_header = TRUE; data->save_bodystructure_body = TRUE; } } if ((mail->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) != 0 && (data->cache_flags & MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) == 0) { unsigned int cache_field = cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx; if (mail_cache_field_exists(cache_view, seq, cache_field) <= 0) { data->access_part |= PARSE_HDR | PARSE_BODY; data->save_bodystructure_header = TRUE; data->save_bodystructure_body = TRUE; } } if ((mail->wanted_fields & MAIL_FETCH_DATE) != 0) { unsigned int cache_field = cache_fields[MAIL_CACHE_SENT_DATE].idx; if (mail_cache_field_exists(cache_view, seq, cache_field) <= 0) { data->access_part |= PARSE_HDR; data->save_sent_date = TRUE; } } if ((mail->wanted_fields & (MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY)) != 0) { /* open stream immediately to set expunged flag if it's already lost */ if ((mail->wanted_fields & MAIL_FETCH_STREAM_HEADER) != 0) data->access_part |= READ_HDR; if ((mail->wanted_fields & MAIL_FETCH_STREAM_BODY) != 0) data->access_part |= READ_BODY; /* open the stream only if we didn't get here from mailbox_save_init() */ hdr = mail_index_get_header(_mail->box->view); if (!_mail->saving && _mail->uid < hdr->next_uid) (void)mail_get_stream(_mail, NULL, NULL, &input); } }