aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt38
-rw-r--r--config.h.in12
-rw-r--r--debian/postinst2
-rw-r--r--doc/markdown/workers/controller.md69
-rw-r--r--doc/markdown/workers/lua_worker.md3
-rw-r--r--doc/markdown/workers/normal.md29
-rw-r--r--rspamd.service2
-rw-r--r--rspamd.socket1
-rw-r--r--src/fuzzy_storage.c46
-rw-r--r--src/libserver/task.c2
-rw-r--r--src/libstat/backends/sqlite3_backend.c21
-rw-r--r--src/libstat/stat_process.c19
-rw-r--r--src/libutil/printf.c35
-rw-r--r--src/libutil/printf.h1
-rw-r--r--src/libutil/sqlite_utils.c2
-rw-r--r--src/rspamadm/fuzzy_stat.lua28
16 files changed, 252 insertions, 58 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index add82c152..263249f38 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,6 +14,11 @@ SET(RSPAMD_VERSION_MAJOR 1)
SET(RSPAMD_VERSION_MINOR 1)
SET(RSPAMD_VERSION_PATCH 0)
+IF(GIT_ID)
+ SET(GIT_VERSION 1)
+ SET(RSPAMD_ID "${GIT_ID}")
+ENDIF()
+
SET(RSPAMD_VERSION "${RSPAMD_VERSION_MAJOR}.${RSPAMD_VERSION_MINOR}.${RSPAMD_VERSION_PATCH}")
SET(RSPAMD_MASTER_SITE_URL "https://rspamd.com")
@@ -984,39 +989,6 @@ IF(NOT DESTDIR)
SET(DESTDIR $ENV{DESTDIR})
ENDIF(NOT DESTDIR)
-# Try to detect tip version from hg
-SET(ID "unknown")
-FIND_PROGRAM(HG "git")
-
-IF(HG)
- EXECUTE_PROCESS(COMMAND "${HG}" rev-parse --verify HEAD
- WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
- OUTPUT_VARIABLE HG_ID ERROR_QUIET)
- IF(HG_ID)
- STRING(SUBSTRING "${HG_ID}" 0 10 RSPAMD_ID)
- MESSAGE(STATUS "Git revision: ${RSPAMD_ID}")
- ELSE(HG_ID)
- FIND_FILE(HG_ARCH ".hg_archival.txt" "${CMAKE_CURRENT_SOURCE_DIR}")
- IF(HG_ARCH)
- FILE(READ ${HG_ARCH} HG_ID)
- IF(HG_ID)
- STRING(REGEX MATCH "node: [0-9a-z]+" _tmp "${HG_ID}")
- STRING(SUBSTRING "${_tmp}" 6 12 RSPAMD_ID)
- ENDIF(HG_ID)
- ENDIF(HG_ARCH)
- ENDIF(HG_ID)
-ELSE(HG)
-# Now check .hg_archival.txt
- FIND_FILE(HG_ARCH ".hg_archival.txt" "${CMAKE_CURRENT_SOURCE_DIR}")
- IF(HG_ARCH)
- FILE(READ ${HG_ARCH} HG_ID)
- IF(HG_ID)
- STRING(REGEX MATCH "node: [0-9a-z]+" _tmp "${HG_ID}")
- STRING(SUBSTRING "${_tmp}" 6 12 RSPAMD_ID)
- ENDIF(HG_ID)
- ENDIF(HG_ARCH)
-ENDIF(HG)
-
################################ SUBDIRS SECTION ###########################
diff --git a/config.h.in b/config.h.in
index 908278013..daa84f723 100644
--- a/config.h.in
+++ b/config.h.in
@@ -4,6 +4,7 @@
#cmakedefine BUILD_STATIC 1
#cmakedefine CURL_FOUND 1
#cmakedefine DEBUG_MODE 1
+#cmakedefine GIT_VERSION 1
#cmakedefine GLIB_COMPAT 1
#cmakedefine GLIB_HASH_COMPAT 1
#cmakedefine GLIB_RE_COMPAT 1
@@ -179,8 +180,15 @@
#define RSPAMD_WWWDIR "${WWWDIR}"
#define RSPAMD_PREFIX "${CMAKE_INSTALL_PREFIX}"
-#define RVERSION "${RSPAMD_VERSION}"
-#define RID "${RSPAMD_ID}"
+
+#ifdef GIT_VERSION
+# define RVERSION "${RSPAMD_VERSION}"
+# define RID "${RSPAMD_ID}"
+#else
+# define RVERSION "${RSPAMD_VERSION}"
+# define RID "release"
+#endif
+
#define RSPAMD_MASTER_SITE_URL "${RSPAMD_MASTER_SITE_URL}"
#define MODULES_NUM ${RSPAMD_MODULES_NUM}
diff --git a/debian/postinst b/debian/postinst
index 8d96fcb18..374209ce7 100644
--- a/debian/postinst
+++ b/debian/postinst
@@ -16,7 +16,7 @@ case "$1" in
--gecos "rspamd spam filtering system" \
--force-badname \
$SERVER_USER
-
+ mkdir -p $SERVER_HOME $SERVER_LOG || true
chown $SERVER_USER: $SERVER_HOME $SERVER_LOG
;;
diff --git a/doc/markdown/workers/controller.md b/doc/markdown/workers/controller.md
index e69de29bb..94c12310a 100644
--- a/doc/markdown/workers/controller.md
+++ b/doc/markdown/workers/controller.md
@@ -0,0 +1,69 @@
+# Controller worker
+
+Controller worker is used to manage rspamd stats, to learn rspamd and to serve WebUI.
+
+Internally, the controller worker is just a web server that accepts requests and sends replies using JSON serialization.
+Each command is defined by URL. Some commands are read only and are considered as `unprivileged` whilst other commands, such as
+maps modification, config modifications and learning requires higher level of privileges: `enable` level. The differece between levels is specified
+by password. If only one password is specified in the configuration, it is used for both type of commands.
+
+## Controller configuration
+
+Rspamd controller worker supports the following options:
+
+* `password`: password for read-only commands
+* `enable_password`: password for write commands
+* `secure_ip`: list or map with IP addresses that are treated as `secure` so **all** commands are allowed from these IPs **without** passwords
+* `static_dir`: directory where interface static files are placed (usually `${WWWDIR}`)
+* `stats_path`: path where controller save persistent stats about rspamd (such as scanned messages count)
+
+## Encryption support
+
+To generate a keypair for the scanner you could use:
+
+ rspamadm keypair -u
+
+After that keypair should appear as following:
+
+~~~nginx
+keypair {
+ pubkey = "tm8zjw3ougwj1qjpyweugqhuyg4576ctg6p7mbrhma6ytjewp4ry";
+ privkey = "ykkrfqbyk34i1ewdmn81ttcco1eaxoqgih38duib1e7b89h9xn3y";
+}
+~~~
+
+You can use its **public** part thereafter when scanning messages as following:
+
+ rspamc --key tm8zjw3ougwj1qjpyweugqhuyg4576ctg6p7mbrhma6ytjewp4ry <file>
+
+## Passwords encryption
+
+Rspamd now suggests to encrypt passwords when storing them in a configuration. Currently, it uses `PBKDF2-Blake2` function to derive key from a password. To encrypt key, you can use `rspamadm pw` command as following:
+
+ rspamadm pw
+ Enter passphrase: <hidden input>
+ $1$cybjp37q4w63iogc4erncz1tgm1ce9i5$kxfx9xc1wk9uuakw7nittbt6dgf3qyqa394cnradg191iqgxr8kb
+
+You can use that line as `password` and `enable_password` values.
+
+## Supported commands
+
+* `/auth`
+* `/symbols`
+* `/actions`
+* `/maps`
+* `/getmap`
+* `/graph`
+* `/pie`
+* `/history`
+* `/historyreset` (priv)
+* `/learnspam` (priv)
+* `/learnham` (priv)
+* `/saveactions` (priv)
+* `/savesymbols` (priv)
+* `/savemap` (priv)
+* `/scan`
+* `/check`
+* `/stat`
+* `/statreset` (priv)
+* `/counters`
diff --git a/doc/markdown/workers/lua_worker.md b/doc/markdown/workers/lua_worker.md
index e69de29bb..cad1ad998 100644
--- a/doc/markdown/workers/lua_worker.md
+++ b/doc/markdown/workers/lua_worker.md
@@ -0,0 +1,3 @@
+# Lua worker
+
+TODO
diff --git a/doc/markdown/workers/normal.md b/doc/markdown/workers/normal.md
index e69de29bb..64cf4c479 100644
--- a/doc/markdown/workers/normal.md
+++ b/doc/markdown/workers/normal.md
@@ -0,0 +1,29 @@
+# Rspamd normal worker
+
+Rspamd normal worker is intended to scan messages for spam. It has the following configuration options available:
+
+* `mime`: turn to `off` if you want to scan non-mime messages (e.g. forum comments or SMS), default: `on`
+* `allow_learn`: turn to `on` if you want to learn messages using this worker (usually you should use [controller](controller.md) worker), default: `off`
+* `timeout`: input/output timeout, default: `1min`
+* `task_timeout`: maximum time to process a single task, default: `8s`
+* `max_tasks`: maximum count of tasks processes simultaneously, default: `0` - no limit
+* `keypair`: encryption keypair
+
+## Encryption support
+
+To generate a keypair for the scanner you could use:
+
+ rspamadm keypair -u
+
+After that keypair should appear as following:
+
+~~~nginx
+keypair {
+ pubkey = "tm8zjw3ougwj1qjpyweugqhuyg4576ctg6p7mbrhma6ytjewp4ry";
+ privkey = "ykkrfqbyk34i1ewdmn81ttcco1eaxoqgih38duib1e7b89h9xn3y";
+}
+~~~
+
+You can use its **public** part thereafter when scanning messages as following:
+
+ rspamc --key tm8zjw3ougwj1qjpyweugqhuyg4576ctg6p7mbrhma6ytjewp4ry <file>
diff --git a/rspamd.service b/rspamd.service
index cc295f67c..c6ad4094c 100644
--- a/rspamd.service
+++ b/rspamd.service
@@ -5,6 +5,8 @@ After=nss-lookup.target
[Service]
ExecStart=/usr/bin/rspamd -c /etc/rspamd/rspamd.conf -f
User=_rspamd
+RuntimeDirectory=rspamd
+RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
diff --git a/rspamd.socket b/rspamd.socket
index f3b3aa0a8..e28becce0 100644
--- a/rspamd.socket
+++ b/rspamd.socket
@@ -4,7 +4,6 @@ Description=rapid spam filtering system
[Socket]
ListenStream=11333
ListenStream=[::1]:11334
-
BindIPv6Only=both
[Install]
diff --git a/src/fuzzy_storage.c b/src/fuzzy_storage.c
index 70cb717a9..c70f5a148 100644
--- a/src/fuzzy_storage.c
+++ b/src/fuzzy_storage.c
@@ -112,6 +112,7 @@ struct rspamd_fuzzy_storage_ctx {
GHashTable *keys;
gboolean encrypted_only;
struct rspamd_keypair_cache *keypair_cache;
+ rspamd_lru_hash_t *errors_ips;
struct rspamd_fuzzy_backend *backend;
GQueue *updates_pending;
};
@@ -677,6 +678,7 @@ accept_fuzzy_socket (gint fd, short what, void *arg)
rspamd_inet_addr_t *addr;
gssize r;
guint8 buf[512];
+ guint64 *nerrors;
/* Got some data */
if (what == EV_READ) {
@@ -721,6 +723,20 @@ accept_fuzzy_socket (gint fd, short what, void *arg)
/* Discard input */
session->ctx->stat.invalid_requests ++;
msg_debug ("invalid fuzzy command of size %z received", r);
+
+ nerrors = rspamd_lru_hash_lookup (session->ctx->errors_ips,
+ addr, -1);
+
+ if (nerrors == NULL) {
+ nerrors = g_malloc (sizeof (*nerrors));
+ *nerrors = 1;
+ rspamd_lru_hash_insert (session->ctx->errors_ips,
+ rspamd_inet_address_copy (addr),
+ nerrors, -1, -1);
+ }
+ else {
+ *nerrors = *nerrors + 1;
+ }
}
REF_RELEASE (session);
@@ -845,7 +861,7 @@ rspamd_fuzzy_stat_to_ucl (struct rspamd_fuzzy_storage_ctx *ctx, gboolean ip_stat
key_stat = key->stat;
if (key_stat) {
- rspamd_snprintf (keyname, sizeof (keyname), "%8xs", k);
+ rspamd_snprintf (keyname, sizeof (keyname), "%8bs", k);
elt = rspamd_fuzzy_storage_stat_key (key_stat);
@@ -890,6 +906,29 @@ rspamd_fuzzy_stat_to_ucl (struct rspamd_fuzzy_storage_ctx *ctx, gboolean ip_stat
0,
false);
+ if (ctx->errors_ips && ip_stat) {
+ ip_hash = rspamd_lru_hash_get_htable (ctx->errors_ips);
+
+ if (ip_hash) {
+ g_hash_table_iter_init (&ip_it, ip_hash);
+ ip_elt = ucl_object_typed_new (UCL_OBJECT);
+
+ while (g_hash_table_iter_next (&ip_it, &k, &v)) {
+ lru_elt = v;
+
+ ucl_object_insert_key (ip_elt,
+ ucl_object_fromint (*(guint64 *)lru_elt->data),
+ rspamd_inet_address_to_string (k), 0, true);
+ }
+
+ ucl_object_insert_key (obj,
+ ip_elt,
+ "errors_ips",
+ 0,
+ false);
+ }
+ }
+
/* Checked by epoch */
elt = ucl_object_typed_new (UCL_ARRAY);
@@ -1093,6 +1132,9 @@ init_fuzzy (struct rspamd_config *cfg)
ctx->keypair_cache_size = DEFAULT_KEYPAIR_CACHE_SIZE;
ctx->keys = g_hash_table_new_full (fuzzy_kp_hash, fuzzy_kp_equal,
NULL, fuzzy_key_dtor);
+ ctx->errors_ips = rspamd_lru_hash_new_full (0, 1024,
+ (GDestroyNotify) rspamd_inet_address_destroy, g_free,
+ rspamd_inet_address_hash, rspamd_inet_address_equal);
rspamd_rcl_register_worker_option (cfg, type, "hashfile",
rspamd_rcl_parse_struct_string, ctx,
@@ -1302,6 +1344,8 @@ start_fuzzy (struct rspamd_worker *worker)
rspamd_keypair_cache_destroy (ctx->keypair_cache);
}
+ rspamd_lru_hash_destroy (ctx->errors_ips);
+
g_hash_table_unref (ctx->keys);
exit (EXIT_SUCCESS);
diff --git a/src/libserver/task.c b/src/libserver/task.c
index 31370a6eb..758b90807 100644
--- a/src/libserver/task.c
+++ b/src/libserver/task.c
@@ -1029,7 +1029,7 @@ rspamd_task_write_log (struct rspamd_task *task)
}
}
- msg_info ("%V", logbuf);
+ msg_info_task ("%V", logbuf);
rspamd_fstring_free (logbuf);
}
diff --git a/src/libstat/backends/sqlite3_backend.c b/src/libstat/backends/sqlite3_backend.c
index 47318d766..c980ddf54 100644
--- a/src/libstat/backends/sqlite3_backend.c
+++ b/src/libstat/backends/sqlite3_backend.c
@@ -122,7 +122,8 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.args = "",
.stmt = NULL,
.result = SQLITE_DONE,
- .ret = ""
+ .flags = 0,
+ .ret = "",
},
[RSPAMD_STAT_BACKEND_TRANSACTION_START_DEF] = {
.idx = RSPAMD_STAT_BACKEND_TRANSACTION_START_DEF,
@@ -130,6 +131,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.args = "",
.stmt = NULL,
.result = SQLITE_DONE,
+ .flags = 0,
.ret = ""
},
[RSPAMD_STAT_BACKEND_TRANSACTION_START_EXCL] = {
@@ -138,6 +140,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.args = "",
.stmt = NULL,
.result = SQLITE_DONE,
+ .flags = 0,
.ret = ""
},
[RSPAMD_STAT_BACKEND_TRANSACTION_COMMIT] = {
@@ -146,6 +149,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.args = "",
.stmt = NULL,
.result = SQLITE_DONE,
+ .flags = 0,
.ret = ""
},
[RSPAMD_STAT_BACKEND_TRANSACTION_ROLLBACK] = {
@@ -154,6 +158,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.args = "",
.stmt = NULL,
.result = SQLITE_DONE,
+ .flags = 0,
.ret = ""
},
[RSPAMD_STAT_BACKEND_GET_TOKEN] = {
@@ -166,6 +171,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.stmt = NULL,
.args = "III",
.result = SQLITE_ROW,
+ .flags = 0,
.ret = "I"
},
[RSPAMD_STAT_BACKEND_SET_TOKEN] = {
@@ -175,6 +181,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.stmt = NULL,
.args = "IIII",
.result = SQLITE_DONE,
+ .flags = 0,
.ret = ""
},
[RSPAMD_STAT_BACKEND_INC_LEARNS] = {
@@ -184,6 +191,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.stmt = NULL,
.args = "II",
.result = SQLITE_DONE,
+ .flags = 0,
.ret = ""
},
[RSPAMD_STAT_BACKEND_DEC_LEARNS] = {
@@ -193,6 +201,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.stmt = NULL,
.args = "II",
.result = SQLITE_DONE,
+ .flags = 0,
.ret = ""
},
[RSPAMD_STAT_BACKEND_GET_LEARNS] = {
@@ -201,6 +210,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.stmt = NULL,
.args = "",
.result = SQLITE_ROW,
+ .flags = 0,
.ret = "I"
},
[RSPAMD_STAT_BACKEND_GET_LANGUAGE] = {
@@ -209,6 +219,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.stmt = NULL,
.args = "T",
.result = SQLITE_ROW,
+ .flags = 0,
.ret = "I"
},
[RSPAMD_STAT_BACKEND_GET_USER] = {
@@ -217,6 +228,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.stmt = NULL,
.args = "T",
.result = SQLITE_ROW,
+ .flags = 0,
.ret = "I"
},
[RSPAMD_STAT_BACKEND_INSERT_USER] = {
@@ -225,6 +237,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.stmt = NULL,
.args = "T",
.result = SQLITE_DONE,
+ .flags = 0,
.ret = "L"
},
[RSPAMD_STAT_BACKEND_INSERT_LANGUAGE] = {
@@ -233,6 +246,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.stmt = NULL,
.args = "T",
.result = SQLITE_DONE,
+ .flags = 0,
.ret = "L"
},
[RSPAMD_STAT_BACKEND_SAVE_TOKENIZER] = {
@@ -241,6 +255,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.stmt = NULL,
.args = "B",
.result = SQLITE_DONE,
+ .flags = 0,
.ret = ""
},
[RSPAMD_STAT_BACKEND_LOAD_TOKENIZER] = {
@@ -249,6 +264,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.stmt = NULL,
.args = "",
.result = SQLITE_ROW,
+ .flags = 0,
.ret = "B"
},
[RSPAMD_STAT_BACKEND_NTOKENS] = {
@@ -257,6 +273,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.stmt = NULL,
.args = "",
.result = SQLITE_ROW,
+ .flags = 0,
.ret = "I"
},
[RSPAMD_STAT_BACKEND_NLANGUAGES] = {
@@ -265,6 +282,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.stmt = NULL,
.args = "",
.result = SQLITE_ROW,
+ .flags = 0,
.ret = "I"
},
[RSPAMD_STAT_BACKEND_NUSERS] = {
@@ -273,6 +291,7 @@ static struct rspamd_sqlite3_prstmt prepared_stmts[RSPAMD_STAT_BACKEND_MAX] =
.stmt = NULL,
.args = "",
.result = SQLITE_ROW,
+ .flags = 0,
.ret = "I"
}
};
diff --git a/src/libstat/stat_process.c b/src/libstat/stat_process.c
index 419827aeb..57482974d 100644
--- a/src/libstat/stat_process.c
+++ b/src/libstat/stat_process.c
@@ -301,13 +301,8 @@ preprocess_init_stat_token (gpointer k, gpointer v, gpointer d)
if (cl_runtime->clcf->min_tokens > 0 &&
(guint32)g_tree_nnodes (cbdata->tok->tokens) < cl_runtime->clcf->min_tokens) {
/* Skip this classifier */
- msg_debug_task ("<%s> contains less tokens than required for %s classifier: "
- "%ud < %ud", cbdata->task->message_id, cl_runtime->clcf->name,
- g_tree_nnodes (cbdata->tok->tokens),
- cl_runtime->clcf->min_tokens);
cur = g_list_next (cur);
cl_runtime->skipped = TRUE;
-
continue;
}
@@ -750,6 +745,16 @@ rspamd_stat_learn (struct rspamd_task *task,
curst = g_list_next (curst);
}
+ if (cl_run->skipped) {
+ msg_info_task (
+ "<%s> contains less tokens than required for %s classifier: "
+ "%ud < %ud",
+ task->message_id,
+ cl_run->clcf->name,
+ g_tree_nnodes (cl_run->tok->tokens),
+ cl_run->clcf->min_tokens);
+ }
+
if (cl_run->cl && !cl_run->skipped) {
cl_ctx = cl_run->cl->init_func (task->task_pool, cl_run->clcf);
@@ -807,8 +812,8 @@ rspamd_stat_learn (struct rspamd_task *task,
}
if (!learned) {
- g_set_error (err, rspamd_stat_quark (), 500, "message cannot be learned"
- " for any classifier defined");
+ g_set_error (err, rspamd_stat_quark (), 500, "message cannot be learned as "
+ "it has too few tokens for any classifier defined");
}
else {
g_atomic_int_inc (&task->worker->srv->stat->messages_learned);
diff --git a/src/libutil/printf.c b/src/libutil/printf.c
index 11c464be8..047532dd2 100644
--- a/src/libutil/printf.c
+++ b/src/libutil/printf.c
@@ -25,6 +25,7 @@
#include "printf.h"
#include "fstring.h"
+#include "str_util.h"
#include <math.h>
/**
@@ -366,7 +367,7 @@ rspamd_vprintf_common (rspamd_printf_append_func func,
glong written = 0, wr, slen;
gint64 i64;
guint64 ui64;
- guint width, sign, hex, humanize, bytes, frac_width, i;
+ guint width, sign, hex, humanize, bytes, frac_width, i, b32;
rspamd_fstring_t *v;
rspamd_ftok_t *tok;
GString *gs;
@@ -398,6 +399,7 @@ rspamd_vprintf_common (rspamd_printf_append_func func,
width = 0;
sign = 1;
hex = 0;
+ b32 = 0;
bytes = 0;
humanize = 0;
frac_width = 0;
@@ -431,6 +433,11 @@ rspamd_vprintf_common (rspamd_printf_append_func func,
sign = 0;
fmt++;
continue;
+ case 'b':
+ b32 = 1;
+ sign = 0;
+ fmt++;
+ continue;
case 'H':
humanize = 1;
bytes = 1;
@@ -566,23 +573,35 @@ rspamd_vprintf_common (rspamd_printf_append_func func,
slen = MIN (slen, width);
}
- if (G_LIKELY(hex == 0)) {
- RSPAMD_PRINTF_APPEND (p, slen);
+ if (G_UNLIKELY (b32)) {
+ gchar *b32buf;
+
+ b32buf = rspamd_encode_base32 (p, slen);
+
+ if (b32buf) {
+ RSPAMD_PRINTF_APPEND (b32buf, strlen (b32buf));
+ g_free (b32buf);
+ }
}
- else {
+ else if (G_UNLIKELY (hex)) {
gchar hexbuf[2];
+
while (slen) {
hexbuf[0] = hex == 2 ? _HEX[*p & 0xf] : _hex[*p & 0xf];
hexbuf[1] = hex == 2 ? _HEX[(*p >> 8) & 0xf] :
- _hex[(*p >> 8) & 0xf];
+ _hex[(*p >> 8) & 0xf];
RSPAMD_PRINTF_APPEND_BUF (hexbuf, 2);
- p ++;
- slen --;
+ p++;
+ slen--;
}
- fmt ++;
+ fmt++;
buf_start = fmt;
}
+ else {
+ RSPAMD_PRINTF_APPEND (p, slen);
+ }
+
continue;
diff --git a/src/libutil/printf.h b/src/libutil/printf.h
index ea73cf0a0..f2c46ffc9 100644
--- a/src/libutil/printf.h
+++ b/src/libutil/printf.h
@@ -50,6 +50,7 @@
* %v GString *
* %s null-terminated string
* %xs hex encoded string
+ * %bs base32 encoded string
* %*s length and string
* %Z '\0'
* %N '\n'
diff --git a/src/libutil/sqlite_utils.c b/src/libutil/sqlite_utils.c
index fd7287c7f..e1e6f7f76 100644
--- a/src/libutil/sqlite_utils.c
+++ b/src/libutil/sqlite_utils.c
@@ -172,6 +172,7 @@ rspamd_sqlite3_run_prstmt (rspamd_mempool_t *pool, sqlite3 *db, GArray *stmts,
}
if (!(nst->flags & RSPAMD_SQLITE3_STMT_MULTIPLE)) {
+ sqlite3_clear_bindings (stmt);
sqlite3_reset (stmt);
}
@@ -183,6 +184,7 @@ rspamd_sqlite3_run_prstmt (rspamd_mempool_t *pool, sqlite3 *db, GArray *stmts,
}
if (!(nst->flags & RSPAMD_SQLITE3_STMT_MULTIPLE)) {
+ sqlite3_clear_bindings (stmt);
sqlite3_reset (stmt);
}
diff --git a/src/rspamadm/fuzzy_stat.lua b/src/rspamadm/fuzzy_stat.lua
index 3b56f9c1b..864e149a0 100644
--- a/src/rspamadm/fuzzy_stat.lua
+++ b/src/rspamadm/fuzzy_stat.lua
@@ -166,8 +166,20 @@ return function(args, res)
-- General stats
for k,v in pairs(pr['data']) do
- if k ~= 'keys' then
+ if k ~= 'keys' and k ~= 'errors_ips' then
res_db[k] = add_result(res_db[k], v, k)
+ elseif k == 'errors_ips' then
+ -- Errors ips
+ if not res_db['errors_ips'] then
+ res_db['errors_ips'] = {}
+ end
+ for ip,nerrors in pairs(v) do
+ if not res_db['errors_ips'][ip] then
+ res_db['errors_ips'][ip] = nerrors
+ else
+ res_db['errors_ips'][ip] = nerrors + res_db['errors_ips'][ip]
+ end
+ end
end
end
@@ -206,7 +218,7 @@ return function(args, res)
print(string.format('Statistics for storage %s', db))
for k,v in pairs(st) do
- if k ~= 'keys' then
+ if k ~= 'keys' and k ~= 'errors_ips' then
print(string.format('%s: %s', k, print_result(v)))
end
end
@@ -234,7 +246,17 @@ return function(args, res)
print('')
end
end
-
+ if st['errors_ips'] and not opts['no-ips'] and not opts['short'] then
+ print('')
+ print('Errors IPs statistics:')
+ table.sort(st['errors_ips'], function(a, b)
+ return a > b
+ end)
+ for i, v in pairs(st['errors_ips']) do
+ print(string.format('%s: %s', i, print_result(v)))
+ end
+ print('')
+ end
end
if not opts['no-ips'] and not opts['short'] then