From bc15f7bcfbe45c51917d153b8174e6b76b5e00bb Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Fri, 28 Jan 2011 21:30:34 +0300 Subject: [PATCH] * Initial release of librspamdclient --- CMakeLists.txt | 1 + lib/CMakeLists.txt | 7 + lib/librspamdclient.c | 930 ++++++++++++++++++++++++++++++++++++++++++ lib/librspamdclient.h | 94 +++++ 4 files changed, 1032 insertions(+) create mode 100644 lib/CMakeLists.txt create mode 100644 lib/librspamdclient.c create mode 100644 lib/librspamdclient.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 17dd4d738..6ee3cf56a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -583,6 +583,7 @@ 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/json) # ADD_SUBDIRECTORY(src/evdns) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt new file mode 100644 index 000000000..e57d18a2e --- /dev/null +++ b/lib/CMakeLists.txt @@ -0,0 +1,7 @@ +# Librspamd +SET(LIBRSPAMDSRC librspamdclient.c ../src/util.c ../src/upstream.c) + +ADD_LIBRARY(rspamdclient SHARED ${LIBRSPAMDSRC}) +ADD_LIBRARY(rspamdclient_static STATIC ${LIBRSPAMDSRC}) +SET_TARGET_PROPERTIES(rspamdclient PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing -I../src/") +SET_TARGET_PROPERTIES(rspamdclient_static PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing -I../src/") \ No newline at end of file diff --git a/lib/librspamdclient.c b/lib/librspamdclient.c new file mode 100644 index 000000000..8fa280440 --- /dev/null +++ b/lib/librspamdclient.c @@ -0,0 +1,930 @@ +/* + * 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 "librspamdclient.h" +#include "config.h" +#include "upstream.h" +#include "util.h" +#include "cfg_file.h" + +#define MAX_RSPAMD_SERVERS 255 +#define DEFAULT_CONNECT_TIMEOUT 500 +#define DEFAULT_READ_TIMEOUT 5000 +#define G_RSPAMD_ERROR rspamd_error_quark () + +struct rspamd_server { + struct upstream up; + struct in_addr addr; + guint16 client_port; + guint16 controller_port; + gchar *name; +}; + +struct rspamd_client { + struct rspamd_server servers[MAX_RSPAMD_SERVERS]; + guint servers_num; + guint connect_timeout; + guint read_timeout; +}; + +struct rspamd_connection { + struct rspamd_server *server; + time_t connection_time; + gint socket; + struct rspamd_result *result; + GString *in_buf; + struct rspamd_metric *cur_metric; +}; + +static struct rspamd_client *client = NULL; + +/** Private functions **/ +static inline GQuark +rspamd_error_quark (void) +{ + return g_quark_from_static_string ("g-rspamd-error-quark"); +} + +static void +metric_free_func (gpointer arg) +{ + struct rspamd_metric *m = arg; + + g_hash_table_destroy (m->symbols); + g_free (m->name); + g_free (m); +} + +static void +symbol_free_func (gpointer arg) +{ + struct rspamd_symbol *s = arg; + GList *cur = s->options; + + while (cur) { + g_free (cur->data); + cur = g_list_next (cur); + } + g_list_free (s->options); + g_free (s->name); + g_free (s); +} + +static struct rspamd_connection * +rspamd_connect_random_server (guint16 port, GError **err) +{ + struct rspamd_server *selected = NULL; + struct rspamd_connection *new; + time_t now; + + if (client->servers_num == 0) { + errno = EINVAL; + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, 1, "No servers can be reached"); + } + return NULL; + } + /* Select random server */ + now = time (NULL); + selected = (struct rspamd_server *)get_random_upstream (client->servers, + client->servers_num, sizeof (struct rspamd_server), + now, DEFAULT_UPSTREAM_ERROR_TIME, DEFAULT_UPSTREAM_DEAD_TIME, DEFAULT_UPSTREAM_MAXERRORS); + if (selected == NULL) { + errno = EINVAL; + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, 1, "No servers can be reached"); + } + return NULL; + } + /* Allocate connection */ + new = g_malloc (sizeof (struct rspamd_connection)); + new->server = selected; + new->connection_time = now; + /* Create socket */ + new->socket = make_tcp_socket (&selected->addr, port, FALSE, TRUE); + if (new->socket == -1) { + goto err; + } + /* Poll waiting for writing */ + if (poll_sync_socket (new->socket, client->connect_timeout , POLLOUT) <= 0) { + errno = ETIMEDOUT; + goto err; + } + + new->in_buf = g_string_sized_new (BUFSIZ); + return new; + +err: + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Could not connect to server %s: %s", + selected->name, strerror (errno)); + } + upstream_fail (&selected->up, now); + g_free (new); + return NULL; +} + +static struct rspamd_metric * +rspamd_create_metric (const gchar *begin, guint len) +{ + struct rspamd_metric *new; + + new = g_malloc0 (sizeof (struct rspamd_metric)); + new->symbols = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, symbol_free_func); + new->name = g_malloc (len + 1); + memcpy (new->name, begin, len); + new->name[len] = '\0'; + + return new; +} + +/* + * Parse line like RSPAMD/{version} {code} {message} + */ +static gboolean +parse_rspamd_first_line (struct rspamd_connection *c, guint len, GError **err) +{ + gchar *b = c->in_buf->str + sizeof("RSPAMD/") - 1, *p; + guint remain = len - sizeof("RSPAMD/") + 1, state = 0, next_state; + + p = b; + while (p - b < remain) { + switch (state) { + case 0: + /* Read version */ + if (g_ascii_isspace (*p)) { + state = 99; + next_state = 1; + } + else if (!g_ascii_isdigit (*p) && *p != '.') { + goto err; + } + p ++; + break; + case 1: + /* Read code */ + if (g_ascii_isspace (*p)) { + state = 99; + next_state = 2; + } + else if (!g_ascii_isdigit (*p)) { + goto err; + } + p ++; + break; + case 2: + /* Read message */ + if (g_ascii_isspace (*p) || p - b == remain - 1) { + state = 99; + next_state = 3; + } + p ++; + break; + case 3: + goto err; + case 99: + /* Skip spaces */ + if (!g_ascii_isspace (*p)) { + state = next_state; + } + else { + p ++; + } + break; + } + } + + if (state != 99) { + goto err; + } + +err: + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid protocol line: %*s at pos: %d", + len, b, (int)(p - b)); + } + upstream_fail (&c->server->up, c->connection_time); + return FALSE; +} + +/* + * Parse line like Metric: ; ; / [ / ] + */ +static gboolean +parse_rspamd_metric_line (struct rspamd_connection *conn, guint len, GError **err) +{ + gchar *b = conn->in_buf->str + sizeof("Metric:") - 1, *p, *c, *err_str; + guint remain = len - sizeof("Metric:") + 1, state = 0, next_state; + struct rspamd_metric *new; + + p = b; + c = b; + while (p - b < remain) { + switch (state) { + case 0: + /* Read metric's name */ + if (g_ascii_isspace (*p)) { + state = 99; + next_state = 0; + } + else if (*p == ';') { + if (p - c <= 1) { + /* Empty metric name */ + goto err; + } + else { + /* Create new metric */ + new = rspamd_create_metric (c, p - c - 1); + if (g_hash_table_lookup (conn->result->metrics, new->name) != NULL) { + /* Duplicate metric */ + metric_free_func (new); + goto err; + } + g_hash_table_insert (conn->result->metrics, new->name, new); + conn->cur_metric = new; + state = 99; + next_state = 1; + } + } + p ++; + break; + case 1: + /* Read boolean result */ + if (*p == ';') { + if (p - c > sizeof("Skip")) { + if (memcmp (c, "Skip", p - c - 1) == 0) { + new->is_skipped = TRUE; + } + state = 99; + next_state = 2; + } + } + p ++; + break; + case 2: + /* Read score */ + if (g_ascii_isspace (*p)) { + new->score = strtod (c, &err_str); + if (*err_str != *p) { + /* Invalid score */ + goto err; + } + state = 99; + next_state = 3; + } + p ++; + break; + case 3: + /* Read / */ + if (*p != '/') { + goto err; + } + else if (g_ascii_isspace (*p)) { + state = 99; + next_state = 4; + } + p ++; + break; + case 4: + /* Read required score */ + if (g_ascii_isspace (*p)) { + new->required_score = strtod (c, &err_str); + if (*err_str != *p) { + /* Invalid score */ + goto err; + } + state = 99; + next_state = 5; + } + p ++; + break; + case 5: + /* Read / if it exists */ + if (*p != '/') { + goto err; + } + else if (g_ascii_isspace (*p)) { + state = 99; + next_state = 6; + } + p ++; + break; + case 6: + /* Read reject score */ + if (g_ascii_isspace (*p) || p - b == remain - 1) { + new->reject_score = strtod (c, &err_str); + if (*err_str != *p && *err_str != *(p + 1)) { + /* Invalid score */ + goto err; + } + state = 99; + } + p ++; + break; + case 99: + /* Skip spaces */ + if (!g_ascii_isspace (*p)) { + state = next_state; + c = p; + } + else { + p ++; + } + break; + } + } + + if (state != 99) { + goto err; + } + +err: + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid metric line: %*s at pos: %d", + len, b, (int)(p - b)); + } + upstream_fail (&conn->server->up, conn->connection_time); + return FALSE; +} + +/* + * Parse line like Symbol: [(score)]; [option1, [option2 ...]] + */ +static gboolean +parse_rspamd_symbol_line (struct rspamd_connection *conn, guint len, GError **err) +{ + gchar *b = conn->in_buf->str + sizeof("Symbol:") - 1, *p, *c, *err_str, *sym; + guint remain = len - sizeof("Symbol:") + 1, state = 0, next_state, l; + struct rspamd_symbol *new; + + p = b; + c = b; + while (p - b < remain) { + switch (state) { + case 0: + /* Read symbol's name */ + if (g_ascii_isspace (*p)) { + state = 99; + next_state = 0; + } + else if (*p == ';' || *p == '(') { + if (p - c <= 1) { + /* Empty symbol name */ + goto err; + } + else { + /* Create new symbol */ + sym = g_malloc (p - c); + sym[p - c - 1] = '\0'; + memcpy (sym, c, p - c - 1); + + if (g_hash_table_lookup (conn->cur_metric->symbols, sym) != NULL) { + /* Duplicate symbol */ + g_free (sym); + goto err; + } + new = g_malloc0 (sizeof (struct rspamd_symbol)); + g_hash_table_insert (conn->cur_metric->symbols, sym, new); + state = 99; + if (*p == '(') { + next_state = 1; + new->weight = 0; + } + else { + next_state = 2; + } + } + } + p ++; + break; + case 1: + /* Read symbol's weight */ + if (*p == ')') { + new->weight = strtod (c, &err_str); + if (*err_str != *p) { + /* Invalid weight */ + goto err; + } + if (*(p + 1) == ';') { + p ++; + } + state = 99; + next_state = 2; + } + p ++; + break; + case 2: + /* Read option */ + if (*p == ',' || p - b == remain - 1) { + /* Insert option into linked list */ + l = p - c; + if (p - b == remain - 1) { + l ++; + } + sym = g_malloc (l + 1); + sym[l] = '\0'; + memcpy (sym, c, l); + new->options = g_list_prepend (new->options, sym); + state = 99; + next_state = 2; + } + p ++; + break; + case 99: + /* Skip spaces */ + if (!g_ascii_isspace (*p)) { + state = next_state; + c = p; + } + else { + p ++; + } + break; + } + } + + if (state != 99) { + goto err; + } + + err: + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid symbol line: %*s at pos: %d", + len, b, (int)(p - b)); + } + upstream_fail (&conn->server->up, conn->connection_time); + return FALSE; +} + +/* + * Parse line like Action: + */ +static gboolean +parse_rspamd_action_line (struct rspamd_connection *conn, guint len, GError **err) +{ + gchar *b = conn->in_buf->str + sizeof("Action:") - 1, *p, *c, *sym; + guint remain = len - sizeof("Action:") + 1, state = 0, next_state; + + p = b; + c = b; + while (p - b < remain) { + switch (state) { + case 0: + /* Read action */ + if (g_ascii_isspace (*p)) { + state = 99; + next_state = 0; + } + else 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); + + conn->cur_metric->action = sym; + state = 99; + } + } + p ++; + break; + case 99: + /* Skip spaces */ + if (!g_ascii_isspace (*p)) { + state = next_state; + c = p; + } + else { + p ++; + } + break; + } + } + + if (state != 99) { + goto err; + } + + err: + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid action line: %*s at pos: %d", + len, b, (int)(p - b)); + } + upstream_fail (&conn->server->up, conn->connection_time); + return FALSE; +} + +/* + * Parse line like Header: + */ +static gboolean +parse_rspamd_header_line (struct rspamd_connection *conn, guint len, GError **err) +{ + gchar *b = conn->in_buf->str, *p, *c, *hname = NULL, *hvalue = NULL; + guint remain = len, state = 0, next_state; + + p = b; + c = b; + while (p - b < remain) { + switch (state) { + case 0: + /* Read header name */ + if (*p == ':') { + if (p - c <= 1) { + /* Empty header name */ + goto err; + } + else { + /* Create header name */ + hname = g_malloc (p - c); + hname[p - c - 1] = '\0'; + memcpy (hname, c, p - c - 1); + next_state = 1; + state = 99; + } + } + p ++; + break; + case 1: + if (p - b == remain - 1) { + if (p - c <= 1) { + /* Empty action name */ + goto err; + } + else { + /* Create header value */ + hvalue = g_malloc (p - c + 1); + hvalue[p - c] = '\0'; + memcpy (hvalue, c, p - c); + g_hash_table_replace (conn->result->headers, hname, hvalue); + state = 99; + } + } + p ++; + break; + case 99: + /* Skip spaces */ + if (!g_ascii_isspace (*p)) { + state = next_state; + c = p; + } + else { + p ++; + } + break; + } + } + + if (state != 99) { + goto err; + } + + err: + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Invalid header line: %*s at pos: %d", + len, b, (int)(p - b)); + } + if (hname) { + g_free (hname); + } + if (hvalue) { + g_free (hvalue); + } + upstream_fail (&conn->server->up, conn->connection_time); + return FALSE; +} + +static gboolean +parse_rspamd_reply_line (struct rspamd_connection *c, guint len, GError **err) +{ + gchar *p = c->in_buf->str; + + g_assert (len > 0); + + /* + * In fact we have 3 states of parsing: + * 1) we have current metric and parse symbols + * 2) we have no current metric and skip everything to headers hash + * 3) we have current metric but got not symbol but header -> put it into headers hash + * Line is parsed using specific state machine + */ + if (c->cur_metric == NULL) { + if (len > sizeof ("RSPAMD/") && memcmp (p, "RSPAMD/", sizeof ("RSPAMD/") - 1) == 0) { + return parse_rspamd_first_line (c, len, err); + } + else if (len > sizeof ("Metric:") && memcmp (p, "Metric:", sizeof("Metric:") - 1) == 0) { + return parse_rspamd_metric_line (c, len, err); + } + else { + return parse_rspamd_header_line (c, len, err); + } + } + else { + if (len > sizeof ("Metric:") && memcmp (p, "Metric:", sizeof("Metric:") - 1) == 0) { + return parse_rspamd_metric_line (c, len, err); + } + else if (len > sizeof ("Symbol:") && memcmp (p, "Symbol:", sizeof("Symbol:") - 1) == 0) { + return parse_rspamd_symbol_line (c, len, err); + } + else if (len > sizeof ("Action:") && memcmp (p, "Action:", sizeof("Action:") - 1) == 0) { + return parse_rspamd_action_line (c, len, err); + } + else { + return parse_rspamd_header_line (c, len, err); + } + } + + /* Not reached */ + return FALSE; +} + +static gboolean +read_rspamd_reply_line (struct rspamd_connection *c, GError **err) +{ + gint len, r; + char p; + + /* Try to obtain string from the input buffer */ + if (c->in_buf->len > 0) { + len = 0; + while (len < c->in_buf->len) { + p = c->in_buf->str[len]; + if (p == '\r' || p == '\n') { + if (parse_rspamd_reply_line (c, len, err)) { + /* Strip '\r\n' */ + while (len < c->in_buf->len && (p == '\r' || p == '\n')) { + p = c->in_buf->str[++len]; + } + /* Move remaining buffer to the begin of string */ + c->in_buf = g_string_erase (c->in_buf, 0, len); + } + else { + return FALSE; + } + } + len ++; + } + } + /* Poll socket */ + if (poll_sync_socket (c->socket, client->read_timeout, POLL_IN) == -1) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Could not connect to server %s: %s", + c->server->name, strerror (errno)); + } + upstream_fail (&c->server->up, c->connection_time); + return FALSE; + } + if (c->in_buf->allocated_len - c->in_buf->len < BUFSIZ / 2) { + /* Grow buffer */ + c->in_buf = g_string_set_size (c->in_buf, c->in_buf->allocated_len * 2); + } + /* 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) { + /* Try to parse remaining data */ + return parse_rspamd_reply_line (c, c->in_buf->len, err); + } + + return TRUE; +} + +/* + * More or less portable version of sendfile + */ +static gboolean +rspamd_sendfile (gint sock, gint fd, GError **err) +{ + + make_socket_blocking (sock); +#ifdef HAVE_SENDFILE +# if defined(FREEBSD) || defined(DARWIN) + off_t off = 0; +# if defined(FREEBSD) + /* FreeBSD version */ + if (sendfile (sock, fd, 0, 0, NULL, &off, 0) != 0) { +# elif defined(DARWIN) + /* Darwin version */ + if (sendfile (sock, fd, 0, &off, NULL, 0) != 0) { +# endif + goto err; + } +# else + ssize_t r; + off_t off = 0; + struct stat st; + + fstat (fd, &st); + /* Linux version */ + r = sendfile (fd, sock, &off, st.st_size); + if (r == -1) { + goto err; + } +# endif +#else + /* Emulating version */ + ssize_t r; + gchar buf[BUFSIZ]; + + while ((r = read (fd, buf, sizeof (buf))) > 0) { + if ((r = write (sock, buf, r)) != r) { + goto err; + } + } +#endif + make_socket_nonblocking (sock); + + return TRUE; + +err: + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, errno, "Sendfile error: %s", + strerror (errno)); + } + return FALSE; +} + +static void +rspamd_free_connection (struct rspamd_connection *c) +{ + make_socket_blocking (c->socket); + (void)close (c->socket); + g_string_free (c->in_buf, TRUE); + + g_free (c); +} + +/** Public API **/ + +/* + * Init rspamd client library + */ +void +rspamd_client_init (void) +{ + if (client != NULL) { + rspamd_client_close (); + } + client = g_malloc0 (sizeof (struct rspamd_client)); + client->read_timeout = DEFAULT_READ_TIMEOUT; + client->connect_timeout = DEFAULT_CONNECT_TIMEOUT; +} + +/* + * Add rspamd server + */ +gboolean +rspamd_add_server (const gchar *host, guint16 port, guint16 controller_port, GError **err) +{ + struct rspamd_server *new; + struct hostent *hent; + + g_assert (client != NULL); + if (client->servers_num >= MAX_RSPAMD_SERVERS) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, 1, "Maximum number of servers reached: %d", MAX_RSPAMD_SERVERS); + } + return FALSE; + } + new = &client->servers[client->servers_num]; + + if (!inet_aton (host, &new->addr)) { + /* Try to call gethostbyname */ + hent = gethostbyname (host); + if (hent == NULL) { + if (*err == NULL) { + *err = g_error_new (G_RSPAMD_ERROR, 1, "Cannot resolve: %s", host); + } + return FALSE; + } + else { + memcpy (&new->addr, hent->h_addr, sizeof (struct in_addr)); + } + } + new->client_port = port; + new->controller_port = controller_port; + new->name = g_strdup (host); + + client->servers_num ++; + return TRUE; +} + +/* + * Set timeouts (values in milliseconds) + */ +void +rspamd_set_timeout (guint connect_timeout, guint read_timeout) +{ + g_assert (client != NULL); + + if (connect_timeout > 0) { + client->connect_timeout = connect_timeout; + } + if (read_timeout > 0) { + client->read_timeout = read_timeout; + } +} + +/* + * Scan message from memory + */ +struct rspamd_result * +rspamd_scan_memory (const guchar *message, gsize length, GHashTable *headers, GError **err) +{ + g_assert (client != NULL); + +} + +/* + * Scan message from file + */ +struct rspamd_result * +rspamd_scan_file (const guchar *filename, GHashTable *headers, GError **err) +{ + g_assert (client != NULL); + +} + +/* + * Scan message from fd + */ +struct rspamd_result * +rspamd_scan_fd (int fd, GHashTable *headers, GError **err) +{ + g_assert (client != NULL); + +} + +/* + * Learn message from memory + */ +gboolean +rspamd_learn_memory (const guchar *message, gsize length, const gchar *symbol, GError **err) +{ + g_assert (client != NULL); + +} + +/* + * Learn message from file + */ +gboolean +rspamd_learn_file (const guchar *filename, const gchar *symbol, GError **err) +{ + g_assert (client != NULL); + +} + +/* + * Learn message from fd + */ +gboolean +rspamd_learn_fd (int fd, const gchar *symbol, GError **err) +{ + g_assert (client != NULL); + +} + +/* + * Free results + */ +void +rspamd_free_result (struct rspamd_result *result) +{ + g_assert (client != NULL); + +} + +/* + * Close library and free associated resources + */ +void +rspamd_client_close (void) +{ + g_assert (client != NULL); +} diff --git a/lib/librspamdclient.h b/lib/librspamdclient.h new file mode 100644 index 000000000..fc5a6f2ca --- /dev/null +++ b/lib/librspamdclient.h @@ -0,0 +1,94 @@ +#ifndef LIBRSPAMD_CLIENT_H +#define LIBRSPAMD_CLIENT_H + +#include + +/** + * Struct for representing symbols + */ +struct rspamd_symbol { + gchar *name; /**< name */ + double weight; /**< weight */ + GList *options; /**< List of options (as const gchar *) */ +}; + +/** + * Struct for representing metrics + */ +struct rspamd_metric { + gchar *name; + gchar *action; + double score; + double required_score; + double reject_score; + gboolean is_skipped; + GHashTable *symbols; +}; + +struct rspamd_connection; +/** + * Result of scan + */ +struct rspamd_result { + struct rspamd_connection *conn; + gboolean is_ok; + GHashTable *metrics; + GHashTable *headers; +}; + +/* + * Init rspamd client library + */ +void rspamd_client_init (void); + +/* + * Add rspamd server + */ +gboolean rspamd_add_server (const gchar *host, guint16 port, guint16 controller_port, GError **err); + +/* + * Set timeouts (values in milliseconds) + */ +void rspamd_set_timeout (guint connect_timeout, guint read_timeout); + +/* + * Scan message from memory + */ +struct rspamd_result * rspamd_scan_memory (const guchar *message, gsize length, GHashTable *headers, GError **err); + +/* + * Scan message from file + */ +struct rspamd_result * rspamd_scan_file (const guchar *filename, GHashTable *headers, GError **err); + +/* + * Scan message from fd + */ +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); + +/* + * Learn message from file + */ +gboolean rspamd_learn_file (const guchar *filename, const gchar *symbol, GError **err); + +/* + * Learn message from fd + */ +gboolean rspamd_learn_fd (int fd, const gchar *symbol, GError **err); + +/* + * Free results + */ +void rspamd_free_result (struct rspamd_result *result); + +/* + * Close library and free associated resources + */ +void rspamd_client_close (void); + +#endif -- 2.39.5