bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen/* n% of timeout_secs */
2dcf09e68a6d84aba506b0a93897b186ea11520fTimo Sirainen/* but min/max. of this many secs */
8017d6c67481d2bcfa1ba1fe229939a0ba8b56e9Timo Sirainen/* This shouldn't matter what it is exactly, just try it sometimes later. */
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen /* unsigned int username_hash => user */
94fb15fffc814d7dcecabf8d90691502311d4b88Timo Sirainen /* sorted by time. may be unsorted while handshakes are going on. */
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen /* If user's expire time is less than this many seconds away,
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen don't assume that other directors haven't yet expired it */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void user_move_iters(struct user_directory *dir, struct user *user)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void user_free(struct user_directory *dir, struct user *user)
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen hash_table_remove(dir->hash, POINTER_CAST(user->username_hash));
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainenstatic bool user_directory_user_has_connections(struct user_directory *dir,
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen time_t expire_timestamp = user->timestamp + dir->timeout_secs;
d40c98b71de3acd7dd9e41178d449ca84d26e406Timo Sirainen /* don't free this user until the kill is finished */
7d8c1ba766770ea1c6d4d6770a31832b5b518648Timo Sirainen if (expire_timestamp + USER_NEAR_EXPIRING_MAX > ioloop_time) {
d40c98b71de3acd7dd9e41178d449ca84d26e406Timo Sirainen i_warning("User %u weakness appears to be stuck, removing it",
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstatic void user_directory_drop_expired(struct user_directory *dir)
27006b32276426acf449bd0ea7a35ed19389ae6bTimo Sirainen !user_directory_user_has_connections(dir, dir->head, &expire_timestamp)) {
27006b32276426acf449bd0ea7a35ed19389ae6bTimo Sirainen i_assert(expire_timestamp > ioloop_time || expire_timestamp == 0);
27006b32276426acf449bd0ea7a35ed19389ae6bTimo Sirainen if (expire_timestamp != dir->to_expire_timestamp) {
27006b32276426acf449bd0ea7a35ed19389ae6bTimo Sirainen struct timeval tv = { .tv_sec = expire_timestamp };
ece0a20249ce26208db3415ba2e79423678856f8Timo Sirainenunsigned int user_directory_count(struct user_directory *dir)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstruct user *user_directory_lookup(struct user_directory *dir,
2dcf09e68a6d84aba506b0a93897b186ea11520fTimo Sirainen user = hash_table_lookup(dir->hash, POINTER_CAST(username_hash));
27006b32276426acf449bd0ea7a35ed19389ae6bTimo Sirainen if (user != NULL && !user_directory_user_has_connections(dir, user, &expire_timestamp)) {
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenuser_directory_add(struct user_directory *dir, unsigned int username_hash,
32a93320fd2b6ada5ac8027166819463c1a007b6Timo Sirainen /* make sure we don't add timestamps higher than ioloop time */
27006b32276426acf449bd0ea7a35ed19389ae6bTimo Sirainen struct timeval tv = { .tv_sec = ioloop_time + dir->timeout_secs };
27006b32276426acf449bd0ea7a35ed19389ae6bTimo Sirainen dir->to_expire = timeout_add_absolute(&tv, user_directory_drop_expired, dir);
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen hash_table_insert(dir->hash, POINTER_CAST(user->username_hash), user);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenvoid user_directory_refresh(struct user_directory *dir, struct user *user)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenvoid user_directory_remove_host(struct user_directory *dir,
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen for (user = dir->head; user != NULL; user = next) {
8621be3846dc097420cce325ad36d1b646f72a09Timo Sirainenstatic int user_timestamp_cmp(struct user *const *user1,
8621be3846dc097420cce325ad36d1b646f72a09Timo Sirainen if ((*user1)->timestamp < (*user2)->timestamp)
8621be3846dc097420cce325ad36d1b646f72a09Timo Sirainen if ((*user1)->timestamp > (*user2)->timestamp)
8621be3846dc097420cce325ad36d1b646f72a09Timo Sirainenvoid user_directory_sort(struct user_directory *dir)
8621be3846dc097420cce325ad36d1b646f72a09Timo Sirainen unsigned int i, users_count = hash_table_count(dir->hash);
efd72f0559ac15585ec5f48893f2e13eb6cf7738Timo Sirainen /* We can't sort the directory while there are iterators
efd72f0559ac15585ec5f48893f2e13eb6cf7738Timo Sirainen or they'll skip users. Do the sort after there are no more
efd72f0559ac15585ec5f48893f2e13eb6cf7738Timo Sirainen iterators. */
8621be3846dc097420cce325ad36d1b646f72a09Timo Sirainen /* place all users into array and sort it */
8621be3846dc097420cce325ad36d1b646f72a09Timo Sirainen for (i = 0; i < users_count; i++, user = user->next)
8621be3846dc097420cce325ad36d1b646f72a09Timo Sirainen /* recreate the linked list */
8621be3846dc097420cce325ad36d1b646f72a09Timo Sirainen DLLIST2_APPEND(&dir->head, &dir->tail, *userp);
430d256a6244b2a780c298febc66e74d6b6e8cc2Timo Sirainen dir->head->timestamp <= dir->tail->timestamp);
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainenbool user_directory_user_is_recently_updated(struct user_directory *dir,
7dd73e056c7d9976080393d9cd731f69111818eeTimo Sirainen return (time_t)(user->timestamp + dir->timeout_secs/2) >= ioloop_time;
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainenbool user_directory_user_is_near_expiring(struct user_directory *dir,
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen (dir->timeout_secs - dir->user_near_expiring_secs);
2dcf09e68a6d84aba506b0a93897b186ea11520fTimo Sirainen i_assert(timeout_secs > USER_NEAR_EXPIRING_MIN);
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen timeout_secs * USER_NEAR_EXPIRING_PERCENTAGE / 100;
b6b9c99fefbbc662bd9a0006566133c4480bf0e8Timo Sirainen I_MIN(dir->user_near_expiring_secs, USER_NEAR_EXPIRING_MAX);
2dcf09e68a6d84aba506b0a93897b186ea11520fTimo Sirainen I_MAX(dir->user_near_expiring_secs, USER_NEAR_EXPIRING_MIN);
2dcf09e68a6d84aba506b0a93897b186ea11520fTimo Sirainen i_assert(dir->timeout_secs/2 > dir->user_near_expiring_secs);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen hash_table_create_direct(&dir->hash, default_pool, 0);
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenvoid user_directory_deinit(struct user_directory **_dir)
14660f677e16a5c36f3c43e9e64f5e021fda627bTimo Sirainenuser_directory_iter_init(struct user_directory *dir,
14660f677e16a5c36f3c43e9e64f5e021fda627bTimo Sirainen iter->stop_after_tail = iter_until_current_tail ? dir->tail : NULL;
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenstruct user *user_directory_iter_next(struct user_directory_iter *iter)
14660f677e16a5c36f3c43e9e64f5e021fda627bTimo Sirainen /* this is the last user we want to iterate */
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainenvoid user_directory_iter_deinit(struct user_directory_iter **_iter)
2670cd577aa57eb9f915a4f4220ae48c9b4fc5fbTimo Sirainen for (i = 0; i < count; i++) {
efd72f0559ac15585ec5f48893f2e13eb6cf7738Timo Sirainen if (array_count(&iter->dir->iters) == 0 && iter->dir->sort_pending)