ENDIF(ENABLE_PERL MATCHES "ON")
ADD_SUBDIRECTORY(src/lua)
ADD_SUBDIRECTORY(lib)
+ADD_SUBDIRECTORY(src/client)
ADD_SUBDIRECTORY(src/json)
# ADD_SUBDIRECTORY(src/evdns)
# 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})
}
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;
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;
}
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;
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;
/* Skip spaces */
if (!g_ascii_isspace (*p)) {
state = next_state;
+ c = p;
}
else {
p ++;
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;
}
}
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);
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;
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 */
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:
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;
}
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 */
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 == '(') {
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;
/* 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;
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;
}
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;
}
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);
}
/* Move remaining buffer to the begin of string */
c->in_buf = g_string_erase (c->in_buf, 0, len);
+ len = 0;
}
else {
return FALSE;
/* 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;
}
/*
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)
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)
{
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;
}
/*
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);
}
/*
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;
}
/*
--- /dev/null
+# 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})
--- /dev/null
+/*
+ * 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;
+}