bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic uint32_t thread_msg_add(struct mail_thread_cache *cache,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(msgid_idx < cache->first_invalid_msgid_str_idx);
139143f1b798472438b813343a48601f1c564060Sergey Kitov node = array_idx_get_space(&cache->thread_nodes, msgid_idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* duplicate message-id, keep the original.
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if the original ever gets expunged, rebuild. */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen msgid_idx = cache->next_invalid_msgid_str_idx++;
139143f1b798472438b813343a48601f1c564060Sergey Kitov node = array_idx_get_space(&cache->thread_nodes, msgid_idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic bool thread_node_has_ancestor(struct mail_thread_cache *cache,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen node = array_idx(&cache->thread_nodes, node->parent_idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void thread_link_reference(struct mail_thread_cache *cache,
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen struct mail_thread_node *node, *parent, *child;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(parent_idx < cache->first_invalid_msgid_str_idx);
a68752dd4de71537a8f7d120aa299044727f125aTimo Sirainen /* either child_idx or parent_idx may cause thread_nodes array to
a68752dd4de71537a8f7d120aa299044727f125aTimo Sirainen grow. in such situation the other pointer may become invalid if
a68752dd4de71537a8f7d120aa299044727f125aTimo Sirainen we don't get the pointers in correct order. */
139143f1b798472438b813343a48601f1c564060Sergey Kitov parent = array_idx_get_space(&cache->thread_nodes, parent_idx);
a68752dd4de71537a8f7d120aa299044727f125aTimo Sirainen child = array_idx_modifiable(&cache->thread_nodes, child_idx);
139143f1b798472438b813343a48601f1c564060Sergey Kitov child = array_idx_get_space(&cache->thread_nodes, child_idx);
a68752dd4de71537a8f7d120aa299044727f125aTimo Sirainen parent = array_idx_modifiable(&cache->thread_nodes, parent_idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (thread_node_has_ancestor(cache, parent, child)) {
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen /* loops to itself - ignore */
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen /* child is an ancestor of parent. Adding child -> parent_node
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen would introduce a loop. If any messages referencing the path
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen between parent_node's parent and child_node get expunged, we
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen have to rebuild the tree because the loop might break.
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen #1: a -> b (a.ref=1, b.ref=1)
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen #2: b -> a (a.ref=2, b.ref=2)
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen #3: c -> a -> b (a.ref=3, b.ref=3, c.ref=1)
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen Expunging #3 wouldn't break the loop, but expunging #1
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen node = array_idx_modifiable(&cache->thread_nodes, idx);
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen /* The same link already exists */
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen /* Set parent_node as child_node's parent */
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen /* Conflicting parent already exists, keep the original */
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen /* If this message gets expunged,
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen the parent is changed. */
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen /* Message doesn't exist, so it was one of the node's
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen children that created the original reference. If
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen that reference gets dropped, the parent is changed.
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen We could catch this in one of several ways:
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen a) Link to parent node gets unreferenced
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen b) Link to this node gets unreferenced
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen c) Any of the child nodes gets expunged
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen b) is probably the least likely to happen,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenthread_link_references(struct mail_thread_cache *cache, uint32_t uid,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const struct mail_index_strmap_rec *msgid_map,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen thread_link_reference(cache, parent_idx, msgid_map->str_idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(parent_idx < cache->first_invalid_msgid_str_idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenvoid mail_thread_add(struct mail_thread_cache *cache,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const struct mail_index_strmap_rec *msgid_map,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(msgid_map->ref_index == MAIL_THREAD_NODE_REF_MSGID);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen idx = thread_msg_add(cache, msgid_map->uid, msgid_map->str_idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen parent_idx = thread_link_references(cache, msgid_map->uid,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen node = array_idx_modifiable(&cache->thread_nodes, idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (node->parent_idx != parent_idx && node->parent_idx != 0) {
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen /* conflicting parent, remove it. */
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen /* If this message gets expunged, we have to revert back to
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen the original parent. */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen thread_link_reference(cache, parent_idx, idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenmail_thread_unref_link(struct mail_thread_cache *cache,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen parent = array_idx_modifiable(&cache->thread_nodes, parent_idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen child = array_idx_modifiable(&cache->thread_nodes, child_idx);
6ff05fc623dfad145a2bc65abc4536395ce5e561Timo Sirainen /* we don't have a root anymore */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenbool mail_thread_remove(struct mail_thread_cache *cache,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const struct mail_index_strmap_rec *msgid_map,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* this message was never added to the cache, skip */
838f57f389d5f4e97d9313ff86625bf1ee3517c6Timo Sirainen while (msgid_map[count].uid == msgid_map->uid)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen node = array_idx_modifiable(&cache->thread_nodes, idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* this catches the duplicate message-id case */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* update link refcounts */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (!mail_thread_unref_link(cache, parent_idx,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (!mail_thread_unref_link(cache, parent_idx, idx))
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* mark this message as expunged */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* we don't know (and don't want to waste time figuring out) if other
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen messages point to this removed message, so don't delete the node */