1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392 |
- /*
- * Copyright 2023 Vsevolod Stakhov
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <contrib/libucl/ucl.h>
- #include "config.h"
- #include "util.h"
- #include "cfg_file.h"
- #include "rspamd.h"
- #include "message.h"
- #include "mime_expressions.h"
- #include "libserver/html/html.h"
- #include "lua/lua_common.h"
- #include "utlist.h"
-
- gboolean rspamd_compare_encoding(struct rspamd_task *task,
- GArray *args,
- void *unused);
- gboolean rspamd_header_exists(struct rspamd_task *task,
- GArray *args,
- void *unused);
- gboolean rspamd_parts_distance(struct rspamd_task *task,
- GArray *args,
- void *unused);
- gboolean rspamd_recipients_distance(struct rspamd_task *task,
- GArray *args,
- void *unused);
- gboolean rspamd_has_only_html_part(struct rspamd_task *task,
- GArray *args,
- void *unused);
- gboolean rspamd_is_recipients_sorted(struct rspamd_task *task,
- GArray *args,
- void *unused);
- gboolean rspamd_compare_transfer_encoding(struct rspamd_task *task,
- GArray *args,
- void *unused);
- gboolean rspamd_is_html_balanced(struct rspamd_task *task,
- GArray *args,
- void *unused);
- gboolean rspamd_has_html_tag(struct rspamd_task *task,
- GArray *args,
- void *unused);
- gboolean rspamd_has_fake_html(struct rspamd_task *task,
- GArray *args,
- void *unused);
- static gboolean rspamd_raw_header_exists(struct rspamd_task *task,
- GArray *args,
- void *unused);
- static gboolean rspamd_check_smtp_data(struct rspamd_task *task,
- GArray *args,
- void *unused);
- static gboolean rspamd_content_type_is_type(struct rspamd_task *task,
- GArray *args,
- void *unused);
- static gboolean rspamd_content_type_is_subtype(struct rspamd_task *task,
- GArray *args,
- void *unused);
- static gboolean rspamd_content_type_has_param(struct rspamd_task *task,
- GArray *args,
- void *unused);
- static gboolean rspamd_content_type_compare_param(struct rspamd_task *task,
- GArray *args,
- void *unused);
- static gboolean rspamd_has_content_part(struct rspamd_task *task,
- GArray *args,
- void *unused);
- static gboolean rspamd_has_content_part_len(struct rspamd_task *task,
- GArray *args,
- void *unused);
- static gboolean rspamd_is_empty_body(struct rspamd_task *task,
- GArray *args,
- void *unused);
- static gboolean rspamd_has_flag_expr(struct rspamd_task *task,
- GArray *args,
- void *unused);
- static gboolean rspamd_has_symbol_expr(struct rspamd_task *task,
- GArray *args,
- void *unused);
-
- static rspamd_expression_atom_t *rspamd_mime_expr_parse(const gchar *line, gsize len,
- rspamd_mempool_t *pool, gpointer ud, GError **err);
- static gdouble rspamd_mime_expr_process(void *ud, rspamd_expression_atom_t *atom);
- static gint rspamd_mime_expr_priority(rspamd_expression_atom_t *atom);
- static void rspamd_mime_expr_destroy(rspamd_expression_atom_t *atom);
-
- /**
- * Regexp structure
- */
- struct rspamd_regexp_atom {
- enum rspamd_re_type type; /**< regexp type */
- gchar *regexp_text; /**< regexp text representation */
- rspamd_regexp_t *regexp; /**< regexp structure */
- union {
- const gchar *header; /**< header name for header regexps */
- const gchar *selector; /**< selector name for lua selector regexp */
- } extra;
- gboolean is_test; /**< true if this expression must be tested */
- gboolean is_strong; /**< true if headers search must be case sensitive */
- gboolean is_multiple; /**< true if we need to match all inclusions of atom */
- };
-
- /**
- * Rspamd expression function
- */
- struct rspamd_function_atom {
- gchar *name; /**< name of function */
- GArray *args; /**< its args */
- };
-
- enum rspamd_mime_atom_type {
- MIME_ATOM_REGEXP = 0,
- MIME_ATOM_INTERNAL_FUNCTION,
- MIME_ATOM_LUA_FUNCTION,
- MIME_ATOM_LOCAL_LUA_FUNCTION, /* New style */
- };
-
- struct rspamd_mime_atom {
- gchar *str;
- union {
- struct rspamd_regexp_atom *re;
- struct rspamd_function_atom *func;
- const gchar *lua_function;
- gint lua_cbref;
- } d;
- enum rspamd_mime_atom_type type;
- };
-
- /*
- * List of internal functions of rspamd
- * Sorted by name to use bsearch
- */
- static struct _fl {
- const gchar *name;
- rspamd_internal_func_t func;
- void *user_data;
- } rspamd_functions_list[] = {
- {"check_smtp_data", rspamd_check_smtp_data, NULL},
- {"compare_encoding", rspamd_compare_encoding, NULL},
- {"compare_parts_distance", rspamd_parts_distance, NULL},
- {"compare_recipients_distance", rspamd_recipients_distance, NULL},
- {"compare_transfer_encoding", rspamd_compare_transfer_encoding, NULL},
- {"content_type_compare_param", rspamd_content_type_compare_param, NULL},
- {"content_type_has_param", rspamd_content_type_has_param, NULL},
- {"content_type_is_subtype", rspamd_content_type_is_subtype, NULL},
- {"content_type_is_type", rspamd_content_type_is_type, NULL},
- {"has_content_part", rspamd_has_content_part, NULL},
- {"has_content_part_len", rspamd_has_content_part_len, NULL},
- {"has_fake_html", rspamd_has_fake_html, NULL},
- {"has_flag", rspamd_has_flag_expr, NULL},
- {"has_html_tag", rspamd_has_html_tag, NULL},
- {"has_only_html_part", rspamd_has_only_html_part, NULL},
- {"has_symbol", rspamd_has_symbol_expr, NULL},
- {"header_exists", rspamd_header_exists, NULL},
- {"is_empty_body", rspamd_is_empty_body, NULL},
- {"is_html_balanced", rspamd_is_html_balanced, NULL},
- {"is_recipients_sorted", rspamd_is_recipients_sorted, NULL},
- {"raw_header_exists", rspamd_raw_header_exists, NULL},
- };
-
- const struct rspamd_atom_subr mime_expr_subr = {
- .parse = rspamd_mime_expr_parse,
- .process = rspamd_mime_expr_process,
- .priority = rspamd_mime_expr_priority,
- .destroy = rspamd_mime_expr_destroy};
-
- static struct _fl *list_ptr = &rspamd_functions_list[0];
- static uint32_t functions_number = sizeof(rspamd_functions_list) /
- sizeof(struct _fl);
- static gboolean list_allocated = FALSE;
-
- /* Bsearch routine */
- static gint
- fl_cmp(const void *s1, const void *s2)
- {
- struct _fl *fl1 = (struct _fl *) s1;
- struct _fl *fl2 = (struct _fl *) s2;
- return strcmp(fl1->name, fl2->name);
- }
-
- static GQuark
- rspamd_mime_expr_quark(void)
- {
- return g_quark_from_static_string("mime-expressions");
- }
-
- #define TYPE_CHECK(str, type, len) (sizeof(type) - 1 == (len) && rspamd_lc_cmp((str), (type), (len)) == 0)
- static gboolean
- rspamd_parse_long_option(const gchar *start, gsize len,
- struct rspamd_regexp_atom *a)
- {
- gboolean ret = FALSE;
-
- if (TYPE_CHECK(start, "body", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_BODY;
- }
- else if (TYPE_CHECK(start, "part", len) ||
- TYPE_CHECK(start, "mime", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_MIME;
- }
- else if (TYPE_CHECK(start, "raw_part", len) ||
- TYPE_CHECK(start, "raw_mime", len) ||
- TYPE_CHECK(start, "mime_raw", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_RAWMIME;
- }
- else if (TYPE_CHECK(start, "header", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_HEADER;
- }
- else if (TYPE_CHECK(start, "mime_header", len) ||
- TYPE_CHECK(start, "header_mime", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_MIMEHEADER;
- }
- else if (TYPE_CHECK(start, "raw_header", len) ||
- TYPE_CHECK(start, "header_raw", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_RAWHEADER;
- }
- else if (TYPE_CHECK(start, "all_header", len) ||
- TYPE_CHECK(start, "header_all", len) ||
- TYPE_CHECK(start, "all_headers", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_ALLHEADER;
- }
- else if (TYPE_CHECK(start, "url", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_URL;
- }
- else if (TYPE_CHECK(start, "email", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_EMAIL;
- }
- else if (TYPE_CHECK(start, "sa_body", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_SABODY;
- }
- else if (TYPE_CHECK(start, "sa_raw_body", len) ||
- TYPE_CHECK(start, "sa_body_raw", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_SARAWBODY;
- }
- else if (TYPE_CHECK(start, "words", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_WORDS;
- }
- else if (TYPE_CHECK(start, "raw_words", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_RAWWORDS;
- }
- else if (TYPE_CHECK(start, "stem_words", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_STEMWORDS;
- }
- else if (TYPE_CHECK(start, "selector", len)) {
- ret = TRUE;
- a->type = RSPAMD_RE_SELECTOR;
- }
-
- return ret;
- }
-
- /*
- * Rspamd regexp utility functions
- */
- static struct rspamd_regexp_atom *
- rspamd_mime_expr_parse_regexp_atom(rspamd_mempool_t *pool, const gchar *line,
- struct rspamd_config *cfg)
- {
- const gchar *begin, *end, *p, *src, *start, *brace;
- gchar *dbegin, *dend, *extra = NULL;
- struct rspamd_regexp_atom *result;
- GError *err = NULL;
- GString *re_flags;
-
- if (line == NULL) {
- msg_err_pool("cannot parse NULL line");
- return NULL;
- }
-
- src = line;
- result = rspamd_mempool_alloc0(pool, sizeof(struct rspamd_regexp_atom));
- /* Skip whitespaces */
- while (g_ascii_isspace(*line)) {
- line++;
- }
- if (*line == '\0') {
- msg_warn_pool("got empty regexp");
- return NULL;
- }
-
- result->type = RSPAMD_RE_MAX;
-
- start = line;
- /* First try to find header name */
- begin = strchr(line, '/');
- if (begin != NULL) {
- p = begin;
- end = NULL;
- while (p != line) {
- if (*p == '=') {
- end = p;
- break;
- }
- p--;
- }
-
- if (end) {
- extra = rspamd_mempool_alloc(pool, end - line + 1);
- rspamd_strlcpy(extra, line, end - line + 1);
- line = end;
- }
- }
- else {
- extra = rspamd_mempool_strdup(pool, line);
- result->type = RSPAMD_RE_MAX;
- line = start;
- }
- /* Find begin of regexp */
- while (*line && *line != '/') {
- line++;
- }
- if (*line != '\0') {
- begin = line + 1;
- }
- else if (extra == NULL) {
- /* Assume that line without // is just a header name */
- extra = rspamd_mempool_strdup(pool, line);
- result->type = RSPAMD_RE_HEADER;
- return result;
- }
- else {
- /* We got header name earlier but have not found // expression, so it is invalid regexp */
- msg_warn_pool(
- "got no header name (eg. header=) but without corresponding regexp, %s",
- src);
- return NULL;
- }
- /* Find end */
- end = begin;
- while (*end && (*end != '/' || *(end - 1) == '\\')) {
- end++;
- }
- if (end == begin || *end != '/') {
- msg_warn_pool("no trailing / in regexp %s", src);
- return NULL;
- }
- /* Parse flags */
- p = end + 1;
- re_flags = g_string_sized_new(32);
-
- while (p != NULL) {
- switch (*p) {
- case 'i':
- case 'm':
- case 's':
- case 'x':
- case 'u':
- case 'O':
- case 'r':
- case 'L':
- /* Handled by rspamd_regexp_t */
- g_string_append_c(re_flags, *p);
- p++;
- break;
- case 'o':
- p++;
- break;
- /* Type flags */
- case 'H':
- result->type = RSPAMD_RE_HEADER;
- p++;
- break;
- case 'R':
- result->type = RSPAMD_RE_ALLHEADER;
- p++;
- break;
- case 'B':
- result->type = RSPAMD_RE_MIMEHEADER;
- p++;
- break;
- case 'C':
- result->type = RSPAMD_RE_SABODY;
- p++;
- break;
- case 'D':
- result->type = RSPAMD_RE_SARAWBODY;
- p++;
- break;
- case 'M':
- result->type = RSPAMD_RE_BODY;
- p++;
- break;
- case 'P':
- result->type = RSPAMD_RE_MIME;
- p++;
- break;
- case 'Q':
- result->type = RSPAMD_RE_RAWMIME;
- p++;
- break;
- case 'U':
- result->type = RSPAMD_RE_URL;
- p++;
- break;
- case 'X':
- result->type = RSPAMD_RE_RAWHEADER;
- p++;
- break;
- case '$':
- result->type = RSPAMD_RE_SELECTOR;
- p++;
- break;
- case '{':
- /* Long definition */
- if ((brace = strchr(p + 1, '}')) != NULL) {
- if (!rspamd_parse_long_option(p + 1, brace - (p + 1), result)) {
- msg_warn_pool("invalid long regexp type: %*s in '%s'",
- (int) (brace - (p + 1)), p + 1, src);
- p = NULL;
- }
- else {
- p = brace + 1;
- }
- }
- else {
- p = NULL;
- }
- break;
- /* Other flags */
- case 'T':
- result->is_test = TRUE;
- p++;
- break;
- case 'S':
- result->is_strong = TRUE;
- p++;
- break;
- case 'A':
- result->is_multiple = TRUE;
- p++;
- break;
- /* Stop flags parsing */
- default:
- p = NULL;
- break;
- }
- }
-
- if (result->type >= RSPAMD_RE_MAX) {
- if (extra) {
- /* Assume header regexp */
- result->extra.header = extra;
- result->type = RSPAMD_RE_HEADER;
- }
- else {
- msg_err_pool("could not read regexp: %s, unknown type", src);
- return NULL;
- }
- }
-
- if ((result->type == RSPAMD_RE_HEADER ||
- result->type == RSPAMD_RE_RAWHEADER ||
- result->type == RSPAMD_RE_MIMEHEADER)) {
- if (extra == NULL) {
- msg_err_pool("header regexp: '%s' has no header part", src);
- return NULL;
- }
- else {
- result->extra.header = extra;
- }
- }
-
- if (result->type == RSPAMD_RE_SELECTOR) {
- if (extra == NULL) {
- msg_err_pool("selector regexp: '%s' has no selector part", src);
- return NULL;
- }
- else {
- result->extra.selector = extra;
- }
- }
-
-
- result->regexp_text = rspamd_mempool_strdup(pool, start);
- dbegin = result->regexp_text + (begin - start);
- dend = result->regexp_text + (end - start);
- *dend = '\0';
-
- result->regexp = rspamd_regexp_new(dbegin, re_flags->str,
- &err);
-
- g_string_free(re_flags, TRUE);
-
- if (result->regexp == NULL || err != NULL) {
- msg_warn_pool("could not read regexp: %s while reading regexp %e",
- src, err);
-
- if (err) {
- g_error_free(err);
- }
-
- return NULL;
- }
-
- if (result->is_multiple) {
- rspamd_regexp_set_maxhits(result->regexp, 0);
- }
- else {
- rspamd_regexp_set_maxhits(result->regexp, 1);
- }
-
- rspamd_regexp_set_ud(result->regexp, result);
-
- *dend = '/';
-
- return result;
- }
-
- struct rspamd_function_atom *
- rspamd_mime_expr_parse_function_atom(rspamd_mempool_t *pool, const gchar *input)
- {
- const gchar *obrace, *ebrace, *p, *c;
- gchar t, *databuf;
- guint len;
- struct rspamd_function_atom *res;
- struct expression_argument arg;
- GError *err = NULL;
- enum {
- start_read_argument = 0,
- in_string,
- in_regexp,
- got_backslash,
- got_comma
- } state,
- prev_state = 0;
-
- obrace = strchr(input, '(');
- ebrace = strrchr(input, ')');
-
- g_assert(obrace != NULL && ebrace != NULL);
-
- res = rspamd_mempool_alloc0(pool, sizeof(*res));
- res->name = rspamd_mempool_alloc(pool, obrace - input + 1);
- rspamd_strlcpy(res->name, input, obrace - input + 1);
- res->args = g_array_new(FALSE, FALSE, sizeof(struct expression_argument));
-
- p = obrace + 1;
- c = p;
- state = start_read_argument;
-
- /* Read arguments */
- while (p <= ebrace) {
- t = *p;
- switch (state) {
- case start_read_argument:
- if (t == '/') {
- state = in_regexp;
- c = p;
- }
- else if (!g_ascii_isspace(t)) {
- state = in_string;
-
- if (t == '\'' || t == '\"') {
- c = p + 1;
- }
- else {
- c = p;
- }
- }
- p++;
- break;
- case in_regexp:
- if (t == '\\') {
- state = got_backslash;
- prev_state = in_regexp;
- }
- else if (t == ',' || p == ebrace) {
- len = p - c + 1;
- databuf = rspamd_mempool_alloc(pool, len);
- rspamd_strlcpy(databuf, c, len);
- arg.type = EXPRESSION_ARGUMENT_REGEXP;
- arg.data = rspamd_regexp_cache_create(NULL, databuf, NULL, &err);
-
- if (arg.data == NULL) {
- /* Fallback to string */
- msg_warn("cannot parse slashed argument %s as regexp: %s",
- databuf, err->message);
- g_error_free(err);
- arg.type = EXPRESSION_ARGUMENT_NORMAL;
- arg.data = databuf;
- }
-
- g_array_append_val(res->args, arg);
- state = got_comma;
- }
- p++;
- break;
- case in_string:
- if (t == '\\') {
- state = got_backslash;
- prev_state = in_string;
- }
- else if (t == ',' || p == ebrace) {
- if (*(p - 1) == '\'' || *(p - 1) == '\"') {
- len = p - c;
- }
- else {
- len = p - c + 1;
- }
-
- databuf = rspamd_mempool_alloc(pool, len);
- rspamd_strlcpy(databuf, c, len);
- arg.type = EXPRESSION_ARGUMENT_NORMAL;
- arg.data = databuf;
- g_array_append_val(res->args, arg);
- state = got_comma;
- }
- p++;
- break;
- case got_backslash:
- state = prev_state;
- p++;
- break;
- case got_comma:
- state = start_read_argument;
- break;
- }
- }
-
- return res;
- }
-
- static rspamd_expression_atom_t *
- rspamd_mime_expr_parse(const gchar *line, gsize len,
- rspamd_mempool_t *pool, gpointer ud, GError **err)
- {
- rspamd_expression_atom_t *a = NULL;
- struct rspamd_mime_atom *mime_atom = NULL;
- const gchar *p, *end, *c = NULL;
- struct rspamd_mime_expr_ud *real_ud = (struct rspamd_mime_expr_ud *) ud;
- struct rspamd_config *cfg;
- rspamd_regexp_t *own_re;
- gchar t;
- gint type = MIME_ATOM_REGEXP, obraces = 0, ebraces = 0;
- enum {
- in_header = 0,
- got_slash,
- in_regexp,
- got_backslash,
- got_second_slash,
- in_flags,
- in_flags_brace,
- got_obrace,
- in_function,
- in_local_function,
- got_ebrace,
- end_atom,
- bad_atom
- } state = 0,
- prev_state = 0;
-
- p = line;
- end = p + len;
- cfg = real_ud->cfg;
-
- while (p < end) {
- t = *p;
-
- switch (state) {
- case in_header:
- if (t == '/') {
- /* Regexp */
- state = got_slash;
- }
- else if (t == '(') {
- /* Function */
- state = got_obrace;
- }
- else if (!g_ascii_isalnum(t) && t != '_' && t != '-' && t != '=') {
- if (t == ':') {
- if (p - line == 3 && memcmp(line, "lua", 3) == 0) {
- type = MIME_ATOM_LOCAL_LUA_FUNCTION;
- state = in_local_function;
- c = p + 1;
- }
- }
- else {
- /* Likely lua function, identified by just a string */
- type = MIME_ATOM_LUA_FUNCTION;
- state = end_atom;
- /* Do not increase p */
- continue;
- }
- }
- else if (g_ascii_isspace(t)) {
- state = bad_atom;
- }
- p++;
- break;
- case got_slash:
- state = in_regexp;
- break;
- case in_regexp:
- if (t == '\\') {
- state = got_backslash;
- prev_state = in_regexp;
- }
- else if (t == '/') {
- state = got_second_slash;
- }
- p++;
- break;
- case got_second_slash:
- state = in_flags;
- break;
- case in_flags:
- if (t == '{') {
- state = in_flags_brace;
- p++;
- }
- else if (!g_ascii_isalpha(t) && t != '$') {
- state = end_atom;
- }
- else {
- p++;
- }
- break;
- case in_flags_brace:
- if (t == '}') {
- state = in_flags;
- }
- p++;
- break;
- case got_backslash:
- state = prev_state;
- p++;
- break;
- case got_obrace:
- state = in_function;
- type = MIME_ATOM_INTERNAL_FUNCTION;
- obraces++;
- break;
- case in_function:
- if (t == '\\') {
- state = got_backslash;
- prev_state = in_function;
- }
- else if (t == '(') {
- obraces++;
- }
- else if (t == ')') {
- ebraces++;
- if (ebraces == obraces) {
- state = got_ebrace;
- }
- }
- p++;
- break;
- case in_local_function:
- if (!(g_ascii_isalnum(t) || t == '-' || t == '_')) {
- g_assert(c != NULL);
- state = end_atom;
- }
- else {
- p++;
- }
- break;
- case got_ebrace:
- state = end_atom;
- break;
- case bad_atom:
- g_set_error(err, rspamd_mime_expr_quark(), 100, "cannot parse"
- " mime atom '%s' when reading symbol '%c' at offset %d, "
- "near %.*s",
- line, t, (gint) (p - line),
- (gint) MIN(end - p, 10), p);
- return NULL;
- case end_atom:
- goto set;
- }
- }
- set:
-
- if (p - line == 0 || (state != got_ebrace && state != got_second_slash &&
- state != in_flags && state != end_atom)) {
- g_set_error(err, rspamd_mime_expr_quark(), 200, "incomplete or empty"
- " mime atom");
- return NULL;
- }
-
- mime_atom = rspamd_mempool_alloc(pool, sizeof(*mime_atom));
- mime_atom->type = type;
- mime_atom->str = rspamd_mempool_alloc(pool, p - line + 1);
- rspamd_strlcpy(mime_atom->str, line, p - line + 1);
-
- if (type == MIME_ATOM_REGEXP) {
- mime_atom->d.re = rspamd_mime_expr_parse_regexp_atom(pool,
- mime_atom->str, cfg);
- if (mime_atom->d.re == NULL) {
- g_set_error(err, rspamd_mime_expr_quark(), 200,
- "cannot parse regexp '%s'",
- mime_atom->str);
- goto err;
- }
- else {
- gint lua_cbref = -1;
-
- /* Check regexp condition */
- if (real_ud->conf_obj != NULL) {
- const ucl_object_t *re_conditions = ucl_object_lookup(real_ud->conf_obj,
- "re_conditions");
-
- if (re_conditions != NULL) {
- if (ucl_object_type(re_conditions) != UCL_OBJECT) {
- g_set_error(err, rspamd_mime_expr_quark(), 320,
- "re_conditions is not a table for '%s'",
- mime_atom->str);
- rspamd_regexp_unref(mime_atom->d.re->regexp);
- goto err;
- }
-
- const ucl_object_t *function_obj = ucl_object_lookup(re_conditions,
- mime_atom->str);
-
- if (function_obj != NULL) {
- if (ucl_object_type(function_obj) != UCL_USERDATA) {
- g_set_error(err, rspamd_mime_expr_quark(), 320,
- "condition for '%s' is invalid, must be function",
- mime_atom->str);
- rspamd_regexp_unref(mime_atom->d.re->regexp);
- goto err;
- }
-
- struct ucl_lua_funcdata *fd = function_obj->value.ud;
-
- lua_cbref = fd->idx;
- }
- }
- }
-
- if (lua_cbref != -1) {
- msg_info_config("added condition for regexp %s", mime_atom->str);
- /* Add SOM_LEFTMOST_FLAG implicitly */
- rspamd_regexp_set_flags(mime_atom->d.re->regexp, rspamd_regexp_get_flags(mime_atom->d.re->regexp) |
- RSPAMD_REGEXP_FLAG_LEFTMOST);
- }
-
- /* Register new item in the cache */
- if (mime_atom->d.re->type == RSPAMD_RE_HEADER ||
- mime_atom->d.re->type == RSPAMD_RE_RAWHEADER ||
- mime_atom->d.re->type == RSPAMD_RE_MIMEHEADER) {
-
- if (mime_atom->d.re->extra.header != NULL) {
- own_re = mime_atom->d.re->regexp;
- mime_atom->d.re->regexp = rspamd_re_cache_add(cfg->re_cache,
- mime_atom->d.re->regexp,
- mime_atom->d.re->type,
- mime_atom->d.re->extra.header,
- strlen(mime_atom->d.re->extra.header) + 1,
- lua_cbref);
- /* Pass ownership to the cache */
- rspamd_regexp_unref(own_re);
- }
- else {
- /* We have header regexp, but no header name is detected */
- g_set_error(err,
- rspamd_mime_expr_quark(),
- 200,
- "no header name in header regexp: '%s'",
- mime_atom->str);
- rspamd_regexp_unref(mime_atom->d.re->regexp);
- goto err;
- }
- }
- else if (mime_atom->d.re->type == RSPAMD_RE_SELECTOR) {
- if (mime_atom->d.re->extra.selector != NULL) {
- own_re = mime_atom->d.re->regexp;
- mime_atom->d.re->regexp = rspamd_re_cache_add(cfg->re_cache,
- mime_atom->d.re->regexp,
- mime_atom->d.re->type,
- mime_atom->d.re->extra.selector,
- strlen(mime_atom->d.re->extra.selector) + 1,
- lua_cbref);
- /* Pass ownership to the cache */
- rspamd_regexp_unref(own_re);
- }
- else {
- /* We have selector regexp, but no selector name is detected */
- g_set_error(err,
- rspamd_mime_expr_quark(),
- 200,
- "no selector name in selector regexp: '%s'",
- mime_atom->str);
- rspamd_regexp_unref(mime_atom->d.re->regexp);
- goto err;
- }
- }
- else {
- own_re = mime_atom->d.re->regexp;
- mime_atom->d.re->regexp = rspamd_re_cache_add(cfg->re_cache,
- mime_atom->d.re->regexp,
- mime_atom->d.re->type,
- NULL,
- 0,
- lua_cbref);
- /* Pass ownership to the cache */
- rspamd_regexp_unref(own_re);
- }
- }
- }
- else if (type == MIME_ATOM_LUA_FUNCTION) {
- mime_atom->d.lua_function = mime_atom->str;
-
- lua_getglobal(cfg->lua_state, mime_atom->str);
-
- if (lua_type(cfg->lua_state, -1) != LUA_TFUNCTION) {
- g_set_error(err, rspamd_mime_expr_quark(), 200,
- "no such lua function '%s'",
- mime_atom->str);
- lua_pop(cfg->lua_state, 1);
-
- goto err;
- }
-
- lua_pop(cfg->lua_state, 1);
- }
- else if (type == MIME_ATOM_LOCAL_LUA_FUNCTION) {
- /* p pointer is set to the start of Lua function name */
-
- if (real_ud->conf_obj == NULL) {
- g_set_error(err, rspamd_mime_expr_quark(), 300,
- "no config object for '%s'",
- mime_atom->str);
- goto err;
- }
-
- const ucl_object_t *functions = ucl_object_lookup(real_ud->conf_obj,
- "functions");
-
- if (functions == NULL) {
- g_set_error(err, rspamd_mime_expr_quark(), 310,
- "no functions defined for '%s'",
- mime_atom->str);
- goto err;
- }
-
- if (ucl_object_type(functions) != UCL_OBJECT) {
- g_set_error(err, rspamd_mime_expr_quark(), 320,
- "functions is not a table for '%s'",
- mime_atom->str);
- goto err;
- }
-
- const ucl_object_t *function_obj;
-
- function_obj = ucl_object_lookup_len(functions, c,
- p - c);
-
- if (function_obj == NULL) {
- g_set_error(err, rspamd_mime_expr_quark(), 320,
- "function %.*s is not found for '%s'",
- (int) (p - c), c, mime_atom->str);
- goto err;
- }
-
- if (ucl_object_type(function_obj) != UCL_USERDATA) {
- g_set_error(err, rspamd_mime_expr_quark(), 320,
- "function %.*s has invalid type for '%s'",
- (int) (p - c), c, mime_atom->str);
- goto err;
- }
-
- struct ucl_lua_funcdata *fd = function_obj->value.ud;
-
- mime_atom->d.lua_cbref = fd->idx;
- }
- else {
- mime_atom->d.func = rspamd_mime_expr_parse_function_atom(pool,
- mime_atom->str);
- if (mime_atom->d.func == NULL) {
- g_set_error(err, rspamd_mime_expr_quark(), 200,
- "cannot parse function '%s'",
- mime_atom->str);
- goto err;
- }
- }
-
- a = rspamd_mempool_alloc0(pool, sizeof(*a));
- a->len = p - line;
- a->priority = 0;
- a->data = mime_atom;
-
- return a;
-
- err:
-
- return NULL;
- }
-
- static gint
- rspamd_mime_expr_process_regexp(struct rspamd_regexp_atom *re,
- struct rspamd_task *task)
- {
- gint ret;
-
- if (re == NULL) {
- msg_info_task("invalid regexp passed");
- return 0;
- }
-
- if (re->type == RSPAMD_RE_HEADER || re->type == RSPAMD_RE_RAWHEADER) {
- ret = rspamd_re_cache_process(task,
- re->regexp,
- re->type,
- re->extra.header,
- strlen(re->extra.header),
- re->is_strong);
- }
- else if (re->type == RSPAMD_RE_SELECTOR) {
- ret = rspamd_re_cache_process(task,
- re->regexp,
- re->type,
- re->extra.selector,
- strlen(re->extra.selector),
- re->is_strong);
- }
- else {
- ret = rspamd_re_cache_process(task,
- re->regexp,
- re->type,
- NULL,
- 0,
- re->is_strong);
- }
-
- if (re->is_test) {
- msg_info_task("test %s regexp '%s' returned %d",
- rspamd_re_cache_type_to_string(re->type),
- re->regexp_text, ret);
- }
-
- return ret;
- }
-
-
- static gint
- rspamd_mime_expr_priority(rspamd_expression_atom_t *atom)
- {
- struct rspamd_mime_atom *mime_atom = atom->data;
- gint ret = 0;
-
- switch (mime_atom->type) {
- case MIME_ATOM_INTERNAL_FUNCTION:
- /* Prioritize internal functions slightly */
- ret = RSPAMD_EXPRESSION_MAX_PRIORITY - RSPAMD_EXPRESSION_MAX_PRIORITY / 8;
- break;
- case MIME_ATOM_LUA_FUNCTION:
- case MIME_ATOM_LOCAL_LUA_FUNCTION:
- ret = RSPAMD_EXPRESSION_MAX_PRIORITY - RSPAMD_EXPRESSION_MAX_PRIORITY / 4;
- break;
- case MIME_ATOM_REGEXP:
- switch (mime_atom->d.re->type) {
- case RSPAMD_RE_HEADER:
- case RSPAMD_RE_RAWHEADER:
- ret = RSPAMD_EXPRESSION_MAX_PRIORITY - RSPAMD_EXPRESSION_MAX_PRIORITY / 16;
- break;
- case RSPAMD_RE_URL:
- case RSPAMD_RE_EMAIL:
- ret = RSPAMD_EXPRESSION_MAX_PRIORITY - RSPAMD_EXPRESSION_MAX_PRIORITY / 8;
- break;
- case RSPAMD_RE_SELECTOR:
- ret = RSPAMD_EXPRESSION_MAX_PRIORITY - RSPAMD_EXPRESSION_MAX_PRIORITY / 8;
- break;
- case RSPAMD_RE_MIME:
- case RSPAMD_RE_RAWMIME:
- ret = RSPAMD_EXPRESSION_MAX_PRIORITY - RSPAMD_EXPRESSION_MAX_PRIORITY / 2;
- break;
- case RSPAMD_RE_WORDS:
- case RSPAMD_RE_RAWWORDS:
- case RSPAMD_RE_STEMWORDS:
- default:
- /* For expensive regexps */
- ret = 0;
- break;
- }
- }
-
- return ret;
- }
-
- static void
- rspamd_mime_expr_destroy(rspamd_expression_atom_t *atom)
- {
- struct rspamd_mime_atom *mime_atom = atom->data;
-
- if (mime_atom) {
- if (mime_atom->type == MIME_ATOM_INTERNAL_FUNCTION) {
- /* Need to cleanup arguments */
- g_array_free(mime_atom->d.func->args, TRUE);
- }
- }
- }
-
- static gboolean
- rspamd_mime_expr_process_function(struct rspamd_function_atom *func,
- struct rspamd_task *task,
- lua_State *L)
- {
- struct _fl *selected, key;
-
- key.name = func->name;
-
- selected = bsearch(&key,
- list_ptr,
- functions_number,
- sizeof(struct _fl),
- fl_cmp);
- if (selected == NULL) {
- /* Try to check lua function */
- return FALSE;
- }
-
- return selected->func(task, func->args, selected->user_data);
- }
-
- static gdouble
- rspamd_mime_expr_process(void *ud, rspamd_expression_atom_t *atom)
- {
- struct rspamd_task *task = (struct rspamd_task *) ud;
- struct rspamd_mime_atom *mime_atom;
- lua_State *L;
- gdouble ret = 0;
-
- g_assert(task != NULL);
- g_assert(atom != NULL);
-
- mime_atom = atom->data;
-
- if (mime_atom->type == MIME_ATOM_REGEXP) {
- ret = rspamd_mime_expr_process_regexp(mime_atom->d.re, task);
- }
- else if (mime_atom->type == MIME_ATOM_LUA_FUNCTION) {
- L = task->cfg->lua_state;
- lua_getglobal(L, mime_atom->d.lua_function);
- rspamd_lua_task_push(L, task);
-
- if (lua_pcall(L, 1, 1, 0) != 0) {
- msg_info_task("lua call to global function '%s' for atom '%s' failed: %s",
- mime_atom->d.lua_function,
- mime_atom->str,
- lua_tostring(L, -1));
- lua_pop(L, 1);
- }
- else {
- if (lua_type(L, -1) == LUA_TBOOLEAN) {
- ret = lua_toboolean(L, -1);
- }
- else if (lua_type(L, -1) == LUA_TNUMBER) {
- ret = lua_tonumber(L, 1);
- }
- else {
- msg_err_task("%s returned wrong return type: %s",
- mime_atom->str, lua_typename(L, lua_type(L, -1)));
- }
- /* Remove result */
- lua_pop(L, 1);
- }
- }
- else if (mime_atom->type == MIME_ATOM_LOCAL_LUA_FUNCTION) {
- gint err_idx;
-
- L = task->cfg->lua_state;
- lua_pushcfunction(L, &rspamd_lua_traceback);
- err_idx = lua_gettop(L);
-
- lua_rawgeti(L, LUA_REGISTRYINDEX, mime_atom->d.lua_cbref);
- rspamd_lua_task_push(L, task);
-
- if (lua_pcall(L, 1, 1, err_idx) != 0) {
- msg_info_task("lua call to local function for atom '%s' failed: %s",
- mime_atom->str,
- lua_tostring(L, -1));
- }
- else {
- if (lua_type(L, -1) == LUA_TBOOLEAN) {
- ret = lua_toboolean(L, -1);
- }
- else if (lua_type(L, -1) == LUA_TNUMBER) {
- ret = lua_tonumber(L, 1);
- }
- else {
- msg_err_task("%s returned wrong return type: %s",
- mime_atom->str, lua_typename(L, lua_type(L, -1)));
- }
- }
-
- lua_settop(L, 0);
- }
- else {
- ret = rspamd_mime_expr_process_function(mime_atom->d.func, task,
- task->cfg->lua_state);
- }
-
- return ret;
- }
-
- void register_expression_function(const gchar *name,
- rspamd_internal_func_t func,
- void *user_data)
- {
- static struct _fl *new;
-
- functions_number++;
-
- new = g_new(struct _fl, functions_number);
- memcpy(new, list_ptr, (functions_number - 1) * sizeof(struct _fl));
- if (list_allocated) {
- g_free(list_ptr);
- }
-
- list_allocated = TRUE;
- new[functions_number - 1].name = name;
- new[functions_number - 1].func = func;
- new[functions_number - 1].user_data = user_data;
- qsort(new, functions_number, sizeof(struct _fl), fl_cmp);
- list_ptr = new;
- }
-
- gboolean
- rspamd_compare_encoding(struct rspamd_task *task, GArray *args, void *unused)
- {
- struct expression_argument *arg;
-
- if (args == NULL || task == NULL) {
- return FALSE;
- }
-
- arg = &g_array_index(args, struct expression_argument, 0);
- if (!arg || arg->type != EXPRESSION_ARGUMENT_NORMAL) {
- msg_warn_task("invalid argument to function is passed");
- return FALSE;
- }
-
- /* XXX: really write this function */
- return TRUE;
- }
-
- gboolean
- rspamd_header_exists(struct rspamd_task *task, GArray *args, void *unused)
- {
- struct expression_argument *arg;
- struct rspamd_mime_header *rh;
-
- if (args == NULL || task == NULL) {
- return FALSE;
- }
-
- arg = &g_array_index(args, struct expression_argument, 0);
- if (!arg || arg->type != EXPRESSION_ARGUMENT_NORMAL) {
- msg_warn_task("invalid argument to function is passed");
- return FALSE;
- }
-
- rh = rspamd_message_get_header_array(task,
- (gchar *) arg->data, FALSE);
-
- debug_task("try to get header %s: %d", (gchar *) arg->data,
- (rh != NULL));
-
- if (rh) {
- return TRUE;
- }
-
- return FALSE;
- }
-
-
- /*
- * This function is designed to find difference between text/html and text/plain parts
- * It takes one argument: difference threshold, if we have two text parts, compare
- * its hashes and check for threshold, if value is greater than threshold, return TRUE
- * and return FALSE otherwise.
- */
- gboolean
- rspamd_parts_distance(struct rspamd_task *task, GArray *args, void *unused)
- {
- gint threshold, threshold2 = -1;
- struct expression_argument *arg;
- gdouble *pdiff, diff;
-
- if (args == NULL || args->len == 0) {
- debug_task("no threshold is specified, assume it 100");
- threshold = 100;
- }
- else {
- errno = 0;
- arg = &g_array_index(args, struct expression_argument, 0);
- if (!arg || arg->type != EXPRESSION_ARGUMENT_NORMAL) {
- msg_warn_task("invalid argument to function is passed");
- return FALSE;
- }
-
- threshold = strtoul((gchar *) arg->data, NULL, 10);
- if (errno != 0) {
- msg_info_task("bad numeric value for threshold \"%s\", assume it 100",
- (gchar *) arg->data);
- threshold = 100;
- }
- if (args->len >= 2) {
- arg = &g_array_index(args, struct expression_argument, 1);
- if (!arg || arg->type != EXPRESSION_ARGUMENT_NORMAL) {
- msg_warn_task("invalid argument to function is passed");
- return FALSE;
- }
-
- errno = 0;
- threshold2 = strtoul((gchar *) arg->data, NULL, 10);
- if (errno != 0) {
- msg_info_task("bad numeric value for threshold \"%s\", ignore it",
- (gchar *) arg->data);
- threshold2 = -1;
- }
- }
- }
-
- if ((pdiff =
- rspamd_mempool_get_variable(task->task_pool,
- "parts_distance")) != NULL) {
- diff = (1.0 - (*pdiff)) * 100.0;
-
- if (diff != -1) {
- if (threshold2 > 0) {
- if (diff >= MIN(threshold, threshold2) &&
- diff < MAX(threshold, threshold2)) {
-
- return TRUE;
- }
- }
- else {
- if (diff <= threshold) {
- return TRUE;
- }
- }
- return FALSE;
- }
- else {
- return FALSE;
- }
- }
-
- return FALSE;
- }
-
- struct addr_list {
- const gchar *name;
- guint namelen;
- const gchar *addr;
- guint addrlen;
- };
-
- static gint
- addr_list_cmp_func(const void *a, const void *b)
- {
- const struct addr_list *addra = (struct addr_list *) a,
- *addrb = (struct addr_list *) b;
-
- if (addra->addrlen != addrb->addrlen) {
- return addra->addrlen - addrb->addrlen;
- }
-
- return memcmp(addra->addr, addrb->addr, addra->addrlen);
- }
-
- #define COMPARE_RCPT_LEN 3
- #define MIN_RCPT_TO_COMPARE 7
-
- gboolean
- rspamd_recipients_distance(struct rspamd_task *task, GArray *args,
- void *unused)
- {
- struct expression_argument *arg;
- struct rspamd_email_address *cur;
- double threshold;
- struct addr_list *ar;
- gint num, i, hits = 0;
-
- if (args == NULL) {
- msg_warn_task("no parameters to function");
- return FALSE;
- }
-
- arg = &g_array_index(args, struct expression_argument, 0);
- if (!arg || arg->type != EXPRESSION_ARGUMENT_NORMAL) {
- msg_warn_task("invalid argument to function is passed");
- return FALSE;
- }
-
- errno = 0;
- threshold = strtod((gchar *) arg->data, NULL);
-
- if (errno != 0) {
- msg_warn_task("invalid numeric value '%s': %s",
- (gchar *) arg->data,
- strerror(errno));
- return FALSE;
- }
-
- if (!MESSAGE_FIELD(task, rcpt_mime)) {
- return FALSE;
- }
-
- num = MESSAGE_FIELD(task, rcpt_mime)->len;
-
- if (num < MIN_RCPT_TO_COMPARE) {
- return FALSE;
- }
-
- ar = rspamd_mempool_alloc0(task->task_pool, num * sizeof(struct addr_list));
-
- /* Fill array */
- num = 0;
- PTR_ARRAY_FOREACH(MESSAGE_FIELD(task, rcpt_mime), i, cur)
- {
- if (cur->addr_len > COMPARE_RCPT_LEN) {
- ar[num].name = cur->addr;
- ar[num].namelen = cur->addr_len;
- ar[num].addr = cur->domain;
- ar[num].addrlen = cur->domain_len;
- num++;
- }
- }
-
- qsort(ar, num, sizeof(*ar), addr_list_cmp_func);
-
- /* Cycle all elements in array */
- for (i = 0; i < num; i++) {
- if (i < num - 1) {
- if (ar[i].namelen == ar[i + 1].namelen) {
- if (rspamd_lc_cmp(ar[i].name, ar[i + 1].name, COMPARE_RCPT_LEN) == 0) {
- hits++;
- }
- }
- }
- }
-
- if ((hits * num / 2.) / (double) num >= threshold) {
- return TRUE;
- }
-
- return FALSE;
- }
-
- gboolean
- rspamd_has_only_html_part(struct rspamd_task *task, GArray *args,
- void *unused)
- {
- struct rspamd_mime_text_part *p;
- guint i, cnt_html = 0, cnt_txt = 0;
-
- PTR_ARRAY_FOREACH(MESSAGE_FIELD(task, text_parts), i, p)
- {
- if (!IS_TEXT_PART_ATTACHMENT(p)) {
- if (IS_TEXT_PART_HTML(p)) {
- cnt_html++;
- }
- else {
- cnt_txt++;
- }
- }
- }
-
- return (cnt_html > 0 && cnt_txt == 0);
- }
-
- static gboolean
- is_recipient_list_sorted(GPtrArray *ar)
- {
- struct rspamd_email_address *addr;
- gboolean res = TRUE;
- rspamd_ftok_t cur, prev;
- gint i;
-
- /* Do not check to short address lists */
- if (ar == NULL || ar->len < MIN_RCPT_TO_COMPARE) {
- return FALSE;
- }
-
- prev.len = 0;
- prev.begin = NULL;
-
- PTR_ARRAY_FOREACH(ar, i, addr)
- {
- cur.begin = addr->addr;
- cur.len = addr->addr_len;
-
- if (prev.len != 0) {
- if (rspamd_ftok_casecmp(&cur, &prev) <= 0) {
- res = FALSE;
- break;
- }
- }
-
- prev = cur;
- }
-
- return res;
- }
-
- gboolean
- rspamd_is_recipients_sorted(struct rspamd_task *task,
- GArray *args,
- void *unused)
- {
- /* Check all types of addresses */
-
- if (MESSAGE_FIELD(task, rcpt_mime)) {
- return is_recipient_list_sorted(MESSAGE_FIELD(task, rcpt_mime));
- }
-
- return FALSE;
- }
-
- gboolean
- rspamd_compare_transfer_encoding(struct rspamd_task *task,
- GArray *args,
- void *unused)
- {
- struct expression_argument *arg;
- guint i;
- struct rspamd_mime_part *part;
- enum rspamd_cte cte;
-
- if (args == NULL) {
- msg_warn_task("no parameters to function");
- return FALSE;
- }
-
- arg = &g_array_index(args, struct expression_argument, 0);
- if (!arg || arg->type != EXPRESSION_ARGUMENT_NORMAL) {
- msg_warn_task("invalid argument to function is passed");
- return FALSE;
- }
-
- cte = rspamd_cte_from_string(arg->data);
-
- if (cte == RSPAMD_CTE_UNKNOWN) {
- msg_warn_task("unknown cte: %s", arg->data);
- return FALSE;
- }
-
- PTR_ARRAY_FOREACH(MESSAGE_FIELD(task, parts), i, part)
- {
- if (IS_PART_TEXT(part)) {
- if (part->cte == cte) {
- return TRUE;
- }
- }
- }
-
- return FALSE;
- }
-
- gboolean
- rspamd_is_html_balanced(struct rspamd_task *task, GArray *args, void *unused)
- {
- /* Totally broken but seems to be never used */
- return TRUE;
- }
-
- gboolean
- rspamd_has_html_tag(struct rspamd_task *task, GArray *args, void *unused)
- {
- struct rspamd_mime_text_part *p;
- struct expression_argument *arg;
- guint i;
- gboolean res = FALSE;
-
- if (args == NULL) {
- msg_warn_task("no parameters to function");
- return FALSE;
- }
-
- arg = &g_array_index(args, struct expression_argument, 0);
- if (!arg || arg->type != EXPRESSION_ARGUMENT_NORMAL) {
- msg_warn_task("invalid argument to function is passed");
- return FALSE;
- }
-
- PTR_ARRAY_FOREACH(MESSAGE_FIELD(task, text_parts), i, p)
- {
- if (IS_TEXT_PART_HTML(p) && p->html) {
- res = rspamd_html_tag_seen(p->html, arg->data);
- }
-
- if (res) {
- break;
- }
- }
-
- return res;
- }
-
- gboolean
- rspamd_has_fake_html(struct rspamd_task *task, GArray *args, void *unused)
- {
- struct rspamd_mime_text_part *p;
- guint i;
- gboolean res = FALSE;
-
- PTR_ARRAY_FOREACH(MESSAGE_FIELD(task, text_parts), i, p)
- {
- if (IS_TEXT_PART_HTML(p) && (rspamd_html_get_tags_count(p->html) < 2)) {
- res = TRUE;
- }
-
- if (res) {
- break;
- }
- }
-
- return res;
- }
-
- static gboolean
- rspamd_raw_header_exists(struct rspamd_task *task, GArray *args, void *unused)
- {
- struct expression_argument *arg;
-
- if (args == NULL || task == NULL) {
- return FALSE;
- }
-
- arg = &g_array_index(args, struct expression_argument, 0);
- if (!arg || arg->type != EXPRESSION_ARGUMENT_NORMAL) {
- msg_warn_task("invalid argument to function is passed");
- return FALSE;
- }
-
- return rspamd_message_get_header_array(task, arg->data, FALSE) != NULL;
- }
-
- static gboolean
- match_smtp_data(struct rspamd_task *task,
- struct expression_argument *arg,
- const gchar *what, gsize len)
- {
- rspamd_regexp_t *re;
- gint r = 0;
-
- if (arg->type == EXPRESSION_ARGUMENT_REGEXP) {
- /* This is a regexp */
- re = arg->data;
- if (re == NULL) {
- msg_warn_task("cannot compile regexp for function");
- return FALSE;
- }
-
-
- if (len > 0) {
- r = rspamd_regexp_search(re, what, len, NULL, NULL, FALSE, NULL);
- }
-
- return r;
- }
- else if (arg->type == EXPRESSION_ARGUMENT_NORMAL &&
- g_ascii_strncasecmp(arg->data, what, len) == 0) {
- return TRUE;
- }
-
- return FALSE;
- }
-
- static gboolean
- rspamd_check_smtp_data(struct rspamd_task *task, GArray *args, void *unused)
- {
- struct expression_argument *arg;
- struct rspamd_email_address *addr = NULL;
- GPtrArray *rcpts = NULL;
- const gchar *type, *str = NULL;
- guint i;
-
- if (args == NULL) {
- msg_warn_task("no parameters to function");
- return FALSE;
- }
-
- arg = &g_array_index(args, struct expression_argument, 0);
-
- if (!arg || !arg->data || arg->type != EXPRESSION_ARGUMENT_NORMAL) {
- msg_warn_task("no parameters to function");
- return FALSE;
- }
- else {
- type = arg->data;
- switch (*type) {
- case 'f':
- case 'F':
- if (g_ascii_strcasecmp(type, "from") == 0) {
- addr = rspamd_task_get_sender(task);
- }
- else {
- msg_warn_task("bad argument to function: %s", type);
- return FALSE;
- }
- break;
- case 'h':
- case 'H':
- if (g_ascii_strcasecmp(type, "helo") == 0) {
- str = task->helo;
- }
- else {
- msg_warn_task("bad argument to function: %s", type);
- return FALSE;
- }
- break;
- case 'u':
- case 'U':
- if (g_ascii_strcasecmp(type, "user") == 0) {
- str = task->auth_user;
- }
- else {
- msg_warn_task("bad argument to function: %s", type);
- return FALSE;
- }
- break;
- case 's':
- case 'S':
- if (g_ascii_strcasecmp(type, "subject") == 0) {
- str = MESSAGE_FIELD(task, subject);
- }
- else {
- msg_warn_task("bad argument to function: %s", type);
- return FALSE;
- }
- break;
- case 'r':
- case 'R':
- if (g_ascii_strcasecmp(type, "rcpt") == 0) {
- rcpts = task->rcpt_envelope;
- }
- else {
- msg_warn_task("bad argument to function: %s", type);
- return FALSE;
- }
- break;
- default:
- msg_warn_task("bad argument to function: %s", type);
- return FALSE;
- }
- }
-
- if (str == NULL && addr == NULL && rcpts == NULL) {
- /* Not enough data so regexp would NOT be found anyway */
- return FALSE;
- }
-
- /* We would process only one more argument, others are ignored */
- if (args->len >= 2) {
- arg = &g_array_index(args, struct expression_argument, 1);
-
- if (arg) {
- if (str != NULL) {
- return match_smtp_data(task, arg, str, strlen(str));
- }
- else if (addr != NULL && addr->addr) {
- return match_smtp_data(task, arg, addr->addr, addr->addr_len);
- }
- else {
- if (rcpts != NULL) {
- for (i = 0; i < rcpts->len; i++) {
- addr = g_ptr_array_index(rcpts, i);
-
- if (addr && addr->addr &&
- match_smtp_data(task, arg,
- addr->addr, addr->addr_len)) {
- return TRUE;
- }
- }
- }
- }
- }
- }
-
- return FALSE;
- }
-
- static inline gboolean
- rspamd_check_ct_attr(const gchar *begin, gsize len,
- struct expression_argument *arg_pattern)
- {
- rspamd_regexp_t *re;
- gboolean r = FALSE;
-
- if (arg_pattern->type == EXPRESSION_ARGUMENT_REGEXP) {
- re = arg_pattern->data;
-
- if (len > 0) {
- r = rspamd_regexp_search(re,
- begin, len,
- NULL, NULL, FALSE, NULL);
- }
-
- if (r) {
- return TRUE;
- }
- }
- else {
- /* Just do strcasecmp */
- gsize plen = strlen(arg_pattern->data);
-
- if (plen == len &&
- g_ascii_strncasecmp(arg_pattern->data, begin, len) == 0) {
- return TRUE;
- }
- }
-
- return FALSE;
- }
-
- static gboolean
- rspamd_content_type_compare_param(struct rspamd_task *task,
- GArray *args,
- void *unused)
- {
-
- struct expression_argument *arg, *arg1, *arg_pattern;
- gboolean recursive = FALSE;
- struct rspamd_mime_part *cur_part;
- guint i;
- rspamd_ftok_t srch;
- struct rspamd_content_type_param *found = NULL, *cur;
- const gchar *param_name;
-
- if (args == NULL || args->len < 2) {
- msg_warn_task("no parameters to function");
- return FALSE;
- }
-
- arg = &g_array_index(args, struct expression_argument, 0);
- g_assert(arg->type == EXPRESSION_ARGUMENT_NORMAL);
- param_name = arg->data;
- arg_pattern = &g_array_index(args, struct expression_argument, 1);
-
- PTR_ARRAY_FOREACH(MESSAGE_FIELD(task, parts), i, cur_part)
- {
- if (args->len >= 3) {
- arg1 = &g_array_index(args, struct expression_argument, 2);
- if (g_ascii_strncasecmp(arg1->data, "true",
- sizeof("true") - 1) == 0) {
- recursive = TRUE;
- }
- }
- else {
- /*
- * If user did not specify argument, let's assume that he wants
- * recursive search if mime part is multipart/mixed
- */
- if (IS_PART_MULTIPART(cur_part)) {
- recursive = TRUE;
- }
- }
-
- rspamd_ftok_t lit;
- RSPAMD_FTOK_FROM_STR(&srch, param_name);
- RSPAMD_FTOK_FROM_STR(&lit, "charset");
-
- if (rspamd_ftok_equal(&srch, &lit)) {
- if (rspamd_check_ct_attr(cur_part->ct->charset.begin,
- cur_part->ct->charset.len, arg_pattern)) {
- return TRUE;
- }
- }
-
- RSPAMD_FTOK_FROM_STR(&lit, "boundary");
- if (rspamd_ftok_equal(&srch, &lit)) {
- if (rspamd_check_ct_attr(cur_part->ct->orig_boundary.begin,
- cur_part->ct->orig_boundary.len, arg_pattern)) {
- return TRUE;
- }
- }
-
- if (cur_part->ct->attrs) {
- found = g_hash_table_lookup(cur_part->ct->attrs, &srch);
-
- if (found) {
- DL_FOREACH(found, cur)
- {
- if (rspamd_check_ct_attr(cur->value.begin,
- cur->value.len, arg_pattern)) {
- return TRUE;
- }
- }
- }
- }
-
- if (!recursive) {
- break;
- }
- }
-
- return FALSE;
- }
-
- static gboolean
- rspamd_content_type_has_param(struct rspamd_task *task,
- GArray *args,
- void *unused)
- {
- struct expression_argument *arg, *arg1;
- gboolean recursive = FALSE;
- struct rspamd_mime_part *cur_part;
- guint i;
- rspamd_ftok_t srch;
- struct rspamd_content_type_param *found = NULL;
- const gchar *param_name;
-
- if (args == NULL || args->len < 1) {
- msg_warn_task("no parameters to function");
- return FALSE;
- }
-
- arg = &g_array_index(args, struct expression_argument, 0);
- g_assert(arg->type == EXPRESSION_ARGUMENT_NORMAL);
- param_name = arg->data;
-
- PTR_ARRAY_FOREACH(MESSAGE_FIELD(task, parts), i, cur_part)
- {
- if (args->len >= 2) {
- arg1 = &g_array_index(args, struct expression_argument, 1);
- if (g_ascii_strncasecmp(arg1->data, "true",
- sizeof("true") - 1) == 0) {
- recursive = TRUE;
- }
- }
- else {
- /*
- * If user did not specify argument, let's assume that he wants
- * recursive search if mime part is multipart/mixed
- */
- if (IS_PART_MULTIPART(cur_part)) {
- recursive = TRUE;
- }
- }
-
-
- rspamd_ftok_t lit;
- RSPAMD_FTOK_FROM_STR(&srch, param_name);
- RSPAMD_FTOK_FROM_STR(&lit, "charset");
-
- if (rspamd_ftok_equal(&srch, &lit)) {
- if (cur_part->ct->charset.len > 0) {
- return TRUE;
- }
- }
-
- RSPAMD_FTOK_FROM_STR(&lit, "boundary");
- if (rspamd_ftok_equal(&srch, &lit)) {
- if (cur_part->ct->boundary.len > 0) {
- return TRUE;
- }
- }
-
- if (cur_part->ct->attrs) {
- found = g_hash_table_lookup(cur_part->ct->attrs, &srch);
-
- if (found) {
- return TRUE;
- }
- }
-
- if (!recursive) {
- break;
- }
- }
-
- return FALSE;
- }
-
- static gboolean
- rspamd_content_type_check(struct rspamd_task *task,
- GArray *args,
- gboolean check_subtype)
- {
- rspamd_ftok_t *param_data, srch;
- rspamd_regexp_t *re;
- struct expression_argument *arg1, *arg_pattern;
- struct rspamd_content_type *ct;
- gint r = 0;
- guint i;
- gboolean recursive = FALSE;
- struct rspamd_mime_part *cur_part;
-
- if (args == NULL || args->len < 1) {
- msg_warn_task("no parameters to function");
- return FALSE;
- }
-
- arg_pattern = &g_array_index(args, struct expression_argument, 0);
-
- PTR_ARRAY_FOREACH(MESSAGE_FIELD(task, parts), i, cur_part)
- {
- ct = cur_part->ct;
-
- if (args->len >= 2) {
- arg1 = &g_array_index(args, struct expression_argument, 1);
- if (g_ascii_strncasecmp(arg1->data, "true",
- sizeof("true") - 1) == 0) {
- recursive = TRUE;
- }
- }
- else {
- /*
- * If user did not specify argument, let's assume that he wants
- * recursive search if mime part is multipart/mixed
- */
- if (IS_PART_MULTIPART(cur_part)) {
- recursive = TRUE;
- }
- }
-
- if (check_subtype) {
- param_data = &ct->subtype;
- }
- else {
- param_data = &ct->type;
- }
-
- if (arg_pattern->type == EXPRESSION_ARGUMENT_REGEXP) {
- re = arg_pattern->data;
-
- if (param_data->len > 0) {
- r = rspamd_regexp_search(re, param_data->begin, param_data->len,
- NULL, NULL, FALSE, NULL);
- }
-
- if (r) {
- return TRUE;
- }
- }
- else {
- /* Just do strcasecmp */
- srch.begin = arg_pattern->data;
- srch.len = strlen(arg_pattern->data);
-
- if (rspamd_ftok_casecmp(param_data, &srch) == 0) {
- return TRUE;
- }
- }
-
- /* Get next part */
- if (!recursive) {
- break;
- }
- }
-
- return FALSE;
- }
-
- static gboolean
- rspamd_content_type_is_type(struct rspamd_task *task,
- GArray *args,
- void *unused)
- {
- return rspamd_content_type_check(task, args, FALSE);
- }
-
- static gboolean
- rspamd_content_type_is_subtype(struct rspamd_task *task,
- GArray *args,
- void *unused)
- {
- return rspamd_content_type_check(task, args, TRUE);
- }
-
- static gboolean
- compare_subtype(struct rspamd_task *task, struct rspamd_content_type *ct,
- struct expression_argument *subtype)
- {
- rspamd_regexp_t *re;
- rspamd_ftok_t srch;
- gint r = 0;
-
- if (subtype == NULL || ct == NULL) {
- msg_warn_task("invalid parameters passed");
- return FALSE;
- }
- if (subtype->type == EXPRESSION_ARGUMENT_REGEXP) {
- re = subtype->data;
-
- if (ct->subtype.len > 0) {
- r = rspamd_regexp_search(re, ct->subtype.begin, ct->subtype.len,
- NULL, NULL, FALSE, NULL);
- }
- }
- else {
- srch.begin = subtype->data;
- srch.len = strlen(subtype->data);
-
- /* Just do strcasecmp */
- if (rspamd_ftok_casecmp(&ct->subtype, &srch) == 0) {
- return TRUE;
- }
- }
-
- return r;
- }
-
- static gboolean
- compare_len(struct rspamd_mime_part *part, guint min, guint max)
- {
- if (min == 0 && max == 0) {
- return TRUE;
- }
-
- if (min == 0) {
- return part->parsed_data.len <= max;
- }
- else if (max == 0) {
- return part->parsed_data.len >= min;
- }
- else {
- return part->parsed_data.len >= min && part->parsed_data.len <= max;
- }
- }
-
- static gboolean
- common_has_content_part(struct rspamd_task *task,
- struct expression_argument *param_type,
- struct expression_argument *param_subtype,
- gint min_len,
- gint max_len)
- {
- rspamd_regexp_t *re;
- struct rspamd_mime_part *part;
- struct rspamd_content_type *ct;
- rspamd_ftok_t srch;
- gint r = 0;
- guint i;
-
- PTR_ARRAY_FOREACH(MESSAGE_FIELD(task, parts), i, part)
- {
- ct = part->ct;
-
- if (ct == NULL) {
- continue;
- }
-
- if (param_type->type == EXPRESSION_ARGUMENT_REGEXP) {
- re = param_type->data;
-
- if (ct->type.len > 0) {
- r = rspamd_regexp_search(re, ct->type.begin, ct->type.len,
- NULL, NULL, FALSE, NULL);
- }
-
- /* Also check subtype and length of the part */
- if (r && param_subtype) {
- r = compare_len(part, min_len, max_len) &&
- compare_subtype(task, ct, param_subtype);
-
- return r;
- }
- }
- else {
- /* Just do strcasecmp */
- srch.begin = param_type->data;
- srch.len = strlen(param_type->data);
-
- if (rspamd_ftok_casecmp(&ct->type, &srch) == 0) {
- if (param_subtype) {
- if (compare_subtype(task, ct, param_subtype)) {
- if (compare_len(part, min_len, max_len)) {
- return TRUE;
- }
- }
- }
- else {
- if (compare_len(part, min_len, max_len)) {
- return TRUE;
- }
- }
- }
- }
- }
-
- return FALSE;
- }
-
- static gboolean
- rspamd_has_content_part(struct rspamd_task *task, GArray *args, void *unused)
- {
- struct expression_argument *param_type = NULL, *param_subtype = NULL;
-
- if (args == NULL) {
- msg_warn_task("no parameters to function");
- return FALSE;
- }
-
- param_type = &g_array_index(args, struct expression_argument, 0);
- if (args->len >= 2) {
- param_subtype = &g_array_index(args, struct expression_argument, 1);
- }
-
- return common_has_content_part(task, param_type, param_subtype, 0, 0);
- }
-
- static gboolean
- rspamd_has_content_part_len(struct rspamd_task *task,
- GArray *args,
- void *unused)
- {
- struct expression_argument *param_type = NULL, *param_subtype = NULL;
- gint min = 0, max = 0;
- struct expression_argument *arg;
-
- if (args == NULL) {
- msg_warn_task("no parameters to function");
- return FALSE;
- }
-
- param_type = &g_array_index(args, struct expression_argument, 0);
-
- if (args->len >= 2) {
- param_subtype = &g_array_index(args, struct expression_argument, 1);
-
- if (args->len >= 3) {
- arg = &g_array_index(args, struct expression_argument, 2);
- errno = 0;
- min = strtoul(arg->data, NULL, 10);
- g_assert(arg->type == EXPRESSION_ARGUMENT_NORMAL);
-
- if (errno != 0) {
- msg_warn_task("invalid numeric value '%s': %s",
- (gchar *) arg->data,
- strerror(errno));
- return FALSE;
- }
-
- if (args->len >= 4) {
- arg = &g_array_index(args, struct expression_argument, 3);
- g_assert(arg->type == EXPRESSION_ARGUMENT_NORMAL);
- max = strtoul(arg->data, NULL, 10);
-
- if (errno != 0) {
- msg_warn_task("invalid numeric value '%s': %s",
- (gchar *) arg->data,
- strerror(errno));
- return FALSE;
- }
- }
- }
- }
-
- return common_has_content_part(task, param_type, param_subtype, min, max);
- }
-
- static gboolean
- rspamd_is_empty_body(struct rspamd_task *task,
- GArray *args,
- void *unused)
- {
- struct rspamd_mime_part *part;
- guint i;
-
- PTR_ARRAY_FOREACH(MESSAGE_FIELD(task, parts), i, part)
- {
- if (part->parsed_data.len > 0) {
- return FALSE;
- }
- }
-
- return TRUE;
- }
-
- #define TASK_FLAG_READ(flag) \
- do { \
- result = !!(task->flags & (flag)); \
- } while (0)
-
- #define TASK_GET_FLAG(flag, strname, macro) \
- do { \
- if (!found && strcmp((flag), strname) == 0) { \
- TASK_FLAG_READ((macro)); \
- found = TRUE; \
- } \
- } while (0)
-
- #define TASK_PROTOCOL_FLAG_READ(flag) \
- do { \
- result = !!(task->protocol_flags & (flag)); \
- } while (0)
-
- #define TASK_GET_PROTOCOL_FLAG(flag, strname, macro) \
- do { \
- if (!found && strcmp((flag), strname) == 0) { \
- TASK_PROTOCOL_FLAG_READ((macro)); \
- found = TRUE; \
- } \
- } while (0)
-
-
- static gboolean
- rspamd_has_flag_expr(struct rspamd_task *task,
- GArray *args,
- void *unused)
- {
- gboolean found = FALSE, result = FALSE;
- struct expression_argument *flag_arg;
- const gchar *flag_str;
-
- if (args == NULL) {
- msg_warn_task("no parameters to function");
- return FALSE;
- }
-
- flag_arg = &g_array_index(args, struct expression_argument, 0);
-
- if (flag_arg->type != EXPRESSION_ARGUMENT_NORMAL) {
- msg_warn_task("invalid parameter to function");
- return FALSE;
- }
-
- flag_str = (const gchar *) flag_arg->data;
-
- TASK_GET_FLAG(flag_str, "pass_all", RSPAMD_TASK_FLAG_PASS_ALL);
- TASK_GET_FLAG(flag_str, "no_log", RSPAMD_TASK_FLAG_NO_LOG);
- TASK_GET_FLAG(flag_str, "no_stat", RSPAMD_TASK_FLAG_NO_STAT);
- TASK_GET_FLAG(flag_str, "skip", RSPAMD_TASK_FLAG_SKIP);
- TASK_GET_PROTOCOL_FLAG(flag_str, "extended_urls",
- RSPAMD_TASK_PROTOCOL_FLAG_EXT_URLS);
- TASK_GET_FLAG(flag_str, "learn_spam", RSPAMD_TASK_FLAG_LEARN_SPAM);
- TASK_GET_FLAG(flag_str, "learn_ham", RSPAMD_TASK_FLAG_LEARN_HAM);
- TASK_GET_FLAG(flag_str, "greylisted", RSPAMD_TASK_FLAG_GREYLISTED);
- TASK_GET_FLAG(flag_str, "broken_headers",
- RSPAMD_TASK_FLAG_BROKEN_HEADERS);
- TASK_GET_FLAG(flag_str, "skip_process",
- RSPAMD_TASK_FLAG_SKIP_PROCESS);
- TASK_GET_PROTOCOL_FLAG(flag_str, "milter",
- RSPAMD_TASK_PROTOCOL_FLAG_MILTER);
- TASK_GET_FLAG(flag_str, "bad_unicode",
- RSPAMD_TASK_FLAG_BAD_UNICODE);
-
- if (!found) {
- msg_warn_task("invalid flag name %s", flag_str);
- return FALSE;
- }
-
- return result;
- }
-
- static gboolean
- rspamd_has_symbol_expr(struct rspamd_task *task,
- GArray *args,
- void *unused)
- {
- struct expression_argument *sym_arg;
- const gchar *symbol_str;
-
- if (args == NULL) {
- msg_warn_task("no parameters to function");
- return FALSE;
- }
-
- sym_arg = &g_array_index(args, struct expression_argument, 0);
-
- if (sym_arg->type != EXPRESSION_ARGUMENT_NORMAL) {
- msg_warn_task("invalid parameter to function");
- return FALSE;
- }
-
- symbol_str = (const gchar *) sym_arg->data;
-
- if (rspamd_task_find_symbol_result(task, symbol_str, NULL)) {
- return TRUE;
- }
-
- return FALSE;
- }
|