user-directory.c revision 27006b32276426acf449bd0ea7a35ed19389ae6b
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "lib.h"
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen#include "ioloop.h"
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen#include "array.h"
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen#include "hash.h"
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen#include "llist.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "mail-host.h"
c2cda8cd0043443566efc5da30f79865508a1947Timo Sirainen
c2cda8cd0043443566efc5da30f79865508a1947Timo Sirainen/* n% of timeout_secs */
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#define USER_NEAR_EXPIRING_PERCENTAGE 10
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen/* but min/max. of this many secs */
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#define USER_NEAR_EXPIRING_MIN 3
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#define USER_NEAR_EXPIRING_MAX 30
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen/* This shouldn't matter what it is exactly, just try it sometimes later. */
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#define USER_BEING_KILLED_EXPIRE_RETRY_SECS 60
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainenstruct user_directory_iter {
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen struct user_directory *dir;
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen struct user *pos;
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen};
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainenstruct user_directory {
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen /* unsigned int username_hash => user */
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen HASH_TABLE(void *, struct user *) hash;
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen /* sorted by time */
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen struct user *head, *tail;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen struct user *prev_insert_pos;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen ARRAY(struct user_directory_iter *) iters;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen user_free_hook_t *user_free_hook;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen unsigned int timeout_secs;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen /* If user's expire time is less than this many seconds away,
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen don't assume that other directors haven't yet expired it */
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen unsigned int user_near_expiring_secs;
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen struct timeout *to_expire;
d6684856fb99e51bc22a6346e08b2d81c996f963Josef 'Jeff' Sipek time_t to_expire_timestamp;
d6684856fb99e51bc22a6346e08b2d81c996f963Josef 'Jeff' Sipek};
d6684856fb99e51bc22a6346e08b2d81c996f963Josef 'Jeff' Sipek
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainenstatic void user_move_iters(struct user_directory *dir, struct user *user)
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen{
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen struct user_directory_iter *const *iterp;
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen array_foreach(&dir->iters, iterp) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if ((*iterp)->pos == user)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen (*iterp)->pos = user->next;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (dir->prev_insert_pos == user)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen dir->prev_insert_pos = user->next;
b3fc5293379feb3640b23622bcc8f5f8d7f1e81dJosef 'Jeff' Sipek}
b3fc5293379feb3640b23622bcc8f5f8d7f1e81dJosef 'Jeff' Sipek
b3fc5293379feb3640b23622bcc8f5f8d7f1e81dJosef 'Jeff' Sipekstatic void user_free(struct user_directory *dir, struct user *user)
b3fc5293379feb3640b23622bcc8f5f8d7f1e81dJosef 'Jeff' Sipek{
b3fc5293379feb3640b23622bcc8f5f8d7f1e81dJosef 'Jeff' Sipek i_assert(user->host->user_count > 0);
b3fc5293379feb3640b23622bcc8f5f8d7f1e81dJosef 'Jeff' Sipek user->host->user_count--;
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (dir->user_free_hook != NULL)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen dir->user_free_hook(user);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen user_move_iters(dir, user);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen
5a1b498b646b5c5dbd1b3f3861df766f560578c5Timo Sirainen hash_table_remove(dir->hash, POINTER_CAST(user->username_hash));
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek DLLIST2_REMOVE(&dir->head, &dir->tail, user);
5a1b498b646b5c5dbd1b3f3861df766f560578c5Timo Sirainen i_free(user);
5a1b498b646b5c5dbd1b3f3861df766f560578c5Timo Sirainen}
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic bool user_directory_user_has_connections(struct user_directory *dir,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct user *user,
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen time_t *expire_timestamp_r)
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen{
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen time_t expire_timestamp = user->timestamp + dir->timeout_secs;
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen if (expire_timestamp > ioloop_time) {
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen *expire_timestamp_r = expire_timestamp;
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen return TRUE;
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen }
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen if (USER_IS_BEING_KILLED(user)) {
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen /* don't free this user until the kill is finished */
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen *expire_timestamp_r = ioloop_time +
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen USER_BEING_KILLED_EXPIRE_RETRY_SECS;
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen return TRUE;
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen }
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (user->weak) {
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if (expire_timestamp + USER_NEAR_EXPIRING_MAX > ioloop_time) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen *expire_timestamp_r = expire_timestamp +
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen USER_NEAR_EXPIRING_MAX;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return TRUE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen i_warning("User %u weakness appears to be stuck, removing it",
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen user->username_hash);
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen }
46ce4d9273e6df12ef1912bbdb1c8b84b104f394Timo Sirainen return FALSE;
46ce4d9273e6df12ef1912bbdb1c8b84b104f394Timo Sirainen}
46ce4d9273e6df12ef1912bbdb1c8b84b104f394Timo Sirainen
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainenstatic void user_directory_drop_expired(struct user_directory *dir)
46ce4d9273e6df12ef1912bbdb1c8b84b104f394Timo Sirainen{
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen time_t expire_timestamp = 0;
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen while (dir->head != NULL &&
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen !user_directory_user_has_connections(dir, dir->head, &expire_timestamp)) {
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen user_free(dir, dir->head);
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen expire_timestamp = 0;
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen }
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen i_assert(expire_timestamp > ioloop_time || expire_timestamp == 0);
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen if (expire_timestamp != dir->to_expire_timestamp) {
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen if (dir->to_expire != NULL)
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen timeout_remove(&dir->to_expire);
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen if (expire_timestamp != 0) {
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen struct timeval tv = { .tv_sec = expire_timestamp };
5af5137f6dc0c9f358b7813e941e26f7bd735b3aTimo Sirainen dir->to_expire_timestamp = tv.tv_sec;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen dir->to_expire = timeout_add_absolute(&tv,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen user_directory_drop_expired, dir);
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainenunsigned int user_directory_count(struct user_directory *dir)
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen{
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen return hash_table_count(dir->hash);
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen}
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstruct user *user_directory_lookup(struct user_directory *dir,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen unsigned int username_hash)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct user *user;
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen time_t expire_timestamp;
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen user_directory_drop_expired(dir);
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen user = hash_table_lookup(dir->hash, POINTER_CAST(username_hash));
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen if (user != NULL && !user_directory_user_has_connections(dir, user, &expire_timestamp)) {
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen user_free(dir, user);
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen user = NULL;
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen }
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen return user;
71aed7ba87b5fd5e96e97a22d89ac025b883d60aTimo Sirainen}
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainenstatic void
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainenuser_directory_insert_backwards(struct user_directory *dir,
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen struct user *pos, struct user *user)
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen{
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen for (; pos != NULL; pos = pos->prev) {
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen if (pos->timestamp <= user->timestamp)
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen break;
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen }
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen if (pos == NULL)
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen DLLIST2_PREPEND(&dir->head, &dir->tail, user);
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen else {
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen user->prev = pos;
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen user->next = pos->next;
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen user->prev->next = user;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen if (user->next != NULL)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen user->next->prev = user;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen else
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen dir->tail = user;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen }
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen}
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainenstatic void
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainenuser_directory_insert_forwards(struct user_directory *dir,
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen struct user *pos, struct user *user)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen{
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen for (; pos != NULL; pos = pos->next) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (pos->timestamp >= user->timestamp)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen break;
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen }
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen if (pos == NULL)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen DLLIST2_APPEND(&dir->head, &dir->tail, user);
a2738cdb6d2733fb3e186331d68009421a19ea00Timo Sirainen else {
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen user->prev = pos->prev;
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen user->next = pos;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (user->prev != NULL)
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen user->prev->next = user;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen else
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen dir->head = user;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen user->next->prev = user;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen }
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen}
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainenstruct user *
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainenuser_directory_add(struct user_directory *dir, unsigned int username_hash,
7af4788b402346c94496095dd819f95ce03fe431Timo Sirainen struct mail_host *host, time_t timestamp)
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen{
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen struct user *user;
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen /* make sure we don't add timestamps higher than ioloop time */
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen if (timestamp > ioloop_time)
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen timestamp = ioloop_time;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen user = i_new(struct user, 1);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen user->username_hash = username_hash;
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen user->host = host;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen user->host->user_count++;
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen user->timestamp = timestamp;
c24ef531ca58abad996482f5c2e8992be9ae8981Timo Sirainen
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen if (dir->tail == NULL || (time_t)dir->tail->timestamp <= timestamp)
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen DLLIST2_APPEND(&dir->head, &dir->tail, user);
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen else {
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen /* need to insert to correct position. we should get here
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen only when handshaking. the handshaking USER requests should
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen come sorted by timestamp. so keep track of the previous
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen insert position, the next USER should be inserted after
2524ef7b34965a1b1895d6140fd8296bf57c78d2Timo Sirainen it. */
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen if (dir->prev_insert_pos == NULL) {
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen /* find the position starting from tail */
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen user_directory_insert_backwards(dir, dir->tail, user);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen } else if (timestamp < (time_t)dir->prev_insert_pos->timestamp) {
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen user_directory_insert_backwards(dir, dir->prev_insert_pos,
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen user);
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen } else {
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen user_directory_insert_forwards(dir, dir->prev_insert_pos,
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen user);
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen }
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen }
fb502495e9306fe51e9d2c0019e622a98e9803abTimo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen if (dir->to_expire == NULL) {
c2cda8cd0043443566efc5da30f79865508a1947Timo Sirainen struct timeval tv = { .tv_sec = ioloop_time + dir->timeout_secs };
c2cda8cd0043443566efc5da30f79865508a1947Timo Sirainen dir->to_expire_timestamp = tv.tv_sec;
c2cda8cd0043443566efc5da30f79865508a1947Timo Sirainen dir->to_expire = timeout_add_absolute(&tv, user_directory_drop_expired, dir);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen dir->prev_insert_pos = user;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen hash_table_insert(dir->hash, POINTER_CAST(user->username_hash), user);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen return user;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen}
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainenvoid user_directory_refresh(struct user_directory *dir, struct user *user)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen{
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen user_move_iters(dir, user);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen user->timestamp = ioloop_time;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen DLLIST2_REMOVE(&dir->head, &dir->tail, user);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen DLLIST2_APPEND(&dir->head, &dir->tail, user);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen}
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainenvoid user_directory_remove_host(struct user_directory *dir,
66dc739bb67d678770e1b7a7bc75f4f6f9523d2aTimo Sirainen struct mail_host *host)
66dc739bb67d678770e1b7a7bc75f4f6f9523d2aTimo Sirainen{
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen struct user *user, *next;
66dc739bb67d678770e1b7a7bc75f4f6f9523d2aTimo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen for (user = dir->head; user != NULL; user = next) {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen next = user->next;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if (user->host == host)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen user_free(dir, user);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen }
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen}
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainenstatic int user_timestamp_cmp(struct user *const *user1,
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen struct user *const *user2)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen{
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if ((*user1)->timestamp < (*user2)->timestamp)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen return -1;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if ((*user1)->timestamp > (*user2)->timestamp)
c6b6ac7819931dfa92c0182ffaa7db07ac6ab0daTimo Sirainen return 1;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen return 0;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen}
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenvoid user_directory_sort(struct user_directory *dir)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen ARRAY(struct user *) users;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct user *user, *const *userp;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen unsigned int i, users_count = hash_table_count(dir->hash);
20344c0e814139e3c365fbb9287478f91512089eTimo Sirainen
20344c0e814139e3c365fbb9287478f91512089eTimo Sirainen if (users_count == 0) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen i_assert(dir->head == NULL);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen /* place all users into array and sort it */
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen i_array_init(&users, users_count);
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen user = dir->head;
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen for (i = 0; i < users_count; i++, user = user->next)
463f6ea04af934a68facaca0ff089bc306de3f98Timo Sirainen array_append(&users, &user, 1);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_assert(user == NULL);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen array_sort(&users, user_timestamp_cmp);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen /* recreate the linked list */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen dir->head = dir->tail = NULL;
20344c0e814139e3c365fbb9287478f91512089eTimo Sirainen array_foreach(&users, userp)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen DLLIST2_APPEND(&dir->head, &dir->tail, *userp);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_assert(dir->head != NULL &&
52041ed691a26ca80e4e805765e9f55ec097c8f1Martti Rannanjärvi dir->head->timestamp <= dir->tail->timestamp);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen array_free(&users);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen}
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenbool user_directory_user_is_recently_updated(struct user_directory *dir,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct user *user)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen{
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen return (time_t)(user->timestamp + dir->timeout_secs/2) >= ioloop_time;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen}
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainenbool user_directory_user_is_near_expiring(struct user_directory *dir,
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen struct user *user)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen{
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen time_t expire_timestamp;
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen expire_timestamp = user->timestamp +
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen (dir->timeout_secs - dir->user_near_expiring_secs);
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen return expire_timestamp < ioloop_time;
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen}
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstruct user_directory *
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenuser_directory_init(unsigned int timeout_secs,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen user_free_hook_t *user_free_hook)
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen{
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen struct user_directory *dir;
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainen
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen i_assert(timeout_secs > USER_NEAR_EXPIRING_MIN);
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi dir = i_new(struct user_directory, 1);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen dir->timeout_secs = timeout_secs;
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi dir->user_near_expiring_secs =
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen timeout_secs * USER_NEAR_EXPIRING_PERCENTAGE / 100;
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen dir->user_near_expiring_secs =
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen I_MIN(dir->user_near_expiring_secs, USER_NEAR_EXPIRING_MAX);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen dir->user_near_expiring_secs =
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen I_MAX(dir->user_near_expiring_secs, USER_NEAR_EXPIRING_MIN);
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen i_assert(dir->timeout_secs/2 > dir->user_near_expiring_secs);
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen dir->user_free_hook = user_free_hook;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen hash_table_create_direct(&dir->hash, default_pool, 0);
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen i_array_init(&dir->iters, 8);
52041ed691a26ca80e4e805765e9f55ec097c8f1Martti Rannanjärvi return dir;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen}
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainenvoid user_directory_deinit(struct user_directory **_dir)
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen{
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen struct user_directory *dir = *_dir;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen *_dir = NULL;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen i_assert(array_count(&dir->iters) == 0);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen while (dir->head != NULL)
52041ed691a26ca80e4e805765e9f55ec097c8f1Martti Rannanjärvi user_free(dir, dir->head);
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen if (dir->to_expire != NULL)
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen timeout_remove(&dir->to_expire);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen hash_table_destroy(&dir->hash);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen array_free(&dir->iters);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen i_free(dir);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen}
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainenstruct user_directory_iter *
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainenuser_directory_iter_init(struct user_directory *dir)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen{
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen struct user_directory_iter *iter;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
52041ed691a26ca80e4e805765e9f55ec097c8f1Martti Rannanjärvi iter = i_new(struct user_directory_iter, 1);
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen iter->dir = dir;
548e394330621952db0f03dd667b70184c4a37b6Timo Sirainen iter->pos = dir->head;
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi array_append(&dir->iters, &iter, 1);
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi user_directory_drop_expired(dir);
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen return iter;
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi}
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainenstruct user *user_directory_iter_next(struct user_directory_iter *iter)
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen{
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen struct user *user;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen user = iter->pos;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if (user == NULL)
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen return NULL;
52041ed691a26ca80e4e805765e9f55ec097c8f1Martti Rannanjärvi
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen iter->pos = user->next;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen return user;
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi}
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomivoid user_directory_iter_deinit(struct user_directory_iter **_iter)
a85c629c5d75a5fd9489ba14d5d4f54f3cddd591Aki Tuomi{
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen struct user_directory_iter *iter = *_iter;
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen struct user_directory_iter *const *iters;
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen unsigned int i, count;
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen *_iter = NULL;
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen iters = array_get(&iter->dir->iters, &count);
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen for (i = 0; i < count; i++) {
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen if (iters[i] == iter) {
4106a25399703eb6cbb166dcbd5bb932cb2f7ad2Timo Sirainen array_delete(&iter->dir->iters, i, 1);
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen break;
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen }
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen }
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen i_free(iter);
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen}
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen