1 /* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "buffer.h"
5 #include "ioloop.h"
6 #include "hash.h"
7 #include "llist.h"
8 #include "base64.h"
9 #include "global-memory.h"
10 #include "stats-settings.h"
11 #include "mail-stats.h"
12 #include "mail-domain.h"
13 #include "mail-user.h"
14
15 static HASH_TABLE(char *, struct mail_user *) mail_users_hash;
16 /* users are sorted by their last_update timestamp, oldest first */
17 static struct mail_user *mail_users_head, *mail_users_tail;
18 struct mail_user *stable_mail_users;
19
mail_user_memsize(const struct mail_user * user)20 static size_t mail_user_memsize(const struct mail_user *user)
21 {
22 return sizeof(*user) + strlen(user->name) + 1;
23 }
24
mail_user_login(const char * username)25 struct mail_user *mail_user_login(const char *username)
26 {
27 struct mail_user *user;
28 const char *domain;
29
30 user = hash_table_lookup(mail_users_hash, username);
31 if (user != NULL) {
32 mail_user_refresh(user, NULL);
33 return user;
34 }
35
36 domain = i_strchr_to_next(username, '@');
37 if (domain == NULL)
38 domain = "";
39
40 user = i_malloc(MALLOC_ADD(sizeof(struct mail_user), stats_alloc_size()));
41 user->stats = (void *)(user + 1);
42 user->name = i_strdup(username);
43 user->reset_timestamp = ioloop_time;
44 user->domain = mail_domain_login_create(domain);
45
46 hash_table_insert(mail_users_hash, user->name, user);
47 DLLIST_PREPEND_FULL(&stable_mail_users, user,
48 stable_prev, stable_next);
49 DLLIST2_APPEND_FULL(&mail_users_head, &mail_users_tail, user,
50 sorted_prev, sorted_next);
51 DLLIST_PREPEND_FULL(&user->domain->users, user,
52 domain_prev, domain_next);
53 mail_domain_ref(user->domain);
54
55 user->last_update = ioloop_timeval;
56 global_memory_alloc(mail_user_memsize(user));
57 return user;
58 }
59
mail_user_disconnected(struct mail_user * user)60 void mail_user_disconnected(struct mail_user *user)
61 {
62 mail_domain_disconnected(user->domain);
63 }
64
mail_user_lookup(const char * username)65 struct mail_user *mail_user_lookup(const char *username)
66 {
67 return hash_table_lookup(mail_users_hash, username);
68 }
69
mail_user_ref(struct mail_user * user)70 void mail_user_ref(struct mail_user *user)
71 {
72 user->refcount++;
73 }
74
mail_user_unref(struct mail_user ** _user)75 void mail_user_unref(struct mail_user **_user)
76 {
77 struct mail_user *user = *_user;
78
79 i_assert(user->refcount > 0);
80 user->refcount--;
81
82 *_user = NULL;
83 }
84
mail_user_free(struct mail_user * user)85 static void mail_user_free(struct mail_user *user)
86 {
87 i_assert(user->refcount == 0);
88 i_assert(user->sessions == NULL);
89
90 global_memory_free(mail_user_memsize(user));
91 hash_table_remove(mail_users_hash, user->name);
92 DLLIST_REMOVE_FULL(&stable_mail_users, user,
93 stable_prev, stable_next);
94 DLLIST2_REMOVE_FULL(&mail_users_head, &mail_users_tail, user,
95 sorted_prev, sorted_next);
96 DLLIST_REMOVE_FULL(&user->domain->users, user,
97 domain_prev, domain_next);
98 mail_domain_unref(&user->domain);
99
100 i_free(user->name);
101 i_free(user);
102 }
103
mail_user_refresh(struct mail_user * user,const struct stats * diff_stats)104 void mail_user_refresh(struct mail_user *user,
105 const struct stats *diff_stats)
106 {
107 if (diff_stats != NULL)
108 stats_add(user->stats, diff_stats);
109 user->last_update = ioloop_timeval;
110 DLLIST2_REMOVE_FULL(&mail_users_head, &mail_users_tail, user,
111 sorted_prev, sorted_next);
112 DLLIST2_APPEND_FULL(&mail_users_head, &mail_users_tail, user,
113 sorted_prev, sorted_next);
114 mail_domain_refresh(user->domain, diff_stats);
115 }
116
mail_user_add_parse(const char * const * args,const char ** error_r)117 int mail_user_add_parse(const char *const *args, const char **error_r)
118 {
119 struct mail_user *user;
120 struct stats *empty_stats, *diff_stats;
121 buffer_t *buf;
122 const char *service, *error;
123
124 /* <user> <service> <diff stats> */
125 if (str_array_length(args) < 3) {
126 *error_r = "ADD-USER: Too few parameters";
127 return -1;
128 }
129
130 user = mail_user_login(args[0]);
131 service = args[1];
132
133 buf = t_buffer_create(256);
134 if (base64_decode(args[2], strlen(args[2]), NULL, buf) < 0) {
135 *error_r = t_strdup_printf("ADD-USER %s %s: Invalid base64 input",
136 user->name, service);
137 return -1;
138 }
139 empty_stats = stats_alloc(pool_datastack_create());
140 diff_stats = stats_alloc(pool_datastack_create());
141 if (!stats_import(buf->data, buf->used, empty_stats, diff_stats, &error)) {
142 *error_r = t_strdup_printf("ADD-USER %s %s: %s",
143 user->name, service, error);
144 return -1;
145 }
146 mail_user_refresh(user, diff_stats);
147 return 0;
148 }
149
mail_users_free_memory(void)150 void mail_users_free_memory(void)
151 {
152 unsigned int diff;
153
154 while (mail_users_head != NULL && mail_users_head->refcount == 0) {
155 mail_user_free(mail_users_head);
156
157 if (global_used_memory < stats_settings->memory_limit ||
158 mail_users_head == NULL)
159 break;
160
161 diff = ioloop_time - mail_users_head->last_update.tv_sec;
162 if (diff < stats_settings->user_min_time)
163 break;
164 }
165 }
166
mail_users_init(void)167 void mail_users_init(void)
168 {
169 hash_table_create(&mail_users_hash, default_pool, 0, str_hash, strcmp);
170 }
171
mail_users_deinit(void)172 void mail_users_deinit(void)
173 {
174 while (mail_users_head != NULL)
175 mail_user_free(mail_users_head);
176 hash_table_destroy(&mail_users_hash);
177 }
178