From 295619fdc102e5528a5370a0dcd58bef9c7dc371 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Tue, 1 Feb 2011 22:01:19 +0300 Subject: [PATCH] * Implement controller interface inside librspamdclient/rspamc * REMOVE perl client (at last) Fixed some stupid moments in a controller's protocol Change version to 0.3.5 --- CMakeLists.txt | 27 +-- lib/librspamdclient.c | 456 +++++++++++++++++++++++++++++++++++++- lib/librspamdclient.h | 26 ++- src/client/rspamc.c | 259 +++++++++++++++++++++- src/controller.c | 17 +- src/plugins/fuzzy_check.c | 20 +- 6 files changed, 750 insertions(+), 55 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c47ccec36..3d01c636a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ PROJECT(rspamd C) SET(RSPAMD_VERSION_MAJOR 0) SET(RSPAMD_VERSION_MINOR 3) -SET(RSPAMD_VERSION_PATCH 4) +SET(RSPAMD_VERSION_PATCH 5) SET(RSPAMD_VERSION "${RSPAMD_VERSION_MAJOR}.${RSPAMD_VERSION_MINOR}.${RSPAMD_VERSION_PATCH}") @@ -19,6 +19,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.6.3 FATAL_ERROR) OPTION(DEBUG_MODE "Enable debug output [default: ON]" ON) OPTION(ENABLE_OPTIMIZATION "Enable optimization [default: OFF]" OFF) +OPTION(ENABLE_PERL "Enable perl client API [default: OFF]" OFF) OPTION(SKIP_RELINK_RPATH "Skip relinking and full RPATH for the install tree" OFF) OPTION(ENABLE_REDIRECTOR "Enable redirector install [default: OFF]" OFF) OPTION(ENABLE_PROFILING "Enable profiling [default: OFF]" OFF) @@ -579,9 +580,6 @@ SET(RSPAMDSRC src/modules.c src/view.c src/worker.c) -IF(ENABLE_PERL MATCHES "ON") - LIST(APPEND RSPAMDSRC src/perl.c) -ENDIF(ENABLE_PERL MATCHES "ON") ADD_SUBDIRECTORY(src/lua) ADD_SUBDIRECTORY(lib) ADD_SUBDIRECTORY(src/client) @@ -652,7 +650,7 @@ ADD_CUSTOM_COMMAND(OUTPUT src/modules.c COMMAND ../utils/gen-modules.sh ${PLUGINSSRC} WORKING_DIRECTORY src) -IF(PERL_EXECUTABLE) +IF(ENABLE_PERL MATCHES "ON") ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_SOURCE_DIR}/perl/Makefile DEPENDS ${CMAKE_SOURCE_DIR}/perl/Makefile.PL COMMAND ${PERL_EXECUTABLE} ./Makefile.PL DESTDIR=${DESTDIR} PREFIX=${PREFIX} INSTALLMAN3DIR=${MAN_PREFIX}/man3 @@ -664,11 +662,10 @@ IF(PERL_EXECUTABLE) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/perl VERBATIM) -ENDIF(PERL_EXECUTABLE) +ENDIF(ENABLE_PERL MATCHES "ON") CONFIGURE_FILE(config.h.in src/config.h) CONFIGURE_FILE(contrib/exim/local_scan.c.in contrib/exim/local_scan_rspamd.c @ONLY) -CONFIGURE_FILE(rspamc.pl.in rspamc.pl @ONLY) CONFIGURE_FILE(rspamd.xml.sample conf/rspamd.xml.sample @ONLY) ######################### LINK SECTION ############################### @@ -687,9 +684,9 @@ IF(ENABLE_PERL MATCHES "ON") ENDIF(PERL_DYNALOADER) ENDIF(ENABLE_PERL MATCHES "ON") -IF(PERL_EXECUTABLE) +IF(ENABLE_PERL MATCHES "ON") ADD_DEPENDENCIES(rspamd perlmodule) -ENDIF(PERL_EXECUTABLE) +ENDIF(ENABLE_PERL MATCHES "ON") TARGET_LINK_LIBRARIES(rspamd rspamd_lua) TARGET_LINK_LIBRARIES(rspamd "${LUA_LIBRARY}") @@ -763,9 +760,7 @@ ENDIF(ENABLE_STATIC MATCHES "ON") # Binaries INSTALL(PROGRAMS rspamd-${RSPAMD_VERSION} DESTINATION bin RENAME rspamd) -IF(PERL_EXECUTABLE) - INSTALL(PROGRAMS rspamc.pl DESTINATION bin RENAME rspamc) -ENDIF(PERL_EXECUTABLE) +INSTALL(PROGRAMS src/client/rspamc DESTINATION bin) # Configs @@ -812,14 +807,14 @@ FOREACH(LUA_CONF ${LUA_CONFIGS}) ENDFOREACH(LUA_CONF) # Perl lib -IF(PERL_EXECUTABLE) +IF(ENABLE_PERL MATCHES "ON") INSTALL(CODE "EXECUTE_PROCESS(COMMAND make install WORKING_DIRECTORY perl)") -ENDIF(PERL_EXECUTABLE) +ENDIF(ENABLE_PERL MATCHES "ON") # Redirector -IF(ENABLE_REDIRECTOR MATCHES "ON" AND PERL_EXECUTABLE) +IF(ENABLE_REDIRECTOR MATCHES "ON" AND ENABLE_PERL MATCHES "ON") INSTALL(PROGRAMS utils/redirector.pl DESTINATION bin RENAME rspamd-redirector) -ENDIF(ENABLE_REDIRECTOR MATCHES "ON" AND PERL_EXECUTABLE) +ENDIF(ENABLE_REDIRECTOR MATCHES "ON" AND ENABLE_PERL MATCHES "ON") # Start scripts IF(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" AND NOT BUILD_PORT) diff --git a/lib/librspamdclient.c b/lib/librspamdclient.c index d7568eb62..0b32e853d 100644 --- a/lib/librspamdclient.c +++ b/lib/librspamdclient.c @@ -761,10 +761,10 @@ rspamd_sendfile (gint sock, gint fd, GError **err) off_t off = 0; # if defined(FREEBSD) /* FreeBSD version */ - if (sendfile (sock, fd, 0, 0, NULL, &off, 0) != 0) { + if (sendfile (fd, sock, 0, 0, NULL, &off, 0) != 0) { # elif defined(DARWIN) /* Darwin version */ - if (sendfile (sock, fd, 0, &off, NULL, 0) != 0) { + if (sendfile (fd, sock, 0, &off, NULL, 0) != 0) { # endif goto err; } @@ -775,7 +775,7 @@ rspamd_sendfile (gint sock, gint fd, GError **err) fstat (fd, &st); /* Linux version */ - r = sendfile (fd, sock, &off, st.st_size); + r = sendfile (sock, fd, &off, st.st_size); if (r == -1) { goto err; } @@ -845,6 +845,147 @@ rspamd_free_connection (struct rspamd_connection *c) g_free (c); } +/* + * Send a single command to controller and get reply + */ +static GString * +rspamd_send_controller_command (struct rspamd_connection *c, const gchar *line, gsize len, gint fd, GError **err) +{ + GString *res = NULL; + static gchar tmpbuf[BUFSIZ]; + gint r = 0; + static const gchar end_marker[] = "END\r\n"; + + /* Set blocking for writing */ + make_socket_blocking (c->socket); + if (write (c->socket, line, len) == -1) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Write error: %s", + strerror (errno)); + } + return NULL; + } + if (fd != -1) { + if (!rspamd_sendfile (c->socket, fd, err)) { + return NULL; + } + } + /* Now set non-blocking mode and read buffer till END\r\n marker */ + make_socket_nonblocking (c->socket); + /* Poll socket */ + do { + if (poll_sync_socket (c->socket, client->read_timeout, POLL_IN) == -1) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Cannot read reply from controller %s: %s", + c->server->name, strerror (errno)); + } + upstream_fail (&c->server->up, c->connection_time); + return NULL; + } + if ((r = read (c->socket, tmpbuf, sizeof (tmpbuf))) > 0) { + /* Check the end of the buffer for END marker */ + if (r >= sizeof (end_marker) - 1 && + memcmp (tmpbuf + r - sizeof (end_marker) + 1, end_marker, sizeof (end_marker) - 1) == 0) { + r -= sizeof (end_marker) - 1; + /* Copy the rest to the result string */ + if (res == NULL) { + res = g_string_new_len (tmpbuf, r); + return res; + } + else { + /* Append data to string */ + if (r > 0) { + res = g_string_append_len (res, tmpbuf, r); + } + return res; + } + } + else { + /* Store data inside res */ + if (res == NULL) { + res = g_string_new_len (tmpbuf, r); + } + else { + /* Append data to string */ + res = g_string_append_len (res, tmpbuf, r); + } + } + } + } while (r > 0); + + /* Incomplete reply, so store error */ + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Cannot read reply from controller %s: %s", + c->server->name, strerror (errno)); + } + upstream_fail (&c->server->up, c->connection_time); + return NULL; +} + +/* + * Authenticate on the controller + */ +static gboolean +rspamd_controller_auth (struct rspamd_connection *c, const gchar *password, GError **err) +{ + gchar outbuf[BUFSIZ]; + static const gchar success_str[] = "password accepted"; + gint r; + GString *in; + + r = rspamd_snprintf (outbuf, sizeof (outbuf), "password %s\r\n", password); + in = rspamd_send_controller_command (c, outbuf, r, -1, err); + + if (in == NULL) { + return FALSE; + } + + if (in->len >= sizeof (success_str) - 1 && + memcmp (in->str, success_str, sizeof (success_str) - 1) == 0) { + g_string_free (in, TRUE); + return TRUE; + } + + g_string_free (in, TRUE); + return FALSE; +} + +/* + * Read greeting from the controller + */ +static gboolean +rspamd_read_controller_greeting (struct rspamd_connection *c, GError **err) +{ + gchar inbuf[BUFSIZ]; + gint r; + static const gchar greeting_str[] = "Rspamd"; + + if (poll_sync_socket (c->socket, client->read_timeout, POLL_IN) == -1) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Cannot read reply from controller %s: %s", + c->server->name, strerror (errno)); + } + upstream_fail (&c->server->up, c->connection_time); + return FALSE; + } + if ((r = read (c->socket, inbuf, sizeof (inbuf))) > 0) { + if (r >= sizeof (greeting_str) - 1 && + memcmp (inbuf, greeting_str, sizeof (greeting_str) - 1) == 0) { + return TRUE; + } + } + else { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Cannot read reply from controller %s: %s", + c->server->name, strerror (errno)); + } + upstream_fail (&c->server->up, c->connection_time); + return FALSE; + } + + return FALSE; +} + /** Public API **/ /* @@ -959,6 +1100,7 @@ rspamd_scan_memory (const guchar *message, gsize length, GHashTable *headers, GE /* Read result cycle */ while (read_rspamd_reply_line (c, err)); + upstream_ok (&c->server->up, c->connection_time); return res; } @@ -1031,6 +1173,7 @@ rspamd_scan_fd (int fd, GHashTable *headers, GError **err) /* Read result cycle */ while (read_rspamd_reply_line (c, err)); + upstream_ok (&c->server->up, c->connection_time); return res; } @@ -1038,30 +1181,324 @@ rspamd_scan_fd (int fd, GHashTable *headers, GError **err) * Learn message from memory */ gboolean -rspamd_learn_memory (const guchar *message, gsize length, const gchar *symbol, GError **err) +rspamd_learn_memory (const guchar *message, gsize length, const gchar *symbol, const gchar *password, GError **err) { + struct rspamd_connection *c; + GString *in; + gchar *outbuf; + guint r; + static const gchar ok_str[] = "learn ok"; + g_assert (client != NULL); + /* Connect to server */ + c = rspamd_connect_random_server (TRUE, err); + + if (c == NULL) { + return FALSE; + } + + /* Read greeting */ + if (! rspamd_read_controller_greeting(c, err)) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid greeting"); + } + return FALSE; + } + /* Perform auth */ + if (! rspamd_controller_auth (c, password, err)) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Authentication error"); + } + return FALSE; + } + + r = length + sizeof ("learn %s %uz\r\n") + strlen (symbol) + sizeof ("4294967296"); + outbuf = g_malloc (r); + r = rspamd_snprintf (outbuf, r, "learn %s %uz\r\n%s", symbol, length, message); + in = rspamd_send_controller_command (c, outbuf, r, -1, err); + g_free (outbuf); + if (in == NULL) { + return FALSE; + } + + /* Search for string learn ok */ + if (in->len > sizeof (ok_str) - 1 && memcmp (in->str, ok_str, sizeof (ok_str) - 1) == 0) { + upstream_ok (&c->server->up, c->connection_time); + return TRUE; + } + else { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Bad reply: %s", in->str); + } + } + return FALSE; } /* * Learn message from file */ gboolean -rspamd_learn_file (const guchar *filename, const gchar *symbol, GError **err) +rspamd_learn_file (const guchar *filename, const gchar *symbol, const gchar *password, GError **err) { + gint fd; g_assert (client != NULL); + /* Open file */ + if ((fd = open (filename, O_RDONLY | O_CLOEXEC)) == -1) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Open error for file %s: %s", + filename, strerror (errno)); + } + return FALSE; + } + + return rspamd_learn_fd (fd, symbol, password, err); } /* * Learn message from fd */ gboolean -rspamd_learn_fd (int fd, const gchar *symbol, GError **err) +rspamd_learn_fd (int fd, const gchar *symbol, const gchar *password, GError **err) { + struct rspamd_connection *c; + GString *in; + gchar *outbuf; + guint r; + struct stat st; + static const gchar ok_str[] = "learn ok"; + g_assert (client != NULL); + /* Connect to server */ + c = rspamd_connect_random_server (TRUE, err); + + if (c == NULL) { + return FALSE; + } + + /* Read greeting */ + if (! rspamd_read_controller_greeting(c, err)) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid greeting"); + } + return FALSE; + } + /* Perform auth */ + if (! rspamd_controller_auth (c, password, err)) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Authentication error"); + } + return FALSE; + } + + /* Get length */ + if (fstat (fd, &st) == -1) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Stat error: %s", + strerror (errno)); + } + return FALSE; + } + r = sizeof ("learn %s %uz\r\n") + strlen (symbol) + sizeof ("4294967296"); + outbuf = g_malloc (r); + r = rspamd_snprintf (outbuf, r, "learn %s %uz\r\n", symbol, st.st_size); + in = rspamd_send_controller_command (c, outbuf, r, fd, err); + g_free (outbuf); + if (in == NULL) { + return FALSE; + } + + /* Search for string learn ok */ + if (in->len > sizeof (ok_str) - 1 && memcmp (in->str, ok_str, sizeof (ok_str) - 1) == 0) { + upstream_ok (&c->server->up, c->connection_time); + return TRUE; + } + else { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Bad reply: %s", in->str); + } + } + + return FALSE; +} + +/* + * Learn message fuzzy from memory + */ +gboolean +rspamd_fuzzy_memory (const guchar *message, gsize length, const gchar *password, gint weight, gint flag, gboolean delete, GError **err) +{ + struct rspamd_connection *c; + GString *in; + gchar *outbuf; + guint r; + static const gchar ok_str[] = "OK"; + + g_assert (client != NULL); + + /* Connect to server */ + c = rspamd_connect_random_server (TRUE, err); + + if (c == NULL) { + return FALSE; + } + + /* Read greeting */ + if (! rspamd_read_controller_greeting(c, err)) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid greeting"); + } + return FALSE; + } + /* Perform auth */ + if (! rspamd_controller_auth (c, password, err)) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Authentication error"); + } + return FALSE; + } + + r = length + sizeof ("fuzzy_add %uz %d %d\r\n") + sizeof ("4294967296") * 3; + outbuf = g_malloc (r); + if (delete) { + r = rspamd_snprintf (outbuf, r, "fuzzy_del %uz %d %d\r\n%s", length, weight, flag, message); + } + else { + r = rspamd_snprintf (outbuf, r, "fuzzy_add %uz %d %d\r\n%s", length, weight, flag, message); + } + in = rspamd_send_controller_command (c, outbuf, r, -1, err); + g_free (outbuf); + if (in == NULL) { + return FALSE; + } + + /* Search for string "OK" */ + if (in->len > sizeof (ok_str) - 1 && memcmp (in->str, ok_str, sizeof (ok_str) - 1) == 0) { + upstream_ok (&c->server->up, c->connection_time); + return TRUE; + } + else { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Bad reply: %s", in->str); + } + } + return FALSE; +} + +/* + * Learn message fuzzy from file + */ +gboolean +rspamd_fuzzy_file (const guchar *filename, const gchar *password, gint weight, gint flag, gboolean delete, GError **err) +{ + gint fd; + g_assert (client != NULL); + + /* Open file */ + if ((fd = open (filename, O_RDONLY | O_CLOEXEC)) == -1) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Open error for file %s: %s", + filename, strerror (errno)); + } + return FALSE; + } + + return rspamd_fuzzy_fd (fd, password, weight, flag, delete, err); +} + +/* + * Learn message fuzzy from fd + */ +gboolean +rspamd_fuzzy_fd (int fd, const gchar *password, gint weight, gint flag, gboolean delete, GError **err) +{ + struct rspamd_connection *c; + GString *in; + gchar *outbuf; + guint r; + struct stat st; + static const gchar ok_str[] = "OK"; + + g_assert (client != NULL); + + /* Connect to server */ + c = rspamd_connect_random_server (TRUE, err); + + if (c == NULL) { + return FALSE; + } + + /* Read greeting */ + if (! rspamd_read_controller_greeting(c, err)) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid greeting"); + } + return FALSE; + } + /* Perform auth */ + if (! rspamd_controller_auth (c, password, err)) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Authentication error"); + } + return FALSE; + } + /* Get length */ + if (fstat (fd, &st) == -1) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Stat error: %s", + strerror (errno)); + } + return FALSE; + } + r = sizeof ("fuzzy_add %uz %d %d\r\n") + sizeof ("4294967296") * 3; + outbuf = g_malloc (r); + if (delete) { + r = rspamd_snprintf (outbuf, r, "fuzzy_del %uz %d %d\r\n", st.st_size, weight, flag); + } + else { + r = rspamd_snprintf (outbuf, r, "fuzzy_add %uz %d %d\r\n", st.st_size, weight, flag); + } + in = rspamd_send_controller_command (c, outbuf, r, fd, err); + + g_free (outbuf); + if (in == NULL) { + return FALSE; + } + + /* Search for string "OK" */ + if (in->len > sizeof (ok_str) - 1 && memcmp (in->str, ok_str, sizeof (ok_str) - 1) == 0) { + upstream_ok (&c->server->up, c->connection_time); + return TRUE; + } + else { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Bad reply: %s", in->str); + } + } + return FALSE; +} + +GString * +rspamd_get_stat (GError **err) +{ + struct rspamd_connection *c; + GString *res; + static const gchar outcmd[] = "stat\r\n"; + + g_assert (client != NULL); + + /* Connect to server */ + c = rspamd_connect_random_server (TRUE, err); + + if (c == NULL) { + return NULL; + } + + res = rspamd_send_controller_command (c, outcmd, strlen (outcmd), -1, err); + + return res; } /* @@ -1071,7 +1508,11 @@ void rspamd_free_result (struct rspamd_result *result) { g_assert (client != NULL); + g_assert (result != NULL); + g_hash_table_destroy (result->headers); + g_hash_table_destroy (result->metrics); + rspamd_free_connection (result->conn); } /* @@ -1081,4 +1522,7 @@ void rspamd_client_close (void) { g_assert (client != NULL); + + g_free (client); + client = NULL; } diff --git a/lib/librspamdclient.h b/lib/librspamdclient.h index fc5a6f2ca..2d2efc164 100644 --- a/lib/librspamdclient.h +++ b/lib/librspamdclient.h @@ -69,17 +69,37 @@ struct rspamd_result * rspamd_scan_fd (int fd, GHashTable *headers, GError **err /* * Learn message from memory */ -gboolean rspamd_learn_memory (const guchar *message, gsize length, const gchar *symbol, GError **err); +gboolean rspamd_learn_memory (const guchar *message, gsize length, const gchar *symbol, const gchar *password, GError **err); /* * Learn message from file */ -gboolean rspamd_learn_file (const guchar *filename, const gchar *symbol, GError **err); +gboolean rspamd_learn_file (const guchar *filename, const gchar *symbol, const gchar *password, GError **err); /* * Learn message from fd */ -gboolean rspamd_learn_fd (int fd, const gchar *symbol, GError **err); +gboolean rspamd_learn_fd (int fd, const gchar *symbol, const gchar *password, GError **err); + +/* + * Learn message fuzzy from memory + */ +gboolean rspamd_fuzzy_memory (const guchar *message, gsize length, const gchar *password, gint weight, gint flag, gboolean delete, GError **err); + +/* + * Learn message fuzzy from file + */ +gboolean rspamd_fuzzy_file (const guchar *filename, const gchar *password, gint weight, gint flag, gboolean delete, GError **err); + +/* + * Learn message fuzzy from fd + */ +gboolean rspamd_fuzzy_fd (int fd, const gchar *password, gint weight, gint flag, gboolean delete, GError **err); + +/* + * Get statistic from server + */ +GString *rspamd_get_stat (GError **err); /* * Free results diff --git a/src/client/rspamc.c b/src/client/rspamc.c index 1bae2970c..874c72074 100644 --- a/src/client/rspamc.c +++ b/src/client/rspamc.c @@ -34,7 +34,8 @@ static gchar *connect_str = "localhost"; static gchar *password; static gchar *statfile; static gchar *ip; -static gdouble weight; +static gint weight = 1; +static gint flag; static gboolean pass_all; static gboolean tty = FALSE; @@ -43,7 +44,8 @@ static GOptionEntry entries[] = { "connect", 'h', 0, G_OPTION_ARG_STRING, &connect_str, "Specify host and port", NULL }, { "password", 'P', 0, G_OPTION_ARG_STRING, &password, "Specify control password", NULL }, { "statfile", 's', 0, G_OPTION_ARG_STRING, &statfile, "Statfile to learn (symbol name)", NULL }, - { "weight", 'w', 0, G_OPTION_ARG_DOUBLE, &weight, "Weight for fuzzy operations", NULL }, + { "weight", 'w', 0, G_OPTION_ARG_INT, &weight, "Weight for fuzzy operations", NULL }, + { "flag", 'f', 0, G_OPTION_ARG_INT, &flag, "Flag for fuzzy operations", NULL }, { "pass", 'p', 0, G_OPTION_ARG_NONE, &pass_all, "Pass all filters", NULL }, { "ip", 'i', 0, G_OPTION_ARG_STRING, &ip, "Emulate that message was received from specified ip address", NULL }, { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL } @@ -52,7 +54,10 @@ static GOptionEntry entries[] = enum rspamc_command { RSPAMC_COMMAND_UNKNOWN = 0, RSPAMC_COMMAND_SYMBOLS, - RSPAMC_COMMAND_LEARN + RSPAMC_COMMAND_LEARN, + RSPAMC_COMMAND_FUZZY_ADD, + RSPAMC_COMMAND_FUZZY_DEL, + RSPAMC_COMMAND_STAT }; /* @@ -93,6 +98,15 @@ check_rspamc_command (const gchar *cmd) else if (g_ascii_strcasecmp (cmd, "LEARN") == 0) { return RSPAMC_COMMAND_LEARN; } + else if (g_ascii_strcasecmp (cmd, "FUZZY_ADD") == 0) { + return RSPAMC_COMMAND_FUZZY_ADD; + } + else if (g_ascii_strcasecmp (cmd, "FUZZY_DEL") == 0) { + return RSPAMC_COMMAND_FUZZY_DEL; + } + else if (g_ascii_strcasecmp (cmd, "STAT") == 0) { + return RSPAMC_COMMAND_STAT; + } return RSPAMC_COMMAND_UNKNOWN; } @@ -282,6 +296,7 @@ scan_rspamd_stdin () exit (EXIT_FAILURE); } print_rspamd_result (res); + rspamd_free_result (res); } static void @@ -302,9 +317,197 @@ scan_rspamd_file (const gchar *file) g_hash_table_destroy (opts); if (err != NULL) { fprintf (stderr, "cannot scan message: %s\n", err->message); - exit (EXIT_FAILURE); } print_rspamd_result (res); + if (res) { + rspamd_free_result (res); + } +} + +static void +learn_rspamd_stdin () +{ + gchar *in_buf; + gint r = 0, len; + GError *err = NULL; + + if (password == NULL || statfile == NULL) { + fprintf (stderr, "cannot learn message without password and symbol name\n"); + exit (EXIT_FAILURE); + } + /* Add server */ + add_rspamd_server (TRUE); + + /* Allocate input buffer */ + len = BUFSIZ; + in_buf = g_malloc (len); + + /* Read stdin */ + while (!feof (stdin)) { + r += fread (in_buf + r, 1, len - r, stdin); + if (len - r < len / 2) { + /* Grow buffer */ + len *= 2; + in_buf = g_realloc (in_buf, len); + } + } + if (!rspamd_learn_memory (in_buf, r, statfile, password, &err)) { + if (err != NULL) { + fprintf (stderr, "cannot learn message: %s\n", err->message); + } + else { + fprintf (stderr, "cannot learn message\n"); + } + exit (EXIT_FAILURE); + } + else { + if (tty) { + printf ("\033[1m"); + } + PRINT_FUNC ("Results for host: %s: learn ok\n", connect_str); + if (tty) { + printf ("\033[0m"); + } + } +} + +static void +learn_rspamd_file (const gchar *file) +{ + GError *err = NULL; + + if (password == NULL || statfile == NULL) { + fprintf (stderr, "cannot learn message without password and symbol name\n"); + exit (EXIT_FAILURE); + } + /* Add server */ + add_rspamd_server (TRUE); + + if (!rspamd_learn_file (file, statfile, password, &err)) { + if (err != NULL) { + fprintf (stderr, "cannot learn message: %s\n", err->message); + } + else { + fprintf (stderr, "cannot learn message\n"); + } + } + else { + if (tty) { + printf ("\033[1m"); + } + PRINT_FUNC ("learn ok\n"); + if (tty) { + printf ("\033[0m"); + } + } +} + +static void +fuzzy_rspamd_stdin (gboolean delete) +{ + gchar *in_buf; + gint r = 0, len; + GError *err = NULL; + + if (password == NULL) { + fprintf (stderr, "cannot learn message without password\n"); + exit (EXIT_FAILURE); + } + /* Add server */ + add_rspamd_server (TRUE); + + /* Allocate input buffer */ + len = BUFSIZ; + in_buf = g_malloc (len); + + /* Read stdin */ + while (!feof (stdin)) { + r += fread (in_buf + r, 1, len - r, stdin); + if (len - r < len / 2) { + /* Grow buffer */ + len *= 2; + in_buf = g_realloc (in_buf, len); + } + } + if (!rspamd_fuzzy_memory (in_buf, r, password, weight, flag, delete, &err)) { + if (err != NULL) { + fprintf (stderr, "cannot learn message: %s\n", err->message); + } + else { + fprintf (stderr, "cannot learn message\n"); + } + exit (EXIT_FAILURE); + } + else { + if (tty) { + printf ("\033[1m"); + } + PRINT_FUNC ("Results for host: %s: learn ok\n", connect_str); + if (tty) { + printf ("\033[0m"); + } + } +} + +static void +fuzzy_rspamd_file (const gchar *file, gboolean delete) +{ + GError *err = NULL; + + if (password == NULL) { + fprintf (stderr, "cannot learn message without password\n"); + exit (EXIT_FAILURE); + } + /* Add server */ + add_rspamd_server (TRUE); + + if (!rspamd_fuzzy_file (file, password, weight, flag, delete, &err)) { + if (err != NULL) { + fprintf (stderr, "cannot learn message: %s\n", err->message); + } + else { + fprintf (stderr, "cannot learn message\n"); + } + } + else { + if (tty) { + printf ("\033[1m"); + } + PRINT_FUNC ("learn ok\n"); + if (tty) { + printf ("\033[0m"); + } + } +} + +static void +rspamd_do_stat () +{ + GError *err = NULL; + GString *res; + + /* Add server */ + add_rspamd_server (TRUE); + + res = rspamd_get_stat (&err); + if (res == NULL) { + if (err != NULL) { + fprintf (stderr, "cannot learn message: %s\n", err->message); + } + else { + fprintf (stderr, "cannot learn message\n"); + } + exit (EXIT_FAILURE); + } + if (tty) { + printf ("\033[1m"); + } + PRINT_FUNC ("Results for host: %s\n\n", connect_str); + if (tty) { + printf ("\033[0m"); + } + res = g_string_append_c (res, '\0'); + printf ("%s\n", res->str); } gint @@ -326,11 +529,24 @@ main (gint argc, gchar **argv, gchar **env) /* One argument is whether command or filename */ if ((cmd = check_rspamc_command (argv[1])) != RSPAMC_COMMAND_UNKNOWN) { /* In case of command read stdin */ - if (cmd == RSPAMC_COMMAND_SYMBOLS) { + switch (cmd) { + case RSPAMC_COMMAND_SYMBOLS: scan_rspamd_stdin (); - } - else { - /* XXX: implement this */ + break; + case RSPAMC_COMMAND_LEARN: + learn_rspamd_stdin (); + break; + case RSPAMC_COMMAND_FUZZY_ADD: + fuzzy_rspamd_stdin (FALSE); + break; + case RSPAMC_COMMAND_FUZZY_DEL: + fuzzy_rspamd_stdin (TRUE); + break; + case RSPAMC_COMMAND_STAT: + rspamd_do_stat (); + default: + fprintf (stderr, "invalid arguments\n"); + exit (EXIT_FAILURE); } } else { @@ -341,11 +557,29 @@ main (gint argc, gchar **argv, gchar **env) if ((cmd = check_rspamc_command (argv[1])) != RSPAMC_COMMAND_UNKNOWN) { /* In case of command read arguments starting from 2 */ for (i = 2; i < argc; i ++) { - if (cmd == RSPAMC_COMMAND_SYMBOLS) { - scan_rspamd_file (argv[i]); + if (tty) { + printf ("\033[1m"); } - else { - /* XXX: implement this */ + PRINT_FUNC ("Results for file: %s\n\n", argv[i]); + if (tty) { + printf ("\033[0m"); + } + switch (cmd) { + case RSPAMC_COMMAND_SYMBOLS: + scan_rspamd_file (argv[i]); + break; + case RSPAMC_COMMAND_LEARN: + learn_rspamd_file (argv[i]); + break; + case RSPAMC_COMMAND_FUZZY_ADD: + fuzzy_rspamd_file (argv[i], FALSE); + break; + case RSPAMC_COMMAND_FUZZY_DEL: + fuzzy_rspamd_file (argv[i], TRUE); + break; + default: + fprintf (stderr, "invalid arguments\n"); + exit (EXIT_FAILURE); } } } @@ -356,6 +590,7 @@ main (gint argc, gchar **argv, gchar **env) } } + rspamd_client_close (); return 0; } diff --git a/src/controller.c b/src/controller.c index a39ec21cb..d204a1aa0 100644 --- a/src/controller.c +++ b/src/controller.c @@ -836,7 +836,7 @@ controller_read_socket (f_str_t * in, void *arg) /* Handle messages without text */ if (tokens == NULL) { - i = rspamd_snprintf (out_buf, sizeof (out_buf), "learn failed, no tokens can be extracted (no text data)" CRLF); + i = rspamd_snprintf (out_buf, sizeof (out_buf), "learn failed, no tokens can be extracted (no text data)" CRLF END); msg_info ("learn failed for message <%s>, no tokens to extract", task->message_id); free_task (task, FALSE); if (!rspamd_dispatcher_write (session->dispatcher, out_buf, i, FALSE, FALSE)) { @@ -860,12 +860,12 @@ controller_read_socket (f_str_t * in, void *arg) session->learn_symbol, tokens, session->in_class, &sum, session->learn_multiplier, &err)) { if (err) { - i = rspamd_snprintf (out_buf, sizeof (out_buf), "learn failed, learn classifier error: %s" CRLF, err->message); + i = rspamd_snprintf (out_buf, sizeof (out_buf), "learn failed, learn classifier error: %s" CRLF END, err->message); msg_info ("learn failed for message <%s>, learn error: %s", task->message_id, err->message); g_error_free (err); } else { - i = rspamd_snprintf (out_buf, sizeof (out_buf), "learn failed, unknown learn classifier error" CRLF); + i = rspamd_snprintf (out_buf, sizeof (out_buf), "learn failed, unknown learn classifier error" CRLF END); msg_info ("learn failed for message <%s>, unknown learn error", task->message_id); } free_task (task, FALSE); @@ -882,7 +882,7 @@ controller_read_socket (f_str_t * in, void *arg) task->message_id, session->learn_symbol, sum); statfile_pool_plan_invalidate (session->worker->srv->statfile_pool, DEFAULT_STATFILE_INVALIDATE_TIME, DEFAULT_STATFILE_INVALIDATE_JITTER); free_task (task, FALSE); - i = rspamd_snprintf (out_buf, sizeof (out_buf), "learn ok, sum weight: %.2f" CRLF, sum); + i = rspamd_snprintf (out_buf, sizeof (out_buf), "learn ok, sum weight: %.2f" CRLF END, sum); if (!rspamd_dispatcher_write (session->dispatcher, out_buf, i, FALSE, FALSE)) { return FALSE; } @@ -902,7 +902,7 @@ controller_read_socket (f_str_t * in, void *arg) msg_warn ("processing of message failed"); free_task (task, FALSE); session->state = STATE_REPLY; - r = rspamd_snprintf (out_buf, sizeof (out_buf), "cannot process message" CRLF); + r = rspamd_snprintf (out_buf, sizeof (out_buf), "cannot process message" CRLF END); if (! rspamd_dispatcher_write (session->dispatcher, out_buf, r, FALSE, FALSE)) { return FALSE; } @@ -920,7 +920,7 @@ controller_read_socket (f_str_t * in, void *arg) c.begin = part->content->data; c.len = part->content->len; if (!session->learn_classifier->tokenizer->tokenize_func (session->learn_classifier->tokenizer, session->session_pool, &c, &tokens)) { - i = rspamd_snprintf (out_buf, sizeof (out_buf), "weights failed, tokenizer error" CRLF); + i = rspamd_snprintf (out_buf, sizeof (out_buf), "weights failed, tokenizer error" CRLF END); free_task (task, FALSE); if (!rspamd_dispatcher_write (session->dispatcher, out_buf, i, FALSE, FALSE)) { return FALSE; @@ -933,7 +933,7 @@ controller_read_socket (f_str_t * in, void *arg) /* Handle messages without text */ if (tokens == NULL) { - i = rspamd_snprintf (out_buf, sizeof (out_buf), "weights failed, no tokens can be extracted (no text data)" CRLF); + i = rspamd_snprintf (out_buf, sizeof (out_buf), "weights failed, no tokens can be extracted (no text data)" CRLF END); free_task (task, FALSE); if (!rspamd_dispatcher_write (session->dispatcher, out_buf, i, FALSE, FALSE)) { return FALSE; @@ -956,13 +956,14 @@ controller_read_socket (f_str_t * in, void *arg) i += rspamd_snprintf (out_buf + i, sizeof (out_buf) - i, "%s: %G" CRLF, w->name, w->weight); cur = g_list_next (cur); } + i += rspamd_snprintf (out_buf + i, sizeof (out_buf) - i, END); if (i != 0) { if (!rspamd_dispatcher_write (session->dispatcher, out_buf, i, FALSE, FALSE)) { return FALSE; } } else { - if (!rspamd_dispatcher_write (session->dispatcher, "weights failed: classifier error", 0, FALSE, TRUE)) { + if (!rspamd_dispatcher_write (session->dispatcher, "weights failed: classifier error" CRLF END, 0, FALSE, TRUE)) { return FALSE; } } diff --git a/src/plugins/fuzzy_check.c b/src/plugins/fuzzy_check.c index e47beee17..f81d549a7 100644 --- a/src/plugins/fuzzy_check.c +++ b/src/plugins/fuzzy_check.c @@ -567,7 +567,7 @@ fuzzy_learn_callback (gint fd, short what, void *arg) else if (buf[0] == 'O' && buf[1] == 'K') { msg_info ("added fuzzy hash '%s' to list: %d for message <%s>", fuzzy_to_string (session->h), session->flag, session->task->message_id); - r = rspamd_snprintf (buf, sizeof (buf), "OK" CRLF); + r = rspamd_snprintf (buf, sizeof (buf), "OK" CRLF "END" CRLF); if (! rspamd_dispatcher_write (session->session->dispatcher, buf, r, FALSE, FALSE)) { return; } @@ -575,7 +575,7 @@ fuzzy_learn_callback (gint fd, short what, void *arg) } else { msg_info ("cannot add fuzzy hash for message <%s>", session->task->message_id); - r = rspamd_snprintf (buf, sizeof (buf), "ERR" CRLF); + r = rspamd_snprintf (buf, sizeof (buf), "ERR" CRLF "END" CRLF); if (! rspamd_dispatcher_write (session->session->dispatcher, buf, r, FALSE, FALSE)) { return; } @@ -591,7 +591,7 @@ fuzzy_learn_callback (gint fd, short what, void *arg) err: msg_err ("got error in IO with server %s:%d, %d, %s", session->server->name, session->server->port, errno, strerror (errno)); - r = rspamd_snprintf (buf, sizeof (buf), "Error" CRLF); + r = rspamd_snprintf (buf, sizeof (buf), "Error" CRLF "END" CRLF); if (! rspamd_dispatcher_write (session->session->dispatcher, buf, r, FALSE, FALSE)) { return; } @@ -741,7 +741,7 @@ register_fuzzy_controller_call (struct controller_session *session, struct worke if ((sock = make_udp_socket (&selected->addr, selected->port, FALSE, TRUE)) == -1) { msg_warn ("cannot connect to %s, %d, %s", selected->name, errno, strerror (errno)); session->state = STATE_REPLY; - r = rspamd_snprintf (out_buf, sizeof (out_buf), "no hashes written" CRLF); + r = rspamd_snprintf (out_buf, sizeof (out_buf), "no hashes written" CRLF "END" CRLF); if (! rspamd_dispatcher_write (session->dispatcher, out_buf, r, FALSE, FALSE)) { return FALSE; } @@ -810,7 +810,7 @@ fuzzy_process_handler (struct controller_session *session, f_str_t * in) msg_warn ("processing of message failed"); free_task (task, FALSE); session->state = STATE_REPLY; - r = rspamd_snprintf (out_buf, sizeof (out_buf), "cannot process message" CRLF); + r = rspamd_snprintf (out_buf, sizeof (out_buf), "cannot process message" CRLF "END" CRLF); if (! rspamd_dispatcher_write (session->dispatcher, out_buf, r, FALSE, FALSE)) { msg_warn ("write error"); } @@ -829,7 +829,7 @@ fuzzy_process_handler (struct controller_session *session, f_str_t * in) if (! register_fuzzy_controller_call (session, task, part->fuzzy, cmd, value, flag, saved)) { /* Cannot write hash */ session->state = STATE_REPLY; - r = rspamd_snprintf (out_buf, sizeof (out_buf), "cannot write fuzzy hash" CRLF); + r = rspamd_snprintf (out_buf, sizeof (out_buf), "cannot write fuzzy hash" CRLF "END" CRLF); if (! rspamd_dispatcher_write (session->dispatcher, out_buf, r, FALSE, FALSE)) { return; } @@ -853,7 +853,7 @@ fuzzy_process_handler (struct controller_session *session, f_str_t * in) if (! register_fuzzy_controller_call (session, task, &fake_fuzzy, cmd, value, flag, saved)) { /* Cannot write hash */ session->state = STATE_REPLY; - r = rspamd_snprintf (out_buf, sizeof (out_buf), "cannot write fuzzy hash" CRLF); + r = rspamd_snprintf (out_buf, sizeof (out_buf), "cannot write fuzzy hash" CRLF "END" CRLF); if (! rspamd_dispatcher_write (session->dispatcher, out_buf, r, FALSE, FALSE)) { return; } @@ -883,7 +883,7 @@ fuzzy_process_handler (struct controller_session *session, f_str_t * in) if (! register_fuzzy_controller_call (session, task, &fake_fuzzy, cmd, value, flag, saved)) { /* Cannot write hash */ session->state = STATE_REPLY; - r = rspamd_snprintf (out_buf, sizeof (out_buf), "cannot write fuzzy hash" CRLF); + r = rspamd_snprintf (out_buf, sizeof (out_buf), "cannot write fuzzy hash" CRLF "END" CRLF); if (! rspamd_dispatcher_write (session->dispatcher, out_buf, r, FALSE, FALSE)) { return; } @@ -905,7 +905,7 @@ fuzzy_process_handler (struct controller_session *session, f_str_t * in) if (*saved == 0) { session->state = STATE_REPLY; - r = rspamd_snprintf (out_buf, sizeof (out_buf), "no hashes written" CRLF); + r = rspamd_snprintf (out_buf, sizeof (out_buf), "no hashes written" CRLF "END" CRLF); if (! rspamd_dispatcher_write (session->dispatcher, out_buf, r, FALSE, FALSE)) { return; } @@ -923,7 +923,7 @@ fuzzy_controller_handler (gchar **args, struct controller_session *session, gint arg = args[0]; if (!arg || *arg == '\0') { msg_info ("empty content length"); - r = rspamd_snprintf (out_buf, sizeof (out_buf), "fuzzy command requires length as argument" CRLF); + r = rspamd_snprintf (out_buf, sizeof (out_buf), "fuzzy command requires length as argument" CRLF "END" CRLF); if (! rspamd_dispatcher_write (session->dispatcher, out_buf, r, FALSE, FALSE)) { return; } -- 2.39.5