From 2a383446ab6254777693544e5acda2a805947f81 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Mon, 31 Jan 2011 20:33:12 +0300 Subject: [PATCH] * Add C client for rspamd that is using librspamdclient --- CMakeLists.txt | 1 + lib/CMakeLists.txt | 2 +- lib/librspamdclient.c | 230 ++++++++++++++++++++---- src/client/CMakeLists.txt | 8 + src/client/rspamc.c | 361 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 563 insertions(+), 39 deletions(-) create mode 100644 src/client/CMakeLists.txt create mode 100644 src/client/rspamc.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ee3cf56a..c47ccec36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -584,6 +584,7 @@ IF(ENABLE_PERL MATCHES "ON") ENDIF(ENABLE_PERL MATCHES "ON") ADD_SUBDIRECTORY(src/lua) ADD_SUBDIRECTORY(lib) +ADD_SUBDIRECTORY(src/client) ADD_SUBDIRECTORY(src/json) # ADD_SUBDIRECTORY(src/evdns) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index e57d18a2e..f625b2833 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,5 +1,5 @@ # Librspamd -SET(LIBRSPAMDSRC librspamdclient.c ../src/util.c ../src/upstream.c) +SET(LIBRSPAMDSRC librspamdclient.c ../src/util.c ../src/upstream.c ../src/mem_pool.c) ADD_LIBRARY(rspamdclient SHARED ${LIBRSPAMDSRC}) ADD_LIBRARY(rspamdclient_static STATIC ${LIBRSPAMDSRC}) diff --git a/lib/librspamdclient.c b/lib/librspamdclient.c index 8fa280440..d7568eb62 100644 --- a/lib/librspamdclient.c +++ b/lib/librspamdclient.c @@ -92,7 +92,7 @@ symbol_free_func (gpointer arg) } static struct rspamd_connection * -rspamd_connect_random_server (guint16 port, GError **err) +rspamd_connect_random_server (gboolean is_control, GError **err) { struct rspamd_server *selected = NULL; struct rspamd_connection *new; @@ -122,7 +122,9 @@ rspamd_connect_random_server (guint16 port, GError **err) new->server = selected; new->connection_time = now; /* Create socket */ - new->socket = make_tcp_socket (&selected->addr, port, FALSE, TRUE); + new->socket = make_tcp_socket (&selected->addr, + is_control ? selected->controller_port : selected->client_port, + FALSE, TRUE); if (new->socket == -1) { goto err; } @@ -159,13 +161,27 @@ rspamd_create_metric (const gchar *begin, guint len) return new; } +static struct rspamd_result * +rspamd_create_result (struct rspamd_connection *c) +{ + struct rspamd_result *new; + + new = g_malloc (sizeof (struct rspamd_result)); + new->conn = c; + new->headers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + new->metrics = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, metric_free_func); + new->is_ok = FALSE; + + return new; +} + /* * Parse line like RSPAMD/{version} {code} {message} */ static gboolean -parse_rspamd_first_line (struct rspamd_connection *c, guint len, GError **err) +parse_rspamd_first_line (struct rspamd_connection *conn, guint len, GError **err) { - gchar *b = c->in_buf->str + sizeof("RSPAMD/") - 1, *p; + gchar *b = conn->in_buf->str + sizeof("RSPAMD/") - 1, *p, *c; guint remain = len - sizeof("RSPAMD/") + 1, state = 0, next_state; p = b; @@ -187,6 +203,9 @@ parse_rspamd_first_line (struct rspamd_connection *c, guint len, GError **err) if (g_ascii_isspace (*p)) { state = 99; next_state = 2; + if (*c == '0') { + conn->result->is_ok = TRUE; + } } else if (!g_ascii_isdigit (*p)) { goto err; @@ -207,6 +226,7 @@ parse_rspamd_first_line (struct rspamd_connection *c, guint len, GError **err) /* Skip spaces */ if (!g_ascii_isspace (*p)) { state = next_state; + c = p; } else { p ++; @@ -219,12 +239,13 @@ parse_rspamd_first_line (struct rspamd_connection *c, guint len, GError **err) goto err; } + return TRUE; err: if (*err == NULL) { *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid protocol line: %*s at pos: %d", - len, b, (int)(p - b)); + remain, b, (int)(p - b)); } - upstream_fail (&c->server->up, c->connection_time); + upstream_fail (&conn->server->up, conn->connection_time); return FALSE; } @@ -255,7 +276,7 @@ parse_rspamd_metric_line (struct rspamd_connection *conn, guint len, GError **er } else { /* Create new metric */ - new = rspamd_create_metric (c, p - c - 1); + new = rspamd_create_metric (c, p - c); if (g_hash_table_lookup (conn->result->metrics, new->name) != NULL) { /* Duplicate metric */ metric_free_func (new); @@ -272,13 +293,13 @@ parse_rspamd_metric_line (struct rspamd_connection *conn, guint len, GError **er case 1: /* Read boolean result */ if (*p == ';') { - if (p - c > sizeof("Skip")) { + if (p - c >= sizeof("Skip")) { if (memcmp (c, "Skip", p - c - 1) == 0) { new->is_skipped = TRUE; } - state = 99; - next_state = 2; } + state = 99; + next_state = 2; } p ++; break; @@ -297,18 +318,18 @@ parse_rspamd_metric_line (struct rspamd_connection *conn, guint len, GError **er break; case 3: /* Read / */ - if (*p != '/') { - goto err; - } - else if (g_ascii_isspace (*p)) { + if (g_ascii_isspace (*p)) { state = 99; next_state = 4; } + else if (*p != '/') { + goto err; + } p ++; break; case 4: /* Read required score */ - if (g_ascii_isspace (*p)) { + if (g_ascii_isspace (*p) || p - b == remain - 1) { new->required_score = strtod (c, &err_str); if (*err_str != *p) { /* Invalid score */ @@ -321,13 +342,13 @@ parse_rspamd_metric_line (struct rspamd_connection *conn, guint len, GError **er break; case 5: /* Read / if it exists */ - if (*p != '/') { - goto err; - } - else if (g_ascii_isspace (*p)) { + if (g_ascii_isspace (*p)) { state = 99; next_state = 6; } + else if (*p != '/') { + goto err; + } p ++; break; case 6: @@ -358,11 +379,12 @@ parse_rspamd_metric_line (struct rspamd_connection *conn, guint len, GError **er if (state != 99) { goto err; } + return TRUE; err: if (*err == NULL) { - *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid metric line: %*s at pos: %d", - len, b, (int)(p - b)); + *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid metric line: %*s at pos: %d, state: %d", + remain, b, (int)(p - b), state); } upstream_fail (&conn->server->up, conn->connection_time); return FALSE; @@ -395,9 +417,9 @@ parse_rspamd_symbol_line (struct rspamd_connection *conn, guint len, GError **er } else { /* Create new symbol */ - sym = g_malloc (p - c); - sym[p - c - 1] = '\0'; - memcpy (sym, c, p - c - 1); + sym = g_malloc (p - c + 1); + sym[p - c] = '\0'; + memcpy (sym, c, p - c); if (g_hash_table_lookup (conn->cur_metric->symbols, sym) != NULL) { /* Duplicate symbol */ @@ -405,6 +427,7 @@ parse_rspamd_symbol_line (struct rspamd_connection *conn, guint len, GError **er goto err; } new = g_malloc0 (sizeof (struct rspamd_symbol)); + new->name = sym; g_hash_table_insert (conn->cur_metric->symbols, sym, new); state = 99; if (*p == '(') { @@ -467,11 +490,12 @@ parse_rspamd_symbol_line (struct rspamd_connection *conn, guint len, GError **er if (state != 99) { goto err; } + return TRUE; err: if (*err == NULL) { *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid symbol line: %*s at pos: %d", - len, b, (int)(p - b)); + remain, b, (int)(p - b)); } upstream_fail (&conn->server->up, conn->connection_time); return FALSE; @@ -494,18 +518,23 @@ parse_rspamd_action_line (struct rspamd_connection *conn, guint len, GError **er /* Read action */ if (g_ascii_isspace (*p)) { state = 99; - next_state = 0; + next_state = 1; + } + else { + state = 1; } - else if (p - b == remain - 1) { + break; + case 1: + if (p - b == remain - 1) { if (p - c <= 1) { /* Empty action name */ goto err; } else { /* Create new action */ - sym = g_malloc (p - c + 1); - sym[p - c] = '\0'; - memcpy (sym, c, p - c); + sym = g_malloc (p - c + 2); + sym[p - c + 1] = '\0'; + memcpy (sym, c, p - c + 1); conn->cur_metric->action = sym; state = 99; @@ -529,11 +558,12 @@ parse_rspamd_action_line (struct rspamd_connection *conn, guint len, GError **er if (state != 99) { goto err; } + return TRUE; err: if (*err == NULL) { *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid action line: %*s at pos: %d", - len, b, (int)(p - b)); + remain, b, (int)(p - b)); } upstream_fail (&conn->server->up, conn->connection_time); return FALSE; @@ -561,9 +591,9 @@ parse_rspamd_header_line (struct rspamd_connection *conn, guint len, GError **er } else { /* Create header name */ - hname = g_malloc (p - c); - hname[p - c - 1] = '\0'; - memcpy (hname, c, p - c - 1); + hname = g_malloc (p - c + 1); + hname[p - c] = '\0'; + memcpy (hname, c, p - c); next_state = 1; state = 99; } @@ -603,11 +633,12 @@ parse_rspamd_header_line (struct rspamd_connection *conn, guint len, GError **er if (state != 99) { goto err; } + return TRUE; err: if (*err == NULL) { *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid header line: %*s at pos: %d", - len, b, (int)(p - b)); + remain, b, (int)(p - b)); } if (hname) { g_free (hname); @@ -682,6 +713,7 @@ read_rspamd_reply_line (struct rspamd_connection *c, GError **err) } /* Move remaining buffer to the begin of string */ c->in_buf = g_string_erase (c->in_buf, 0, len); + len = 0; } else { return FALSE; @@ -706,12 +738,13 @@ read_rspamd_reply_line (struct rspamd_connection *c, GError **err) /* Read new data to a string */ if ((r = read (c->socket, c->in_buf->str + c->in_buf->len, - c->in_buf->allocated_len - c->in_buf->len)) < 0) { + c->in_buf->allocated_len - c->in_buf->len)) > 0) { /* Try to parse remaining data */ - return parse_rspamd_reply_line (c, c->in_buf->len, err); + c->in_buf->len += r; + return read_rspamd_reply_line (c, err); } - return TRUE; + return FALSE; } /* @@ -721,6 +754,7 @@ static gboolean rspamd_sendfile (gint sock, gint fd, GError **err) { + /* Make socket blocking for further operations */ make_socket_blocking (sock); #ifdef HAVE_SENDFILE # if defined(FREEBSD) || defined(DARWIN) @@ -769,6 +803,38 @@ err: return FALSE; } +static gboolean +rspamd_send_normal_command (struct rspamd_connection *c, const gchar *command, + gsize clen, GHashTable *headers, GError **err) +{ + static gchar outbuf[16384]; + GHashTableIter it; + gpointer key, value; + gint r; + + /* Write command */ + r = rspamd_snprintf (outbuf, sizeof (outbuf), "%s RSPAMC/1.2\r\n", command); + r += rspamd_snprintf (outbuf + r, sizeof (outbuf) - r, "Content-Length: %uz\r\n", clen); + /* Iterate through headers */ + if (headers != NULL) { + g_hash_table_iter_init (&it, headers); + while (g_hash_table_iter_next (&it, &key, &value)) { + r += rspamd_snprintf (outbuf + r, sizeof (outbuf) - r, "%s: %s\r\n", key, value); + } + } + r += rspamd_snprintf (outbuf + r, sizeof (outbuf) - r, "\r\n"); + + if ((r = write (c->socket, outbuf, r)) == -1) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Write error: %s", + strerror (errno)); + } + return FALSE; + } + + return TRUE; +} + static void rspamd_free_connection (struct rspamd_connection *c) { @@ -856,8 +922,44 @@ rspamd_set_timeout (guint connect_timeout, guint read_timeout) struct rspamd_result * rspamd_scan_memory (const guchar *message, gsize length, GHashTable *headers, GError **err) { + struct rspamd_connection *c; + struct rspamd_result *res = NULL; + g_assert (client != NULL); + /* Connect to server */ + c = rspamd_connect_random_server (FALSE, err); + + if (c == NULL) { + return NULL; + } + + /* Set socket blocking for writing */ + make_socket_blocking (c->socket); + /* Send command */ + if (!rspamd_send_normal_command (c, "SYMBOLS", length, headers, err)) { + return NULL; + } + + /* Send message */ + if (write (c->socket, message, length) == -1) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Write error: %s", + strerror (errno)); + } + return NULL; + } + + /* Create result structure */ + res = rspamd_create_result (c); + c->result = res; + /* Restore non-blocking mode for reading operations */ + make_socket_nonblocking (c->socket); + + /* Read result cycle */ + while (read_rspamd_reply_line (c, err)); + + return res; } /* @@ -866,8 +968,19 @@ rspamd_scan_memory (const guchar *message, gsize length, GHashTable *headers, GE struct rspamd_result * rspamd_scan_file (const guchar *filename, GHashTable *headers, 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 NULL; + } + + return rspamd_scan_fd (fd, headers, err); } /* @@ -876,8 +989,49 @@ rspamd_scan_file (const guchar *filename, GHashTable *headers, GError **err) struct rspamd_result * rspamd_scan_fd (int fd, GHashTable *headers, GError **err) { + struct rspamd_connection *c; + struct rspamd_result *res = NULL; + struct stat st; + g_assert (client != NULL); + /* Connect to server */ + c = rspamd_connect_random_server (FALSE, err); + + if (c == NULL) { + return NULL; + } + + /* Get length */ + if (fstat (fd, &st) == -1) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Stat error: %s", + strerror (errno)); + } + return NULL; + } + /* Set socket blocking for writing */ + make_socket_blocking (c->socket); + /* Send command */ + if (!rspamd_send_normal_command (c, "SYMBOLS", (gsize)st.st_size, headers, err)) { + return NULL; + } + + /* Send message */ + if (!rspamd_sendfile (c->socket, fd, err)) { + return NULL; + } + + /* Create result structure */ + res = rspamd_create_result (c); + c->result = res; + /* Restore non-blocking mode for reading operations */ + make_socket_nonblocking (c->socket); + + /* Read result cycle */ + while (read_rspamd_reply_line (c, err)); + + return res; } /* diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt new file mode 100644 index 000000000..186b458f3 --- /dev/null +++ b/src/client/CMakeLists.txt @@ -0,0 +1,8 @@ +# rspamc +SET(RSPAMCSRC rspamc.c) + +ADD_EXECUTABLE(rspamc ${RSPAMCSRC}) +SET_TARGET_PROPERTIES(rspamc PROPERTIES COMPILE_FLAGS "-I.. -I../../lib") +TARGET_LINK_LIBRARIES(rspamc rspamdclient) +TARGET_LINK_LIBRARIES(rspamc ${CMAKE_REQUIRED_LIBRARIES}) +TARGET_LINK_LIBRARIES(rspamc ${GLIB2_LIBRARIES}) diff --git a/src/client/rspamc.c b/src/client/rspamc.c new file mode 100644 index 000000000..1bae2970c --- /dev/null +++ b/src/client/rspamc.c @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2011, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "librspamdclient.h" + +#define PRINT_FUNC printf + +#define DEFAULT_PORT 11333 +#define DEFAULT_CONTROL_PORT 11334 + +static gchar *connect_str = "localhost"; +static gchar *password; +static gchar *statfile; +static gchar *ip; +static gdouble weight; +static gboolean pass_all; +static gboolean tty = FALSE; + +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 }, + { "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 } +}; + +enum rspamc_command { + RSPAMC_COMMAND_UNKNOWN = 0, + RSPAMC_COMMAND_SYMBOLS, + RSPAMC_COMMAND_LEARN +}; + +/* + * Parse command line + */ +static void +read_cmd_line (gint *argc, gchar ***argv) +{ + GError *error = NULL; + GOptionContext *context; + + /* Prepare parser */ + context = g_option_context_new ("- run rspamc client"); + g_option_context_set_summary (context, "Summary:\n Rspamd client version " RVERSION "\n Release id: " RID); + g_option_context_add_main_entries (context, entries, NULL); + + /* Parse options */ + if (!g_option_context_parse (context, argc, argv, &error)) { + fprintf (stderr, "option parsing failed: %s\n", error->message); + exit (EXIT_FAILURE); + } + + /* Argc and argv are shifted after this function */ +} + +/* + * Check rspamc command from string (used for arguments parsing) + */ +static enum rspamc_command +check_rspamc_command (const gchar *cmd) +{ + if (g_ascii_strcasecmp (cmd, "SYMBOLS") == 0 || + g_ascii_strcasecmp (cmd, "CHECK") == 0 || + g_ascii_strcasecmp (cmd, "REPORT") == 0) { + /* These all are symbols, don't use other commands */ + return RSPAMC_COMMAND_SYMBOLS; + } + else if (g_ascii_strcasecmp (cmd, "LEARN") == 0) { + return RSPAMC_COMMAND_LEARN; + } + + return RSPAMC_COMMAND_UNKNOWN; +} + +/* + * Parse connect_str and add server to librspamdclient + */ +static void +add_rspamd_server (gboolean is_control) +{ + gchar **vec, *err_str; + guint16 port; + GError *err = NULL; + + if (connect_str == NULL) { + fprintf (stderr, "cannot connect to rspamd server - empty string\n"); + exit (EXIT_FAILURE); + } + vec = g_strsplit_set (connect_str, ":", 2); + if (vec == NULL || *vec == NULL) { + fprintf (stderr, "cannot connect to rspamd server: %s\n", connect_str); + exit (EXIT_FAILURE); + } + + if (vec[1] == NULL) { + port = is_control ? DEFAULT_CONTROL_PORT : DEFAULT_PORT; + } + else { + port = strtoul (vec[1], &err_str, 10); + if (*err_str != '\0') { + fprintf (stderr, "cannot connect to rspamd server: %s, at pos %s\n", connect_str, err_str); + exit (EXIT_FAILURE); + } + } + + if (! rspamd_add_server (vec[0], port, port, &err)) { + fprintf (stderr, "cannot connect to rspamd server: %s, error: %s\n", connect_str, err->message); + exit (EXIT_FAILURE); + } +} + +static void +show_metric_result (gpointer key, gpointer value, gpointer ud) +{ + struct rspamd_metric *metric = value; + struct rspamd_symbol *s; + GList *cur; + GHashTableIter it; + gpointer k, v; + gboolean first; + + if (metric->is_skipped) { + PRINT_FUNC ("%s: Skipped\n", key); + } + else { + if (tty) { + PRINT_FUNC ("\033[1m%s:\033[0m %s [ %.2f / %.2f ]\n", key, + metric->score > metric->required_score ? "True" : "False", + metric->score, metric->required_score); + } + else { + PRINT_FUNC ("%s: %s [ %.2f / %.2f ]\n", key, + metric->score > metric->required_score ? "True" : "False", + metric->score, metric->required_score); + } + if (tty) { + if (metric->action) { + PRINT_FUNC ("\033[1mAction:\033[0m %s\n", metric->action); + } + PRINT_FUNC ("\033[1mSymbols: \033[0m"); + } + else { + if (metric->action) { + PRINT_FUNC ("Action: %s\n", metric->action); + } + PRINT_FUNC ("Symbols: "); + } + if (metric->symbols) { + first = TRUE; + g_hash_table_iter_init (&it, metric->symbols); + while (g_hash_table_iter_next (&it, &k, &v)) { + s = v; + if (! first) { + PRINT_FUNC (", "); + } + else { + first = FALSE; + } + PRINT_FUNC ("%s(%.2f)", s->name, s->weight); + if (s->options) { + PRINT_FUNC ("("); + cur = g_list_first (s->options); + while (cur) { + if (cur->next) { + PRINT_FUNC ("%s,", cur->data); + } + else { + PRINT_FUNC ("%s)", cur->data); + } + cur = g_list_next (cur); + } + } + + } + } + PRINT_FUNC ("\n"); + } +} + +static void +print_rspamd_result (struct rspamd_result *res) +{ + GHashTableIter it; + gpointer k, v; + + g_assert (res != 0); + + if (tty) { + printf ("\033[1m"); + } + PRINT_FUNC ("Results for host: %s\n\n", connect_str); + if (tty) { + printf ("\033[0m"); + } + g_hash_table_foreach (res->metrics, show_metric_result, NULL); + /* Show other headers */ + g_hash_table_iter_init (&it, res->headers); + PRINT_FUNC ("\n"); + while (g_hash_table_iter_next (&it, &k, &v)) { + if (tty) { + PRINT_FUNC ("\033[1m%s:\033[0m %s\n", k, v); + } + else { + PRINT_FUNC ("%s: %s\n", k, v); + } + } + PRINT_FUNC ("\n"); +} + +static void +add_options (GHashTable *opts) +{ + if (ip != NULL) { + g_hash_table_insert (opts, "Ip", ip); + } + if (pass_all) { + g_hash_table_insert (opts, "Pass", "all"); + } +} + +/* + * Scan STDIN + */ +static void +scan_rspamd_stdin () +{ + gchar *in_buf; + + gint r = 0, len; + GError *err = NULL; + struct rspamd_result *res; + GHashTable *opts; + + /* Init options hash */ + opts = g_hash_table_new (g_str_hash, g_str_equal); + add_options (opts); + /* Add server */ + add_rspamd_server (FALSE); + + /* 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); + } + } + res = rspamd_scan_memory (in_buf, r, opts, &err); + g_hash_table_destroy (opts); + if (err != NULL) { + fprintf (stderr, "cannot scan message: %s\n", err->message); + exit (EXIT_FAILURE); + } + print_rspamd_result (res); +} + +static void +scan_rspamd_file (const gchar *file) +{ + GError *err = NULL; + struct rspamd_result *res; + GHashTable *opts; + + /* Init options hash */ + opts = g_hash_table_new (g_str_hash, g_str_equal); + add_options (opts); + + /* Add server */ + add_rspamd_server (FALSE); + + res = rspamd_scan_file (file, opts, &err); + g_hash_table_destroy (opts); + if (err != NULL) { + fprintf (stderr, "cannot scan message: %s\n", err->message); + exit (EXIT_FAILURE); + } + print_rspamd_result (res); +} + +gint +main (gint argc, gchar **argv, gchar **env) +{ + enum rspamc_command cmd; + gint i; + + rspamd_client_init (); + + read_cmd_line (&argc, &argv); + tty = isatty (STDOUT_FILENO); + /* Now read other args from argc and argv */ + if (argc == 1) { + /* No args, just read stdin */ + scan_rspamd_stdin (); + } + else if (argc == 2) { + /* 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) { + scan_rspamd_stdin (); + } + else { + /* XXX: implement this */ + } + } + else { + scan_rspamd_file (argv[1]); + } + } + else { + 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]); + } + else { + /* XXX: implement this */ + } + } + } + else { + for (i = 1; i < argc; i ++) { + scan_rspamd_file (argv[i]); + } + } + } + + + return 0; +} -- 2.39.5