user-directory.c revision 7d8c1ba766770ea1c6d4d6770a31832b5b518648
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen#include "ioloop.h"
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen#include "array.h"
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen#include "hash.h"
105addcb709523868418cc3e3baad7ad3453a91eTimo Sirainen#include "llist.h"
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen#include "mail-host.h"
105addcb709523868418cc3e3baad7ad3453a91eTimo Sirainen
105addcb709523868418cc3e3baad7ad3453a91eTimo Sirainen/* n% of timeout_secs */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define USER_NEAR_EXPIRING_PERCENTAGE 10
105addcb709523868418cc3e3baad7ad3453a91eTimo Sirainen/* but min/max. of this many secs */
105addcb709523868418cc3e3baad7ad3453a91eTimo Sirainen#define USER_NEAR_EXPIRING_MIN 3
105addcb709523868418cc3e3baad7ad3453a91eTimo Sirainen#define USER_NEAR_EXPIRING_MAX 30
105addcb709523868418cc3e3baad7ad3453a91eTimo Sirainen/* This shouldn't matter what it is exactly, just try it sometimes later. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define USER_BEING_KILLED_EXPIRE_RETRY_SECS 60
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainenstruct user_directory_iter {
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen struct user_directory *dir;
105addcb709523868418cc3e3baad7ad3453a91eTimo Sirainen struct user *pos;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen};
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct user_directory {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* unsigned int username_hash => user */
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen HASH_TABLE(void *, struct user *) hash;
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen /* sorted by time */
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen struct user *head, *tail;
56f45b3f3ae20e5c933701f4657dda5ef1916855Timo Sirainen struct user *prev_insert_pos;
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen ARRAY(struct user_directory_iter *) iters;
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen user_free_hook_t *user_free_hook;
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen unsigned int timeout_secs;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen /* If user's expire time is less than this many seconds away,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen don't assume that other directors haven't yet expired it */
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen unsigned int user_near_expiring_secs;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct timeout *to_expire;
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen};
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainenstatic void user_move_iters(struct user_directory *dir, struct user *user)
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct user_directory_iter *const *iterp;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen array_foreach(&dir->iters, iterp) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((*iterp)->pos == user)
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen (*iterp)->pos = user->next;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen if (dir->prev_insert_pos == user)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dir->prev_insert_pos = user->next;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void user_free(struct user_directory *dir, struct user *user)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(user->host->user_count > 0);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user->host->user_count--;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
659fe5d24825b160cae512538088020d97a60239Timo Sirainen if (dir->user_free_hook != NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dir->user_free_hook(user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user_move_iters(dir, user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
659fe5d24825b160cae512538088020d97a60239Timo Sirainen hash_table_remove(dir->hash, POINTER_CAST(user->username_hash));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen DLLIST2_REMOVE(&dir->head, &dir->tail, user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(user);
232d5bef3c709e90e24f0874a36854b92187bb6cTimo Sirainen}
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenstatic bool user_directory_user_has_connections(struct user_directory *dir,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen struct user *user,
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen unsigned int *retry_secs_r)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen{
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen time_t expire_timestamp = user->timestamp + dir->timeout_secs;
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (expire_timestamp > ioloop_time) {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen *retry_secs_r = expire_timestamp - ioloop_time;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen if (USER_IS_BEING_KILLED(user)) {
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen /* don't free this user until the kill is finished */
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen *retry_secs_r = USER_BEING_KILLED_EXPIRE_RETRY_SECS;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen return TRUE;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen }
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
a928e7efabb1672b1476e597106d4b4b81ac6f3cTimo Sirainen if (user->weak) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if (expire_timestamp + USER_NEAR_EXPIRING_MAX > ioloop_time) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen *retry_secs_r = expire_timestamp +
838f56174b963779a88083a0d0e85b30d2d846e7Timo Sirainen USER_NEAR_EXPIRING_MAX - ioloop_time;
838f56174b963779a88083a0d0e85b30d2d846e7Timo Sirainen return TRUE;
838f56174b963779a88083a0d0e85b30d2d846e7Timo Sirainen }
838f56174b963779a88083a0d0e85b30d2d846e7Timo Sirainen
838f56174b963779a88083a0d0e85b30d2d846e7Timo Sirainen i_warning("User %u weakness appears to be stuck, removing it",
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen user->username_hash);
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen }
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen return FALSE;
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen}
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainenstatic void user_directory_drop_expired(struct user_directory *dir)
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen{
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen unsigned int retry_secs = 0;
838f56174b963779a88083a0d0e85b30d2d846e7Timo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen while (dir->head != NULL &&
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen !user_directory_user_has_connections(dir, dir->head, &retry_secs))
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen user_free(dir, dir->head);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen if (dir->to_expire != NULL)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen timeout_remove(&dir->to_expire);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (retry_secs > 0) {
6e07b4251bf6a3cf34019c351a32a65c08392e58Timo Sirainen dir->to_expire = timeout_add(retry_secs * 1000,
6e07b4251bf6a3cf34019c351a32a65c08392e58Timo Sirainen user_directory_drop_expired, dir);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen }
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen}
d143077bd518de129b8d446fb58e003903e50867Timo Sirainen
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainenunsigned int user_directory_count(struct user_directory *dir)
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen{
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen return hash_table_count(dir->hash);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen}
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainenstruct user *user_directory_lookup(struct user_directory *dir,
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen unsigned int username_hash)
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen{
a928e7efabb1672b1476e597106d4b4b81ac6f3cTimo Sirainen struct user *user;
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen unsigned int retry_secs;
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen user_directory_drop_expired(dir);
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen user = hash_table_lookup(dir->hash, POINTER_CAST(username_hash));
0d70a702dec63d22535684fec6a7247c5f153208Timo Sirainen if (user != NULL && !user_directory_user_has_connections(dir, user, &retry_secs)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user_free(dir, user);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen user = NULL;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return user;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic void
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenuser_directory_insert_backwards(struct user_directory *dir,
94aa90d2d17a7aebcda5a4193a62e80ddbb169b7Timo Sirainen struct user *pos, struct user *user)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen{
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen for (; pos != NULL; pos = pos->prev) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (pos->timestamp <= user->timestamp)
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen break;
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen }
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen if (pos == NULL)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen DLLIST2_PREPEND(&dir->head, &dir->tail, user);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen else {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen user->prev = pos;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen user->next = pos->next;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen user->prev->next = user;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (user->next != NULL)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen user->next->prev = user;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen else
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen dir->tail = user;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen }
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic void
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenuser_directory_insert_forwards(struct user_directory *dir,
0d0451206a91e9f96e522075dce28a89adc2325dTimo Sirainen struct user *pos, struct user *user)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for (; pos != NULL; pos = pos->next) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (pos->timestamp >= user->timestamp)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen break;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen }
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (pos == NULL)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen DLLIST2_APPEND(&dir->head, &dir->tail, user);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen else {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen user->prev = pos->prev;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen user->next = pos;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (user->prev != NULL)
c680a6b35b459045e92814778908da5a93922107Timo Sirainen user->prev->next = user;
c680a6b35b459045e92814778908da5a93922107Timo Sirainen else
c680a6b35b459045e92814778908da5a93922107Timo Sirainen dir->head = user;
c680a6b35b459045e92814778908da5a93922107Timo Sirainen user->next->prev = user;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct user *
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenuser_directory_add(struct user_directory *dir, unsigned int username_hash,
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen struct mail_host *host, time_t timestamp)
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen{
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen struct user *user;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen /* make sure we don't add timestamps higher than ioloop time */
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (timestamp > ioloop_time)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen timestamp = ioloop_time;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user = i_new(struct user, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user->username_hash = username_hash;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user->host = host;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user->host->user_count++;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user->timestamp = timestamp;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (dir->tail == NULL || (time_t)dir->tail->timestamp <= timestamp)
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen DLLIST2_APPEND(&dir->head, &dir->tail, user);
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen else {
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* need to insert to correct position. we should get here
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen only when handshaking. the handshaking USER requests should
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen come sorted by timestamp. so keep track of the previous
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen insert position, the next USER should be inserted after
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen it. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (dir->prev_insert_pos == NULL) {
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen /* find the position starting from tail */
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen user_directory_insert_backwards(dir, dir->tail, user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else if (timestamp < (time_t)dir->prev_insert_pos->timestamp) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user_directory_insert_backwards(dir, dir->prev_insert_pos,
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen user);
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen } else {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user_directory_insert_forwards(dir, dir->prev_insert_pos,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user);
8a3d609fdd84f5938c82e8e7eeb84a24ab41b317Timo Sirainen }
8a3d609fdd84f5938c82e8e7eeb84a24ab41b317Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (dir->to_expire == NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dir->to_expire = timeout_add(dir->timeout_secs * 1000,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen user_directory_drop_expired, dir);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dir->prev_insert_pos = user;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen hash_table_insert(dir->hash, POINTER_CAST(user->username_hash), user);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return user;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid user_directory_refresh(struct user_directory *dir, struct user *user)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen user_move_iters(dir, user);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen user->timestamp = ioloop_time;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen DLLIST2_REMOVE(&dir->head, &dir->tail, user);
902222fb0928d1701f20a384b73f327b1d9a15ddTimo Sirainen DLLIST2_APPEND(&dir->head, &dir->tail, user);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen}
902222fb0928d1701f20a384b73f327b1d9a15ddTimo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenvoid user_directory_remove_host(struct user_directory *dir,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_host *host)
{
struct user *user, *next;
for (user = dir->head; user != NULL; user = next) {
next = user->next;
if (user->host == host)
user_free(dir, user);
}
}
static int user_timestamp_cmp(struct user *const *user1,
struct user *const *user2)
{
if ((*user1)->timestamp < (*user2)->timestamp)
return -1;
if ((*user1)->timestamp > (*user2)->timestamp)
return 1;
return 0;
}
void user_directory_sort(struct user_directory *dir)
{
ARRAY(struct user *) users;
struct user *user, *const *userp;
unsigned int i, users_count = hash_table_count(dir->hash);
if (users_count == 0) {
i_assert(dir->head == NULL);
return;
}
/* place all users into array and sort it */
i_array_init(&users, users_count);
user = dir->head;
for (i = 0; i < users_count; i++, user = user->next)
array_append(&users, &user, 1);
i_assert(user == NULL);
array_sort(&users, user_timestamp_cmp);
/* recreate the linked list */
dir->head = dir->tail = NULL;
array_foreach(&users, userp)
DLLIST2_APPEND(&dir->head, &dir->tail, *userp);
i_assert(dir->head != NULL &&
dir->head->timestamp <= dir->tail->timestamp);
array_free(&users);
}
bool user_directory_user_is_recently_updated(struct user_directory *dir,
struct user *user)
{
return (time_t)(user->timestamp + dir->timeout_secs/2) >= ioloop_time;
}
bool user_directory_user_is_near_expiring(struct user_directory *dir,
struct user *user)
{
time_t expire_timestamp;
expire_timestamp = user->timestamp +
(dir->timeout_secs - dir->user_near_expiring_secs);
return expire_timestamp < ioloop_time;
}
struct user_directory *
user_directory_init(unsigned int timeout_secs,
user_free_hook_t *user_free_hook)
{
struct user_directory *dir;
i_assert(timeout_secs > USER_NEAR_EXPIRING_MIN);
dir = i_new(struct user_directory, 1);
dir->timeout_secs = timeout_secs;
dir->user_near_expiring_secs =
timeout_secs * USER_NEAR_EXPIRING_PERCENTAGE / 100;
dir->user_near_expiring_secs =
I_MIN(dir->user_near_expiring_secs, USER_NEAR_EXPIRING_MAX);
dir->user_near_expiring_secs =
I_MAX(dir->user_near_expiring_secs, USER_NEAR_EXPIRING_MIN);
i_assert(dir->timeout_secs/2 > dir->user_near_expiring_secs);
dir->user_free_hook = user_free_hook;
hash_table_create_direct(&dir->hash, default_pool, 0);
i_array_init(&dir->iters, 8);
return dir;
}
void user_directory_deinit(struct user_directory **_dir)
{
struct user_directory *dir = *_dir;
*_dir = NULL;
i_assert(array_count(&dir->iters) == 0);
while (dir->head != NULL)
user_free(dir, dir->head);
if (dir->to_expire != NULL)
timeout_remove(&dir->to_expire);
hash_table_destroy(&dir->hash);
array_free(&dir->iters);
i_free(dir);
}
struct user_directory_iter *
user_directory_iter_init(struct user_directory *dir)
{
struct user_directory_iter *iter;
iter = i_new(struct user_directory_iter, 1);
iter->dir = dir;
iter->pos = dir->head;
array_append(&dir->iters, &iter, 1);
user_directory_drop_expired(dir);
return iter;
}
struct user *user_directory_iter_next(struct user_directory_iter *iter)
{
struct user *user;
user = iter->pos;
if (user == NULL)
return NULL;
iter->pos = user->next;
return user;
}
void user_directory_iter_deinit(struct user_directory_iter **_iter)
{
struct user_directory_iter *iter = *_iter;
struct user_directory_iter *const *iters;
unsigned int i, count;
*_iter = NULL;
iters = array_get(&iter->dir->iters, &count);
for (i = 0; i < count; i++) {
if (iters[i] == iter) {
array_delete(&iter->dir->iters, i, 1);
break;
}
}
i_free(iter);
}