1
/* Copyright (c) 2010-2011 Dovecot authors, see the included COPYING file */
1
/* Copyright (c) 2010-2012 Dovecot authors, see the included COPYING file */
8
#include "mail-user-hash.h"
9
9
#include "mail-host.h"
10
10
#include "user-directory.h"
12
#define MAX_CLOCK_DRIFT_SECS 2
12
/* n% of timeout_secs */
13
#define USER_NEAR_EXPIRING_PERCENTAGE 10
14
/* but max. of this many secs */
15
#define USER_NEAR_EXPIRING_MAX 30
14
17
struct user_directory_iter {
15
18
struct user_directory *dir;
21
24
struct hash_table *hash;
22
25
/* sorted by time */
23
26
struct user *head, *tail;
27
struct user *prev_insert_pos;
25
29
ARRAY_DEFINE(iters, struct user_directory_iter *);
31
char *username_hash_fmt;
27
32
unsigned int timeout_secs;
33
/* If user's expire time is less than this many seconds away,
34
don't assume that other directors haven't yet expired it */
35
unsigned int user_near_expiring_secs;
30
38
static void user_move_iters(struct user_directory *dir, struct user *user)
35
43
if ((*iterp)->pos == user)
36
44
(*iterp)->pos = user->next;
47
if (dir->prev_insert_pos == user)
48
dir->prev_insert_pos = user->next;
40
51
static void user_free(struct user_directory *dir, struct user *user)
63
static bool user_directory_user_has_connections(struct user_directory *dir,
66
time_t expire_timestamp = user->timestamp + dir->timeout_secs;
68
return expire_timestamp >= ioloop_time;
52
71
static void user_directory_drop_expired(struct user_directory *dir)
54
73
while (dir->head != NULL &&
64
83
return hash_table_lookup(dir->hash, POINTER_CAST(username_hash));
87
user_directory_insert_backwards(struct user_directory *dir,
88
struct user *pos, struct user *user)
90
for (; pos != NULL; pos = pos->prev) {
91
if ((time_t)pos->timestamp <= user->timestamp)
95
DLLIST2_PREPEND(&dir->head, &dir->tail, user);
98
user->next = pos->next;
99
user->prev->next = user;
100
if (user->next != NULL)
101
user->next->prev = user;
108
user_directory_insert_forwards(struct user_directory *dir,
109
struct user *pos, struct user *user)
111
for (; pos != NULL; pos = pos->next) {
112
if ((time_t)pos->timestamp >= user->timestamp)
116
DLLIST2_APPEND(&dir->head, &dir->tail, user);
118
user->prev = pos->prev;
120
if (user->prev != NULL)
121
user->prev->next = user;
124
user->next->prev = user;
68
129
user_directory_add(struct user_directory *dir, unsigned int username_hash,
69
130
struct mail_host *host, time_t timestamp)
71
struct user *user, *pos;
134
/* make sure we don't add timestamps higher than ioloop time */
135
if (timestamp > ioloop_time)
136
timestamp = ioloop_time;
73
138
user = i_new(struct user, 1);
74
139
user->username_hash = username_hash;
79
144
if (dir->tail == NULL || (time_t)dir->tail->timestamp <= timestamp)
80
145
DLLIST2_APPEND(&dir->head, &dir->tail, user);
82
/* need to insert to correct position */
83
for (pos = dir->tail; pos != NULL; pos = pos->prev) {
84
if ((time_t)pos->timestamp <= timestamp)
88
DLLIST2_PREPEND(&dir->head, &dir->tail, user);
91
user->next = pos->next;
92
user->prev->next = user;
93
user->next->prev = user;
147
/* need to insert to correct position. we should get here
148
only when handshaking. the handshaking USER requests should
149
come sorted by timestamp. so keep track of the previous
150
insert position, the next USER should be inserted after
152
if (dir->prev_insert_pos == NULL) {
153
/* find the position starting from tail */
154
user_directory_insert_backwards(dir, dir->tail, user);
155
} else if (timestamp < dir->prev_insert_pos->timestamp) {
156
user_directory_insert_backwards(dir, dir->prev_insert_pos,
159
user_directory_insert_forwards(dir, dir->prev_insert_pos,
164
dir->prev_insert_pos = user;
97
165
hash_table_insert(dir->hash, POINTER_CAST(user->username_hash), user);
123
unsigned int user_directory_get_username_hash(const char *username)
125
/* NOTE: If you modify this, modify also
126
director_username_hash() in login-common/login-proxy.c */
127
unsigned char md5[MD5_RESULTLEN];
128
unsigned int i, hash = 0;
130
md5_get_digest(username, strlen(username), md5);
131
for (i = 0; i < sizeof(hash); i++)
132
hash = (hash << CHAR_BIT) | md5[i];
136
bool user_directory_user_has_connections(struct user_directory *dir,
139
time_t expire_timestamp = user->timestamp + dir->timeout_secs;
141
return expire_timestamp - MAX_CLOCK_DRIFT_SECS >= ioloop_time;
144
struct user_directory *user_directory_init(unsigned int timeout_secs)
191
unsigned int user_directory_get_username_hash(struct user_directory *dir,
192
const char *username)
194
return mail_user_hash(username, dir->username_hash_fmt);
197
bool user_directory_user_is_recently_updated(struct user_directory *dir,
200
return (time_t)(user->timestamp + dir->timeout_secs/2) >= ioloop_time;
203
bool user_directory_user_is_near_expiring(struct user_directory *dir,
206
time_t expire_timestamp;
208
expire_timestamp = user->timestamp +
209
(dir->timeout_secs - dir->user_near_expiring_secs);
210
return expire_timestamp < ioloop_time;
213
struct user_directory *
214
user_directory_init(unsigned int timeout_secs, const char *username_hash_fmt)
146
216
struct user_directory *dir;
148
218
dir = i_new(struct user_directory, 1);
149
219
dir->timeout_secs = timeout_secs;
220
dir->user_near_expiring_secs =
221
timeout_secs * USER_NEAR_EXPIRING_PERCENTAGE / 100;
222
dir->user_near_expiring_secs =
223
I_MIN(dir->user_near_expiring_secs, USER_NEAR_EXPIRING_MAX);
224
dir->user_near_expiring_secs =
225
I_MAX(dir->user_near_expiring_secs, 1);
227
dir->username_hash_fmt = i_strdup(username_hash_fmt);
150
228
dir->hash = hash_table_create(default_pool, default_pool,
152
230
i_array_init(&dir->iters, 8);