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