123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878 |
- /*
- * Copyright (c) 2009, Rambler media
- * 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 BY Rambler media ''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 Rambler 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 "main.h"
- #include "util.h"
- #include "cfg_file.h"
- #include "settings.h"
- #include "message.h"
-
- /* Max line size as it is defined in rfc2822 */
- #define OUTBUFSIZ 1000
- /*
- * Just check if the passed message is spam or not and reply as
- * described below
- */
- #define MSG_CMD_CHECK "check"
- /*
- * Check if message is spam or not, and return score plus list
- * of symbols hit
- */
- #define MSG_CMD_SYMBOLS "symbols"
- /*
- * Check if message is spam or not, and return score plus report
- */
- #define MSG_CMD_REPORT "report"
- /*
- * Check if message is spam or not, and return score plus report
- * if the message is spam
- */
- #define MSG_CMD_REPORT_IFSPAM "report_ifspam"
- /*
- * Ignore this message -- client opened connection then changed
- */
- #define MSG_CMD_SKIP "skip"
- /*
- * Return a confirmation that spamd is alive
- */
- #define MSG_CMD_PING "ping"
- /*
- * Process this message as described above and return modified message
- */
- #define MSG_CMD_PROCESS "process"
-
- /*
- * spamassassin greeting:
- */
- #define SPAMC_GREETING "SPAMC"
- /*
- * rspamd greeting:
- */
- #define RSPAMC_GREETING "RSPAMC"
- /*
- * Headers
- */
- #define CONTENT_LENGTH_HEADER "Content-Length"
- #define HELO_HEADER "Helo"
- #define FROM_HEADER "From"
- #define IP_ADDR_HEADER "IP"
- #define NRCPT_HEADER "Recipient-Number"
- #define RCPT_HEADER "Rcpt"
- #define SUBJECT_HEADER "Subject"
- #define QUEUE_ID_HEADER "Queue-ID"
- #define ERROR_HEADER "Error"
- #define USER_HEADER "User"
- #define PASS_HEADER "Pass"
- #define DELIVER_TO_HEADER "Deliver-To"
-
- static GList *custom_commands = NULL;
-
- /* For default metric, dirty hack, but much faster than hash lookup */
- static double default_score, default_required_score;
-
- static char *
- separate_command (f_str_t * in, char c)
- {
- int r = 0;
- char *p = in->begin, *b;
- b = p;
-
- while (r < in->len) {
- if (*p == c) {
- *p = '\0';
- in->begin = p + 1;
- in->len -= r + 1;
- return b;
- }
- p++;
- r++;
- }
-
- return NULL;
- }
-
- static int
- parse_command (struct worker_task *task, f_str_t * line)
- {
- char *token;
- struct custom_command *cmd;
- GList *cur;
-
- task->proto_ver = RSPAMC_PROTO_1_1;
- token = separate_command (line, ' ');
- if (line == NULL || token == NULL) {
- debug_task ("bad command: %s", token);
- return -1;
- }
-
- switch (token[0]) {
- case 'c':
- case 'C':
- /* check */
- if (g_ascii_strcasecmp (token + 1, MSG_CMD_CHECK + 1) == 0) {
- task->cmd = CMD_CHECK;
- }
- else {
- debug_task ("bad command: %s", token);
- return -1;
- }
- break;
- case 's':
- case 'S':
- /* symbols, skip */
- if (g_ascii_strcasecmp (token + 1, MSG_CMD_SYMBOLS + 1) == 0) {
- task->cmd = CMD_SYMBOLS;
- }
- else if (g_ascii_strcasecmp (token + 1, MSG_CMD_SKIP + 1) == 0) {
- task->cmd = CMD_SKIP;
- }
- else {
- debug_task ("bad command: %s", token);
- return -1;
- }
- break;
- case 'p':
- case 'P':
- /* ping, process */
- if (g_ascii_strcasecmp (token + 1, MSG_CMD_PING + 1) == 0) {
- task->cmd = CMD_PING;
- }
- else if (g_ascii_strcasecmp (token + 1, MSG_CMD_PROCESS + 1) == 0) {
- task->cmd = CMD_PROCESS;
- }
- else {
- debug_task ("bad command: %s", token);
- return -1;
- }
- break;
- case 'r':
- case 'R':
- /* report, report_ifspam */
- if (g_ascii_strcasecmp (token + 1, MSG_CMD_REPORT + 1) == 0) {
- task->cmd = CMD_REPORT;
- }
- else if (g_ascii_strcasecmp (token + 1, MSG_CMD_REPORT_IFSPAM + 1) == 0) {
- task->cmd = CMD_REPORT_IFSPAM;
- }
- else {
- debug_task ("bad command: %s", token);
- return -1;
- }
- break;
- default:
- cur = custom_commands;
- while (cur) {
- cmd = cur->data;
- if (g_ascii_strcasecmp (token, cmd->name) == 0) {
- task->cmd = CMD_OTHER;
- task->custom_cmd = cmd;
- break;
- }
- cur = g_list_next (cur);
- }
-
- if (cur == NULL) {
- debug_task ("bad command: %s", token);
- return -1;
- }
- break;
- }
-
- if (g_ascii_strncasecmp (line->begin, RSPAMC_GREETING, sizeof (RSPAMC_GREETING) - 1) == 0) {
- task->proto = RSPAMC_PROTO;
- task->proto_ver = RSPAMC_PROTO_1_0;
- if (*(line->begin + sizeof (RSPAMC_GREETING) - 1) == '/') {
- /* Extract protocol version */
- token = line->begin + sizeof (RSPAMC_GREETING);
- if (strncmp (token, RSPAMC_PROTO_1_1, sizeof (RSPAMC_PROTO_1_1) - 1) == 0) {
- task->proto_ver = RSPAMC_PROTO_1_1;
- }
- }
- }
- else if (g_ascii_strncasecmp (line->begin, SPAMC_GREETING, sizeof (SPAMC_GREETING) - 1) == 0) {
- task->proto = SPAMC_PROTO;
- }
- else {
- return -1;
- }
-
- task->state = READ_HEADER;
-
- return 0;
- }
-
- static int
- parse_header (struct worker_task *task, f_str_t * line)
- {
- char *headern, *err, *tmp;
-
- /* Check end of headers */
- if (line->len == 0) {
- debug_task ("got empty line, assume it as end of headers");
- if (task->cmd == CMD_PING || task->cmd == CMD_SKIP) {
- task->state = WRITE_REPLY;
- }
- else {
- if (task->content_length > 0) {
- rspamd_set_dispatcher_policy (task->dispatcher, BUFFER_CHARACTER, task->content_length);
- task->state = READ_MESSAGE;
- }
- else {
- task->last_error = "Unknown content length";
- task->error_code = RSPAMD_LENGTH_ERROR;
- task->state = WRITE_ERROR;
- return -1;
- }
- }
- return 0;
- }
-
- headern = separate_command (line, ':');
-
- if (line == NULL || headern == NULL) {
- return -1;
- }
- /* Eat whitespaces */
- g_strstrip (headern);
- fstrstrip (line);
-
- switch (headern[0]) {
- case 'c':
- case 'C':
- /* content-length */
- if (g_ascii_strncasecmp (headern, CONTENT_LENGTH_HEADER, sizeof (CONTENT_LENGTH_HEADER) - 1) == 0) {
- if (task->content_length == 0) {
- tmp = memory_pool_fstrdup (task->task_pool, line);
- task->content_length = strtoul (tmp, &err, 10);
- debug_task ("read Content-Length header, value: %lu", (unsigned long int)task->content_length);
- }
- }
- else {
- msg_info ("wrong header: %s", headern);
- return -1;
- }
- break;
- case 'd':
- case 'D':
- /* Deliver-To */
- if (g_ascii_strncasecmp (headern, DELIVER_TO_HEADER, sizeof (DELIVER_TO_HEADER) - 1) == 0) {
- task->deliver_to = memory_pool_fstrdup (task->task_pool, line);
- debug_task ("read deliver-to header, value: %s", task->deliver_to);
- }
- else {
- msg_info ("wrong header: %s", headern);
- return -1;
- }
- break;
- case 'h':
- case 'H':
- /* helo */
- if (g_ascii_strncasecmp (headern, HELO_HEADER, sizeof (HELO_HEADER) - 1) == 0) {
- task->helo = memory_pool_fstrdup (task->task_pool, line);
- debug_task ("read helo header, value: %s", task->helo);
- }
- else {
- msg_info ("wrong header: %s", headern);
- return -1;
- }
- break;
- case 'f':
- case 'F':
- /* from */
- if (g_ascii_strncasecmp (headern, FROM_HEADER, sizeof (FROM_HEADER) - 1) == 0) {
- task->from = memory_pool_fstrdup (task->task_pool, line);
- debug_task ("read from header, value: %s", task->from);
- }
- else {
- msg_info ("wrong header: %s", headern);
- return -1;
- }
- break;
- case 'q':
- case 'Q':
- /* Queue id */
- if (g_ascii_strncasecmp (headern, QUEUE_ID_HEADER, sizeof (QUEUE_ID_HEADER) - 1) == 0) {
- task->queue_id = memory_pool_fstrdup (task->task_pool, line);
- debug_task ("read queue_id header, value: %s", task->queue_id);
- }
- else {
- msg_info ("wrong header: %s", headern);
- return -1;
- }
- break;
- case 'r':
- case 'R':
- /* rcpt */
- if (g_ascii_strncasecmp (headern, RCPT_HEADER, sizeof (RCPT_HEADER) - 1) == 0) {
- tmp = memory_pool_fstrdup (task->task_pool, line);
- task->rcpt = g_list_prepend (task->rcpt, tmp);
- debug_task ("read rcpt header, value: %s", tmp);
- }
- else if (g_ascii_strncasecmp (headern, NRCPT_HEADER, sizeof (NRCPT_HEADER) - 1) == 0) {
- tmp = memory_pool_fstrdup (task->task_pool, line);
- task->nrcpt = strtoul (tmp, &err, 10);
- debug_task ("read rcpt header, value: %d", (int)task->nrcpt);
- }
- else {
- msg_info ("wrong header: %s", headern);
- return -1;
- }
- break;
- case 'i':
- case 'I':
- /* ip_addr */
- if (g_ascii_strncasecmp (headern, IP_ADDR_HEADER, sizeof (IP_ADDR_HEADER) - 1) == 0) {
- tmp = memory_pool_fstrdup (task->task_pool, line);
- if (!inet_aton (tmp, &task->from_addr)) {
- msg_info ("bad ip header: '%s'", tmp);
- return -1;
- }
- debug_task ("read IP header, value: %s", tmp);
- }
- else {
- msg_info ("wrong header: %s", headern);
- return -1;
- }
- break;
- case 'p':
- case 'P':
- /* Pass header */
- if (g_ascii_strncasecmp (headern, PASS_HEADER, sizeof (PASS_HEADER) - 1) == 0) {
- if (line->len == sizeof ("all") - 1 && g_ascii_strncasecmp (line->begin, "all", sizeof ("all") - 1) == 0) {
- task->pass_all_filters = TRUE;
- msg_info ("pass all filters");
- }
- }
- break;
- case 's':
- case 'S':
- if (g_ascii_strncasecmp (headern, SUBJECT_HEADER, sizeof (SUBJECT_HEADER) - 1) == 0) {
- task->subject = memory_pool_fstrdup (task->task_pool, line);
- }
- else {
- return -1;
- }
- break;
- case 'u':
- case 'U':
- if (g_ascii_strncasecmp (headern, USER_HEADER, sizeof (USER_HEADER) - 1) == 0) {
- /* XXX: use this header somehow */
- task->user = memory_pool_fstrdup (task->task_pool, line);
- }
- else {
- return -1;
- }
- break;
- default:
- msg_info ("wrong header: %s", headern);
- return -1;
- }
-
- return 0;
- }
-
- int
- read_rspamd_input_line (struct worker_task *task, f_str_t * line)
- {
- switch (task->state) {
- case READ_COMMAND:
- return parse_command (task, line);
- break;
- case READ_HEADER:
- return parse_header (task, line);
- break;
- default:
- return -1;
- }
- return -1;
- }
-
- struct metric_callback_data {
- struct worker_task *task;
- char *log_buf;
- int log_offset;
- int log_size;
- };
-
- static void
- write_hashes_to_log (struct worker_task *task, char *logbuf, int offset, int size)
- {
- GList *cur;
- struct mime_text_part *text_part;
-
- cur = task->text_parts;
-
- while (cur && offset < size) {
- text_part = cur->data;
- if (text_part->fuzzy) {
- if (cur->next != NULL) {
- offset += snprintf (logbuf + offset, size - offset, " part: %Xd,", text_part->fuzzy->h);
- }
- else {
- offset += snprintf (logbuf + offset, size - offset, " part: %Xd", text_part->fuzzy->h);
- }
- }
- cur = g_list_next (cur);
- }
- }
-
- static void
- show_url_header (struct worker_task *task)
- {
- int r = 0;
- char outbuf[OUTBUFSIZ], c;
- struct uri *url;
- GList *cur;
- f_str_t host;
-
- r = snprintf (outbuf, sizeof (outbuf), "Urls: ");
- cur = task->urls;
- while (cur) {
- url = cur->data;
- if (task->cfg->log_urls) {
- /* Write this url to log as well */
- msg_info ("url found: <%s>, score: [%.2f / %.2f]", struri (url), default_score, default_required_score);
- }
- host.begin = url->host;
- host.len = url->hostlen;
- /* Skip long hosts to avoid protocol coollisions */
- if (host.len > OUTBUFSIZ) {
- cur = g_list_next (cur);
- continue;
- }
- /* Do header folding */
- if (host.len + r >= OUTBUFSIZ - 3) {
- outbuf[r++] = '\r';
- outbuf[r++] = '\n';
- outbuf[r] = ' ';
- rspamd_dispatcher_write (task->dispatcher, outbuf, r, TRUE, FALSE);
- r = 0;
- }
- /* Write url host to buf */
- if (g_list_next (cur) != NULL) {
- c = *(host.begin + host.len);
- *(host.begin + host.len) = '\0';
- debug_task ("write url: %s", host.begin);
- r += snprintf (outbuf + r, sizeof (outbuf) - r, "%s, ", host.begin);
- *(host.begin + host.len) = c;
- }
- else {
- c = *(host.begin + host.len);
- *(host.begin + host.len) = '\0';
- debug_task ("write url: %s", host.begin);
- r += snprintf (outbuf + r, sizeof (outbuf) - r, "%s" CRLF, host.begin);
- *(host.begin + host.len) = c;
- }
- cur = g_list_next (cur);
- }
- rspamd_dispatcher_write (task->dispatcher, outbuf, r, FALSE, FALSE);
- }
-
- static void
- metric_symbols_callback (gpointer key, gpointer value, void *user_data)
- {
- struct metric_callback_data *cd = (struct metric_callback_data *)user_data;
- struct worker_task *task = cd->task;
- int r = 0;
- char outbuf[OUTBUFSIZ];
- struct symbol *s = (struct symbol *)value;
- GList *cur;
-
- if (s->options) {
- r = snprintf (outbuf, OUTBUFSIZ, "Symbol: %s; ", (char *)key);
- cur = s->options;
- while (cur) {
- if (g_list_next (cur)) {
- r += snprintf (outbuf + r, OUTBUFSIZ - r, "%s,", (char *)cur->data);
- }
- else {
- r += snprintf (outbuf + r, OUTBUFSIZ - r, "%s" CRLF, (char *)cur->data);
- }
- cur = g_list_next (cur);
- }
- /* End line with CRLF strictly */
- if (r >= OUTBUFSIZ - 1) {
- outbuf[OUTBUFSIZ - 2] = '\r';
- outbuf[OUTBUFSIZ - 1] = '\n';
- }
- }
- else {
- r = snprintf (outbuf, OUTBUFSIZ, "Symbol: %s" CRLF, (char *)key);
- }
- cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "%s,", (char *)key);
-
- rspamd_dispatcher_write (task->dispatcher, outbuf, r, FALSE, FALSE);
- }
-
- static void
- show_metric_symbols (struct metric_result *metric_res, struct metric_callback_data *cd)
- {
- int r = 0;
- GList *symbols, *cur;
- char outbuf[OUTBUFSIZ];
-
- if (cd->task->proto == SPAMC_PROTO) {
- symbols = g_hash_table_get_keys (metric_res->symbols);
- cur = symbols;
- while (cur) {
- if (g_list_next (cur) != NULL) {
- r += snprintf (outbuf + r, sizeof (outbuf) - r, "%s,", (char *)cur->data);
- }
- else {
- r += snprintf (outbuf + r, sizeof (outbuf) - r, "%s" CRLF, (char *)cur->data);
- }
- cur = g_list_next (cur);
- }
- g_list_free (symbols);
- rspamd_dispatcher_write (cd->task->dispatcher, outbuf, r, FALSE, FALSE);
- }
- else {
- g_hash_table_foreach (metric_res->symbols, metric_symbols_callback, cd);
- /* Remove last , from log buf */
- if (cd->log_buf[cd->log_offset - 1] == ',') {
- cd->log_buf[--cd->log_offset] = '\0';
- }
- }
- }
-
-
- static void
- show_metric_result (gpointer metric_name, gpointer metric_value, void *user_data)
- {
- struct metric_callback_data *cd = (struct metric_callback_data *)user_data;
- struct worker_task *task = cd->task;
- int r;
- char outbuf[OUTBUFSIZ];
- struct metric_result *metric_res = (struct metric_result *)metric_value;
- struct metric *m;
- int is_spam = 0;
- double ms = 0, rs = 0;
-
-
- if (metric_name == NULL || metric_value == NULL) {
- m = g_hash_table_lookup (task->cfg->metrics, DEFAULT_METRIC);
- default_required_score = m->required_score;
- default_score = 0;
- if (!check_metric_settings (task, m, &ms, &rs)) {
- ms = m->required_score;
- rs = m->reject_score;
- }
- if (task->proto == SPAMC_PROTO) {
- r = snprintf (outbuf, sizeof (outbuf), "Spam: False ; 0 / %.2f" CRLF, ms);
- }
- else {
- if (strcmp (task->proto_ver, RSPAMC_PROTO_1_1) == 0) {
- if (!task->is_skipped) {
- r = snprintf (outbuf, sizeof (outbuf), "Metric: default; False; 0 / %.2f / %.2f" CRLF, ms, rs);
- }
- else {
- r = snprintf (outbuf, sizeof (outbuf), "Metric: default; Skip; 0 / %.2f / %.2f" CRLF, ms, rs);
- }
- }
- else {
- r = snprintf (outbuf, sizeof (outbuf), "Metric: default; False; 0 / %.2f" CRLF, ms);
- }
- }
- if (!task->is_skipped) {
- cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: F: [0/%.2f/%.2f] [", "default", ms, rs);
- }
- else {
- cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: S: [0/%.2f/%.2f] [", "default", ms, rs);
- }
- }
- else {
- /* XXX: dirty hack */
- if (strcmp (metric_res->metric->name, DEFAULT_METRIC) == 0) {
- default_required_score = metric_res->metric->required_score;
- default_score = metric_res->score;
- }
-
- if (!check_metric_settings (task, metric_res->metric, &ms, &rs)) {
- ms = metric_res->metric->required_score;
- rs = metric_res->metric->reject_score;
- }
- if (metric_res->score >= ms) {
- is_spam = 1;
- }
- if (task->proto == SPAMC_PROTO) {
- r = snprintf (outbuf, sizeof (outbuf), "Spam: %s ; %.2f / %.2f" CRLF, (is_spam) ? "True" : "False", metric_res->score, ms);
- }
- else {
- if (strcmp (task->proto_ver, RSPAMC_PROTO_1_1) == 0) {
- if (!task->is_skipped) {
- r = snprintf (outbuf, sizeof (outbuf), "Metric: %s; %s; %.2f / %.2f / %.2f" CRLF,
- (char *)metric_name, (is_spam) ? "True" : "False", metric_res->score, ms, rs);
- }
- else {
- r = snprintf (outbuf, sizeof (outbuf), "Metric: %s; Skip; %.2f / %.2f / %.2f" CRLF,
- (char *)metric_name, metric_res->score, ms, rs);
- }
- }
- else {
- r = snprintf (outbuf, sizeof (outbuf), "Metric: %s; %s; %.2f / %.2f" CRLF,
- (char *)metric_name, (is_spam) ? "True" : "False", metric_res->score, ms);
- }
- }
- if (!task->is_skipped) {
- cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: %s: [%.2f/%.2f/%.2f] [",
- (char *)metric_name, is_spam ? "T" : "F", metric_res->score, ms, rs);
- }
- else {
- cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "(%s: %s: [%.2f/%.2f/%.2f] [",
- (char *)metric_name, "S", metric_res->score, ms, rs);
-
- }
- }
- if (task->cmd == CMD_PROCESS) {
- #ifndef GMIME24
- g_mime_message_add_header (task->message, "X-Spam-Status", outbuf);
- #else
- g_mime_object_append_header (GMIME_OBJECT (task->message), "X-Spam-Status", outbuf);
- #endif
- }
- else {
- rspamd_dispatcher_write (task->dispatcher, outbuf, r, FALSE, FALSE);
-
- if (task->cmd == CMD_SYMBOLS && metric_value != NULL) {
- show_metric_symbols (metric_res, cd);
- }
- }
- #ifdef HAVE_CLOCK_GETTIME
- cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "]), len: %ld, time: %sms",
- (long int)task->msg->len, calculate_check_time (&task->ts, task->cfg->clock_res));
- #else
- cd->log_offset += snprintf (cd->log_buf + cd->log_offset, cd->log_size - cd->log_offset, "]), len: %ld, time: %sms",
- (long int)task->msg->len, calculate_check_time (&task->tv, task->cfg->clock_res));
- #endif
- }
-
- static void
- show_messages (struct worker_task *task)
- {
- int r = 0;
- char outbuf[OUTBUFSIZ];
- GList *cur;
-
- cur = task->messages;
- while (cur) {
- r += snprintf (outbuf + r, sizeof (outbuf) - r, "Message: %s" CRLF, (char *)cur->data);
- cur = g_list_next (cur);
- }
-
- rspamd_dispatcher_write (task->dispatcher, outbuf, r, FALSE, FALSE);
- }
-
- static int
- write_check_reply (struct worker_task *task)
- {
- int r;
- char outbuf[OUTBUFSIZ], logbuf[OUTBUFSIZ];
- struct metric_result *metric_res;
- struct metric_callback_data cd;
-
- r = snprintf (outbuf, sizeof (outbuf), "%s/%s 0 %s" CRLF, (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER,
- task->proto_ver, "OK");
- rspamd_dispatcher_write (task->dispatcher, outbuf, r, TRUE, FALSE);
-
- cd.task = task;
- cd.log_buf = logbuf;
- cd.log_offset = snprintf (logbuf, sizeof (logbuf), "msg ok, id: <%s>, ", task->message_id);
- cd.log_size = sizeof (logbuf);
-
- if (task->proto == SPAMC_PROTO) {
- /* Ignore metrics, just write report for 'default' metric */
- metric_res = g_hash_table_lookup (task->results, "default");
- if (metric_res == NULL) {
- /* Implicit metric result */
- show_metric_result (NULL, NULL, (void *)&cd);
- }
- else {
- show_metric_result ((gpointer) "default", (gpointer) metric_res, (void *)&cd);
- }
- }
- else {
- /* Show default metric first */
- metric_res = g_hash_table_lookup (task->results, "default");
- if (metric_res == NULL) {
- /* Implicit metric result */
- show_metric_result (NULL, NULL, (void *)&cd);
- }
- else {
- show_metric_result ((gpointer) "default", (gpointer) metric_res, (void *)&cd);
- }
- g_hash_table_remove (task->results, "default");
-
- /* Write result for each metric separately */
- g_hash_table_foreach (task->results, show_metric_result, &cd);
- /* Messages */
- show_messages (task);
- /* URL stat */
- show_url_header (task);
- }
-
- write_hashes_to_log (task, logbuf, cd.log_offset, cd.log_size);
- msg_info ("%s", logbuf);
- rspamd_dispatcher_write (task->dispatcher, CRLF, sizeof (CRLF) - 1, FALSE, TRUE);
-
- task->worker->srv->stat->messages_scanned++;
- if (default_score >= default_required_score) {
- task->worker->srv->stat->messages_spam ++;
- }
- else {
- task->worker->srv->stat->messages_ham ++;
- }
-
- return 0;
- }
-
- static int
- write_process_reply (struct worker_task *task)
- {
- int r;
- char *outmsg;
- char outbuf[OUTBUFSIZ], logbuf[OUTBUFSIZ];
- struct metric_result *metric_res;
- struct metric_callback_data cd;
-
- r = snprintf (outbuf, sizeof (outbuf), "%s/%s 0 %s" CRLF "Content-Length: %zd" CRLF CRLF,
- (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER,
- task->proto_ver, "OK", task->msg->len);
-
- cd.task = task;
- cd.log_buf = logbuf;
- cd.log_offset = snprintf (logbuf, sizeof (logbuf), "msg ok, id: <%s>, ", task->message_id);
- cd.log_size = sizeof (logbuf);
-
- if (task->proto == SPAMC_PROTO) {
- /* Ignore metrics, just write report for 'default' metric */
- metric_res = g_hash_table_lookup (task->results, "default");
- if (metric_res == NULL) {
- /* Implicit metric result */
- show_metric_result (NULL, NULL, (void *)&cd);
- }
- else {
- show_metric_result ((gpointer) "default", (gpointer) metric_res, (void *)&cd);
- }
- }
- else {
- /* Show default metric first */
- metric_res = g_hash_table_lookup (task->results, "default");
- if (metric_res == NULL) {
- /* Implicit metric result */
- show_metric_result (NULL, NULL, (void *)&cd);
- }
- else {
- show_metric_result ((gpointer) "default", (gpointer) metric_res, (void *)&cd);
- }
- g_hash_table_remove (task->results, "default");
-
- /* Write result for each metric separately */
- g_hash_table_foreach (task->results, show_metric_result, &cd);
- /* Messages */
- show_messages (task);
- }
- write_hashes_to_log (task, logbuf, cd.log_offset, cd.log_size);
- msg_info ("%s", logbuf);
-
- outmsg = g_mime_object_to_string (GMIME_OBJECT (task->message));
-
- rspamd_dispatcher_write (task->dispatcher, outbuf, r, TRUE, FALSE);
- rspamd_dispatcher_write (task->dispatcher, outmsg, strlen (outmsg), FALSE, TRUE);
-
- task->worker->srv->stat->messages_scanned++;
- if (default_score >= default_required_score) {
- task->worker->srv->stat->messages_spam ++;
- }
- else {
- task->worker->srv->stat->messages_ham ++;
- }
-
- memory_pool_add_destructor (task->task_pool, (pool_destruct_func) g_free, outmsg);
-
- return 0;
- }
-
- int
- write_reply (struct worker_task *task)
- {
- int r;
- char outbuf[OUTBUFSIZ];
-
- debug_task ("writing reply to client");
- if (task->error_code != 0) {
- /* Write error message and error code to reply */
- if (task->proto == SPAMC_PROTO) {
- r = snprintf (outbuf, sizeof (outbuf), "%s/%s %d %s" CRLF CRLF,
- SPAMD_REPLY_BANNER, task->proto_ver, task->error_code, SPAMD_ERROR);
- debug_task ("writing error: %s", outbuf);
- }
- else {
- r = snprintf (outbuf, sizeof (outbuf), "%s/%s %d %s" CRLF "%s: %s" CRLF CRLF,
- RSPAMD_REPLY_BANNER, task->proto_ver, task->error_code, SPAMD_ERROR, ERROR_HEADER, task->last_error);
- debug_task ("writing error: %s", outbuf);
- }
- /* Write to bufferevent error message */
- rspamd_dispatcher_write (task->dispatcher, outbuf, r, FALSE, FALSE);
- }
- else {
- switch (task->cmd) {
- case CMD_REPORT_IFSPAM:
- case CMD_REPORT:
- case CMD_CHECK:
- case CMD_SYMBOLS:
- return write_check_reply (task);
- break;
- case CMD_PROCESS:
- return write_process_reply (task);
- break;
- case CMD_SKIP:
- r = snprintf (outbuf, sizeof (outbuf), "%s/%s 0 %s" CRLF,
- (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER, task->proto_ver, SPAMD_OK);
- rspamd_dispatcher_write (task->dispatcher, outbuf, r, FALSE, FALSE);
- break;
- case CMD_PING:
- r = snprintf (outbuf, sizeof (outbuf), "%s/%s 0 PONG" CRLF,
- (task->proto == SPAMC_PROTO) ? SPAMD_REPLY_BANNER : RSPAMD_REPLY_BANNER, task->proto_ver);
- rspamd_dispatcher_write (task->dispatcher, outbuf, r, FALSE, FALSE);
- break;
- case CMD_OTHER:
- return task->custom_cmd->func (task);
- }
- }
-
- return 0;
- }
-
- void
- register_protocol_command (const char *name, protocol_reply_func func)
- {
- struct custom_command *cmd;
-
- cmd = g_malloc (sizeof (struct custom_command));
- cmd->name = name;
- cmd->func = func;
-
- custom_commands = g_list_prepend (custom_commands, cmd);
- }
|