1 /*-
2  * Copyright 2016 Vsevolod Stakhov
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "config.h"
17 #include "libserver/dynamic_cfg.h"
18 #include "libserver/cfg_file_private.h"
19 #include "libutil/rrd.h"
20 #include "libserver/maps/map.h"
21 #include "libserver/maps/map_helpers.h"
22 #include "libserver/maps/map_private.h"
23 #include "libserver/http/http_private.h"
24 #include "libserver/http/http_router.h"
25 #include "libstat/stat_api.h"
26 #include "rspamd.h"
27 #include "libserver/worker_util.h"
28 #include "worker_private.h"
29 #include "lua/lua_common.h"
30 #include "cryptobox.h"
31 #include "ottery.h"
32 #include "fuzzy_wire.h"
33 #include "unix-std.h"
34 #include "utlist.h"
35 #include "libmime/lang_detection.h"
36 #include <math.h>
37 
38 /* 60 seconds for worker's IO */
39 #define DEFAULT_WORKER_IO_TIMEOUT 60000
40 
41 /* HTTP paths */
42 #define PATH_AUTH "/auth"
43 #define PATH_SYMBOLS "/symbols"
44 #define PATH_ACTIONS "/actions"
45 #define PATH_MAPS "/maps"
46 #define PATH_GET_MAP "/getmap"
47 #define PATH_GRAPH "/graph"
48 #define PATH_PIE_CHART "/pie"
49 #define PATH_HEALTHY "/healthy"
50 #define PATH_HISTORY "/history"
51 #define PATH_HISTORY_RESET "/historyreset"
52 #define PATH_LEARN_SPAM "/learnspam"
53 #define PATH_LEARN_HAM "/learnham"
54 #define PATH_METRICS "/metrics"
55 #define PATH_READY "/ready"
56 #define PATH_SAVE_ACTIONS "/saveactions"
57 #define PATH_SAVE_SYMBOLS "/savesymbols"
58 #define PATH_SAVE_MAP "/savemap"
59 #define PATH_SCAN "/scan"
60 #define PATH_CHECK "/check"
61 #define PATH_CHECKV2 "/checkv2"
62 #define PATH_STAT "/stat"
63 #define PATH_STAT_RESET "/statreset"
64 #define PATH_COUNTERS "/counters"
65 #define PATH_ERRORS "/errors"
66 #define PATH_NEIGHBOURS "/neighbours"
67 #define PATH_PLUGINS "/plugins"
68 #define PATH_PING "/ping"
69 
70 #define msg_err_session(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL, \
71         session->pool->tag.tagname, session->pool->tag.uid, \
72         G_STRFUNC, \
73         __VA_ARGS__)
74 #define msg_warn_session(...)   rspamd_default_log_function (G_LOG_LEVEL_WARNING, \
75         session->pool->tag.tagname, session->pool->tag.uid, \
76         G_STRFUNC, \
77         __VA_ARGS__)
78 #define msg_info_session(...)   rspamd_default_log_function (G_LOG_LEVEL_INFO, \
79         session->pool->tag.tagname, session->pool->tag.uid, \
80         G_STRFUNC, \
81         __VA_ARGS__)
82 #define msg_err_ctx(...) rspamd_default_log_function(G_LOG_LEVEL_CRITICAL, \
83         "controller", ctx->cfg->cfg_pool->tag.uid, \
84         G_STRFUNC, \
85         __VA_ARGS__)
86 #define msg_warn_ctx(...)   rspamd_default_log_function (G_LOG_LEVEL_WARNING, \
87         "controller", ctx->cfg->cfg_pool->tag.uid, \
88         G_STRFUNC, \
89         __VA_ARGS__)
90 #define msg_info_ctx(...)   rspamd_default_log_function (G_LOG_LEVEL_INFO, \
91         "controller", ctx->cfg->cfg_pool->tag.uid, \
92         G_STRFUNC, \
93         __VA_ARGS__)
94 
95 #define msg_debug_session(...)  rspamd_conditional_debug_fast (NULL, session->from_addr, \
96         rspamd_controller_log_id, "controller", session->pool->tag.uid, \
97         G_STRFUNC, \
98         __VA_ARGS__)
99 
100 INIT_LOG_MODULE(controller)
101 
102 /* Graph colors */
103 #define COLOR_CLEAN "#58A458"
104 #define COLOR_PROBABLE_SPAM "#D67E7E"
105 #define COLOR_GREYLIST "#A0A0A0"
106 #define COLOR_REJECT "#CB4B4B"
107 #define COLOR_TOTAL "#9440ED"
108 
109 static const guint64 rspamd_controller_ctx_magic = 0xf72697805e6941faULL;
110 
111 extern void fuzzy_stat_command (struct rspamd_task *task);
112 
113 gpointer init_controller_worker (struct rspamd_config *cfg);
114 void start_controller_worker (struct rspamd_worker *worker);
115 
116 worker_t controller_worker = {
117 	"controller",                   /* Name */
118 	init_controller_worker,         /* Init function */
119 	start_controller_worker,        /* Start function */
120 	RSPAMD_WORKER_HAS_SOCKET | RSPAMD_WORKER_KILLABLE |
121 			RSPAMD_WORKER_SCANNER | RSPAMD_WORKER_CONTROLLER,
122 	RSPAMD_WORKER_SOCKET_TCP,       /* TCP socket */
123 	RSPAMD_WORKER_VER       /* Version info */
124 };
125 /*
126  * Worker's context
127  */
128 struct rspamd_controller_worker_ctx {
129 	guint64 magic;
130 	/* Events base */
131 	struct ev_loop *event_loop;
132 	/* DNS resolver */
133 	struct rspamd_dns_resolver *resolver;
134 	/* Config */
135 	struct rspamd_config *cfg;
136 	/* END OF COMMON PART */
137 	ev_tstamp timeout;
138 	/* Whether we use ssl for this server */
139 	gboolean use_ssl;
140 	/* Webui password */
141 	gchar *password;
142 	/* Privilleged password */
143 	gchar *enable_password;
144 	/* Cached versions of the passwords */
145 	rspamd_ftok_t cached_password;
146 	rspamd_ftok_t cached_enable_password;
147 	/* HTTP server */
148 	struct rspamd_http_context *http_ctx;
149 	struct rspamd_http_connection_router *http;
150 	/* Server's start time */
151 	ev_tstamp start_time;
152 	/* Main server */
153 	struct rspamd_main *srv;
154 	/* SSL cert */
155 	gchar *ssl_cert;
156 	/* SSL private key */
157 	gchar *ssl_key;
158 	/* A map of secure IP */
159 	const ucl_object_t *secure_ip;
160 	struct rspamd_radix_map_helper *secure_map;
161 
162 	/* Static files dir */
163 	gchar *static_files_dir;
164 
165 	/* Custom commands registered by plugins */
166 	GHashTable *custom_commands;
167 
168 	/* Plugins registered from lua */
169 	GHashTable *plugins;
170 
171 	/* Worker */
172 	struct rspamd_worker *worker;
173 
174 	/* Local keypair */
175 	gpointer key;
176 
177 	struct rspamd_rrd_file *rrd;
178 	struct rspamd_lang_detector *lang_det;
179 	gdouble task_timeout;
180 
181 	/* Health check stuff */
182 	guint workers_count;
183 	guint scanners_count;
184 	guint workers_hb_lost;
185 	ev_timer health_check_timer;
186 };
187 
188 struct rspamd_controller_plugin_cbdata {
189 	lua_State *L;
190 	struct rspamd_controller_worker_ctx *ctx;
191 	gchar *plugin;
192 	struct ucl_lua_funcdata *handler;
193 	ucl_object_t *obj;
194 	gboolean is_enable;
195 	gboolean need_task;
196 	guint version;
197 };
198 
199 static gboolean
rspamd_is_encrypted_password(const gchar * password,struct rspamd_controller_pbkdf const ** pbkdf)200 rspamd_is_encrypted_password (const gchar *password,
201 		struct rspamd_controller_pbkdf const **pbkdf)
202 {
203 	const gchar *start, *end;
204 	gint64 id;
205 	gsize size, i;
206 	gboolean ret = FALSE;
207 	const struct rspamd_controller_pbkdf *p;
208 
209 	if (password[0] == '$') {
210 		/* Parse id */
211 		start = password + 1;
212 		end = start;
213 		size = 0;
214 
215 		while (*end != '\0' && g_ascii_isdigit (*end)) {
216 			size++;
217 			end++;
218 		}
219 
220 		if (size > 0) {
221 			gchar *endptr;
222 			id = strtoul (start, &endptr, 10);
223 
224 			if ((endptr == NULL || *endptr == *end)) {
225 				for (i = 0; i < RSPAMD_PBKDF_ID_MAX - 1; i ++) {
226 					p = &pbkdf_list[i];
227 
228 					if (p->id == id) {
229 						ret = TRUE;
230 						if (pbkdf != NULL) {
231 							*pbkdf = &pbkdf_list[i];
232 						}
233 
234 						break;
235 					}
236 				}
237 			}
238 		}
239 	}
240 
241 	return ret;
242 }
243 
244 static const gchar *
rspamd_encrypted_password_get_str(const gchar * password,gsize skip,gsize * length)245 rspamd_encrypted_password_get_str (const gchar * password, gsize skip,
246 		gsize * length)
247 {
248 	const gchar *str, *start, *end;
249 	gsize size;
250 
251 	start = password + skip;
252 	end = start;
253 	size = 0;
254 
255 	while (*end != '\0' && g_ascii_isalnum (*end)) {
256 		size++;
257 		end++;
258 	}
259 
260 	if (size) {
261 		str = start;
262 		*length = size;
263 	}
264 	else {
265 		str = NULL;
266 	}
267 
268 	return str;
269 }
270 
271 static gboolean
rspamd_check_encrypted_password(struct rspamd_controller_worker_ctx * ctx,const rspamd_ftok_t * password,const gchar * check,const struct rspamd_controller_pbkdf * pbkdf,gboolean is_enable)272 rspamd_check_encrypted_password (struct rspamd_controller_worker_ctx *ctx,
273 		const rspamd_ftok_t * password, const gchar * check,
274 		const struct rspamd_controller_pbkdf *pbkdf,
275 		gboolean is_enable)
276 {
277 	const gchar *salt, *hash;
278 	gchar *salt_decoded, *key_decoded;
279 	gsize salt_len = 0, key_len = 0;
280 	gboolean ret = TRUE;
281 	guchar *local_key;
282 	rspamd_ftok_t *cache;
283 	gpointer m;
284 
285 	/* First of all check cached versions to save resources */
286 	if (is_enable && ctx->cached_enable_password.len != 0) {
287 		if (password->len != ctx->cached_enable_password.len ||
288 				!rspamd_constant_memcmp (password->begin,
289 						ctx->cached_enable_password.begin, password->len)) {
290 			msg_info_ctx ("incorrect or absent enable password has been specified");
291 			return FALSE;
292 		}
293 
294 		return TRUE;
295 	}
296 	else if (!is_enable && ctx->cached_password.len != 0) {
297 		if (password->len != ctx->cached_password.len ||
298 				!rspamd_constant_memcmp (password->begin,
299 						ctx->cached_password.begin, password->len)) {
300 			/* We still need to check enable password here */
301 			if (ctx->cached_enable_password.len != 0) {
302 				if (password->len != ctx->cached_enable_password.len ||
303 						!rspamd_constant_memcmp (password->begin,
304 								ctx->cached_enable_password.begin,
305 								password->len)) {
306 					msg_info_ctx (
307 							"incorrect or absent password has been specified");
308 
309 					return FALSE;
310 				}
311 				else {
312 					/* Cached matched */
313 					return TRUE;
314 				}
315 			}
316 			else {
317 				/* We might want to check uncached version */
318 				goto check_uncached;
319 			}
320 		}
321 		else {
322 			/* Cached matched */
323 			return TRUE;
324 		}
325 	}
326 
327 check_uncached:
328 	g_assert (pbkdf != NULL);
329 	/* get salt */
330 	salt = rspamd_encrypted_password_get_str (check, 3, &salt_len);
331 	/* get hash */
332 	hash = rspamd_encrypted_password_get_str (check, 3 + salt_len + 1,
333 			&key_len);
334 	if (salt != NULL && hash != NULL) {
335 
336 		/* decode salt */
337 		salt_decoded = rspamd_decode_base32 (salt, salt_len, &salt_len, RSPAMD_BASE32_DEFAULT);
338 
339 		if (salt_decoded == NULL || salt_len != pbkdf->salt_len) {
340 			/* We have some unknown salt here */
341 			msg_info_ctx ("incorrect salt: %z, while %z expected",
342 					salt_len, pbkdf->salt_len);
343 			g_free (salt_decoded);
344 
345 			return FALSE;
346 		}
347 
348 		key_decoded = rspamd_decode_base32 (hash, key_len, &key_len, RSPAMD_BASE32_DEFAULT);
349 
350 		if (key_decoded == NULL || key_len != pbkdf->key_len) {
351 			/* We have some unknown salt here */
352 			msg_info_ctx ("incorrect key: %z, while %z expected",
353 					key_len, pbkdf->key_len);
354 			g_free (salt_decoded);
355 			g_free (key_decoded); /* valid even if key_decoded == NULL */
356 
357 			return FALSE;
358 		}
359 
360 		local_key = g_alloca (pbkdf->key_len);
361 		rspamd_cryptobox_pbkdf (password->begin, password->len,
362 				salt_decoded, salt_len,
363 				local_key, pbkdf->key_len, pbkdf->complexity,
364 				pbkdf->type);
365 
366 		if (!rspamd_constant_memcmp (key_decoded, local_key, pbkdf->key_len)) {
367 			msg_info_ctx ("incorrect or absent password has been specified");
368 			ret = FALSE;
369 		}
370 
371 		g_free (salt_decoded);
372 		g_free (key_decoded);
373 	}
374 
375 	if (ret) {
376 		/* Save cached version */
377 		cache = is_enable ? &ctx->cached_enable_password : &ctx->cached_password;
378 
379 		if (cache->len == 0) {
380 			/* Mmap region */
381 #ifdef MAP_NOCORE
382 			m = mmap (NULL, password->len, PROT_WRITE,
383 					MAP_PRIVATE | MAP_ANON | MAP_NOCORE, -1, 0);
384 #else
385 			m = mmap (NULL, password->len, PROT_WRITE,
386 					MAP_PRIVATE | MAP_ANON, -1, 0);
387 #endif
388 			if (m != MAP_FAILED) {
389 				memcpy (m, password->begin, password->len);
390 				(void)mprotect (m, password->len, PROT_READ);
391 				(void)mlock (m, password->len);
392 				cache->begin = m;
393 				cache->len = password->len;
394 			}
395 			else {
396 				msg_err_ctx ("cannot store cached password, mmap failed: %s",
397 						strerror (errno));
398 			}
399 		}
400 	}
401 
402 	return ret;
403 }
404 
405 /**
406  * Checks for X-Forwarded-For header and update client's address if needed
407  *
408  * This function is intended to be called for a trusted client to ensure that
409  * a request is not proxied through it
410  * @return 0 if no forwarded found, 1 if forwarded found and it is yet trusted
411  * and -1 if forwarded is denied
412  */
413 static gint
rspamd_controller_check_forwarded(struct rspamd_controller_session * session,struct rspamd_http_message * msg,struct rspamd_controller_worker_ctx * ctx)414 rspamd_controller_check_forwarded (struct rspamd_controller_session *session,
415 		struct rspamd_http_message *msg,
416 		struct rspamd_controller_worker_ctx *ctx)
417 {
418 	const rspamd_ftok_t *hdr;
419 	const gchar *comma;
420 	const char *hdr_name = "X-Forwarded-For", *alt_hdr_name = "X-Real-IP";
421 	char ip_buf[INET6_ADDRSTRLEN + 1];
422 	rspamd_inet_addr_t *addr = NULL;
423 	gint ret = 0;
424 
425 	hdr = rspamd_http_message_find_header (msg, hdr_name);
426 
427 	if (hdr) {
428 		/*
429 		 * We need to parse and update the header
430 		 * X-Forwarded-For: client, proxy1, proxy2
431 		 */
432 		comma = rspamd_memrchr (hdr->begin, ',', hdr->len);
433 		if (comma != NULL) {
434 			while (comma < hdr->begin + hdr->len &&
435 					(*comma == ',' || g_ascii_isspace (*comma))) {
436 				comma ++;
437 			}
438 		}
439 		else {
440 			comma = hdr->begin;
441 		}
442 		if (rspamd_parse_inet_address (&addr, comma,
443 				(hdr->begin + hdr->len) - comma,
444 				RSPAMD_INET_ADDRESS_PARSE_NO_UNIX)) {
445 			/* We have addr now, so check if it is still trusted */
446 			if (ctx->secure_map &&
447 					rspamd_match_radix_map_addr (ctx->secure_map, addr) != NULL) {
448 				/* rspamd_inet_address_to_string is not reentrant */
449 				rspamd_strlcpy (ip_buf, rspamd_inet_address_to_string (addr),
450 						sizeof (ip_buf));
451 				msg_info_session ("allow unauthorized proxied connection "
452 						"from a trusted IP %s via %s",
453 						ip_buf,
454 						rspamd_inet_address_to_string (session->from_addr));
455 				ret = 1;
456 			}
457 			else {
458 				ret = -1;
459 			}
460 
461 			rspamd_inet_address_free (addr);
462 		}
463 		else {
464 			msg_warn_session ("cannot parse forwarded IP: %T", hdr);
465 			ret = -1;
466 		}
467 	}
468 	else {
469 		/* Try also X-Real-IP */
470 		hdr = rspamd_http_message_find_header (msg, alt_hdr_name);
471 
472 		if (hdr) {
473 			if (rspamd_parse_inet_address (&addr, hdr->begin, hdr->len,
474 					RSPAMD_INET_ADDRESS_PARSE_NO_UNIX)) {
475 				/* We have addr now, so check if it is still trusted */
476 				if (ctx->secure_map &&
477 						rspamd_match_radix_map_addr (ctx->secure_map, addr) != NULL) {
478 					/* rspamd_inet_address_to_string is not reentrant */
479 					rspamd_strlcpy (ip_buf, rspamd_inet_address_to_string (addr),
480 							sizeof (ip_buf));
481 					msg_info_session ("allow unauthorized proxied connection "
482 							"from a trusted IP %s via %s",
483 							ip_buf,
484 							rspamd_inet_address_to_string (session->from_addr));
485 					ret = 1;
486 				}
487 				else {
488 					ret = -1;
489 				}
490 
491 				rspamd_inet_address_free (addr);
492 			}
493 			else {
494 				msg_warn_session ("cannot parse real IP: %T", hdr);
495 				ret = -1;
496 			}
497 		}
498 	}
499 
500 	return ret;
501 }
502 
503 /* Check for password if it is required by configuration */
504 static gboolean
rspamd_controller_check_password(struct rspamd_http_connection_entry * entry,struct rspamd_controller_session * session,struct rspamd_http_message * msg,gboolean is_enable)505 rspamd_controller_check_password (struct rspamd_http_connection_entry *entry,
506 		struct rspamd_controller_session *session,
507 		struct rspamd_http_message *msg, gboolean is_enable)
508 {
509 	const gchar *check;
510 	const rspamd_ftok_t *password;
511 	rspamd_ftok_t lookup;
512 	GHashTable *query_args = NULL;
513 	struct rspamd_controller_worker_ctx *ctx = session->ctx;
514 	gboolean check_normal = TRUE, check_enable = TRUE, ret = TRUE,
515 		use_enable = FALSE;
516 	const struct rspamd_controller_pbkdf *pbkdf = NULL;
517 
518 	/* Access list logic */
519 	if (rspamd_inet_address_get_af (session->from_addr) == AF_UNIX) {
520 		ret = rspamd_controller_check_forwarded (session, msg, ctx);
521 
522 		if (ret == 1) {
523 			session->is_enable = TRUE;
524 
525 			return TRUE;
526 		}
527 		else if (ret == 0) {
528 			/* No forwarded found */
529 			msg_info_session ("allow unauthorized connection from a unix socket");
530 			session->is_enable = TRUE;
531 
532 			return TRUE;
533 		}
534 	}
535 	else if (ctx->secure_map
536 			&& rspamd_match_radix_map_addr (ctx->secure_map, session->from_addr)
537 					!= NULL) {
538 		ret = rspamd_controller_check_forwarded (session, msg, ctx);
539 
540 		if (ret == 1) {
541 			session->is_enable = TRUE;
542 
543 			return TRUE;
544 		}
545 		else if (ret == 0) {
546 			/* No forwarded found */
547 			msg_info_session ("allow unauthorized connection from a trusted IP %s",
548 							rspamd_inet_address_to_string (session->from_addr));
549 			session->is_enable = TRUE;
550 
551 			return TRUE;
552 		}
553 	}
554 
555 	/* Password logic */
556 	password = rspamd_http_message_find_header (msg, "Password");
557 
558 	if (password == NULL) {
559 		/* Try to get password from query args */
560 		query_args = rspamd_http_message_parse_query (msg);
561 
562 		lookup.begin = (gchar *)"password";
563 		lookup.len = sizeof ("password") - 1;
564 
565 		password = g_hash_table_lookup (query_args, &lookup);
566 	}
567 
568 	if (password == NULL) {
569 		if (ctx->secure_map == NULL) {
570 			if (ctx->password == NULL && !is_enable) {
571 				return TRUE;
572 			}
573 			else if (is_enable && (ctx->password == NULL &&
574 					ctx->enable_password == NULL)) {
575 				session->is_enable = TRUE;
576 				return TRUE;
577 			}
578 		}
579 
580 		msg_info_session ("absent password has been specified; source ip: %s",
581 				rspamd_inet_address_to_string_pretty (session->from_addr));
582 		ret = FALSE;
583 	}
584 	else {
585 		if (rspamd_ftok_cstr_equal (password, "q1", FALSE) ||
586 				rspamd_ftok_cstr_equal (password, "q2", FALSE)) {
587 			msg_info_session ("deny default password for remote access; source ip: %s",
588 					rspamd_inet_address_to_string_pretty (session->from_addr));
589 			ret = FALSE;
590 			goto end;
591 		}
592 
593 		if (is_enable) {
594 			/* For privileged commands we strictly require enable password */
595 			if (ctx->enable_password != NULL) {
596 				check = ctx->enable_password;
597 				use_enable = TRUE;
598 			}
599 			else {
600 				/* Use just a password (legacy mode) */
601 				msg_info(
602 						"using password as enable_password for a privileged command");
603 				check = ctx->password;
604 			}
605 
606 			if (check != NULL) {
607 				if (!rspamd_is_encrypted_password (check, &pbkdf)) {
608 					ret = FALSE;
609 
610 					if (strlen (check) == password->len) {
611 						ret = rspamd_constant_memcmp (password->begin, check,
612 								password->len);
613 					}
614 				}
615 				else {
616 					ret = rspamd_check_encrypted_password (ctx, password, check,
617 							pbkdf, use_enable);
618 				}
619 			}
620 			else {
621 				msg_warn_session (
622 						"no password to check while executing a privileged command; source ip: %s",
623 						rspamd_inet_address_to_string_pretty (session->from_addr));
624 				ret = FALSE;
625 			}
626 
627 			if (ret) {
628 				session->is_enable = TRUE;
629 			}
630 		}
631 		else {
632 			/* Accept both normal and enable passwords */
633 			if (ctx->password != NULL) {
634 				check = ctx->password;
635 
636 				if (!rspamd_is_encrypted_password (check, &pbkdf)) {
637 					check_normal = FALSE;
638 
639 					if (strlen (check) == password->len) {
640 						check_normal = rspamd_constant_memcmp (password->begin,
641 								check,
642 								password->len);
643 					}
644 				}
645 				else {
646 					check_normal = rspamd_check_encrypted_password (ctx,
647 							password,
648 							check, pbkdf, FALSE);
649 				}
650 
651 			}
652 			else {
653 				check_normal = FALSE;
654 			}
655 
656 			if (ctx->enable_password != NULL) {
657 				check = ctx->enable_password;
658 
659 				if (!rspamd_is_encrypted_password (check, &pbkdf)) {
660 					check_enable = FALSE;
661 
662 					if (strlen (check) == password->len) {
663 						check_enable = rspamd_constant_memcmp (password->begin,
664 								check,
665 								password->len);
666 					}
667 				}
668 				else {
669 					check_enable = rspamd_check_encrypted_password (ctx,
670 							password,
671 							check, pbkdf, TRUE);
672 				}
673 
674 				if (check_enable) {
675 					session->is_enable = TRUE;
676 				}
677 			}
678 			else {
679 				check_enable = FALSE;
680 
681 				if (check_normal) {
682 					/*
683 					 * If no enable password is specified use normal password as
684 					 * enable password
685 					 */
686 					session->is_enable = TRUE;
687 				}
688 			}
689 		}
690 	}
691 
692 	if (check_normal == FALSE && check_enable == FALSE) {
693 		msg_info ("absent or incorrect password has been specified; source ip: %s",
694 				rspamd_inet_address_to_string_pretty (session->from_addr));
695 		ret = FALSE;
696 	}
697 
698 end:
699 	if (query_args != NULL) {
700 		g_hash_table_unref (query_args);
701 	}
702 
703 	if (!ret) {
704 		rspamd_controller_send_error (entry, 403, "Unauthorized");
705 	}
706 
707 	return ret;
708 }
709 
710 /* Command handlers */
711 
712 /*
713  * Auth command handler:
714  * request: /auth
715  * headers: Password
716  * reply: json {"auth": "ok", "version": "0.5.2", "uptime": "some uptime", "error": "none"}
717  */
718 static int
rspamd_controller_handle_auth(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)719 rspamd_controller_handle_auth (struct rspamd_http_connection_entry *conn_ent,
720 	struct rspamd_http_message *msg)
721 {
722 	struct rspamd_controller_session *session = conn_ent->ud;
723 	struct rspamd_stat *st;
724 	int64_t uptime;
725 	gulong data[5];
726 	ucl_object_t *obj;
727 
728 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
729 		return 0;
730 	}
731 
732 	obj = ucl_object_typed_new (UCL_OBJECT);
733 	st = session->ctx->srv->stat;
734 	data[0] = st->actions_stat[METRIC_ACTION_NOACTION];
735 	data[1] = st->actions_stat[METRIC_ACTION_ADD_HEADER] +
736 		st->actions_stat[METRIC_ACTION_REWRITE_SUBJECT];
737 	data[2] = st->actions_stat[METRIC_ACTION_GREYLIST];
738 	data[3] = st->actions_stat[METRIC_ACTION_REJECT];
739 	data[4] = st->actions_stat[METRIC_ACTION_SOFT_REJECT];
740 
741 	/* Get uptime */
742 	uptime = ev_time () - session->ctx->start_time;
743 
744 	ucl_object_insert_key (obj, ucl_object_fromstring (
745 			RVERSION),			   "version",  0, false);
746 	ucl_object_insert_key (obj, ucl_object_fromstring (
747 			"ok"),				   "auth",	   0, false);
748 	ucl_object_insert_key (obj,	   ucl_object_fromint (
749 			uptime),			   "uptime",   0, false);
750 	ucl_object_insert_key (obj,	   ucl_object_fromint (
751 			data[0]),			   "clean",	   0, false);
752 	ucl_object_insert_key (obj,	   ucl_object_fromint (
753 			data[1]),			   "probable", 0, false);
754 	ucl_object_insert_key (obj,	   ucl_object_fromint (
755 			data[2]),			   "greylist", 0, false);
756 	ucl_object_insert_key (obj,	   ucl_object_fromint (
757 			data[3]),			   "reject",   0, false);
758 	ucl_object_insert_key (obj,	   ucl_object_fromint (
759 			data[4]),			   "soft_reject",   0, false);
760 	ucl_object_insert_key (obj,	   ucl_object_fromint (
761 			st->messages_scanned), "scanned",  0, false);
762 	ucl_object_insert_key (obj,	   ucl_object_fromint (
763 			st->messages_learned), "learned",  0, false);
764 	ucl_object_insert_key (obj, ucl_object_frombool (!session->is_enable),
765 			"read_only", 0, false);
766 	ucl_object_insert_key (obj, ucl_object_fromstring (session->ctx->cfg->checksum),
767 			"config_id", 0, false);
768 
769 	rspamd_controller_send_ucl (conn_ent, obj);
770 	ucl_object_unref (obj);
771 
772 	return 0;
773 }
774 
775 /*
776  * Symbols command handler:
777  * request: /symbols
778  * reply: json [{
779  *  "name": "group_name",
780  *  "symbols": [
781  *      {
782  *      "name": "name",
783  *      "weight": 0.1,
784  *      "description": "description of symbol"
785  *      },
786  *      {...}
787  * },
788  * {...}]
789  */
790 static int
rspamd_controller_handle_symbols(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)791 rspamd_controller_handle_symbols (struct rspamd_http_connection_entry *conn_ent,
792 	struct rspamd_http_message *msg)
793 {
794 	struct rspamd_controller_session *session = conn_ent->ud;
795 	GHashTableIter it, sit;
796 	struct rspamd_symbols_group *gr;
797 	struct rspamd_symbol *sym;
798 	ucl_object_t *obj, *top, *sym_obj, *group_symbols;
799 	gpointer k, v;
800 
801 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
802 		return 0;
803 	}
804 
805 	top = ucl_object_typed_new (UCL_ARRAY);
806 
807 	/* Go through all symbols groups in the default metric */
808 	g_hash_table_iter_init (&it, session->cfg->groups);
809 
810 	while (g_hash_table_iter_next (&it, &k, &v)) {
811 		gr = v;
812 		obj = ucl_object_typed_new (UCL_OBJECT);
813 		ucl_object_insert_key (obj, ucl_object_fromstring (
814 				gr->name), "group", 0, false);
815 		/* Iterate through all symbols */
816 
817 		g_hash_table_iter_init (&sit, gr->symbols);
818 		group_symbols = ucl_object_typed_new (UCL_ARRAY);
819 
820 		while (g_hash_table_iter_next (&sit, &k, &v)) {
821 			gdouble tm = 0.0, freq = 0, freq_dev = 0;
822 
823 			sym = v;
824 			sym_obj = ucl_object_typed_new (UCL_OBJECT);
825 
826 			ucl_object_insert_key (sym_obj, ucl_object_fromstring (sym->name),
827 				"symbol", 0, false);
828 			ucl_object_insert_key (sym_obj,
829 				ucl_object_fromdouble (*sym->weight_ptr),
830 				"weight", 0, false);
831 			if (sym->description) {
832 				ucl_object_insert_key (sym_obj,
833 					ucl_object_fromstring (sym->description),
834 					"description", 0, false);
835 			}
836 
837 			if (rspamd_symcache_stat_symbol (session->ctx->cfg->cache,
838 					sym->name, &freq, &freq_dev, &tm, NULL)) {
839 				ucl_object_insert_key (sym_obj,
840 						ucl_object_fromdouble (freq),
841 						"frequency", 0, false);
842 				ucl_object_insert_key (sym_obj,
843 						ucl_object_fromdouble (freq_dev),
844 						"frequency_stddev", 0, false);
845 				ucl_object_insert_key (sym_obj,
846 						ucl_object_fromdouble (tm),
847 						"time", 0, false);
848 			}
849 
850 			ucl_array_append (group_symbols, sym_obj);
851 		}
852 
853 		ucl_object_insert_key (obj, group_symbols, "rules", 0, false);
854 		ucl_array_append (top, obj);
855 	}
856 
857 	rspamd_controller_send_ucl (conn_ent, top);
858 	ucl_object_unref (top);
859 
860 	return 0;
861 }
862 
863 /*
864  * Actions command handler:
865  * request: /actions
866  * reply: json [{
867  *  "action": "no action",
868  *  "value": 1.1
869  * },
870  * {...}]
871  */
872 static int
rspamd_controller_handle_actions(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)873 rspamd_controller_handle_actions (struct rspamd_http_connection_entry *conn_ent,
874 	struct rspamd_http_message *msg)
875 {
876 	struct rspamd_controller_session *session = conn_ent->ud;
877 	struct rspamd_action *act, *tmp;
878 	ucl_object_t *obj, *top;
879 
880 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
881 		return 0;
882 	}
883 
884 	top = ucl_object_typed_new (UCL_ARRAY);
885 
886 	HASH_ITER (hh, session->cfg->actions, act, tmp) {
887 		obj = ucl_object_typed_new (UCL_OBJECT);
888 		ucl_object_insert_key (obj,
889 				ucl_object_fromstring (act->name),
890 				"action", 0, false);
891 		ucl_object_insert_key (obj,
892 				ucl_object_fromdouble (act->threshold),
893 				"value", 0, false);
894 		ucl_array_append (top, obj);
895 	}
896 
897 	rspamd_controller_send_ucl (conn_ent, top);
898 	ucl_object_unref (top);
899 
900 	return 0;
901 }
902 
903 static gboolean
rspamd_controller_can_edit_map(struct rspamd_map_backend * bk)904 rspamd_controller_can_edit_map (struct rspamd_map_backend *bk)
905 {
906 	gchar *fpath;
907 
908 	if (access (bk->uri, W_OK) == 0) {
909 		return TRUE;
910 	}
911 	else if (access (bk->uri, R_OK) == -1 && errno == ENOENT) {
912 		fpath = g_path_get_dirname (bk->uri);
913 
914 		if (fpath) {
915 			if (access (fpath, W_OK) == 0) {
916 				g_free (fpath);
917 
918 				return TRUE;
919 			}
920 
921 			g_free (fpath);
922 		}
923 	}
924 
925 	return FALSE;
926 }
927 
928 /*
929  * Maps command handler:
930  * request: /maps
931  * headers: Password
932  * reply: json [
933  *      {
934  *      "map": "name",
935  *      "description": "description",
936  *      "editable": true
937  *      },
938  *      {...}
939  * ]
940  */
941 static int
rspamd_controller_handle_maps(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)942 rspamd_controller_handle_maps (struct rspamd_http_connection_entry *conn_ent,
943 	struct rspamd_http_message *msg)
944 {
945 	struct rspamd_controller_session *session = conn_ent->ud;
946 	GList *cur;
947 	struct rspamd_map *map;
948 	struct rspamd_map_backend *bk;
949 	guint i;
950 	gboolean editable;
951 	ucl_object_t *obj, *top;
952 
953 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
954 		return 0;
955 	}
956 
957 	top = ucl_object_typed_new (UCL_ARRAY);
958 
959 	/* Iterate over all maps */
960 	cur = session->ctx->cfg->maps;
961 	while (cur) {
962 		map = cur->data;
963 
964 		PTR_ARRAY_FOREACH (map->backends, i, bk) {
965 
966 			if (bk->protocol == MAP_PROTO_FILE) {
967 				editable = rspamd_controller_can_edit_map (bk);
968 
969 				if (!editable && access (bk->uri, R_OK) == -1) {
970 					/* Skip unreadable and non-existing maps */
971 					continue;
972 				}
973 
974 				obj = ucl_object_typed_new (UCL_OBJECT);
975 				ucl_object_insert_key (obj,	   ucl_object_fromint (bk->id),
976 						"map", 0, false);
977 				if (map->description) {
978 					ucl_object_insert_key (obj, ucl_object_fromstring (map->description),
979 							"description", 0, false);
980 				}
981 				ucl_object_insert_key (obj, ucl_object_fromstring (bk->uri),
982 						"uri", 0, false);
983 				ucl_object_insert_key (obj,	  ucl_object_frombool (editable),
984 						"editable", 0, false);
985 				ucl_array_append (top, obj);
986 			}
987 		}
988 		cur = g_list_next (cur);
989 	}
990 
991 	rspamd_controller_send_ucl (conn_ent, top);
992 	ucl_object_unref (top);
993 
994 	return 0;
995 }
996 
997 /*
998  * Get map command handler:
999  * request: /getmap
1000  * headers: Password, Map
1001  * reply: plain-text
1002  */
1003 static int
rspamd_controller_handle_get_map(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)1004 rspamd_controller_handle_get_map (struct rspamd_http_connection_entry *conn_ent,
1005 	struct rspamd_http_message *msg)
1006 {
1007 	struct rspamd_controller_session *session = conn_ent->ud;
1008 	GList *cur;
1009 	struct rspamd_map *map;
1010 	struct rspamd_map_backend *bk = NULL;
1011 	const rspamd_ftok_t *idstr;
1012 	struct stat st;
1013 	gint fd;
1014 	gulong id, i;
1015 	gboolean found = FALSE;
1016 	struct rspamd_http_message *reply;
1017 
1018 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
1019 		return 0;
1020 	}
1021 
1022 	idstr = rspamd_http_message_find_header (msg, "Map");
1023 
1024 	if (idstr == NULL) {
1025 		msg_info_session ("absent map id");
1026 		rspamd_controller_send_error (conn_ent, 400, "Id header missing");
1027 		return 0;
1028 	}
1029 
1030 	if (!rspamd_strtoul (idstr->begin, idstr->len, &id)) {
1031 		msg_info_session ("invalid map id");
1032 		rspamd_controller_send_error (conn_ent, 400, "Invalid map id");
1033 		return 0;
1034 	}
1035 
1036 	/* Now let's be sure that we have map defined in configuration */
1037 	cur = session->ctx->cfg->maps;
1038 	while (cur && !found) {
1039 		map = cur->data;
1040 
1041 		PTR_ARRAY_FOREACH (map->backends, i, bk) {
1042 			if (bk->id == id && bk->protocol == MAP_PROTO_FILE) {
1043 				found = TRUE;
1044 				break;
1045 			}
1046 		}
1047 
1048 		cur = g_list_next (cur);
1049 	}
1050 
1051 	if (!found || bk == NULL) {
1052 		msg_info_session ("map not found");
1053 		rspamd_controller_send_error (conn_ent, 404, "Map not found");
1054 		return 0;
1055 	}
1056 
1057 	if (stat (bk->uri, &st) == -1 || (fd = open (bk->uri, O_RDONLY)) == -1) {
1058 		reply = rspamd_http_new_message (HTTP_RESPONSE);
1059 		reply->date = time (NULL);
1060 		reply->code = 200;
1061 	}
1062 	else {
1063 
1064 		reply = rspamd_http_new_message (HTTP_RESPONSE);
1065 		reply->date = time (NULL);
1066 		reply->code = 200;
1067 
1068 		if (st.st_size > 0) {
1069 			if (!rspamd_http_message_set_body_from_fd (reply, fd)) {
1070 				close (fd);
1071 				rspamd_http_message_unref (reply);
1072 				msg_err_session ("cannot read map %s: %s", bk->uri, strerror (errno));
1073 				rspamd_controller_send_error (conn_ent, 500, "Map read error");
1074 				return 0;
1075 			}
1076 		}
1077 		else {
1078 			rspamd_fstring_t *empty_body = rspamd_fstring_new_init ("", 0);
1079 			rspamd_http_message_set_body_from_fstring_steal (reply, empty_body);
1080 		}
1081 
1082 		close (fd);
1083 	}
1084 
1085 	rspamd_http_connection_reset (conn_ent->conn);
1086 	rspamd_http_router_insert_headers (conn_ent->rt, reply);
1087 	rspamd_http_connection_write_message (conn_ent->conn, reply, NULL,
1088 			"text/plain", conn_ent,
1089 			conn_ent->rt->timeout);
1090 	conn_ent->is_reply = TRUE;
1091 
1092 	return 0;
1093 }
1094 
1095 static ucl_object_t *
rspamd_controller_pie_element(enum rspamd_action_type action,const char * label,gdouble data)1096 rspamd_controller_pie_element (enum rspamd_action_type action,
1097 		const char *label, gdouble data)
1098 {
1099 	ucl_object_t *res = ucl_object_typed_new (UCL_OBJECT);
1100 	const char *colors[METRIC_ACTION_MAX] = {
1101 		[METRIC_ACTION_REJECT] = "#FF0000",
1102 		[METRIC_ACTION_SOFT_REJECT] = "#cc9966",
1103 		[METRIC_ACTION_REWRITE_SUBJECT] = "#ff6600",
1104 		[METRIC_ACTION_ADD_HEADER] = "#FFD700",
1105 		[METRIC_ACTION_GREYLIST] = "#436EEE",
1106 		[METRIC_ACTION_NOACTION] = "#66cc00"
1107 	};
1108 
1109 	ucl_object_insert_key (res, ucl_object_fromstring (colors[action]),
1110 			"color", 0, false);
1111 	ucl_object_insert_key (res, ucl_object_fromstring (label), "label", 0, false);
1112 	ucl_object_insert_key (res, ucl_object_fromdouble (data), "data", 0, false);
1113 	ucl_object_insert_key (res, ucl_object_fromdouble (data), "value", 0, false);
1114 
1115 	return res;
1116 }
1117 
1118 /*
1119  * Pie chart command handler:
1120  * request: /pie
1121  * headers: Password
1122  * reply: json [
1123  *      { label: "Foo", data: 11 },
1124  *      { label: "Bar", data: 20 },
1125  *      {...}
1126  * ]
1127  */
1128 static int
rspamd_controller_handle_pie_chart(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)1129 rspamd_controller_handle_pie_chart (
1130 	struct rspamd_http_connection_entry *conn_ent,
1131 	struct rspamd_http_message *msg)
1132 {
1133 	struct rspamd_controller_session *session = conn_ent->ud;
1134 	struct rspamd_controller_worker_ctx *ctx;
1135 	gdouble data[5], total;
1136 	ucl_object_t *top;
1137 
1138 	ctx = session->ctx;
1139 
1140 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
1141 		return 0;
1142 	}
1143 
1144 	top = ucl_object_typed_new (UCL_ARRAY);
1145 	total = ctx->srv->stat->messages_scanned;
1146 	if (total != 0) {
1147 
1148 		data[0] = ctx->srv->stat->actions_stat[METRIC_ACTION_NOACTION];
1149 		data[1] = ctx->srv->stat->actions_stat[METRIC_ACTION_SOFT_REJECT];
1150 		data[2] = (ctx->srv->stat->actions_stat[METRIC_ACTION_ADD_HEADER] +
1151 			ctx->srv->stat->actions_stat[METRIC_ACTION_REWRITE_SUBJECT]);
1152 		data[3] = ctx->srv->stat->actions_stat[METRIC_ACTION_GREYLIST];
1153 		data[4] = ctx->srv->stat->actions_stat[METRIC_ACTION_REJECT];
1154 	}
1155 	else {
1156 		memset (data, 0, sizeof (data));
1157 	}
1158 	ucl_array_append (top, rspamd_controller_pie_element (
1159 			METRIC_ACTION_NOACTION, "Clean", data[0]));
1160 	ucl_array_append (top, rspamd_controller_pie_element (
1161 			METRIC_ACTION_SOFT_REJECT, "Temporarily rejected", data[1]));
1162 	ucl_array_append (top, rspamd_controller_pie_element (
1163 			METRIC_ACTION_ADD_HEADER, "Probable spam", data[2]));
1164 	ucl_array_append (top, rspamd_controller_pie_element (
1165 			METRIC_ACTION_GREYLIST, "Greylisted", data[3]));
1166 	ucl_array_append (top, rspamd_controller_pie_element (
1167 			METRIC_ACTION_REJECT, "Rejected", data[4]));
1168 
1169 	rspamd_controller_send_ucl (conn_ent, top);
1170 	ucl_object_unref (top);
1171 
1172 	return 0;
1173 }
1174 
1175 void
rspamd_controller_graph_point(gulong t,gulong step,struct rspamd_rrd_query_result * rrd_result,gdouble * acc,ucl_object_t ** elt)1176 rspamd_controller_graph_point (gulong t, gulong step,
1177 		struct rspamd_rrd_query_result* rrd_result,
1178 		gdouble *acc,
1179 		ucl_object_t **elt)
1180 {
1181 	guint nan_cnt;
1182 	gdouble sum = 0.0, yval;
1183 	ucl_object_t* data_elt;
1184 	guint i, j;
1185 
1186 	for (i = 0; i < rrd_result->ds_count; i++) {
1187 		sum = 0.0;
1188 		nan_cnt = 0;
1189 		data_elt = ucl_object_typed_new (UCL_OBJECT);
1190 		ucl_object_insert_key (data_elt, ucl_object_fromint (t), "x", 1, false);
1191 
1192 		for (j = 0; j < step; j++) {
1193 			yval = acc[i + j * rrd_result->ds_count];
1194 			if (!isfinite (yval)) {
1195 				nan_cnt++;
1196 			}
1197 			else {
1198 				sum += yval;
1199 			}
1200 		}
1201 		if (nan_cnt == step) {
1202 			ucl_object_insert_key (data_elt, ucl_object_typed_new (UCL_NULL),
1203 					"y", 1, false);
1204 		}
1205 		else {
1206 			ucl_object_insert_key (data_elt,
1207 					ucl_object_fromdouble (sum / (gdouble) step), "y", 1,
1208 					false);
1209 		}
1210 		ucl_array_append (elt[i], data_elt);
1211 	}
1212 }
1213 
1214 /*
1215  * Graph command handler:
1216  * request: /graph?type=<day|week|month|year>
1217  * headers: Password
1218  * reply: json [
1219  *      { label: "Foo", data: 11 },
1220  *      { label: "Bar", data: 20 },
1221  *      {...}
1222  * ]
1223  */
1224 static int
rspamd_controller_handle_graph(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)1225 rspamd_controller_handle_graph (
1226 		struct rspamd_http_connection_entry *conn_ent,
1227 		struct rspamd_http_message *msg)
1228 {
1229 	GHashTable *query;
1230 	struct rspamd_controller_session *session = conn_ent->ud;
1231 	struct rspamd_controller_worker_ctx *ctx;
1232 	rspamd_ftok_t srch, *value;
1233 	struct rspamd_rrd_query_result *rrd_result;
1234 	gulong i, k, start_row, cnt, t, ts, step;
1235 	gdouble *acc;
1236 	ucl_object_t *res, *elt[METRIC_ACTION_MAX];
1237 	enum {
1238 		rra_day = 0,
1239 		rra_week,
1240 		rra_month,
1241 		rra_year,
1242 		rra_invalid
1243 	} rra_num = rra_invalid;
1244 	/* How many points are we going to send to display */
1245 	static const guint desired_points = 500;
1246 
1247 	ctx = session->ctx;
1248 
1249 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
1250 		return 0;
1251 	}
1252 
1253 	if (ctx->rrd == NULL) {
1254 		msg_err_session ("no rrd configured");
1255 		rspamd_controller_send_error (conn_ent, 404, "No rrd configured for graphs");
1256 
1257 		return 0;
1258 	}
1259 
1260 	query = rspamd_http_message_parse_query (msg);
1261 	srch.begin = (gchar *)"type";
1262 	srch.len = 4;
1263 
1264 	if (query == NULL || (value = g_hash_table_lookup (query, &srch)) == NULL) {
1265 		msg_err_session ("absent graph type query");
1266 		rspamd_controller_send_error (conn_ent, 400, "Absent graph type");
1267 
1268 		if (query) {
1269 			g_hash_table_unref (query);
1270 		}
1271 
1272 		return 0;
1273 	}
1274 
1275 	if (value->len == 3 && rspamd_lc_cmp (value->begin, "day", value->len) == 0) {
1276 		rra_num = rra_day;
1277 	}
1278 	else if (value->len == 4 && rspamd_lc_cmp (value->begin, "week", value->len) == 0) {
1279 		rra_num = rra_week;
1280 	}
1281 	else if (value->len == 5 && rspamd_lc_cmp (value->begin, "month", value->len) == 0) {
1282 		rra_num = rra_month;
1283 	}
1284 	else if (value->len == 4 && rspamd_lc_cmp (value->begin, "year", value->len) == 0) {
1285 		rra_num = rra_year;
1286 	}
1287 
1288 	g_hash_table_unref (query);
1289 
1290 	if (rra_num == rra_invalid) {
1291 		msg_err_session ("invalid graph type query");
1292 		rspamd_controller_send_error (conn_ent, 400, "Invalid graph type");
1293 
1294 		return 0;
1295 	}
1296 
1297 	rrd_result = rspamd_rrd_query (ctx->rrd, rra_num);
1298 
1299 	if (rrd_result == NULL) {
1300 		msg_err_session ("cannot query rrd");
1301 		rspamd_controller_send_error (conn_ent, 500, "Cannot query rrd");
1302 
1303 		return 0;
1304 	}
1305 
1306 	g_assert (rrd_result->ds_count == G_N_ELEMENTS (elt));
1307 
1308 	res = ucl_object_typed_new (UCL_ARRAY);
1309 	/* How much full updates happened since the last update */
1310 	ts = rrd_result->last_update / rrd_result->pdp_per_cdp - rrd_result->rra_rows;
1311 
1312 	for (i = 0; i < rrd_result->ds_count; i ++) {
1313 		elt[i] = ucl_object_typed_new (UCL_ARRAY);
1314 	}
1315 
1316 	start_row = rrd_result->cur_row == rrd_result->rra_rows - 1 ?
1317 				0 : rrd_result->cur_row;
1318 	t = ts * rrd_result->pdp_per_cdp;
1319 	k = 0;
1320 
1321 	/* Create window */
1322 	step = ceil (((gdouble)rrd_result->rra_rows) / desired_points);
1323 	g_assert (step >= 1);
1324 	acc = g_malloc0 (sizeof (double) * rrd_result->ds_count * step);
1325 
1326 	for (i = start_row, cnt = 0; cnt < rrd_result->rra_rows;
1327 			cnt ++) {
1328 
1329 		memcpy (&acc[k * rrd_result->ds_count],
1330 				&rrd_result->data[i * rrd_result->ds_count],
1331 				sizeof (gdouble) * rrd_result->ds_count);
1332 
1333 		if (k < step - 1) {
1334 			k ++;
1335 		}
1336 		else {
1337 			t = ts * rrd_result->pdp_per_cdp;
1338 
1339 			/* Need a fresh point */
1340 			rspamd_controller_graph_point (t, step, rrd_result, acc, elt);
1341 			k = 0;
1342 		}
1343 
1344 		if (i == rrd_result->rra_rows - 1) {
1345 			i = 0;
1346 		}
1347 		else {
1348 			i ++;
1349 		}
1350 
1351 		ts ++;
1352 	}
1353 
1354 	if (k > 0) {
1355 		rspamd_controller_graph_point (t, k, rrd_result, acc, elt);
1356 	}
1357 
1358 	for (i = 0; i < rrd_result->ds_count; i++) {
1359 		ucl_array_append (res, elt[i]);
1360 	}
1361 
1362 	rspamd_controller_send_ucl (conn_ent, res);
1363 	ucl_object_unref (res);
1364 	g_free (acc);
1365 	g_free (rrd_result);
1366 
1367 	return 0;
1368 }
1369 
1370 static void
rspamd_controller_handle_legacy_history(struct rspamd_controller_session * session,struct rspamd_controller_worker_ctx * ctx,struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)1371 rspamd_controller_handle_legacy_history (
1372 		struct rspamd_controller_session *session,
1373 		struct rspamd_controller_worker_ctx *ctx,
1374 		struct rspamd_http_connection_entry *conn_ent,
1375 		struct rspamd_http_message *msg)
1376 {
1377 	struct roll_history_row *row, *copied_rows;
1378 	guint i, rows_proc, row_num;
1379 	struct tm tm;
1380 	gchar timebuf[32], **syms;
1381 	ucl_object_t *top, *obj;
1382 
1383 	top = ucl_object_typed_new (UCL_ARRAY);
1384 
1385 	/* Set lock on history */
1386 	copied_rows = g_malloc (sizeof (*copied_rows) * ctx->srv->history->nrows);
1387 	memcpy (copied_rows, ctx->srv->history->rows,
1388 			sizeof (*copied_rows) * ctx->srv->history->nrows);
1389 
1390 	/* Go through all rows */
1391 	row_num = ctx->srv->history->cur_row;
1392 
1393 	for (i = 0, rows_proc = 0; i < ctx->srv->history->nrows; i++, row_num++) {
1394 		if (row_num == ctx->srv->history->nrows) {
1395 			row_num = 0;
1396 		}
1397 		row = &copied_rows[row_num];
1398 		/* Get only completed rows */
1399 		if (row->completed) {
1400 			rspamd_localtime (row->timestamp, &tm);
1401 			strftime (timebuf, sizeof (timebuf) - 1, "%Y-%m-%d %H:%M:%S", &tm);
1402 			obj = ucl_object_typed_new (UCL_OBJECT);
1403 			ucl_object_insert_key (obj, ucl_object_fromstring (
1404 					timebuf),		  "time", 0, false);
1405 			ucl_object_insert_key (obj, ucl_object_fromint (
1406 					row->timestamp), "unix_time", 0, false);
1407 			ucl_object_insert_key (obj, ucl_object_fromstring (
1408 					row->message_id), "id",	  0, false);
1409 			ucl_object_insert_key (obj, ucl_object_fromstring (row->from_addr),
1410 					"ip", 0, false);
1411 			ucl_object_insert_key (obj,
1412 					ucl_object_fromstring (rspamd_action_to_str (
1413 							row->action)), "action", 0, false);
1414 
1415 			if (!isnan (row->score)) {
1416 				ucl_object_insert_key (obj, ucl_object_fromdouble (
1417 						row->score),		  "score",			0, false);
1418 			}
1419 			else {
1420 				ucl_object_insert_key (obj,
1421 						ucl_object_fromdouble (0.0), "score", 0, false);
1422 			}
1423 
1424 			if (!isnan (row->required_score)) {
1425 				ucl_object_insert_key (obj,
1426 						ucl_object_fromdouble (
1427 								row->required_score), "required_score", 0, false);
1428 			}
1429 			else {
1430 				ucl_object_insert_key (obj,
1431 						ucl_object_fromdouble (0.0), "required_score", 0, false);
1432 			}
1433 
1434 			syms = g_strsplit_set (row->symbols, ", ", -1);
1435 
1436 			if (syms) {
1437 				guint nelts = g_strv_length (syms);
1438 				ucl_object_t *syms_obj = ucl_object_typed_new (UCL_OBJECT);
1439 				ucl_object_reserve (syms_obj, nelts);
1440 
1441 				for (guint j = 0; j < nelts; j++) {
1442 					g_strstrip (syms[j]);
1443 
1444 					if (strlen (syms[j]) == 0) {
1445 						/* Empty garbadge */
1446 						continue;
1447 					}
1448 
1449 					ucl_object_t *cur = ucl_object_typed_new (UCL_OBJECT);
1450 
1451 					ucl_object_insert_key (cur, ucl_object_fromdouble (0.0),
1452 							"score", 0, false);
1453 					ucl_object_insert_key (syms_obj, cur, syms[j], 0, true);
1454 				}
1455 
1456 				ucl_object_insert_key (obj, syms_obj, "symbols", 0, false);
1457 				g_strfreev (syms);
1458 			}
1459 
1460 			ucl_object_insert_key (obj, ucl_object_fromint (row->len),
1461 					"size", 0, false);
1462 			ucl_object_insert_key (obj,
1463 					ucl_object_fromdouble (row->scan_time),
1464 					"scan_time", 0, false);
1465 
1466 			if (row->user[0] != '\0') {
1467 				ucl_object_insert_key (obj, ucl_object_fromstring (row->user),
1468 						"user", 0, false);
1469 			}
1470 			if (row->from_addr[0] != '\0') {
1471 				ucl_object_insert_key (obj, ucl_object_fromstring (
1472 						row->from_addr), "from", 0, false);
1473 			}
1474 			ucl_array_append (top, obj);
1475 			rows_proc++;
1476 		}
1477 	}
1478 
1479 	rspamd_controller_send_ucl (conn_ent, top);
1480 	ucl_object_unref (top);
1481 	g_free (copied_rows);
1482 }
1483 
1484 static gboolean
rspamd_controller_history_lua_fin_task(void * ud)1485 rspamd_controller_history_lua_fin_task (void *ud)
1486 {
1487 	return TRUE;
1488 }
1489 
1490 static void
rspamd_controller_handle_lua_history(lua_State * L,struct rspamd_controller_session * session,struct rspamd_controller_worker_ctx * ctx,struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg,gboolean reset)1491 rspamd_controller_handle_lua_history (lua_State *L,
1492 		struct rspamd_controller_session *session,
1493 		struct rspamd_controller_worker_ctx *ctx,
1494 		struct rspamd_http_connection_entry *conn_ent,
1495 		struct rspamd_http_message *msg,
1496 		gboolean reset)
1497 {
1498 	struct rspamd_task *task, **ptask;
1499 	struct rspamd_http_connection_entry **pconn_ent;
1500 	GHashTable *params;
1501 	rspamd_ftok_t srch, *found;
1502 	glong from = 0, to = -1;
1503 
1504 	params = rspamd_http_message_parse_query (msg);
1505 
1506 	if (params) {
1507 		/* Check from and to */
1508 		RSPAMD_FTOK_ASSIGN (&srch, "from");
1509 		found = g_hash_table_lookup (params, &srch);
1510 
1511 		if (found) {
1512 			rspamd_strtol (found->begin, found->len, &from);
1513 		}
1514 		RSPAMD_FTOK_ASSIGN (&srch, "to");
1515 		found = g_hash_table_lookup (params, &srch);
1516 
1517 		if (found) {
1518 			rspamd_strtol (found->begin, found->len, &to);
1519 		}
1520 
1521 		g_hash_table_unref (params);
1522 	}
1523 
1524 	lua_getglobal (L, "rspamd_plugins");
1525 
1526 	if (lua_istable (L, -1)) {
1527 		lua_pushstring (L, "history");
1528 		lua_gettable (L, -2);
1529 
1530 		if (lua_istable (L, -1)) {
1531 			lua_pushstring (L, "handler");
1532 			lua_gettable (L, -2);
1533 
1534 			if (lua_isfunction (L, -1)) {
1535 				task = rspamd_task_new (session->ctx->worker, session->cfg,
1536 						session->pool, ctx->lang_det, ctx->event_loop, FALSE);
1537 
1538 				task->resolver = ctx->resolver;
1539 				task->s = rspamd_session_create (session->pool,
1540 						rspamd_controller_history_lua_fin_task,
1541 						NULL,
1542 						(event_finalizer_t )rspamd_task_free,
1543 						task);
1544 				task->fin_arg = conn_ent;
1545 
1546 				ptask = lua_newuserdata (L, sizeof (*ptask));
1547 				*ptask = task;
1548 				rspamd_lua_setclass (L, "rspamd{task}", -1);
1549 				pconn_ent = lua_newuserdata (L, sizeof (*pconn_ent));
1550 				*pconn_ent = conn_ent;
1551 				rspamd_lua_setclass (L, "rspamd{csession}", -1);
1552 				lua_pushinteger (L, from);
1553 				lua_pushinteger (L, to);
1554 				lua_pushboolean (L, reset);
1555 
1556 				if (lua_pcall (L, 5, 0, 0) != 0) {
1557 					msg_err_session ("call to history function failed: %s",
1558 							lua_tostring (L, -1));
1559 					lua_settop (L, 0);
1560 					rspamd_task_free (task);
1561 
1562 					goto err;
1563 				}
1564 
1565 				task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
1566 				task->sock = -1;
1567 				session->task = task;
1568 
1569 				rspamd_session_pending (task->s);
1570 			}
1571 			else {
1572 				msg_err_session ("rspamd_plugins.history.handler is not a function");
1573 				lua_settop (L, 0);
1574 				goto err;
1575 			}
1576 		}
1577 		else {
1578 			msg_err_session ("rspamd_plugins.history is not a table");
1579 			lua_settop (L, 0);
1580 			goto err;
1581 		}
1582 	}
1583 	else {
1584 		msg_err_session ("rspamd_plugins is absent or has incorrect type");
1585 		lua_settop (L, 0);
1586 		goto err;
1587 	}
1588 
1589 	lua_settop (L, 0);
1590 
1591 	return;
1592 err:
1593 	rspamd_controller_send_error (conn_ent, 500, "Internal error");
1594 }
1595 
1596 /*
1597  * Healthy command handler:
1598  * request: /healthy
1599  * headers: Password
1600  * reply: json {"success":true}
1601  */
1602 static int
rspamd_controller_handle_healthy(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)1603 rspamd_controller_handle_healthy (struct rspamd_http_connection_entry *conn_ent,
1604 								  struct rspamd_http_message *msg)
1605 {
1606 	struct rspamd_controller_session *session = conn_ent->ud;
1607 
1608 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
1609 		return 0;
1610 	}
1611 
1612 	if (session->ctx->workers_hb_lost != 0) {
1613 		rspamd_controller_send_error (conn_ent, 500,
1614 				"%d workers are not responding", session->ctx->workers_hb_lost);
1615 	}
1616 	else {
1617 		rspamd_controller_send_string (conn_ent, "{\"success\":true}");
1618 	}
1619 
1620 	return 0;
1621 }
1622 
1623 /*
1624  * Ready command handler:
1625  * request: /ready
1626  * headers: Password
1627  * reply: json {"success":true} or {"error":"error message"}
1628  */
1629 static int
rspamd_controller_handle_ready(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)1630 rspamd_controller_handle_ready (struct rspamd_http_connection_entry *conn_ent,
1631 								struct rspamd_http_message *msg)
1632 {
1633 	struct rspamd_controller_session *session = conn_ent->ud;
1634 
1635 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
1636 		return 0;
1637 	}
1638 
1639 	if (session->ctx->scanners_count == 0) {
1640 		rspamd_controller_send_error (conn_ent, 500, "no healthy scanner workers are running");
1641 	}
1642 	else {
1643 		rspamd_controller_send_string (conn_ent, "{\"success\":true}");
1644 	}
1645 
1646 	return 0;
1647 }
1648 
1649 /*
1650  * History command handler:
1651  * request: /history
1652  * headers: Password
1653  * reply: json [
1654  *      { label: "Foo", data: 11 },
1655  *      { label: "Bar", data: 20 },
1656  *      {...}
1657  * ]
1658  */
1659 static int
rspamd_controller_handle_history(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)1660 rspamd_controller_handle_history (struct rspamd_http_connection_entry *conn_ent,
1661 	struct rspamd_http_message *msg)
1662 {
1663 	struct rspamd_controller_session *session = conn_ent->ud;
1664 	struct rspamd_controller_worker_ctx *ctx;
1665 	lua_State *L;
1666 	ctx = session->ctx;
1667 
1668 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
1669 		return 0;
1670 	}
1671 
1672 	L = ctx->cfg->lua_state;
1673 
1674 	if (!ctx->srv->history->disabled) {
1675 		rspamd_controller_handle_legacy_history (session, ctx, conn_ent, msg);
1676 	}
1677 	else {
1678 		rspamd_controller_handle_lua_history (L, session, ctx, conn_ent, msg,
1679 				FALSE);
1680 	}
1681 
1682 	return 0;
1683 }
1684 
1685 /*
1686  * Errors command handler:
1687  * request: /errors
1688  * headers: Password
1689  * reply: json [
1690  *      { ts: 100500, type: normal, pid: 100, module: lua, message: bad things },
1691  *      {...}
1692  * ]
1693  */
1694 static int
rspamd_controller_handle_errors(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)1695 rspamd_controller_handle_errors (struct rspamd_http_connection_entry *conn_ent,
1696 	struct rspamd_http_message *msg)
1697 {
1698 	struct rspamd_controller_session *session = conn_ent->ud;
1699 	struct rspamd_controller_worker_ctx *ctx;
1700 	ucl_object_t *top;
1701 
1702 	ctx = session->ctx;
1703 
1704 	if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
1705 		return 0;
1706 	}
1707 
1708 	top = rspamd_log_errorbuf_export (ctx->worker->srv->logger);
1709 	rspamd_controller_send_ucl (conn_ent, top);
1710 	ucl_object_unref (top);
1711 
1712 	return 0;
1713 }
1714 
1715 /*
1716  * Neighbours command handler:
1717  * request: /neighbours
1718  * headers: Password
1719  * reply: json {name: {url: "http://...", host: "host"}}
1720  */
1721 static int
rspamd_controller_handle_neighbours(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)1722 rspamd_controller_handle_neighbours (struct rspamd_http_connection_entry *conn_ent,
1723 	struct rspamd_http_message *msg)
1724 {
1725 	struct rspamd_controller_session *session = conn_ent->ud;
1726 	struct rspamd_controller_worker_ctx *ctx;
1727 
1728 	ctx = session->ctx;
1729 
1730 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
1731 		return 0;
1732 	}
1733 
1734 	rspamd_controller_send_ucl (conn_ent, ctx->cfg->neighbours);
1735 
1736 	return 0;
1737 }
1738 
1739 
1740 static int
rspamd_controller_handle_history_reset(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)1741 rspamd_controller_handle_history_reset (struct rspamd_http_connection_entry *conn_ent,
1742 		struct rspamd_http_message *msg)
1743 {
1744 	struct rspamd_controller_session *session = conn_ent->ud;
1745 	struct rspamd_controller_worker_ctx *ctx;
1746 	struct roll_history_row *row;
1747 	guint completed_rows, i, t;
1748 	lua_State *L;
1749 
1750 	ctx = session->ctx;
1751 	L = ctx->cfg->lua_state;
1752 
1753 	if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
1754 		return 0;
1755 	}
1756 
1757 	if (!ctx->srv->history->disabled) {
1758 		/* Clean from start to the current row */
1759 		completed_rows = g_atomic_int_get (&ctx->srv->history->cur_row);
1760 
1761 		completed_rows = MIN (completed_rows, ctx->srv->history->nrows - 1);
1762 
1763 		for (i = 0; i <= completed_rows; i ++) {
1764 			t = g_atomic_int_get (&ctx->srv->history->cur_row);
1765 
1766 			/* We somehow come to the race condition */
1767 			if (i > t) {
1768 				break;
1769 			}
1770 
1771 			row = &ctx->srv->history->rows[i];
1772 			memset (row, 0, sizeof (*row));
1773 		}
1774 
1775 		msg_info_session ("<%s> cleared %d entries from history",
1776 				rspamd_inet_address_to_string (session->from_addr),
1777 				completed_rows);
1778 		rspamd_controller_send_string (conn_ent, "{\"success\":true}");
1779 	}
1780 	else {
1781 		rspamd_controller_handle_lua_history (L, session, ctx, conn_ent, msg,
1782 				TRUE);
1783 	}
1784 
1785 	return 0;
1786 }
1787 
1788 static gboolean
rspamd_controller_lua_fin_task(void * ud)1789 rspamd_controller_lua_fin_task (void *ud)
1790 {
1791 	struct rspamd_task *task = ud;
1792 	struct rspamd_http_connection_entry *conn_ent;
1793 
1794 	conn_ent = task->fin_arg;
1795 
1796 	if (task->err != NULL) {
1797 		rspamd_controller_send_error (conn_ent, task->err->code, "%s",
1798 				task->err->message);
1799 	}
1800 
1801 	return TRUE;
1802 }
1803 
1804 static int
rspamd_controller_handle_lua(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)1805 rspamd_controller_handle_lua (struct rspamd_http_connection_entry *conn_ent,
1806 		struct rspamd_http_message *msg)
1807 {
1808 	struct rspamd_controller_session *session = conn_ent->ud;
1809 	struct rspamd_task *task, **ptask;
1810 	struct rspamd_http_connection_entry **pconn;
1811 	struct rspamd_controller_worker_ctx *ctx;
1812 	gchar filebuf[PATH_MAX], realbuf[PATH_MAX];
1813 	struct http_parser_url u;
1814 	rspamd_ftok_t lookup;
1815 	struct stat st;
1816 	lua_State *L;
1817 
1818 	if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
1819 		return 0;
1820 	}
1821 
1822 	ctx = session->ctx;
1823 	L = ctx->cfg->lua_state;
1824 
1825 	/* Find lua script */
1826 	if (msg->url != NULL && msg->url->len != 0) {
1827 
1828 		http_parser_parse_url (RSPAMD_FSTRING_DATA (msg->url),
1829 				RSPAMD_FSTRING_LEN (msg->url), TRUE, &u);
1830 
1831 		if (u.field_set & (1 << UF_PATH)) {
1832 			lookup.begin = RSPAMD_FSTRING_DATA (msg->url) +
1833 					u.field_data[UF_PATH].off;
1834 			lookup.len = u.field_data[UF_PATH].len;
1835 		}
1836 		else {
1837 			lookup.begin = RSPAMD_FSTRING_DATA (msg->url);
1838 			lookup.len = RSPAMD_FSTRING_LEN (msg->url);
1839 		}
1840 
1841 		rspamd_snprintf (filebuf, sizeof (filebuf), "%s%c%T",
1842 				ctx->static_files_dir, G_DIR_SEPARATOR, &lookup);
1843 
1844 		if (realpath (filebuf, realbuf) == NULL ||
1845 				lstat (realbuf, &st) == -1) {
1846 			rspamd_controller_send_error (conn_ent, 404, "Cannot find path: %s",
1847 					strerror (errno));
1848 
1849 			return 0;
1850 		}
1851 
1852 		/* TODO: add caching here, should be trivial */
1853 		/* Now we load and execute the code fragment, which should return a function */
1854 		if (luaL_loadfile (L, realbuf) != 0) {
1855 			rspamd_controller_send_error (conn_ent, 500, "Cannot load path: %s",
1856 					lua_tostring (L, -1));
1857 			lua_settop (L, 0);
1858 
1859 			return 0;
1860 		}
1861 
1862 		if (lua_pcall (L, 0, 1, 0) != 0) {
1863 			rspamd_controller_send_error (conn_ent, 501, "Cannot run path: %s",
1864 					lua_tostring (L, -1));
1865 			lua_settop (L, 0);
1866 
1867 			return 0;
1868 		}
1869 
1870 		if (lua_type (L, -1) != LUA_TFUNCTION) {
1871 			rspamd_controller_send_error (conn_ent, 502, "Bad return type: %s",
1872 					lua_typename (L, lua_type (L, -1)));
1873 			lua_settop (L, 0);
1874 
1875 			return 0;
1876 		}
1877 
1878 	}
1879 	else {
1880 		rspamd_controller_send_error (conn_ent, 404, "Empty path is not permitted");
1881 
1882 		return 0;
1883 	}
1884 
1885 	task = rspamd_task_new (session->ctx->worker, session->cfg, session->pool,
1886 			ctx->lang_det, ctx->event_loop, FALSE);
1887 
1888 	task->resolver = ctx->resolver;
1889 	task->s = rspamd_session_create (session->pool,
1890 			rspamd_controller_lua_fin_task,
1891 			NULL,
1892 			(event_finalizer_t )rspamd_task_free,
1893 			task);
1894 	task->fin_arg = conn_ent;
1895 	task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
1896 	task->sock = -1;
1897 	session->task = task;
1898 
1899 	if (msg->body_buf.len > 0) {
1900 		if (!rspamd_task_load_message (task, msg, msg->body_buf.begin, msg->body_buf.len)) {
1901 			rspamd_controller_send_error (conn_ent, task->err->code, "%s",
1902 					task->err->message);
1903 			return 0;
1904 		}
1905 	}
1906 
1907 	ptask = lua_newuserdata (L, sizeof (*ptask));
1908 	rspamd_lua_setclass (L, "rspamd{task}", -1);
1909 	*ptask = task;
1910 
1911 	pconn = lua_newuserdata (L, sizeof (*pconn));
1912 	rspamd_lua_setclass (L, "rspamd{csession}", -1);
1913 	*pconn = conn_ent;
1914 
1915 	if (lua_pcall (L, 2, 0, 0) != 0) {
1916 		rspamd_controller_send_error (conn_ent, 503, "Cannot run callback: %s",
1917 				lua_tostring (L, -1));
1918 		lua_settop (L, 0);
1919 
1920 		return 0;
1921 	}
1922 
1923 	rspamd_session_pending (task->s);
1924 
1925 	return 0;
1926 }
1927 
1928 static gboolean
rspamd_controller_learn_fin_task(void * ud)1929 rspamd_controller_learn_fin_task (void *ud)
1930 {
1931 	struct rspamd_task *task = ud;
1932 	struct rspamd_controller_session *session;
1933 	struct rspamd_http_connection_entry *conn_ent;
1934 
1935 	conn_ent = task->fin_arg;
1936 	session = conn_ent->ud;
1937 
1938 	if (task->err != NULL) {
1939 		msg_info_session ("cannot learn <%s>: %e",
1940 				MESSAGE_FIELD (task, message_id), task->err);
1941 		rspamd_controller_send_error (conn_ent, task->err->code, "%s",
1942 				task->err->message);
1943 
1944 		return TRUE;
1945 	}
1946 
1947 	if (RSPAMD_TASK_IS_PROCESSED (task)) {
1948 		/* Successful learn */
1949 		msg_info_task ("<%s> learned message as %s: %s",
1950 				rspamd_inet_address_to_string (session->from_addr),
1951 				session->is_spam ? "spam" : "ham",
1952 				MESSAGE_FIELD (task, message_id));
1953 		rspamd_controller_send_string (conn_ent, "{\"success\":true}");
1954 		return TRUE;
1955 	}
1956 
1957 	if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_LEARN)) {
1958 		msg_info_task ("cannot learn <%s>: %e",
1959 				MESSAGE_FIELD (task, message_id), task->err);
1960 
1961 		if (task->err) {
1962 			rspamd_controller_send_error (conn_ent, task->err->code, "%s",
1963 					task->err->message);
1964 		}
1965 		else {
1966 			rspamd_controller_send_error (conn_ent, 500,
1967 								"Internal error");
1968 		}
1969 	}
1970 
1971 	if (RSPAMD_TASK_IS_PROCESSED (task)) {
1972 		if (task->err) {
1973 			rspamd_controller_send_error (conn_ent, task->err->code, "%s",
1974 					task->err->message);
1975 		}
1976 		else {
1977 			msg_info_task ("<%s> learned message as %s: %s",
1978 					rspamd_inet_address_to_string (session->from_addr),
1979 					session->is_spam ? "spam" : "ham",
1980 					MESSAGE_FIELD (task, message_id));
1981 			rspamd_controller_send_string (conn_ent, "{\"success\":true}");
1982 		}
1983 
1984 		return TRUE;
1985 	}
1986 
1987 	/* One more iteration */
1988 	return FALSE;
1989 }
1990 
1991 static void
rspamd_controller_scan_reply(struct rspamd_task * task)1992 rspamd_controller_scan_reply (struct rspamd_task *task)
1993 {
1994 	struct rspamd_http_message *msg;
1995 	struct rspamd_http_connection_entry *conn_ent;
1996 
1997 	conn_ent = task->fin_arg;
1998 	msg = rspamd_http_new_message (HTTP_RESPONSE);
1999 	msg->date = time (NULL);
2000 	msg->code = 200;
2001 	rspamd_protocol_http_reply (msg, task, NULL);
2002 	rspamd_http_connection_reset (conn_ent->conn);
2003 	rspamd_http_router_insert_headers (conn_ent->rt, msg);
2004 	rspamd_http_connection_write_message (conn_ent->conn, msg, NULL,
2005 			"application/json", conn_ent, conn_ent->rt->timeout);
2006 	conn_ent->is_reply = TRUE;
2007 }
2008 
2009 static gboolean
rspamd_controller_check_fin_task(void * ud)2010 rspamd_controller_check_fin_task (void *ud)
2011 {
2012 	struct rspamd_task *task = ud;
2013 	struct rspamd_http_connection_entry *conn_ent;
2014 
2015 	msg_debug_task ("finish task");
2016 	conn_ent = task->fin_arg;
2017 
2018 	if (task->err) {
2019 		msg_info_task ("cannot check <%s>: %e",
2020 				MESSAGE_FIELD_CHECK (task, message_id), task->err);
2021 		rspamd_controller_send_error (conn_ent, task->err->code, "%s",
2022 				task->err->message);
2023 		return TRUE;
2024 	}
2025 
2026 	if (RSPAMD_TASK_IS_PROCESSED (task)) {
2027 		rspamd_controller_scan_reply (task);
2028 		return TRUE;
2029 	}
2030 
2031 	if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_ALL)) {
2032 		rspamd_controller_scan_reply (task);
2033 		return TRUE;
2034 	}
2035 
2036 	if (RSPAMD_TASK_IS_PROCESSED (task)) {
2037 		rspamd_controller_scan_reply (task);
2038 		return TRUE;
2039 	}
2040 
2041 	/* One more iteration */
2042 	return FALSE;
2043 }
2044 
2045 static int
rspamd_controller_handle_learn_common(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg,gboolean is_spam)2046 rspamd_controller_handle_learn_common (
2047 	struct rspamd_http_connection_entry *conn_ent,
2048 	struct rspamd_http_message *msg,
2049 	gboolean is_spam)
2050 {
2051 	struct rspamd_controller_session *session = conn_ent->ud;
2052 	struct rspamd_controller_worker_ctx *ctx;
2053 	struct rspamd_task *task;
2054 	const rspamd_ftok_t *cl_header;
2055 
2056 	ctx = session->ctx;
2057 
2058 	if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
2059 		return 0;
2060 	}
2061 
2062 	if (rspamd_http_message_get_body (msg, NULL) == NULL) {
2063 		msg_err_session ("got zero length body, cannot continue");
2064 		rspamd_controller_send_error (conn_ent,
2065 			400,
2066 			"Empty body is not permitted");
2067 		return 0;
2068 	}
2069 
2070 	task = rspamd_task_new (session->ctx->worker, session->cfg, session->pool,
2071 			session->ctx->lang_det, ctx->event_loop, FALSE);
2072 
2073 	task->resolver = ctx->resolver;
2074 	task->s = rspamd_session_create (session->pool,
2075 			rspamd_controller_learn_fin_task,
2076 			NULL,
2077 			(event_finalizer_t )rspamd_task_free,
2078 			task);
2079 	task->fin_arg = conn_ent;
2080 	task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
2081 	task->sock = -1;
2082 	session->task = task;
2083 
2084 	cl_header = rspamd_http_message_find_header (msg, "classifier");
2085 	if (cl_header) {
2086 		session->classifier = rspamd_mempool_ftokdup (session->pool, cl_header);
2087 	}
2088 	else {
2089 		session->classifier = NULL;
2090 	}
2091 
2092 	if (!rspamd_task_load_message (task, msg, msg->body_buf.begin, msg->body_buf.len)) {
2093 		goto end;
2094 	}
2095 
2096 	rspamd_learn_task_spam (task, is_spam, session->classifier, NULL);
2097 
2098 	if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_LEARN)) {
2099 		msg_warn_session ("<%s> message cannot be processed",
2100 				MESSAGE_FIELD (task, message_id));
2101 		goto end;
2102 	}
2103 
2104 end:
2105 	session->is_spam = is_spam;
2106 	rspamd_session_pending (task->s);
2107 
2108 	return 0;
2109 }
2110 
2111 /*
2112  * Learn spam command handler:
2113  * request: /learnspam
2114  * headers: Password
2115  * input: plaintext data
2116  * reply: json {"success":true} or {"error":"error message"}
2117  */
2118 static int
rspamd_controller_handle_learnspam(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)2119 rspamd_controller_handle_learnspam (
2120 	struct rspamd_http_connection_entry *conn_ent,
2121 	struct rspamd_http_message *msg)
2122 {
2123 	return rspamd_controller_handle_learn_common (conn_ent, msg, TRUE);
2124 }
2125 /*
2126  * Learn ham command handler:
2127  * request: /learnham
2128  * headers: Password
2129  * input: plaintext data
2130  * reply: json {"success":true} or {"error":"error message"}
2131  */
2132 static int
rspamd_controller_handle_learnham(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)2133 rspamd_controller_handle_learnham (
2134 	struct rspamd_http_connection_entry *conn_ent,
2135 	struct rspamd_http_message *msg)
2136 {
2137 	return rspamd_controller_handle_learn_common (conn_ent, msg, FALSE);
2138 }
2139 
2140 /*
2141  * Scan command handler:
2142  * request: /scan
2143  * headers: Password
2144  * input: plaintext data
2145  * reply: json {scan data} or {"error":"error message"}
2146  */
2147 static int
rspamd_controller_handle_scan(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)2148 rspamd_controller_handle_scan (struct rspamd_http_connection_entry *conn_ent,
2149 	struct rspamd_http_message *msg)
2150 {
2151 	struct rspamd_controller_session *session = conn_ent->ud;
2152 	struct rspamd_controller_worker_ctx *ctx;
2153 	struct rspamd_task *task;
2154 
2155 	ctx = session->ctx;
2156 
2157 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
2158 		return 0;
2159 	}
2160 
2161 	task = rspamd_task_new (session->ctx->worker, session->cfg, session->pool,
2162 			ctx->lang_det, ctx->event_loop, FALSE);
2163 
2164 	task->resolver = ctx->resolver;
2165 	task->s = rspamd_session_create (session->pool,
2166 			rspamd_controller_check_fin_task,
2167 			NULL,
2168 			(event_finalizer_t )rspamd_task_free,
2169 			task);
2170 	task->fin_arg = conn_ent;
2171 	task->http_conn = rspamd_http_connection_ref (conn_ent->conn);
2172 	task->sock = conn_ent->conn->fd;
2173 	task->flags |= RSPAMD_TASK_FLAG_MIME;
2174 	task->resolver = ctx->resolver;
2175 
2176 	if (!rspamd_protocol_handle_request (task, msg)) {
2177 		goto end;
2178 	}
2179 
2180 	if (!rspamd_task_load_message (task, msg, msg->body_buf.begin, msg->body_buf.len)) {
2181 		goto end;
2182 	}
2183 
2184 	if (!rspamd_task_process (task, RSPAMD_TASK_PROCESS_ALL)) {
2185 		goto end;
2186 	}
2187 
2188 	if (ctx->task_timeout > 0.0) {
2189 		task->timeout_ev.data = task;
2190 		ev_timer_init (&task->timeout_ev, rspamd_task_timeout,
2191 				ctx->task_timeout, ctx->task_timeout);
2192 		ev_timer_start (task->event_loop, &task->timeout_ev);
2193 		ev_set_priority (&task->timeout_ev, EV_MAXPRI);
2194 	}
2195 
2196 end:
2197 	session->task = task;
2198 	rspamd_session_pending (task->s);
2199 
2200 	return 0;
2201 }
2202 
2203 /*
2204  * Save actions command handler:
2205  * request: /saveactions
2206  * headers: Password
2207  * input: json array [<spam>,<probable spam>,<greylist>]
2208  * reply: json {"success":true} or {"error":"error message"}
2209  */
2210 static int
rspamd_controller_handle_saveactions(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)2211 rspamd_controller_handle_saveactions (
2212 	struct rspamd_http_connection_entry *conn_ent,
2213 	struct rspamd_http_message *msg)
2214 {
2215 	struct rspamd_controller_session *session = conn_ent->ud;
2216 	struct ucl_parser *parser;
2217 	ucl_object_t *obj;
2218 	const ucl_object_t *cur;
2219 	struct rspamd_controller_worker_ctx *ctx;
2220 	const gchar *error;
2221 	gdouble score;
2222 	gint i, added = 0;
2223 	enum rspamd_action_type act;
2224 	ucl_object_iter_t it = NULL;
2225 
2226 	ctx = session->ctx;
2227 
2228 	if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
2229 		return 0;
2230 	}
2231 
2232 	if (rspamd_http_message_get_body (msg, NULL) == NULL) {
2233 		msg_err_session ("got zero length body, cannot continue");
2234 		rspamd_controller_send_error (conn_ent,
2235 			400,
2236 			"Empty body is not permitted");
2237 		return 0;
2238 	}
2239 
2240 	parser = ucl_parser_new (0);
2241 	if (!ucl_parser_add_chunk (parser, msg->body_buf.begin, msg->body_buf.len)) {
2242 		if ((error = ucl_parser_get_error (parser)) != NULL) {
2243 			msg_err_session ("cannot parse input: %s", error);
2244 			rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
2245 			ucl_parser_free (parser);
2246 			return 0;
2247 		}
2248 
2249 		msg_err_session ("cannot parse input: unknown error");
2250 		rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
2251 		ucl_parser_free (parser);
2252 		return 0;
2253 	}
2254 
2255 	obj = ucl_parser_get_object (parser);
2256 	ucl_parser_free (parser);
2257 
2258 	if (obj->type != UCL_ARRAY || obj->len != 4) {
2259 		msg_err_session ("input is not an array of 4 elements");
2260 		rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
2261 		ucl_object_unref (obj);
2262 		return 0;
2263 	}
2264 
2265 	it = ucl_object_iterate_new (obj);
2266 
2267 	for (i = 0; i < 4; i++) {
2268 		cur = ucl_object_iterate_safe (it, TRUE);
2269 
2270 		switch (i) {
2271 		case 0:
2272 		default:
2273 			act = METRIC_ACTION_REJECT;
2274 			break;
2275 		case 1:
2276 			act = METRIC_ACTION_REWRITE_SUBJECT;
2277 			break;
2278 		case 2:
2279 			act = METRIC_ACTION_ADD_HEADER;
2280 			break;
2281 		case 3:
2282 			act = METRIC_ACTION_GREYLIST;
2283 			break;
2284 		}
2285 
2286 		if (ucl_object_type (cur) == UCL_NULL) {
2287 			/* Assume NaN */
2288 			score = NAN;
2289 		}
2290 		else {
2291 			score = ucl_object_todouble (cur);
2292 		}
2293 
2294 		if ((isnan (session->cfg->actions[act].threshold) != isnan (score)) ||
2295 				(session->cfg->actions[act].threshold != score)) {
2296 			add_dynamic_action (ctx->cfg, DEFAULT_METRIC, act, score);
2297 			added ++;
2298 		}
2299 		else {
2300 			if (remove_dynamic_action (ctx->cfg, DEFAULT_METRIC, act)) {
2301 				added ++;
2302 			}
2303 		}
2304 	}
2305 
2306 	ucl_object_iterate_free (it);
2307 
2308 	if (dump_dynamic_config (ctx->cfg)) {
2309 		msg_info_session ("<%s> modified %d actions",
2310 			rspamd_inet_address_to_string (session->from_addr),
2311 			added);
2312 
2313 		rspamd_controller_send_string (conn_ent, "{\"success\":true}");
2314 	}
2315 	else {
2316 		rspamd_controller_send_error (conn_ent, 500, "Save error");
2317 	}
2318 
2319 	ucl_object_unref (obj);
2320 
2321 	return 0;
2322 }
2323 
2324 /*
2325  * Save symbols command handler:
2326  * request: /savesymbols
2327  * headers: Password
2328  * input: json data
2329  * reply: json {"success":true} or {"error":"error message"}
2330  */
2331 static int
rspamd_controller_handle_savesymbols(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)2332 rspamd_controller_handle_savesymbols (
2333 	struct rspamd_http_connection_entry *conn_ent,
2334 	struct rspamd_http_message *msg)
2335 {
2336 	struct rspamd_controller_session *session = conn_ent->ud;
2337 	struct ucl_parser *parser;
2338 	ucl_object_t *obj;
2339 	const ucl_object_t *cur, *jname, *jvalue;
2340 	ucl_object_iter_t iter = NULL;
2341 	struct rspamd_controller_worker_ctx *ctx;
2342 	const gchar *error;
2343 	gdouble val;
2344 	struct rspamd_symbol *sym;
2345 	int added = 0;
2346 
2347 	ctx = session->ctx;
2348 
2349 	if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
2350 		return 0;
2351 	}
2352 
2353 	if (rspamd_http_message_get_body (msg, NULL) == NULL) {
2354 		msg_err_session ("got zero length body, cannot continue");
2355 		rspamd_controller_send_error (conn_ent,
2356 			400,
2357 			"Empty body is not permitted");
2358 		return 0;
2359 	}
2360 
2361 	parser = ucl_parser_new (0);
2362 	if (!ucl_parser_add_chunk (parser, msg->body_buf.begin, msg->body_buf.len)) {
2363 		if ((error = ucl_parser_get_error (parser)) != NULL) {
2364 			msg_err_session ("cannot parse input: %s", error);
2365 			rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
2366 			ucl_parser_free (parser);
2367 			return 0;
2368 		}
2369 
2370 		msg_err_session ("cannot parse input: unknown error");
2371 		rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
2372 		ucl_parser_free (parser);
2373 		return 0;
2374 	}
2375 
2376 	obj = ucl_parser_get_object (parser);
2377 	ucl_parser_free (parser);
2378 
2379 	if (obj->type != UCL_ARRAY) {
2380 		msg_err_session ("input is not an array");
2381 		rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
2382 		ucl_object_unref (obj);
2383 		return 0;
2384 	}
2385 
2386 	iter = ucl_object_iterate_new (obj);
2387 
2388 	while ((cur = ucl_object_iterate_safe (iter, true))) {
2389 		if (cur->type != UCL_OBJECT) {
2390 			msg_err_session ("json array data error");
2391 			rspamd_controller_send_error (conn_ent, 400, "Cannot parse input");
2392 			ucl_object_unref (obj);
2393 			ucl_object_iterate_free (iter);
2394 
2395 			return 0;
2396 		}
2397 
2398 		jname = ucl_object_lookup (cur, "name");
2399 		jvalue = ucl_object_lookup (cur, "value");
2400 		val = ucl_object_todouble (jvalue);
2401 		sym = g_hash_table_lookup (session->cfg->symbols, ucl_object_tostring (jname));
2402 
2403 		if (sym && fabs (*sym->weight_ptr - val) > 0.01) {
2404 			if (!add_dynamic_symbol (ctx->cfg, DEFAULT_METRIC,
2405 				ucl_object_tostring (jname), val)) {
2406 				msg_err_session ("add symbol failed for %s",
2407 					ucl_object_tostring (jname));
2408 				rspamd_controller_send_error (conn_ent, 506,
2409 					"Add symbol failed");
2410 				ucl_object_unref (obj);
2411 				ucl_object_iterate_free (iter);
2412 
2413 				return 0;
2414 			}
2415 			added ++;
2416 		}
2417 		else if (sym && ctx->cfg->dynamic_conf) {
2418 			if (remove_dynamic_symbol (ctx->cfg, DEFAULT_METRIC,
2419 					ucl_object_tostring (jname))) {
2420 				added ++;
2421 			}
2422 		}
2423 	}
2424 
2425 	ucl_object_iterate_free (iter);
2426 
2427 	if (added > 0) {
2428 		if (ctx->cfg->dynamic_conf) {
2429 			if (dump_dynamic_config (ctx->cfg)) {
2430 				msg_info_session ("<%s> modified %d symbols",
2431 						rspamd_inet_address_to_string (session->from_addr),
2432 						added);
2433 
2434 				rspamd_controller_send_string (conn_ent, "{\"success\":true}");
2435 			}
2436 			else {
2437 				rspamd_controller_send_error (conn_ent, 500, "Save error");
2438 			}
2439 		}
2440 		else {
2441 			rspamd_controller_send_string (conn_ent, "{\"success\":true}");
2442 		}
2443 	}
2444 	else {
2445 		msg_err_session ("no symbols to save");
2446 		rspamd_controller_send_error (conn_ent, 404, "No symbols to save");
2447 	}
2448 
2449 	ucl_object_unref (obj);
2450 
2451 	return 0;
2452 }
2453 
2454 /*
2455  * Save map command handler:
2456  * request: /savemap
2457  * headers: Password, Map
2458  * input: plaintext data
2459  * reply: json {"success":true} or {"error":"error message"}
2460  */
2461 static int
rspamd_controller_handle_savemap(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)2462 rspamd_controller_handle_savemap (struct rspamd_http_connection_entry *conn_ent,
2463 	struct rspamd_http_message *msg)
2464 {
2465 	struct rspamd_controller_session *session = conn_ent->ud;
2466 	GList *cur;
2467 	struct rspamd_map *map = NULL;
2468 	struct rspamd_map_backend *bk;
2469 	struct rspamd_controller_worker_ctx *ctx;
2470 	const rspamd_ftok_t *idstr;
2471 	gulong id, i;
2472 	gboolean found = FALSE;
2473 	gchar tempname[PATH_MAX];
2474 	gint fd;
2475 
2476 	ctx = session->ctx;
2477 
2478 	if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
2479 		return 0;
2480 	}
2481 
2482 	if (rspamd_http_message_get_body (msg, NULL) == NULL) {
2483 		msg_err_session ("got zero length body, cannot continue");
2484 		rspamd_controller_send_error (conn_ent,
2485 			400,
2486 			"Empty body is not permitted");
2487 		return 0;
2488 	}
2489 
2490 	idstr = rspamd_http_message_find_header (msg, "Map");
2491 
2492 	if (idstr == NULL) {
2493 		msg_info_session ("absent map id");
2494 		rspamd_controller_send_error (conn_ent, 400, "Map id not specified");
2495 		return 0;
2496 	}
2497 
2498 	if (!rspamd_strtoul (idstr->begin, idstr->len, &id)) {
2499 		msg_info_session ("invalid map id: %T", idstr);
2500 		rspamd_controller_send_error (conn_ent, 400, "Map id is invalid");
2501 		return 0;
2502 	}
2503 
2504 	/* Now let's be sure that we have map defined in configuration */
2505 	cur = ctx->cfg->maps;
2506 	while (cur && !found) {
2507 		map = cur->data;
2508 
2509 		PTR_ARRAY_FOREACH (map->backends, i, bk) {
2510 			if (bk->id == id && bk->protocol == MAP_PROTO_FILE) {
2511 				found = TRUE;
2512 				break;
2513 			}
2514 		}
2515 		cur = g_list_next (cur);
2516 	}
2517 
2518 	if (!found) {
2519 		msg_info_session ("map not found: %L", id);
2520 		rspamd_controller_send_error (conn_ent, 404, "Map id not found");
2521 		return 0;
2522 	}
2523 
2524 	rspamd_snprintf (tempname, sizeof (tempname), "%s.newXXXXXX", bk->uri);
2525 	fd = g_mkstemp_full (tempname, O_WRONLY, 00644);
2526 
2527 	if (fd == -1) {
2528 		msg_info_session ("map %s open error: %s", tempname, strerror (errno));
2529 		rspamd_controller_send_error (conn_ent, 404,
2530 				"Cannot open map: %s",
2531 				strerror (errno));
2532 		return 0;
2533 	}
2534 
2535 	if (write (fd, msg->body_buf.begin, msg->body_buf.len) == -1) {
2536 		msg_info_session ("map %s write error: %s", tempname, strerror (errno));
2537 		unlink (tempname);
2538 		close (fd);
2539 		rspamd_controller_send_error (conn_ent, 500, "Map write error: %s",
2540 				strerror (errno));
2541 		return 0;
2542 	}
2543 
2544 	/* Rename */
2545 	if (rename (tempname, bk->uri) == -1) {
2546 		msg_info_session ("map %s rename error: %s", tempname, strerror (errno));
2547 		unlink (tempname);
2548 		close (fd);
2549 		rspamd_controller_send_error (conn_ent, 500, "Map rename error: %s",
2550 				strerror (errno));
2551 		return 0;
2552 	}
2553 
2554 	msg_info_session ("<%s>, map %s saved",
2555 			rspamd_inet_address_to_string (session->from_addr),
2556 			bk->uri);
2557 	close (fd);
2558 
2559 	rspamd_controller_send_string (conn_ent, "{\"success\":true}");
2560 
2561 	return 0;
2562 }
2563 
2564 struct rspamd_stat_cbdata {
2565 	struct rspamd_http_connection_entry *conn_ent;
2566 	ucl_object_t *top;
2567 	ucl_object_t *stat;
2568 	struct rspamd_task *task;
2569 	guint64 learned;
2570 };
2571 
2572 static gboolean
rspamd_controller_stat_fin_task(void * ud)2573 rspamd_controller_stat_fin_task (void *ud)
2574 {
2575 	struct rspamd_stat_cbdata *cbdata = ud;
2576 	struct rspamd_http_connection_entry *conn_ent;
2577 	ucl_object_t *top, *ar;
2578 	GList *fuzzy_elts, *cur;
2579 	struct rspamd_fuzzy_stat_entry *entry;
2580 
2581 	conn_ent = cbdata->conn_ent;
2582 	top = cbdata->top;
2583 
2584 	ucl_object_insert_key (top,
2585 			ucl_object_fromint (cbdata->learned), "total_learns", 0, false);
2586 
2587 	if (cbdata->stat) {
2588 		ucl_object_insert_key (top, cbdata->stat, "statfiles", 0, false);
2589 	}
2590 
2591 	fuzzy_elts = rspamd_mempool_get_variable (cbdata->task->task_pool, "fuzzy_stat");
2592 
2593 	if (fuzzy_elts) {
2594 		ar = ucl_object_typed_new (UCL_OBJECT);
2595 
2596 		for (cur = fuzzy_elts; cur != NULL; cur = g_list_next (cur)) {
2597 			entry = cur->data;
2598 
2599 			if (entry->name) {
2600 				ucl_object_insert_key (ar, ucl_object_fromint (entry->fuzzy_cnt),
2601 						entry->name, 0, true);
2602 			}
2603 		}
2604 
2605 		ucl_object_insert_key (top, ar, "fuzzy_hashes", 0, false);
2606 	}
2607 
2608 	rspamd_controller_send_ucl (conn_ent, top);
2609 
2610 
2611 	return TRUE;
2612 }
2613 
2614 static void
rspamd_controller_stat_cleanup_task(void * ud)2615 rspamd_controller_stat_cleanup_task (void *ud)
2616 {
2617 	struct rspamd_stat_cbdata *cbdata = ud;
2618 
2619 	rspamd_task_free (cbdata->task);
2620 	ucl_object_unref (cbdata->top);
2621 }
2622 
2623 /*
2624  * Stat command handler:
2625  * request: /stat (/resetstat)
2626  * headers: Password
2627  * reply: json data
2628  */
2629 static int
rspamd_controller_handle_stat_common(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg,gboolean do_reset)2630 rspamd_controller_handle_stat_common (
2631 	struct rspamd_http_connection_entry *conn_ent,
2632 	struct rspamd_http_message *msg,
2633 	gboolean do_reset)
2634 {
2635 	struct rspamd_controller_session *session = conn_ent->ud;
2636 	ucl_object_t *top, *sub;
2637 	gint i;
2638 	int64_t uptime;
2639 	guint64 spam = 0, ham = 0;
2640 	rspamd_mempool_stat_t mem_st;
2641 	struct rspamd_stat *stat, stat_copy;
2642 	struct rspamd_controller_worker_ctx *ctx;
2643 	struct rspamd_task *task;
2644 	struct rspamd_stat_cbdata *cbdata;
2645 
2646 	memset (&mem_st, 0, sizeof (mem_st));
2647 	rspamd_mempool_stat (&mem_st);
2648 	memcpy (&stat_copy, session->ctx->worker->srv->stat, sizeof (stat_copy));
2649 	stat = &stat_copy;
2650 	ctx = session->ctx;
2651 
2652 	task = rspamd_task_new (session->ctx->worker, session->cfg, session->pool,
2653 			ctx->lang_det, ctx->event_loop, FALSE);
2654 	task->resolver = ctx->resolver;
2655 	cbdata = rspamd_mempool_alloc0 (session->pool, sizeof (*cbdata));
2656 	cbdata->conn_ent = conn_ent;
2657 	cbdata->task = task;
2658 	top = ucl_object_typed_new (UCL_OBJECT);
2659 	cbdata->top = top;
2660 
2661 	task->s = rspamd_session_create (session->pool,
2662 			rspamd_controller_stat_fin_task,
2663 			NULL,
2664 			rspamd_controller_stat_cleanup_task,
2665 			cbdata);
2666 	task->fin_arg = cbdata;
2667 	task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
2668 	task->sock = conn_ent->conn->fd;
2669 
2670 	ucl_object_insert_key (top, ucl_object_fromstring (
2671 			RVERSION), "version",  0, false);
2672 	ucl_object_insert_key (top, ucl_object_fromstring (
2673 			session->ctx->cfg->checksum), "config_id", 0, false);
2674 	uptime = ev_time () - session->ctx->start_time;
2675 	ucl_object_insert_key (top, ucl_object_fromint (
2676 			uptime), "uptime", 0, false);
2677 	ucl_object_insert_key (top, ucl_object_frombool (!session->is_enable),
2678 			"read_only", 0, false);
2679 	ucl_object_insert_key (top, ucl_object_fromint (
2680 			stat->messages_scanned), "scanned", 0, false);
2681 	ucl_object_insert_key (top, ucl_object_fromint (
2682 			stat->messages_learned), "learned", 0, false);
2683 
2684 	if (stat->messages_scanned > 0) {
2685 		sub = ucl_object_typed_new (UCL_OBJECT);
2686 		for (i = METRIC_ACTION_REJECT; i <= METRIC_ACTION_NOACTION; i++) {
2687 			ucl_object_insert_key (sub,
2688 				ucl_object_fromint (stat->actions_stat[i]),
2689 				rspamd_action_to_str (i), 0, false);
2690 			if (i < METRIC_ACTION_GREYLIST) {
2691 				spam += stat->actions_stat[i];
2692 			}
2693 			else {
2694 				ham += stat->actions_stat[i];
2695 			}
2696 			if (do_reset) {
2697 #ifndef HAVE_ATOMIC_BUILTINS
2698 				session->ctx->worker->srv->stat->actions_stat[i] = 0;
2699 #else
2700 				__atomic_store_n(&session->ctx->worker->srv->stat->actions_stat[i],
2701 						0, __ATOMIC_RELEASE);
2702 #endif
2703 			}
2704 		}
2705 		ucl_object_insert_key (top, sub, "actions", 0, false);
2706 	}
2707 
2708 	ucl_object_insert_key (top, ucl_object_fromint (
2709 			spam), "spam_count", 0, false);
2710 	ucl_object_insert_key (top, ucl_object_fromint (
2711 			ham),  "ham_count",	 0, false);
2712 	ucl_object_insert_key (top,
2713 		ucl_object_fromint (stat->connections_count), "connections", 0, false);
2714 	ucl_object_insert_key (top,
2715 		ucl_object_fromint (stat->control_connections_count),
2716 		"control_connections", 0, false);
2717 
2718 	ucl_object_insert_key (top,
2719 		ucl_object_fromint (mem_st.pools_allocated), "pools_allocated", 0,
2720 		false);
2721 	ucl_object_insert_key (top,
2722 		ucl_object_fromint (mem_st.pools_freed), "pools_freed", 0, false);
2723 	ucl_object_insert_key (top,
2724 		ucl_object_fromint (mem_st.bytes_allocated), "bytes_allocated", 0,
2725 		false);
2726 	ucl_object_insert_key (top,
2727 		ucl_object_fromint (
2728 			mem_st.chunks_allocated), "chunks_allocated", 0, false);
2729 	ucl_object_insert_key (top,
2730 		ucl_object_fromint (mem_st.shared_chunks_allocated),
2731 		"shared_chunks_allocated", 0, false);
2732 	ucl_object_insert_key (top,
2733 		ucl_object_fromint (mem_st.chunks_freed), "chunks_freed", 0, false);
2734 	ucl_object_insert_key (top,
2735 		ucl_object_fromint (
2736 			mem_st.oversized_chunks), "chunks_oversized", 0, false);
2737 	ucl_object_insert_key (top,
2738 			ucl_object_fromint (mem_st.fragmented_size), "fragmented", 0, false);
2739 
2740 	if (do_reset) {
2741 		session->ctx->srv->stat->messages_scanned = 0;
2742 		session->ctx->srv->stat->messages_learned = 0;
2743 		session->ctx->srv->stat->connections_count = 0;
2744 		session->ctx->srv->stat->control_connections_count = 0;
2745 		rspamd_mempool_stat_reset ();
2746 	}
2747 
2748 	fuzzy_stat_command (task);
2749 
2750 	/* Now write statistics for each statfile */
2751 	rspamd_stat_statistics (task, session->ctx->cfg, &cbdata->learned,
2752 			&cbdata->stat);
2753 	session->task = task;
2754 	rspamd_session_pending (task->s);
2755 
2756 	return 0;
2757 }
2758 
2759 static int
rspamd_controller_handle_stat(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)2760 rspamd_controller_handle_stat (struct rspamd_http_connection_entry *conn_ent,
2761 	struct rspamd_http_message *msg)
2762 {
2763 	struct rspamd_controller_session *session = conn_ent->ud;
2764 
2765 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
2766 		return 0;
2767 	}
2768 
2769 	return rspamd_controller_handle_stat_common (conn_ent, msg, FALSE);
2770 }
2771 
2772 static int
rspamd_controller_handle_statreset(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)2773 rspamd_controller_handle_statreset (
2774 	struct rspamd_http_connection_entry *conn_ent,
2775 	struct rspamd_http_message *msg)
2776 {
2777 	struct rspamd_controller_session *session = conn_ent->ud;
2778 
2779 	if (!rspamd_controller_check_password (conn_ent, session, msg, TRUE)) {
2780 		return 0;
2781 	}
2782 
2783 	msg_info_session ("<%s> reset stat",
2784 			rspamd_inet_address_to_string (session->from_addr));
2785 	return rspamd_controller_handle_stat_common (conn_ent, msg, TRUE);
2786 }
2787 
2788 /*
2789  * Metrics command handler:
2790  * request: /metrics
2791  * headers: Password
2792  * reply: OpenMetrics
2793  */
2794 
2795 static gboolean
rspamd_controller_metrics_fin_task(void * ud)2796 rspamd_controller_metrics_fin_task (void *ud) {
2797 	struct rspamd_stat_cbdata *cbdata = ud;
2798 	struct rspamd_http_connection_entry *conn_ent;
2799 	ucl_object_t *top;
2800 	GList *fuzzy_elts, *cur;
2801 	struct rspamd_fuzzy_stat_entry *entry;
2802 	rspamd_fstring_t *output;
2803 	gint i;
2804 
2805 	conn_ent = cbdata->conn_ent;
2806 	top = cbdata->top;
2807 
2808 	ucl_object_insert_key (top,
2809 			ucl_object_fromint (cbdata->learned), "total_learns", 0, false);
2810 
2811 	output = rspamd_fstring_sized_new (1024);
2812 	rspamd_printf_fstring (&output, "# HELP rspamd_build_info A metric with a constant '1' value labeled by version from which rspamd was built.\n");
2813 	rspamd_printf_fstring (&output, "# TYPE rspamd_build_info gauge\n");
2814 	rspamd_printf_fstring (&output, "rspamd_build_info{version=\"%s\"} 1\n",
2815 		ucl_object_tostring (ucl_object_lookup (top, "version")));
2816 	rspamd_printf_fstring (&output, "# HELP rspamd_config A metric with a constant '1' value labeled by id of the current config.\n");
2817 	rspamd_printf_fstring (&output, "# TYPE rspamd_config gauge\n");
2818 	rspamd_printf_fstring (&output, "rspamd_config{id=\"%s\"} 1\n",
2819 		ucl_object_tostring (ucl_object_lookup (top, "config_id")));
2820 	rspamd_printf_fstring (&output, "# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.\n");
2821 	rspamd_printf_fstring (&output, "# TYPE process_start_time_seconds gauge\n");
2822 	rspamd_printf_fstring (&output, "process_start_time_seconds %L\n",
2823 		ucl_object_toint (ucl_object_lookup (top, "start_time")));
2824 	rspamd_printf_fstring (&output, "# HELP rspamd_read_only Whether the rspamd instance is read-only.\n");
2825 	rspamd_printf_fstring (&output, "# TYPE rspamd_read_only gauge\n");
2826 	rspamd_printf_fstring (&output, "rspamd_read_only %L\n",
2827 		ucl_object_toint (ucl_object_lookup (top, "read_only")));
2828 	rspamd_printf_fstring (&output, "# HELP rspamd_scanned_total Scanned messages.\n");
2829 	rspamd_printf_fstring (&output, "# TYPE rspamd_scanned_total counter\n");
2830 	rspamd_printf_fstring (&output, "rspamd_scanned_total %L\n",
2831 		ucl_object_toint (ucl_object_lookup (top, "scanned")));
2832 	rspamd_printf_fstring (&output, "# HELP rspamd_learned_total Learned messages.\n");
2833 	rspamd_printf_fstring (&output, "# TYPE rspamd_learned_total counter\n");
2834 	rspamd_printf_fstring (&output, "rspamd_learned_total %L\n",
2835 		ucl_object_toint (ucl_object_lookup (top, "learned")));
2836 	rspamd_printf_fstring (&output, "# HELP rspamd_spam_total Messages classified as spam.\n");
2837 	rspamd_printf_fstring (&output, "# TYPE rspamd_spam_total counter\n");
2838 	rspamd_printf_fstring (&output, "rspamd_spam_total %L\n",
2839 		ucl_object_toint (ucl_object_lookup (top, "spam_count")));
2840 	rspamd_printf_fstring (&output, "# HELP rspamd_ham_total Messages classified as ham.\n");
2841 	rspamd_printf_fstring (&output, "# TYPE rspamd_ham_total counter\n");
2842 	rspamd_printf_fstring (&output, "rspamd_ham_total %L\n",
2843 		ucl_object_toint (ucl_object_lookup (top, "ham_count")));
2844 	rspamd_printf_fstring (&output, "# HELP rspamd_connections Active connections.\n");
2845 	rspamd_printf_fstring (&output, "# TYPE rspamd_connections gauge\n");
2846 	rspamd_printf_fstring (&output, "rspamd_connections %L\n",
2847 		ucl_object_toint (ucl_object_lookup (top, "connections")));
2848 	rspamd_printf_fstring (&output, "# HELP rspamd_control_connections_total Control connections.\n");
2849 	rspamd_printf_fstring (&output, "# TYPE rspamd_control_connections_total counter\n");
2850 	rspamd_printf_fstring (&output, "rspamd_control_connections_total %L\n",
2851 		ucl_object_toint (ucl_object_lookup (top, "control_connections")));
2852 	rspamd_printf_fstring (&output, "# HELP rspamd_pools_allocated Pools allocated.\n");
2853 	rspamd_printf_fstring (&output, "# TYPE rspamd_pools_allocated gauge\n");
2854 	rspamd_printf_fstring (&output, "rspamd_pools_allocated %L\n",
2855 		ucl_object_toint (ucl_object_lookup (top, "pools_allocated")));
2856 	rspamd_printf_fstring (&output, "# HELP rspamd_pools_freed Pools freed.\n");
2857 	rspamd_printf_fstring (&output, "# TYPE rspamd_pools_freed gauge\n");
2858 	rspamd_printf_fstring (&output, "rspamd_pools_freed %L\n",
2859 		ucl_object_toint (ucl_object_lookup (top, "pools_freed")));
2860 	rspamd_printf_fstring (&output, "# HELP rspamd_allocated_bytes Bytes allocated.\n");
2861 	rspamd_printf_fstring (&output, "# TYPE rspamd_allocated_bytes gauge\n");
2862 	rspamd_printf_fstring (&output, "rspamd_allocated_bytes %L\n",
2863 		ucl_object_toint (ucl_object_lookup (top, "bytes_allocated")));
2864 	rspamd_printf_fstring (&output, "# HELP rspamd_chunks_allocated Chunks allocated.\n");
2865 	rspamd_printf_fstring (&output, "# TYPE rspamd_chunks_allocated gauge\n");
2866 	rspamd_printf_fstring (&output, "rspamd_chunks_allocated %L\n",
2867 		ucl_object_toint (ucl_object_lookup (top, "chunks_allocated")));
2868 	rspamd_printf_fstring (&output, "# HELP rspamd_shared_chunks_allocated Shared chunks allocated.\n");
2869 	rspamd_printf_fstring (&output, "# TYPE rspamd_shared_chunks_allocated gauge\n");
2870 	rspamd_printf_fstring (&output, "rspamd_shared_chunks_allocated %L\n",
2871 		ucl_object_toint (ucl_object_lookup (top, "shared_chunks_allocated")));
2872 	rspamd_printf_fstring (&output, "# HELP rspamd_chunks_freed Chunks freed.\n");
2873 	rspamd_printf_fstring (&output, "# TYPE rspamd_chunks_freed gauge\n");
2874 	rspamd_printf_fstring (&output, "rspamd_chunks_freed %L\n",
2875 		ucl_object_toint (ucl_object_lookup (top, "chunks_freed")));
2876 	rspamd_printf_fstring (&output, "# HELP rspamd_chunks_oversized Chunks oversized.\n");
2877 	rspamd_printf_fstring (&output, "# TYPE rspamd_chunks_oversized gauge\n");
2878 	rspamd_printf_fstring (&output, "rspamd_chunks_oversized %L\n",
2879 		ucl_object_toint (ucl_object_lookup (top, "chunks_oversized")));
2880 	rspamd_printf_fstring (&output, "# HELP rspamd_fragmented Fragmented.\n");
2881 	rspamd_printf_fstring (&output, "# TYPE rspamd_fragmented gauge\n");
2882 	rspamd_printf_fstring (&output, "rspamd_fragmented %L\n",
2883 		ucl_object_toint (ucl_object_lookup (top, "fragmented")));
2884 	rspamd_printf_fstring (&output, "# HELP rspamd_learns_total Total learns.\n");
2885 	rspamd_printf_fstring (&output, "# TYPE rspamd_learns_total counter\n");
2886 	rspamd_printf_fstring (&output, "rspamd_learns_total %L\n",
2887 		ucl_object_toint (ucl_object_lookup (top, "total_learns")));
2888 
2889 	const ucl_object_t *acts_obj = ucl_object_lookup (top, "actions");
2890 
2891 	if (acts_obj) {
2892 		rspamd_printf_fstring (&output, "# HELP rspamd_actions_total Actions labelled by action type.\n");
2893 		rspamd_printf_fstring (&output, "# TYPE rspamd_actions_total counter\n");
2894 		for (i = METRIC_ACTION_REJECT; i <= METRIC_ACTION_NOACTION; i++) {
2895 			const char *str_act = rspamd_action_to_str (i);
2896 			const ucl_object_t *act = ucl_object_lookup (acts_obj, str_act);
2897 
2898 			if (act) {
2899 				rspamd_printf_fstring(&output, "rspamd_actions_total{type=\"%s\"} %L\n",
2900 						str_act,
2901 						ucl_object_toint(act));
2902 			}
2903 			else {
2904 				rspamd_printf_fstring (&output, "rspamd_actions_total{type=\"%s\"} 0\n",
2905 						str_act);
2906 			}
2907 		}
2908 	}
2909 
2910 	if (cbdata->stat) {
2911 		const ucl_object_t *cur_elt;
2912 		ucl_object_iter_t it = NULL;
2913 		rspamd_fstring_t *revision;
2914 		rspamd_fstring_t *used;
2915 		rspamd_fstring_t *total;
2916 		rspamd_fstring_t *size;
2917 		rspamd_fstring_t *languages;
2918 		rspamd_fstring_t *users;
2919 
2920 		revision = rspamd_fstring_sized_new (16);
2921 		used = rspamd_fstring_sized_new (16);
2922 		total = rspamd_fstring_sized_new (16);
2923 		size = rspamd_fstring_sized_new (16);
2924 		languages = rspamd_fstring_sized_new (16);
2925 		users = rspamd_fstring_sized_new (16);
2926 
2927 		while ((cur_elt = ucl_object_iterate (cbdata->stat, &it, true))) {
2928 			const char *sym = ucl_object_tostring (ucl_object_lookup (cur_elt, "symbol"));
2929 			const char *type = ucl_object_tostring (ucl_object_lookup (cur_elt, "type"));
2930 
2931 			if (sym && type) {
2932 				rspamd_printf_fstring (&revision, "rspamd_statfiles_revision{symbol=\"%s\",type=\"%s\"} %L\n",
2933 						sym,
2934 						type,
2935 						ucl_object_toint (ucl_object_lookup (cur_elt, "revision")));
2936 				rspamd_printf_fstring (&used, "rspamd_statfiles_used{symbol=\"%s\",type=\"%s\"} %L\n",
2937 						sym,
2938 						type,
2939 						ucl_object_toint (ucl_object_lookup (cur_elt, "used")));
2940 				rspamd_printf_fstring (&total, "rspamd_statfiles_totals{symbol=\"%s\",type=\"%s\"} %L\n",
2941 						sym,
2942 						type,
2943 						ucl_object_toint (ucl_object_lookup (cur_elt, "total")));
2944 				rspamd_printf_fstring (&size, "rspamd_statfiles_size{symbol=\"%s\",type=\"%s\"} %L\n",
2945 						sym,
2946 						type,
2947 						ucl_object_toint (ucl_object_lookup (cur_elt, "size")));
2948 				rspamd_printf_fstring (&languages, "rspamd_statfiles_languages{symbol=\"%s\",type=\"%s\"} %L\n",
2949 						sym,
2950 						type,
2951 						ucl_object_toint (ucl_object_lookup (cur_elt, "languages")));
2952 				rspamd_printf_fstring (&users, "rspamd_statfiles_users{symbol=\"%s\",type=\"%s\"} %L\n",
2953 						sym,
2954 						type,
2955 						ucl_object_toint (ucl_object_lookup (cur_elt, "users")));
2956 			}
2957 		}
2958 
2959 		if (RSPAMD_FSTRING_LEN(revision) > 0) {
2960 			rspamd_printf_fstring (&output, "# HELP rspamd_statfiles_revision Stat files revision.\n");
2961 			rspamd_printf_fstring (&output, "# TYPE rspamd_statfiles_revision gauge\n");
2962 			output = rspamd_fstring_append (output,
2963 					RSPAMD_FSTRING_DATA(revision), RSPAMD_FSTRING_LEN(revision));
2964 		}
2965 		if (RSPAMD_FSTRING_LEN(used) > 0) {
2966 			rspamd_printf_fstring (&output, "# HELP rspamd_statfiles_used Stat files used.\n");
2967 			rspamd_printf_fstring (&output, "# TYPE rspamd_statfiles_used gauge\n");
2968 			output = rspamd_fstring_append (output,
2969 					RSPAMD_FSTRING_DATA(used), RSPAMD_FSTRING_LEN(used));
2970 		}
2971 		if (RSPAMD_FSTRING_LEN(total) > 0) {
2972 			rspamd_printf_fstring (&output, "# HELP rspamd_statfiles_totals Stat files total.\n");
2973 			rspamd_printf_fstring (&output, "# TYPE rspamd_statfiles_totals gauge\n");
2974 			output = rspamd_fstring_append (output,
2975 					RSPAMD_FSTRING_DATA(total), RSPAMD_FSTRING_LEN(total));
2976 		}
2977 		if (RSPAMD_FSTRING_LEN(size) > 0) {
2978 			rspamd_printf_fstring (&output, "# HELP rspamd_statfiles_size Stat files size.\n");
2979 			rspamd_printf_fstring (&output, "# TYPE rspamd_statfiles_size gauge\n");
2980 			output = rspamd_fstring_append (output,
2981 					RSPAMD_FSTRING_DATA(size), RSPAMD_FSTRING_LEN(size));
2982 		}
2983 		if (RSPAMD_FSTRING_LEN(languages) > 0) {
2984 			rspamd_printf_fstring (&output, "# HELP rspamd_statfiles_languages Stat files languages.\n");
2985 			rspamd_printf_fstring (&output, "# TYPE rspamd_statfiles_languages gauge\n");
2986 			output = rspamd_fstring_append (output,
2987 					RSPAMD_FSTRING_DATA(languages), RSPAMD_FSTRING_LEN(languages));
2988 		}
2989 		if (RSPAMD_FSTRING_LEN(users) > 0) {
2990 			rspamd_printf_fstring (&output, "# HELP rspamd_statfiles_users Stat files users.\n");
2991 			rspamd_printf_fstring (&output, "# TYPE rspamd_statfiles_users gauge\n");
2992 			output = rspamd_fstring_append (output,
2993 					RSPAMD_FSTRING_DATA(users), RSPAMD_FSTRING_LEN(users));
2994 		}
2995 
2996 		rspamd_fstring_free (revision);
2997 		rspamd_fstring_free (used);
2998 		rspamd_fstring_free (total);
2999 		rspamd_fstring_free (size);
3000 		rspamd_fstring_free (languages);
3001 		rspamd_fstring_free (users);
3002 	}
3003 
3004 	fuzzy_elts = rspamd_mempool_get_variable (cbdata->task->task_pool, "fuzzy_stat");
3005 
3006 	if (fuzzy_elts) {
3007 		rspamd_printf_fstring (&output, "# HELP rspamd_fuzzy_stat Fuzzy stat labelled by storage.\n");
3008 		rspamd_printf_fstring (&output, "# TYPE rspamd_fuzzy_stat gauge\n");
3009 		for (cur = fuzzy_elts; cur != NULL; cur = g_list_next (cur)) {
3010 			entry = cur->data;
3011 
3012 			if (entry->name) {
3013 				rspamd_printf_fstring (&output, "rspamd_fuzzy_stat{storage=\"%s\"} %ud\n",
3014 						entry->name, entry->fuzzy_cnt);
3015 			}
3016 		}
3017 	}
3018 
3019 	rspamd_printf_fstring (&output, "# EOF\n");
3020 
3021 	rspamd_controller_send_openmetrics (conn_ent, output);
3022 
3023 	return TRUE;
3024 }
3025 
3026 static int
rspamd_controller_handle_metrics_common(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg,gboolean do_reset)3027 rspamd_controller_handle_metrics_common (
3028 	struct rspamd_http_connection_entry *conn_ent,
3029 	struct rspamd_http_message *msg,
3030 	gboolean do_reset)
3031 {
3032 	struct rspamd_controller_session *session = conn_ent->ud;
3033 	ucl_object_t *top, *sub;
3034 	gint i;
3035 	int64_t uptime;
3036 	guint64 spam = 0, ham = 0;
3037 	rspamd_mempool_stat_t mem_st;
3038 	struct rspamd_stat *stat, stat_copy;
3039 	struct rspamd_controller_worker_ctx *ctx;
3040 	struct rspamd_task *task;
3041 	struct rspamd_stat_cbdata *cbdata;
3042 
3043 	memset (&mem_st, 0, sizeof (mem_st));
3044 	rspamd_mempool_stat (&mem_st);
3045 	memcpy (&stat_copy, session->ctx->worker->srv->stat, sizeof (stat_copy));
3046 	stat = &stat_copy;
3047 	ctx = session->ctx;
3048 
3049 	task = rspamd_task_new (session->ctx->worker, session->cfg, session->pool,
3050 			ctx->lang_det, ctx->event_loop, FALSE);
3051 	task->resolver = ctx->resolver;
3052 	cbdata = rspamd_mempool_alloc0 (session->pool, sizeof (*cbdata));
3053 	cbdata->conn_ent = conn_ent;
3054 	cbdata->task = task;
3055 	top = ucl_object_typed_new (UCL_OBJECT);
3056 	cbdata->top = top;
3057 
3058 	task->s = rspamd_session_create (session->pool,
3059 			rspamd_controller_metrics_fin_task,
3060 			NULL,
3061 			rspamd_controller_stat_cleanup_task,
3062 			cbdata);
3063 	task->fin_arg = cbdata;
3064 	task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
3065 	task->sock = conn_ent->conn->fd;
3066 
3067 	ucl_object_insert_key (top, ucl_object_fromstring (
3068 			RVERSION), "version",  0, false);
3069 	ucl_object_insert_key (top, ucl_object_fromstring (
3070 			session->ctx->cfg->checksum), "config_id", 0, false);
3071 	uptime = ev_time () - session->ctx->start_time;
3072 	ucl_object_insert_key (top, ucl_object_fromint (
3073 			uptime), "uptime", 0, false);
3074 	ucl_object_insert_key (top, ucl_object_fromint (
3075 			session->ctx->start_time), "start_time", 0, false);
3076 	ucl_object_insert_key (top, ucl_object_frombool (!session->is_enable),
3077 			"read_only", 0, false);
3078 	ucl_object_insert_key (top, ucl_object_fromint (
3079 			stat->messages_scanned), "scanned", 0, false);
3080 	ucl_object_insert_key (top, ucl_object_fromint (
3081 			stat->messages_learned), "learned", 0, false);
3082 
3083 	if (stat->messages_scanned > 0) {
3084 		sub = ucl_object_typed_new (UCL_OBJECT);
3085 		for (i = METRIC_ACTION_REJECT; i <= METRIC_ACTION_NOACTION; i++) {
3086 			ucl_object_insert_key (sub,
3087 				ucl_object_fromint (stat->actions_stat[i]),
3088 				rspamd_action_to_str (i), 0, false);
3089 			if (i < METRIC_ACTION_GREYLIST) {
3090 				spam += stat->actions_stat[i];
3091 			}
3092 			else {
3093 				ham += stat->actions_stat[i];
3094 			}
3095 			if (do_reset) {
3096 #ifndef HAVE_ATOMIC_BUILTINS
3097 				session->ctx->worker->srv->stat->actions_stat[i] = 0;
3098 #else
3099 				__atomic_store_n(&session->ctx->worker->srv->stat->actions_stat[i],
3100 						0, __ATOMIC_RELEASE);
3101 #endif
3102 			}
3103 		}
3104 		ucl_object_insert_key (top, sub, "actions", 0, false);
3105 	}
3106 
3107 	ucl_object_insert_key (top, ucl_object_fromint (
3108 			spam), "spam_count", 0, false);
3109 	ucl_object_insert_key (top, ucl_object_fromint (
3110 			ham),  "ham_count",	 0, false);
3111 	ucl_object_insert_key (top,
3112 		ucl_object_fromint (stat->connections_count), "connections", 0, false);
3113 	ucl_object_insert_key (top,
3114 		ucl_object_fromint (stat->control_connections_count),
3115 		"control_connections", 0, false);
3116 
3117 	ucl_object_insert_key (top,
3118 		ucl_object_fromint (mem_st.pools_allocated), "pools_allocated", 0,
3119 		false);
3120 	ucl_object_insert_key (top,
3121 		ucl_object_fromint (mem_st.pools_freed), "pools_freed", 0, false);
3122 	ucl_object_insert_key (top,
3123 		ucl_object_fromint (mem_st.bytes_allocated), "bytes_allocated", 0,
3124 		false);
3125 	ucl_object_insert_key (top,
3126 		ucl_object_fromint (
3127 			mem_st.chunks_allocated), "chunks_allocated", 0, false);
3128 	ucl_object_insert_key (top,
3129 		ucl_object_fromint (mem_st.shared_chunks_allocated),
3130 		"shared_chunks_allocated", 0, false);
3131 	ucl_object_insert_key (top,
3132 		ucl_object_fromint (mem_st.chunks_freed), "chunks_freed", 0, false);
3133 	ucl_object_insert_key (top,
3134 		ucl_object_fromint (
3135 			mem_st.oversized_chunks), "chunks_oversized", 0, false);
3136 	ucl_object_insert_key (top,
3137 			ucl_object_fromint (mem_st.fragmented_size), "fragmented", 0, false);
3138 
3139 	if (do_reset) {
3140 		session->ctx->srv->stat->messages_scanned = 0;
3141 		session->ctx->srv->stat->messages_learned = 0;
3142 		session->ctx->srv->stat->connections_count = 0;
3143 		session->ctx->srv->stat->control_connections_count = 0;
3144 		rspamd_mempool_stat_reset ();
3145 	}
3146 
3147 	fuzzy_stat_command (task);
3148 
3149 	/* Now write statistics for each statfile */
3150 	rspamd_stat_statistics (task, session->ctx->cfg, &cbdata->learned,
3151 			&cbdata->stat);
3152 	session->task = task;
3153 
3154 	rspamd_session_pending (task->s);
3155 
3156 
3157 	return 0;
3158 }
3159 
3160 
3161 static int
rspamd_controller_handle_metrics(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)3162 rspamd_controller_handle_metrics (struct rspamd_http_connection_entry *conn_ent,
3163 	struct rspamd_http_message *msg)
3164 {
3165 	struct rspamd_controller_session *session = conn_ent->ud;
3166 
3167 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
3168 		return 0;
3169 	}
3170 	return rspamd_controller_handle_metrics_common (conn_ent, msg, FALSE);
3171 }
3172 
3173 
3174 /*
3175  * Counters command handler:
3176  * request: /counters
3177  * headers: Password
3178  * reply: json array of all counters
3179  */
3180 static int
rspamd_controller_handle_counters(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)3181 rspamd_controller_handle_counters (
3182 	struct rspamd_http_connection_entry *conn_ent,
3183 	struct rspamd_http_message *msg)
3184 {
3185 	struct rspamd_controller_session *session = conn_ent->ud;
3186 	ucl_object_t *top;
3187 	struct rspamd_symcache *cache;
3188 
3189 	if (!rspamd_controller_check_password (conn_ent, session, msg, FALSE)) {
3190 		return 0;
3191 	}
3192 
3193 	cache = session->ctx->cfg->cache;
3194 
3195 	if (cache != NULL) {
3196 		top = rspamd_symcache_counters (cache);
3197 		rspamd_controller_send_ucl (conn_ent, top);
3198 		ucl_object_unref (top);
3199 	}
3200 	else {
3201 		rspamd_controller_send_error (conn_ent, 500, "Invalid cache");
3202 	}
3203 
3204 	return 0;
3205 }
3206 
3207 static int
rspamd_controller_handle_custom(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)3208 rspamd_controller_handle_custom (struct rspamd_http_connection_entry *conn_ent,
3209 	struct rspamd_http_message *msg)
3210 {
3211 	struct rspamd_controller_session *session = conn_ent->ud;
3212 	struct rspamd_custom_controller_command *cmd;
3213 	gchar *url_str;
3214 	struct http_parser_url u;
3215 	rspamd_ftok_t lookup;
3216 
3217 	http_parser_parse_url (msg->url->str, msg->url->len, TRUE, &u);
3218 
3219 	if (u.field_set & (1 << UF_PATH)) {
3220 		gsize unnorm_len;
3221 		lookup.begin = msg->url->str + u.field_data[UF_PATH].off;
3222 		lookup.len = u.field_data[UF_PATH].len;
3223 
3224 		rspamd_http_normalize_path_inplace ((gchar *)lookup.begin,
3225 				lookup.len,
3226 				&unnorm_len);
3227 		lookup.len = unnorm_len;
3228 	}
3229 	else {
3230 		lookup.begin = msg->url->str;
3231 		lookup.len = msg->url->len;
3232 	}
3233 
3234 	url_str = rspamd_ftok_cstr (&lookup);
3235 	cmd = g_hash_table_lookup (session->ctx->custom_commands, url_str);
3236 	g_free (url_str);
3237 
3238 	if (cmd == NULL || cmd->handler == NULL) {
3239 		msg_err_session ("custom command %T has not been found", &lookup);
3240 		rspamd_controller_send_error (conn_ent, 404, "No command associated");
3241 		return 0;
3242 	}
3243 
3244 	if (!rspamd_controller_check_password (conn_ent, session, msg,
3245 		cmd->privilleged)) {
3246 		return 0;
3247 	}
3248 	if (cmd->require_message && (rspamd_http_message_get_body (msg, NULL) == NULL)) {
3249 		msg_err_session ("got zero length body, cannot continue");
3250 		rspamd_controller_send_error (conn_ent,
3251 			400,
3252 			"Empty body is not permitted");
3253 		return 0;
3254 	}
3255 
3256 	/* Transfer query arguments to headers */
3257 	if (u.field_set & (1u << UF_QUERY)) {
3258 		GHashTable *query_args;
3259 		GHashTableIter it;
3260 		gpointer k, v;
3261 		rspamd_ftok_t *key, *value;
3262 
3263 		/* In case if we have a query, we need to store it somewhere */
3264 		query_args = rspamd_http_message_parse_query (msg);
3265 
3266 		/* Insert the rest of query params as HTTP headers */
3267 		g_hash_table_iter_init (&it, query_args);
3268 
3269 		while (g_hash_table_iter_next (&it, &k, &v)) {
3270 			key = k;
3271 			value = v;
3272 			/* Steal strings */
3273 			g_hash_table_iter_steal (&it);
3274 			url_str = rspamd_ftok_cstr (key);
3275 			rspamd_http_message_add_header_len (msg, url_str,
3276 					value->begin, value->len);
3277 			g_free (url_str);
3278 		}
3279 
3280 		g_hash_table_unref (query_args);
3281 	}
3282 
3283 	return cmd->handler (conn_ent, msg, cmd->ctx);
3284 }
3285 
3286 static int
rspamd_controller_handle_plugins(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)3287 rspamd_controller_handle_plugins (struct rspamd_http_connection_entry *conn_ent,
3288 	struct rspamd_http_message *msg)
3289 {
3290 	struct rspamd_controller_session *session = conn_ent->ud;
3291 	struct rspamd_controller_plugin_cbdata *cbd;
3292 	GHashTableIter it;
3293 	gpointer k, v;
3294 	ucl_object_t *plugins;
3295 
3296 	if (!rspamd_controller_check_password (conn_ent, session, msg,
3297 			FALSE)) {
3298 		return 0;
3299 	}
3300 
3301 	plugins = ucl_object_typed_new (UCL_OBJECT);
3302 	g_hash_table_iter_init (&it, session->ctx->plugins);
3303 
3304 	while (g_hash_table_iter_next (&it, &k, &v)) {
3305 		ucl_object_t *elt, *npath;
3306 
3307 		cbd = v;
3308 		elt = (ucl_object_t *)ucl_object_lookup (plugins, cbd->plugin);
3309 
3310 		if (elt == NULL) {
3311 			elt = ucl_object_typed_new (UCL_OBJECT);
3312 			ucl_object_insert_key (elt, ucl_object_fromint (cbd->version),
3313 					"version", 0, false);
3314 			npath = ucl_object_typed_new (UCL_ARRAY);
3315 			ucl_object_insert_key (elt, npath, "paths", 0, false);
3316 			ucl_object_insert_key (plugins, elt, cbd->plugin, 0, false);
3317 		}
3318 		else {
3319 			npath = (ucl_object_t *)ucl_object_lookup (elt, "paths");
3320 		}
3321 
3322 		g_assert (npath != NULL);
3323 		rspamd_ftok_t *key_tok = (rspamd_ftok_t *)k;
3324 		ucl_array_append (npath, ucl_object_fromlstring (key_tok->begin, key_tok->len));
3325 	}
3326 
3327 	rspamd_controller_send_ucl (conn_ent, plugins);
3328 	ucl_object_unref (plugins);
3329 
3330 	return 0;
3331 }
3332 
3333 static int
rspamd_controller_handle_ping(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)3334 rspamd_controller_handle_ping (struct rspamd_http_connection_entry *conn_ent,
3335 		struct rspamd_http_message *msg)
3336 {
3337 	struct rspamd_http_message *rep_msg;
3338 	rspamd_fstring_t *reply;
3339 
3340 	rep_msg = rspamd_http_new_message (HTTP_RESPONSE);
3341 	rep_msg->date = time (NULL);
3342 	rep_msg->code = 200;
3343 	rep_msg->status = rspamd_fstring_new_init ("OK", 2);
3344 	reply = rspamd_fstring_new_init ("pong" CRLF, strlen ("pong" CRLF));
3345 	rspamd_http_message_set_body_from_fstring_steal (rep_msg, reply);
3346 	rspamd_http_connection_reset (conn_ent->conn);
3347 	rspamd_http_router_insert_headers (conn_ent->rt, rep_msg);
3348 	rspamd_http_connection_write_message (conn_ent->conn,
3349 			rep_msg,
3350 			NULL,
3351 			"text/plain",
3352 			conn_ent,
3353 			conn_ent->rt->timeout);
3354 	conn_ent->is_reply = TRUE;
3355 
3356 	return 0;
3357 }
3358 
3359 /*
3360  * Called on unknown methods and is used to deal with CORS as per
3361  * https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
3362  */
3363 static int
rspamd_controller_handle_unknown(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)3364 rspamd_controller_handle_unknown (struct rspamd_http_connection_entry *conn_ent,
3365 	struct rspamd_http_message *msg)
3366 {
3367 	struct rspamd_http_message *rep;
3368 
3369 	if (msg->method == HTTP_OPTIONS) {
3370 		/* Assume CORS request */
3371 
3372 		rep = rspamd_http_new_message (HTTP_RESPONSE);
3373 		rep->date = time (NULL);
3374 		rep->code = 200;
3375 		rep->status = rspamd_fstring_new_init ("OK", 2);
3376 		rspamd_http_message_add_header (rep, "Access-Control-Allow-Methods",
3377 				"POST, GET, OPTIONS");
3378 		rspamd_http_message_add_header (rep, "Access-Control-Allow-Headers",
3379 						"Content-Type,Password,Map,Weight,Flag");
3380 		rspamd_http_connection_reset (conn_ent->conn);
3381 		rspamd_http_router_insert_headers (conn_ent->rt, rep);
3382 		rspamd_http_connection_write_message (conn_ent->conn,
3383 				rep,
3384 				NULL,
3385 				"text/plain",
3386 				conn_ent,
3387 				conn_ent->rt->timeout);
3388 		conn_ent->is_reply = TRUE;
3389 	}
3390 	else {
3391 		rep = rspamd_http_new_message (HTTP_RESPONSE);
3392 		rep->date = time (NULL);
3393 		rep->code = 500;
3394 		rep->status = rspamd_fstring_new_init ("Invalid method",
3395 				strlen ("Invalid method"));
3396 		rspamd_http_connection_reset (conn_ent->conn);
3397 		rspamd_http_router_insert_headers (conn_ent->rt, rep);
3398 		rspamd_http_connection_write_message (conn_ent->conn,
3399 				rep,
3400 				NULL,
3401 				"text/plain",
3402 				conn_ent,
3403 				conn_ent->rt->timeout);
3404 		conn_ent->is_reply = TRUE;
3405 	}
3406 
3407 	return 0;
3408 }
3409 
3410 static int
rspamd_controller_handle_lua_plugin(struct rspamd_http_connection_entry * conn_ent,struct rspamd_http_message * msg)3411 rspamd_controller_handle_lua_plugin (struct rspamd_http_connection_entry *conn_ent,
3412 	struct rspamd_http_message *msg)
3413 {
3414 	struct rspamd_controller_session *session = conn_ent->ud;
3415 	struct rspamd_controller_plugin_cbdata *cbd;
3416 	struct rspamd_task *task, **ptask;
3417 	struct rspamd_http_connection_entry **pconn;
3418 	struct rspamd_controller_worker_ctx *ctx;
3419 	lua_State *L;
3420 	struct http_parser_url u;
3421 	rspamd_ftok_t lookup;
3422 
3423 
3424 	http_parser_parse_url (msg->url->str, msg->url->len, TRUE, &u);
3425 
3426 	if (u.field_set & (1 << UF_PATH)) {
3427 		gsize unnorm_len;
3428 		lookup.begin = msg->url->str + u.field_data[UF_PATH].off;
3429 		lookup.len = u.field_data[UF_PATH].len;
3430 
3431 		rspamd_http_normalize_path_inplace ((gchar *)lookup.begin,
3432 				lookup.len,
3433 				&unnorm_len);
3434 		lookup.len = unnorm_len;
3435 	}
3436 	else {
3437 		lookup.begin = msg->url->str;
3438 		lookup.len = msg->url->len;
3439 	}
3440 
3441 	cbd = g_hash_table_lookup (session->ctx->plugins, &lookup);
3442 
3443 	if (cbd == NULL || cbd->handler == NULL) {
3444 		msg_err_session ("plugin handler %T has not been found", &lookup);
3445 		rspamd_controller_send_error (conn_ent, 404, "No command associated");
3446 		return 0;
3447 	}
3448 
3449 	L = cbd->L;
3450 	ctx = cbd->ctx;
3451 
3452 	if (!rspamd_controller_check_password (conn_ent, session, msg,
3453 		cbd->is_enable)) {
3454 		return 0;
3455 	}
3456 	if (cbd->need_task && (rspamd_http_message_get_body (msg, NULL) == NULL)) {
3457 		msg_err_session ("got zero length body, cannot continue");
3458 		rspamd_controller_send_error (conn_ent,
3459 			400,
3460 			"Empty body is not permitted");
3461 		return 0;
3462 	}
3463 
3464 	task = rspamd_task_new (session->ctx->worker, session->cfg, session->pool,
3465 			ctx->lang_det, ctx->event_loop, FALSE);
3466 
3467 	task->resolver = ctx->resolver;
3468 	task->s = rspamd_session_create (session->pool,
3469 			rspamd_controller_lua_fin_task,
3470 			NULL,
3471 			(event_finalizer_t )rspamd_task_free,
3472 			task);
3473 	task->fin_arg = conn_ent;
3474 	task->http_conn = rspamd_http_connection_ref (conn_ent->conn);;
3475 	task->sock = -1;
3476 	session->task = task;
3477 
3478 	if (msg->body_buf.len > 0) {
3479 		if (!rspamd_task_load_message (task, msg, msg->body_buf.begin, msg->body_buf.len)) {
3480 			rspamd_controller_send_error (conn_ent, task->err->code, "%s",
3481 					task->err->message);
3482 			return 0;
3483 		}
3484 	}
3485 
3486 	/* Callback */
3487 	lua_rawgeti (L, LUA_REGISTRYINDEX, cbd->handler->idx);
3488 
3489 	/* Task */
3490 	ptask = lua_newuserdata (L, sizeof (*ptask));
3491 	rspamd_lua_setclass (L, "rspamd{task}", -1);
3492 	*ptask = task;
3493 
3494 	/* Connection */
3495 	pconn = lua_newuserdata (L, sizeof (*pconn));
3496 	rspamd_lua_setclass (L, "rspamd{csession}", -1);
3497 	*pconn = conn_ent;
3498 
3499 	/* Query arguments */
3500 	GHashTable *params;
3501 	GHashTableIter it;
3502 	gpointer k, v;
3503 
3504 	params = rspamd_http_message_parse_query (msg);
3505 	lua_createtable (L, g_hash_table_size (params), 0);
3506 	g_hash_table_iter_init (&it, params);
3507 
3508 	while (g_hash_table_iter_next (&it, &k, &v)) {
3509 		rspamd_ftok_t *key_tok = (rspamd_ftok_t *)k,
3510 			*value_tok = (rspamd_ftok_t *)v;
3511 
3512 		lua_pushlstring (L, key_tok->begin, key_tok->len);
3513 		/* TODO: consider rspamd_text here */
3514 		lua_pushlstring (L, value_tok->begin, value_tok->len);
3515 		lua_settable (L, -3);
3516 	}
3517 
3518 	g_hash_table_unref (params);
3519 
3520 	if (lua_pcall (L, 3, 0, 0) != 0) {
3521 		rspamd_controller_send_error (conn_ent, 503, "Cannot run callback: %s",
3522 				lua_tostring (L, -1));
3523 		lua_settop (L, 0);
3524 
3525 		return 0;
3526 	}
3527 
3528 	rspamd_session_pending (task->s);
3529 
3530 	return 0;
3531 }
3532 
3533 
3534 static void
rspamd_controller_error_handler(struct rspamd_http_connection_entry * conn_ent,GError * err)3535 rspamd_controller_error_handler (struct rspamd_http_connection_entry *conn_ent,
3536 	GError *err)
3537 {
3538 	struct rspamd_controller_session *session = conn_ent->ud;
3539 
3540 	msg_err_session ("http error occurred: %s", err->message);
3541 }
3542 
3543 static void
rspamd_controller_finish_handler(struct rspamd_http_connection_entry * conn_ent)3544 rspamd_controller_finish_handler (struct rspamd_http_connection_entry *conn_ent)
3545 {
3546 	struct rspamd_controller_session *session = conn_ent->ud;
3547 
3548 	session->ctx->worker->srv->stat->control_connections_count++;
3549 
3550 	if (session->task != NULL) {
3551 		rspamd_session_destroy (session->task->s);
3552 	}
3553 
3554 	session->wrk->nconns --;
3555 	rspamd_inet_address_free (session->from_addr);
3556 	REF_RELEASE (session->cfg);
3557 
3558 	if (session->pool) {
3559 		msg_debug_session ("destroy session %p", session);
3560 		rspamd_mempool_delete (session->pool);
3561 	}
3562 
3563 	g_free (session);
3564 }
3565 
3566 static void
rspamd_controller_accept_socket(EV_P_ ev_io * w,int revents)3567 rspamd_controller_accept_socket (EV_P_ ev_io *w, int revents)
3568 {
3569 	struct rspamd_worker *worker = (struct rspamd_worker *)w->data;
3570 	struct rspamd_controller_worker_ctx *ctx;
3571 	struct rspamd_controller_session *session;
3572 	rspamd_inet_addr_t *addr = NULL;
3573 	gint nfd;
3574 
3575 	ctx = worker->ctx;
3576 
3577 	if ((nfd =
3578 		rspamd_accept_from_socket (w->fd, &addr,
3579 				rspamd_worker_throttle_accept_events, worker->accept_events)) == -1) {
3580 		msg_warn_ctx ("accept failed: %s", strerror (errno));
3581 		return;
3582 	}
3583 	/* Check for EAGAIN */
3584 	if (nfd == 0) {
3585 		rspamd_inet_address_free (addr);
3586 		return;
3587 	}
3588 
3589 	session = g_malloc0 (sizeof (struct rspamd_controller_session));
3590 	session->pool = rspamd_mempool_new (rspamd_mempool_suggest_size (),
3591 			"csession", 0);
3592 	session->ctx = ctx;
3593 	session->cfg = ctx->cfg;
3594 	session->lang_det = ctx->lang_det;
3595 	REF_RETAIN (session->cfg);
3596 
3597 	session->from_addr = addr;
3598 	session->wrk = worker;
3599 	worker->nconns ++;
3600 
3601 	rspamd_http_router_handle_socket (ctx->http, nfd, session);
3602 }
3603 
3604 static void
rspamd_controller_password_sane(struct rspamd_controller_worker_ctx * ctx,const gchar * password,const gchar * type)3605 rspamd_controller_password_sane (struct rspamd_controller_worker_ctx *ctx,
3606 		const gchar *password, const gchar *type)
3607 {
3608 	const struct rspamd_controller_pbkdf *pbkdf = &pbkdf_list[0];
3609 
3610 	if (password == NULL) {
3611 		msg_warn_ctx ("%s is not set, so you should filter controller "
3612 				"availability "
3613 				"by using of firewall or `secure_ip` option", type);
3614 		return;
3615 	}
3616 
3617 	g_assert (pbkdf != NULL);
3618 
3619 	if (!rspamd_is_encrypted_password (password, NULL)) {
3620 		/* Suggest encryption to a user */
3621 
3622 		msg_warn_ctx ("your %s is not encrypted, we strongly "
3623 				"recommend to replace it with the encrypted one", type);
3624 	}
3625 }
3626 
3627 gpointer
init_controller_worker(struct rspamd_config * cfg)3628 init_controller_worker (struct rspamd_config *cfg)
3629 {
3630 	struct rspamd_controller_worker_ctx *ctx;
3631 	GQuark type;
3632 
3633 	type = g_quark_try_string ("controller");
3634 
3635 	ctx = rspamd_mempool_alloc0 (cfg->cfg_pool,
3636 			sizeof (struct rspamd_controller_worker_ctx));
3637 
3638 	ctx->magic = rspamd_controller_ctx_magic;
3639 	ctx->timeout = DEFAULT_WORKER_IO_TIMEOUT;
3640 	ctx->task_timeout = NAN;
3641 
3642 	rspamd_rcl_register_worker_option (cfg,
3643 			type,
3644 			"password",
3645 			rspamd_rcl_parse_struct_string,
3646 			ctx,
3647 			G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, password),
3648 			0,
3649 			"Password for read-only commands");
3650 
3651 	rspamd_rcl_register_worker_option (cfg,
3652 			type,
3653 			"enable_password",
3654 			rspamd_rcl_parse_struct_string,
3655 			ctx,
3656 			G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
3657 					enable_password),
3658 			0,
3659 			"Password for read and write commands");
3660 
3661 	rspamd_rcl_register_worker_option (cfg,
3662 			type,
3663 			"ssl",
3664 			rspamd_rcl_parse_struct_boolean,
3665 			ctx,
3666 			G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, use_ssl),
3667 			0,
3668 			"Unimplemented");
3669 
3670 	rspamd_rcl_register_worker_option (cfg,
3671 			type,
3672 			"ssl_cert",
3673 			rspamd_rcl_parse_struct_string,
3674 			ctx,
3675 			G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, ssl_cert),
3676 			0,
3677 			"Unimplemented");
3678 
3679 	rspamd_rcl_register_worker_option (cfg,
3680 			type,
3681 			"ssl_key",
3682 			rspamd_rcl_parse_struct_string,
3683 			ctx,
3684 			G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, ssl_key),
3685 			0,
3686 			"Unimplemented");
3687 	rspamd_rcl_register_worker_option (cfg,
3688 			type,
3689 			"timeout",
3690 			rspamd_rcl_parse_struct_time,
3691 			ctx,
3692 			G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
3693 					timeout),
3694 			RSPAMD_CL_FLAG_TIME_FLOAT,
3695 			"Protocol timeout");
3696 
3697 	rspamd_rcl_register_worker_option (cfg,
3698 			type,
3699 			"secure_ip",
3700 			rspamd_rcl_parse_struct_ucl,
3701 			ctx,
3702 			G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, secure_ip),
3703 			0,
3704 			"List of IP addresses that are allowed for password-less access");
3705 
3706 	rspamd_rcl_register_worker_option (cfg,
3707 			type,
3708 			"trusted_ips",
3709 			rspamd_rcl_parse_struct_ucl,
3710 			ctx,
3711 			G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx, secure_ip),
3712 			0,
3713 			"List of IP addresses that are allowed for password-less access");
3714 
3715 	rspamd_rcl_register_worker_option (cfg,
3716 			type,
3717 			"static_dir",
3718 			rspamd_rcl_parse_struct_string,
3719 			ctx,
3720 			G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
3721 					static_files_dir),
3722 			0,
3723 			"Directory for static files served by controller's HTTP server");
3724 
3725 	rspamd_rcl_register_worker_option (cfg,
3726 			type,
3727 			"keypair",
3728 			rspamd_rcl_parse_struct_keypair,
3729 			ctx,
3730 			G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
3731 					key),
3732 			0,
3733 			"Encryption keypair");
3734 
3735 	rspamd_rcl_register_worker_option (cfg,
3736 			type,
3737 			"task_timeout",
3738 			rspamd_rcl_parse_struct_time,
3739 			ctx,
3740 			G_STRUCT_OFFSET (struct rspamd_controller_worker_ctx,
3741 					task_timeout),
3742 			RSPAMD_CL_FLAG_TIME_FLOAT,
3743 			"Maximum task processing time, default: 8.0 seconds");
3744 
3745 	return ctx;
3746 }
3747 
3748 /* Lua bindings */
3749 LUA_FUNCTION_DEF (csession, get_ev_base);
3750 LUA_FUNCTION_DEF (csession, get_cfg);
3751 LUA_FUNCTION_DEF (csession, send_ucl);
3752 LUA_FUNCTION_DEF (csession, send_string);
3753 LUA_FUNCTION_DEF (csession, send_error);
3754 
3755 static const struct luaL_reg lua_csessionlib_m[] = {
3756 	LUA_INTERFACE_DEF (csession, get_ev_base),
3757 	LUA_INTERFACE_DEF (csession, get_cfg),
3758 	LUA_INTERFACE_DEF (csession, send_ucl),
3759 	LUA_INTERFACE_DEF (csession, send_string),
3760 	LUA_INTERFACE_DEF (csession, send_error),
3761 	{"__tostring", rspamd_lua_class_tostring},
3762 	{NULL, NULL}
3763 };
3764 
3765 /* Basic functions of LUA API for worker object */
3766 static void
luaopen_controller(lua_State * L)3767 luaopen_controller (lua_State * L)
3768 {
3769 	rspamd_lua_new_class (L, "rspamd{csession}", lua_csessionlib_m);
3770 	lua_pop (L, 1);
3771 }
3772 
3773 struct rspamd_http_connection_entry *
lua_check_controller_entry(lua_State * L,gint pos)3774 lua_check_controller_entry (lua_State * L, gint pos)
3775 {
3776 	void *ud = rspamd_lua_check_udata (L, pos, "rspamd{csession}");
3777 	luaL_argcheck (L, ud != NULL, pos, "'csession' expected");
3778 	return ud ? *((struct rspamd_http_connection_entry **)ud) : NULL;
3779 }
3780 
3781 static int
lua_csession_get_ev_base(lua_State * L)3782 lua_csession_get_ev_base (lua_State *L)
3783 {
3784 	struct rspamd_http_connection_entry *c = lua_check_controller_entry (L, 1);
3785 	struct ev_loop **pbase;
3786 	struct rspamd_controller_session *s;
3787 
3788 	if (c) {
3789 		s = c->ud;
3790 		pbase = lua_newuserdata (L, sizeof (struct ev_loop *));
3791 		rspamd_lua_setclass (L, "rspamd{ev_base}", -1);
3792 		*pbase = s->ctx->event_loop;
3793 	}
3794 	else {
3795 		return luaL_error (L, "invalid arguments");
3796 	}
3797 
3798 	return 1;
3799 }
3800 
3801 static int
lua_csession_get_cfg(lua_State * L)3802 lua_csession_get_cfg (lua_State *L)
3803 {
3804 	struct rspamd_http_connection_entry *c = lua_check_controller_entry (L, 1);
3805 	struct rspamd_config **pcfg;
3806 	struct rspamd_controller_session *s;
3807 
3808 	if (c) {
3809 		s = c->ud;
3810 		pcfg = lua_newuserdata (L, sizeof (gpointer));
3811 		rspamd_lua_setclass (L, "rspamd{config}", -1);
3812 		*pcfg = s->ctx->cfg;
3813 	}
3814 	else {
3815 		return luaL_error (L, "invalid arguments");
3816 	}
3817 
3818 	return 1;
3819 }
3820 
3821 static int
lua_csession_send_ucl(lua_State * L)3822 lua_csession_send_ucl (lua_State *L)
3823 {
3824 	struct rspamd_http_connection_entry *c = lua_check_controller_entry (L, 1);
3825 	ucl_object_t *obj = ucl_object_lua_import_escape (L, 2);
3826 
3827 	if (c) {
3828 		rspamd_controller_send_ucl (c, obj);
3829 	}
3830 	else {
3831 		ucl_object_unref (obj);
3832 		return luaL_error (L, "invalid arguments");
3833 	}
3834 
3835 	ucl_object_unref (obj);
3836 
3837 	return 0;
3838 }
3839 
3840 static int
lua_csession_send_error(lua_State * L)3841 lua_csession_send_error (lua_State *L)
3842 {
3843 	struct rspamd_http_connection_entry *c = lua_check_controller_entry (L, 1);
3844 	guint err_code = lua_tonumber (L, 2);
3845 	const gchar *err_str = lua_tostring (L, 3);
3846 
3847 	if (c) {
3848 		rspamd_controller_send_error (c, err_code, "%s", err_str);
3849 	}
3850 	else {
3851 		return luaL_error (L, "invalid arguments");
3852 	}
3853 
3854 	return 0;
3855 }
3856 
3857 static int
lua_csession_send_string(lua_State * L)3858 lua_csession_send_string (lua_State *L)
3859 {
3860 	struct rspamd_http_connection_entry *c = lua_check_controller_entry (L, 1);
3861 	const gchar *str = lua_tostring (L, 2);
3862 
3863 	if (c) {
3864 		rspamd_controller_send_string (c, str);
3865 	}
3866 	else {
3867 		return luaL_error (L, "invalid arguments");
3868 	}
3869 
3870 	return 0;
3871 }
3872 
3873 static void
rspamd_plugin_cbdata_dtor(gpointer p)3874 rspamd_plugin_cbdata_dtor (gpointer p)
3875 {
3876 	struct rspamd_controller_plugin_cbdata *cbd = p;
3877 
3878 	g_free (cbd->plugin);
3879 	ucl_object_unref (cbd->obj); /* This also releases lua references */
3880 	g_free (cbd);
3881 }
3882 
3883 static void
rspamd_controller_register_plugin_path(lua_State * L,struct rspamd_controller_worker_ctx * ctx,const ucl_object_t * webui_data,const ucl_object_t * handler,const gchar * path,const gchar * plugin_name)3884 rspamd_controller_register_plugin_path (lua_State *L,
3885 		struct rspamd_controller_worker_ctx *ctx,
3886 		const ucl_object_t *webui_data,
3887 		const ucl_object_t *handler,
3888 		const gchar *path,
3889 		const gchar *plugin_name)
3890 {
3891 	struct rspamd_controller_plugin_cbdata *cbd;
3892 	const ucl_object_t *elt;
3893 	rspamd_fstring_t *full_path;
3894 
3895 	cbd = g_malloc0 (sizeof (*cbd));
3896 	cbd->L = L;
3897 	cbd->ctx = ctx;
3898 	cbd->handler = ucl_object_toclosure (handler);
3899 	cbd->plugin = g_strdup (plugin_name);
3900 	cbd->obj = ucl_object_ref (webui_data);
3901 
3902 	elt = ucl_object_lookup (webui_data, "version");
3903 
3904 	if (elt) {
3905 		cbd->version = ucl_object_toint (elt);
3906 	}
3907 
3908 	elt = ucl_object_lookup (webui_data, "enable");
3909 
3910 	if (elt && ucl_object_toboolean (elt)) {
3911 		cbd->is_enable = TRUE;
3912 	}
3913 
3914 	elt = ucl_object_lookup (webui_data, "need_task");
3915 
3916 	if (elt && !!ucl_object_toboolean (elt)) {
3917 		cbd->need_task = TRUE;
3918 	}
3919 
3920 	full_path = rspamd_fstring_new_init ("/plugins/", sizeof ("/plugins/") - 1);
3921 	/* Zero terminated */
3922 	rspamd_printf_fstring (&full_path, "%s/%s%c",
3923 			plugin_name, path, '\0');
3924 
3925 	rspamd_http_router_add_path (ctx->http,
3926 			full_path->str,
3927 			rspamd_controller_handle_lua_plugin);
3928 	rspamd_ftok_t *key_tok = rspamd_ftok_map (full_path);
3929 	/* Truncate stupid \0 symbol to enable lookup */
3930 	key_tok->len --;
3931 	g_hash_table_insert (ctx->plugins, key_tok, cbd);
3932 }
3933 
3934 static void
rspamd_controller_register_plugins_paths(struct rspamd_controller_worker_ctx * ctx)3935 rspamd_controller_register_plugins_paths (struct rspamd_controller_worker_ctx *ctx)
3936 {
3937 	lua_State *L = ctx->cfg->lua_state;
3938 	ucl_object_t *webui_data;
3939 	const ucl_object_t *handler_obj, *cur;
3940 	ucl_object_iter_t it = NULL;
3941 
3942 	lua_getglobal (L, "rspamd_plugins");
3943 
3944 	if (lua_istable (L, -1)) {
3945 
3946 		for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 2)) {
3947 			lua_pushvalue (L, -2); /* Store key */
3948 
3949 			lua_pushstring (L, "webui");
3950 			lua_gettable (L, -3); /* value is at -3 index */
3951 
3952 			if (lua_istable (L, -1)) {
3953 				webui_data = ucl_object_lua_import_escape (L, -1);
3954 
3955 				while ((cur = ucl_object_iterate (webui_data, &it, true)) != NULL) {
3956 					handler_obj = ucl_object_lookup (cur, "handler");
3957 
3958 					if (handler_obj && ucl_object_key (cur)) {
3959 						rspamd_controller_register_plugin_path (L, ctx,
3960 								cur, handler_obj, ucl_object_key (cur),
3961 								lua_tostring (L, -2));
3962 					}
3963 					else {
3964 						msg_err_ctx ("bad webui definition for plugin: %s",
3965 								lua_tostring (L, -2));
3966 					}
3967 				}
3968 
3969 				ucl_object_unref (webui_data);
3970 			}
3971 
3972 			lua_pop (L, 1); /* remove table value */
3973 		}
3974 	}
3975 
3976 	lua_pop (L, 1); /* rspamd_plugins global */
3977 }
3978 
3979 static void
rspamd_controller_health_rep(struct rspamd_worker * worker,struct rspamd_srv_reply * rep,gint rep_fd,gpointer ud)3980 rspamd_controller_health_rep (struct rspamd_worker *worker,
3981 				struct rspamd_srv_reply *rep, gint rep_fd,
3982 				gpointer ud)
3983 {
3984 	struct rspamd_controller_worker_ctx *ctx = (struct rspamd_controller_worker_ctx *)ud;
3985 
3986 	ctx->workers_count = rep->reply.health.workers_count;
3987 	ctx->scanners_count = rep->reply.health.scanners_count;
3988 	ctx->workers_hb_lost = rep->reply.health.workers_hb_lost;
3989 
3990 	ev_timer_again (ctx->event_loop, &ctx->health_check_timer);
3991 }
3992 
3993 static void
rspamd_controller_health_timer(EV_P_ ev_timer * w,int revents)3994 rspamd_controller_health_timer (EV_P_ ev_timer *w, int revents)
3995 {
3996 	struct rspamd_controller_worker_ctx *ctx = (struct rspamd_controller_worker_ctx *)w->data;
3997 	struct rspamd_srv_command srv_cmd;
3998 
3999 	memset (&srv_cmd, 0, sizeof (srv_cmd));
4000 	srv_cmd.type = RSPAMD_SRV_HEALTH;
4001 	rspamd_srv_send_command (ctx->worker, ctx->event_loop, &srv_cmd, -1,
4002 			rspamd_controller_health_rep, ctx);
4003 	ev_timer_stop (EV_A_ w);
4004 }
4005 
4006 /*
4007  * Start worker process
4008  */
4009 __attribute__((noreturn))
4010 void
start_controller_worker(struct rspamd_worker * worker)4011 start_controller_worker (struct rspamd_worker *worker)
4012 {
4013 	struct rspamd_controller_worker_ctx *ctx = worker->ctx;
4014 	struct module_ctx *mctx;
4015 	GHashTableIter iter;
4016 	gpointer key, value;
4017 	guint i;
4018 	gpointer m;
4019 
4020 	g_assert (rspamd_worker_check_context (worker->ctx, rspamd_controller_ctx_magic));
4021 	ctx->event_loop = rspamd_prepare_worker (worker,
4022 			"controller",
4023 			rspamd_controller_accept_socket);
4024 
4025 	ctx->start_time = ev_time ();
4026 	ctx->worker = worker;
4027 	ctx->cfg = worker->srv->cfg;
4028 	ctx->srv = worker->srv;
4029 	ctx->custom_commands = g_hash_table_new (rspamd_strcase_hash,
4030 			rspamd_strcase_equal);
4031 	ctx->plugins = g_hash_table_new_full (rspamd_ftok_icase_hash,
4032 			rspamd_ftok_icase_equal, rspamd_fstring_mapped_ftok_free,
4033 			rspamd_plugin_cbdata_dtor);
4034 
4035 	if (isnan (ctx->task_timeout)) {
4036 		if (isnan (ctx->cfg->task_timeout)) {
4037 			ctx->task_timeout = 0;
4038 		}
4039 		else {
4040 			ctx->task_timeout = ctx->cfg->task_timeout;
4041 		}
4042 	}
4043 
4044 	if (ctx->secure_ip != NULL) {
4045 		rspamd_config_radix_from_ucl (ctx->cfg, ctx->secure_ip,
4046 				"Allow unauthenticated requests from these addresses",
4047 				&ctx->secure_map,
4048 				NULL,
4049 				worker, "controller secure ip");
4050 	}
4051 
4052 	ctx->lang_det = ctx->cfg->lang_det;
4053 
4054 	rspamd_controller_password_sane (ctx, ctx->password, "normal password");
4055 	rspamd_controller_password_sane (ctx, ctx->enable_password, "enable "
4056 			"password");
4057 
4058 	/* Accept event */
4059 	ctx->http_ctx = rspamd_http_context_create (ctx->cfg, ctx->event_loop,
4060 			ctx->cfg->ups_ctx);
4061 	rspamd_mempool_add_destructor (ctx->cfg->cfg_pool,
4062 			(rspamd_mempool_destruct_t)rspamd_http_context_free,
4063 			ctx->http_ctx);
4064 	ctx->http = rspamd_http_router_new (rspamd_controller_error_handler,
4065 			rspamd_controller_finish_handler, ctx->timeout,
4066 			ctx->static_files_dir, ctx->http_ctx);
4067 
4068 	/* Add callbacks for different methods */
4069 	rspamd_http_router_add_path (ctx->http,
4070 			PATH_AUTH,
4071 			rspamd_controller_handle_auth);
4072 	rspamd_http_router_add_path (ctx->http,
4073 			PATH_SYMBOLS,
4074 			rspamd_controller_handle_symbols);
4075 	rspamd_http_router_add_path (ctx->http,
4076 			PATH_ACTIONS,
4077 			rspamd_controller_handle_actions);
4078 	rspamd_http_router_add_path (ctx->http,
4079 			PATH_MAPS,
4080 			rspamd_controller_handle_maps);
4081 	rspamd_http_router_add_path (ctx->http,
4082 			PATH_GET_MAP,
4083 			rspamd_controller_handle_get_map);
4084 	rspamd_http_router_add_path (ctx->http,
4085 			PATH_PIE_CHART,
4086 			rspamd_controller_handle_pie_chart);
4087 	rspamd_http_router_add_path (ctx->http,
4088 			PATH_GRAPH,
4089 			rspamd_controller_handle_graph);
4090 	rspamd_http_router_add_path (ctx->http,
4091 			PATH_HEALTHY,
4092 			rspamd_controller_handle_healthy);
4093 	rspamd_http_router_add_path (ctx->http,
4094 			PATH_READY,
4095 			rspamd_controller_handle_ready);
4096 	rspamd_http_router_add_path (ctx->http,
4097 			PATH_HISTORY,
4098 			rspamd_controller_handle_history);
4099 	rspamd_http_router_add_path (ctx->http,
4100 			PATH_HISTORY_RESET,
4101 			rspamd_controller_handle_history_reset);
4102 	rspamd_http_router_add_path (ctx->http,
4103 			PATH_LEARN_SPAM,
4104 			rspamd_controller_handle_learnspam);
4105 	rspamd_http_router_add_path (ctx->http,
4106 			PATH_LEARN_HAM,
4107 			rspamd_controller_handle_learnham);
4108 	rspamd_http_router_add_path (ctx->http,
4109 			PATH_METRICS,
4110 			rspamd_controller_handle_metrics);
4111 	rspamd_http_router_add_path (ctx->http,
4112 			PATH_SAVE_ACTIONS,
4113 			rspamd_controller_handle_saveactions);
4114 	rspamd_http_router_add_path (ctx->http,
4115 			PATH_SAVE_SYMBOLS,
4116 			rspamd_controller_handle_savesymbols);
4117 	rspamd_http_router_add_path (ctx->http,
4118 			PATH_SAVE_MAP,
4119 			rspamd_controller_handle_savemap);
4120 	rspamd_http_router_add_path (ctx->http,
4121 			PATH_SCAN,
4122 			rspamd_controller_handle_scan);
4123 	rspamd_http_router_add_path (ctx->http,
4124 			PATH_CHECK,
4125 			rspamd_controller_handle_scan);
4126 	rspamd_http_router_add_path (ctx->http,
4127 			PATH_CHECKV2,
4128 			rspamd_controller_handle_scan);
4129 	rspamd_http_router_add_path (ctx->http,
4130 			PATH_STAT,
4131 			rspamd_controller_handle_stat);
4132 	rspamd_http_router_add_path (ctx->http,
4133 			PATH_STAT_RESET,
4134 			rspamd_controller_handle_statreset);
4135 	rspamd_http_router_add_path (ctx->http,
4136 			PATH_COUNTERS,
4137 			rspamd_controller_handle_counters);
4138 	rspamd_http_router_add_path (ctx->http,
4139 			PATH_ERRORS,
4140 			rspamd_controller_handle_errors);
4141 	rspamd_http_router_add_path (ctx->http,
4142 			PATH_NEIGHBOURS,
4143 			rspamd_controller_handle_neighbours);
4144 	rspamd_http_router_add_path (ctx->http,
4145 			PATH_PLUGINS,
4146 			rspamd_controller_handle_plugins);
4147 	rspamd_http_router_add_path (ctx->http,
4148 			PATH_PING,
4149 			rspamd_controller_handle_ping);
4150 	rspamd_controller_register_plugins_paths (ctx);
4151 
4152 #if 0
4153 	rspamd_regexp_t *lua_re = rspamd_regexp_new ("^/.*/.*\\.lua$", NULL, NULL);
4154 	rspamd_http_router_add_regexp (ctx->http, lua_re,
4155 			rspamd_controller_handle_lua);
4156 	rspamd_regexp_unref (lua_re);
4157 #endif
4158 	luaopen_controller (ctx->cfg->lua_state);
4159 
4160 	if (ctx->key) {
4161 		rspamd_http_router_set_key (ctx->http, ctx->key);
4162 	}
4163 
4164 	PTR_ARRAY_FOREACH (ctx->cfg->c_modules, i, mctx) {
4165 		if (mctx->mod->module_attach_controller_func != NULL) {
4166 			mctx->mod->module_attach_controller_func (mctx,
4167 					ctx->custom_commands);
4168 		}
4169 	}
4170 
4171 	g_hash_table_iter_init (&iter, ctx->custom_commands);
4172 	while (g_hash_table_iter_next (&iter, &key, &value)) {
4173 		rspamd_http_router_add_path (ctx->http,
4174 			key,
4175 			rspamd_controller_handle_custom);
4176 	}
4177 
4178 	if (worker->srv->cfg->neighbours && worker->srv->cfg->neighbours->len > 0) {
4179 		rspamd_http_router_add_header (ctx->http,
4180 				"Access-Control-Allow-Origin", "*");
4181 	}
4182 
4183 	/* Disable all results caching, see #3330 */
4184 	rspamd_http_router_add_header (ctx->http,
4185 			"Cache-Control", "no-store");
4186 
4187 	rspamd_http_router_set_unknown_handler (ctx->http,
4188 			rspamd_controller_handle_unknown);
4189 
4190 	ctx->resolver = rspamd_dns_resolver_init (worker->srv->logger,
4191 			ctx->event_loop,
4192 			worker->srv->cfg);
4193 
4194 	rspamd_upstreams_library_config (worker->srv->cfg, worker->srv->cfg->ups_ctx,
4195 			ctx->event_loop, ctx->resolver->r);
4196 	rspamd_symcache_start_refresh (worker->srv->cfg->cache, ctx->event_loop,
4197 			worker);
4198 	rspamd_stat_init (worker->srv->cfg, ctx->event_loop);
4199 	rspamd_worker_init_controller (worker, &ctx->rrd);
4200 	rspamd_lua_run_postloads (ctx->cfg->lua_state, ctx->cfg, ctx->event_loop, worker);
4201 
4202 	/* TODO: maybe make it configurable */
4203 	ev_timer_init (&ctx->health_check_timer, rspamd_controller_health_timer,
4204 			1.0, 60.0);
4205 	ctx->health_check_timer.data = ctx;
4206 	ev_timer_start (ctx->event_loop, &ctx->health_check_timer);
4207 
4208 #ifdef WITH_HYPERSCAN
4209 	rspamd_control_worker_add_cmd_handler (worker,
4210 			RSPAMD_CONTROL_HYPERSCAN_LOADED,
4211 			rspamd_worker_hyperscan_ready,
4212 			NULL);
4213 #endif
4214 
4215 	/* Start event loop */
4216 	ev_loop (ctx->event_loop, 0);
4217 	rspamd_worker_block_signals ();
4218 	rspamd_controller_on_terminate (worker, ctx->rrd);
4219 
4220 	rspamd_stat_close ();
4221 	rspamd_http_router_free (ctx->http);
4222 
4223 	if (ctx->cached_password.len > 0) {
4224 		m = (gpointer)ctx->cached_password.begin;
4225 		munmap (m, ctx->cached_password.len);
4226 	}
4227 
4228 	if (ctx->cached_enable_password.len > 0) {
4229 		m = (gpointer) ctx->cached_enable_password.begin;
4230 		munmap (m, ctx->cached_enable_password.len);
4231 	}
4232 
4233 	g_hash_table_unref (ctx->plugins);
4234 	g_hash_table_unref (ctx->custom_commands);
4235 
4236 	REF_RELEASE (ctx->cfg);
4237 	rspamd_log_close (worker->srv->logger);
4238 
4239 	exit (EXIT_SUCCESS);
4240 }
4241