#line 982 "../../src/builtin/snarf.m4"
/* -*- buffer-read-only: t -*- vi: set ro:
   THIS FILE IS GENERATED AUTOMATICALLY.  PLEASE DO NOT EDIT.
*/
#line 982
#ifdef HAVE_CONFIG_H
#line 982
# include <config.h>
#line 982
#endif
#line 982
#include <sys/types.h>
#line 982

#line 982
#include "mailfromd.h"
#line 982
#include "prog.h"
#line 982
#include "builtin.h"
#line 982

#line 982

#line 1022 "../../src/builtin/snarf.m4"

/* End of snarf.m4 */
#line 1 "dspam.bi"
/* This file is part of Mailfromd.             -*- c -*-
   Copyright (C) 2006-2022 Sergey Poznyakoff

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */


#ifdef WITH_DSPAM
#line 18


#include "srvcfg.h"
#undef HAVE_CONFIG_H
#define CONFIG_DEFAULT "/dev/null"
#define LOGDIR "/dev/null"
#include <libdspam.h>
#include "mflib/dspam.h"
#include "msg.h"

/* User parameters */
static size_t dspam_user_loc
#line 29 "dspam.bi"
;
static size_t dspam_group_loc
#line 30 "dspam.bi"
;
static size_t dspam_config_loc
#line 31 "dspam.bi"
;
static size_t dspam_profile_loc
#line 32 "dspam.bi"
;
/* Output variables */
static size_t dspam_signature_loc
#line 34 "dspam.bi"
;
static size_t dspam_probability_loc
#line 35 "dspam.bi"
;
static size_t dspam_confidence_loc
#line 36 "dspam.bi"
;
static size_t dspam_prec_loc
#line 37 "dspam.bi"
;
#define DEFAULT_DSPAM_PREC 3

static int _dspam_initialized;

static void
_dspam_shutdown()
{
	dspam_shutdown_driver(NULL);
}

struct transtab
{
	int trans_from;
	int trans_to;
};

static struct builtin_const_trans mode_trans[] = {
	{ _MFL_DSM_PROCESS, DSM_PROCESS },
	{ _MFL_DSM_CLASSIFY, DSM_CLASSIFY }
};

static struct builtin_const_trans flag_trans[] = {
	{ _MFL_DSF_SIGNATURE, DSF_SIGNATURE },
	{ _MFL_DSF_NOISE, DSF_NOISE },
	{ _MFL_DSF_WHITELIST, DSF_WHITELIST }
};

static struct builtin_const_trans tokenizer_trans[] = {
	{ _MFL_DSZ_WORD, DSZ_WORD },
	{ _MFL_DSZ_CHAIN, DSZ_CHAIN },
	{ _MFL_DSZ_SBPH, DSZ_SBPH },
	{ _MFL_DSZ_OSB, DSZ_OSB },
};

static struct builtin_const_trans tmod_trans[] = {
	{ _MFL_DST_TEFT, DST_TEFT },
	{ _MFL_DST_TOE, DST_TOE },
	{ _MFL_DST_TUM, DST_TUM }
};

static struct builtin_const_trans class_trans[] = {
	{ _MFL_DSR_ISSPAM, DSR_ISSPAM },    
	{ _MFL_DSR_ISINNOCENT, DSR_ISINNOCENT },
	{ _MFL_DSR_NONE, DSR_NONE }
};

static struct builtin_const_trans source_trans[] = {
	{ _MFL_DSS_ERROR, DSS_ERROR },
	{ _MFL_DSS_CORPUS, DSS_CORPUS },    
	{ _MFL_DSS_INOCULATION, DSS_INOCULATION },
	{ _MFL_DSS_NONE, DSS_NONE }
};

static void
ctx_cleanup(void *ptr)
{	
	DSPAM_CTX *ctx = ptr;
	dspam_destroy(ctx);
}


struct config_entry {
	int argc;
	char **argv;
#       define config_keyword argv[0]
#       define config_value argv[1]
};

static void
free_config_entry(void *data)
{
	struct config_entry *entry = data;
	mu_argcv_free(entry->argc, entry->argv);
}

static int
compare_config_entry(const void *a, const void *b)
{
	struct config_entry const *ent_a = a;
	struct config_entry const *ent_b = b;
	return strcasecmp(ent_a->config_keyword, ent_b->config_keyword);
}

struct config_entry *
config_find(mu_list_t config, const char *kw)
{
	if (config) {
		struct config_entry key, *ret;
		key.argc = 1;
		key.argv = (char **)&kw;
		if (mu_list_locate(config, &key, (void **)&ret) == 0)
			return ret;
	}
	return NULL;
}

const char *
config_find_value(mu_list_t config, const char *kw)
{
	struct config_entry *ent = config_find(config, kw);
	if (ent)
		return ent->config_value;
	return NULL;
}

static int
read_config(mu_list_t config, const char *file_name)
{
	int rc;
	mu_stream_t str, flt;
	char *buf = NULL;
	size_t size = 0, n;
	static const char *args[] = { "INLINE-COMMENT", "#", "-r" };
	
	if ((rc = mu_file_stream_create(&str, file_name, MU_STREAM_READ))) {
		mu_error(_("cannot open configuration file `%s': %s"),
			 file_name, mu_strerror(rc));
		return rc;
	}

	rc = mu_filter_create_args(&flt, str,
				   "INLINE-COMMENT",
				   MU_ARRAY_SIZE(args), args,
				   MU_FILTER_DECODE,
				   MU_STREAM_READ);
	mu_stream_unref(str);
	if (rc) {
		mu_error (_("cannot open filter stream for `%s': %s"),
			  file_name, mu_strerror (rc));
		return rc;
	}
	str = flt;

	while (mu_stream_getline(str, &buf, &size, &n) == 0 && n > 0) {
		struct config_entry *ent;
		struct mu_wordsplit ws;
		
		if (mu_wordsplit(buf, &ws, MU_WRDSF_DEFFLAGS)) {
			mu_error("mu_wordsplit: %s",
				 mu_wordsplit_strerror(&ws));
			break;
		}

		if (ws.ws_wordc) {
			ent = mu_alloc(sizeof(*ent));
			ent->argc = ws.ws_wordc;
			ent->argv = ws.ws_wordv;
			mu_list_append(config, ent);
			ws.ws_wordc = 0;
			ws.ws_wordv = NULL;
		} /* FIXME: diagnostics */
		mu_wordsplit_free(&ws);
	}
	free(buf);
	mu_stream_close(str);
	mu_stream_destroy(&str);
	return 0;
}


static void *
alloc_config()
{
	mu_list_t config;
	
	mu_list_create(&config);
	mu_list_set_destroy_item(config, free_config_entry);
	mu_list_set_comparator(config, compare_config_entry);
	return config;
}

static void
destroy_config(void *data)
{
	mu_list_t config = data;
	mu_list_destroy(&config);
}


#line 216

#line 216
static int DSPAM_CONFIG_id;
#line 216 "dspam.bi"



#line 218

#line 218
static mu_list_t
#line 218
get_config(eval_environ_t env, mu_list_t config)
#line 218
{
#line 218
	/* Initialize dspam library and set up global variables, if
	   needed */
#line 218
	if (!_dspam_initialized) {
#line 218
		const char *config_file = (char*)env_data_ref(env, mf_c_val(*env_data_ref(env, dspam_config_loc),size) );
#line 218
		if (config_file && config_file[0])
#line 218
			read_config(config, config_file);
#line 218
	
#line 218
			if (!(libdspam_init(config_find_value(config,
#line 218
							  "StorageDriver"))
#line 218
			  == 0))
#line 218
		(
#line 218
	env_throw_bi(env, mfe_failure, NULL, "libdspam_init failed")
#line 218
)
#line 218
;
#line 218
			
#line 218
		dspam_init_driver(NULL);
#line 218
		atexit(_dspam_shutdown);
#line 218
		_dspam_initialized = 1;
#line 218

#line 218
		if ((char*)env_data_ref(env, mf_c_val(*env_data_ref(env, dspam_user_loc),size) ) == NULL)
#line 218
			
#line 218
{ size_t __off;
#line 218
  const char *__s = mf_server_user;
#line 218
  if (__s)
#line 218
     strcpy((char*)env_data_ref(env, __off = heap_reserve(env, strlen(__s) + 1)), __s);
#line 218
  else
#line 218
     __off = 0;
#line 218
  mf_c_val(*env_data_ref(env, dspam_user_loc),size) = (__off); }
#line 218
;
#line 218

#line 218
		if (mf_c_val(*env_data_ref(env, dspam_prec_loc),uint)  == 0)
#line 218
			mf_c_val(*env_data_ref(env, dspam_prec_loc),uint) = (DEFAULT_DSPAM_PREC);
#line 218
	}
#line 218

#line 218
	return config;
#line 218
}
#line 218

#line 218

#line 218

#line 248


struct keyword_prop {
	char *name;
	int len;
	int flag;
};

#define PROP_ATTACH    1
#define PROP_ALGORITHM 2
#define PROP_TOKENIZER 3
#define PROP_PVALUE    4

static struct keyword_prop keyword_prop[] = {
	{ "IgnoreHeader", 0, PROP_ATTACH },
	{ "MySQL", 5, PROP_ATTACH },
	{ "PgSQL", 5, PROP_ATTACH },
	{ "SQLite", 6, PROP_ATTACH },
	{ "LocalMX", 0, PROP_ATTACH },
	{ "Storage", 7, PROP_ATTACH },
	{ "Processor", 9, PROP_ATTACH },
	{ "Hash", 4, PROP_ATTACH },
	{ "Algorithm", 0, PROP_ALGORITHM },
	{ "PValue", 0, PROP_PVALUE },
	{ "Tokenizer", 0, PROP_TOKENIZER },
	{ NULL }
};

static struct mu_kwd algorithm_kwd[] = {
	{ "graham", DSA_GRAHAM },
	{ "burton", DSA_BURTON },
	{ "robinson", DSA_ROBINSON },
	{ "naive", DSA_NAIVE },
	{ "chi-square", DSA_CHI_SQUARE },
	{ NULL }
};

static struct mu_kwd pvalue_kwd[] = {
	{ "robinson", DSP_ROBINSON },
	{ "markov", DSP_MARKOV },
	{ NULL }
};

static struct mu_kwd tokenizer_kwd[] = {
	{ "word", DSZ_WORD },
	{ "chain", DSZ_CHAIN },
	{ "chained", DSZ_CHAIN },
	{ "sbph", DSZ_SBPH },
	{ "osb", DSZ_OSB },
	{ NULL }
};

static void
set_context_attributes(DSPAM_CTX *ctx, mu_list_t config, const char *profile,
		       int ignore_tokenizer)
{
	mu_iterator_t itr;
	int algo = 0;
	int algo_set = 0;
	int pvalue = 0;
	int pvalue_set = 0;
	int tokenizer = 0;
	int tokenizer_set = 0;
	int n;
	
	if (!profile || !profile[0])
		profile = config_find_value(config, "DefaultProfile");

	mu_list_get_iterator(config, &itr);
	for (mu_iterator_first(itr); !mu_iterator_is_done(itr);
	     mu_iterator_next(itr)) {
		struct config_entry *ent;
		struct keyword_prop *prop;
		
		mu_iterator_current (itr, (void **)&ent);
		for (prop = keyword_prop; prop->name; prop++) {
			char *p;
			
			if ((prop->len ?
			     strncasecmp(ent->config_keyword, prop->name,
					 prop->len) :
			     strcasecmp(ent->config_keyword, prop->name))
			    == 0) {
				switch (prop->flag) {
				case PROP_ATTACH:
					dspam_addattribute(ctx,
							   ent->config_keyword,
							   ent->config_value);
					break;

				case PROP_ALGORITHM:
					algo_set = 1;
					if (mu_kwd_xlat_name_ci(algorithm_kwd,
							 ent->config_value,
								&n) == 0)
						algo |= n;
					break;

				case PROP_PVALUE:
					if (pvalue_set)
						continue;
					if (mu_kwd_xlat_name_ci(pvalue_kwd,
							 ent->config_value,
								&n) == 0) {
						pvalue = n;
						pvalue_set = 1;
					}
					break;
					
				case PROP_TOKENIZER:
					tokenizer_set = 1;
					if (mu_kwd_xlat_name_ci(tokenizer_kwd,
							 ent->config_value,
								&n) == 0)
						tokenizer |= n;
					break;
				}
			} else if (profile &&
				   (p = strchr(ent->config_keyword, '.')) &&
				   strcasecmp(p + 1, profile) == 0) {
				size_t len = p - ent->config_keyword;
				char *key = mu_alloc(len + 1);
				memcpy(key, ent->config_keyword, len);
				key[len] = 0;
				dspam_addattribute(ctx, key,
						   ent->config_value);
				free(key);
			}
		}
	}
	mu_iterator_destroy(&itr);

	if (algo_set)
		ctx->algorithms = algo | (pvalue_set ? pvalue : DSP_GRAHAM);
	
	if (!ignore_tokenizer && tokenizer_set)
		ctx->tokenizer = tokenizer;

	if ((ctx->algorithms & DSA_CHI_SQUARE) &&
	    !(ctx->algorithms & DSP_ROBINSON))
		mu_diag_output(MU_DIAG_WARNING,
			       "Chi-Square algorithm enabled with other "
			       "algorithms: false positives may ensue");
}

/* number dspam(number msg, number flags; number class_source) */

void
#line 395
bi_dspam(eval_environ_t env)
#line 395

#line 395

#line 395 "dspam.bi"
{
#line 395
	
#line 395

#line 395
long __bi_argcnt;
#line 395
long  nmsg;
#line 395
        long  mode_flags;
#line 395
        long  class_src;
#line 395
        
#line 395
get_numeric_arg(env, 1, &nmsg);
#line 395
        get_numeric_arg(env, 2, &mode_flags);
#line 395
        get_numeric_arg(env, 3, &class_src);
#line 395
        
#line 395
get_numeric_arg(env, 0, &__bi_argcnt);
#line 395
        adjust_stack(env, __bi_argcnt + 1);
#line 395

#line 395

#line 395
	if (builtin_module_trace(BUILTIN_IDX_dspam))
#line 395
		prog_trace(env, "dspam %lu %lu %lu",nmsg, mode_flags, ((__bi_argcnt > 2) ? class_src : 0));;
#line 395

{
	int rc;
	DSPAM_CTX *ctx;               	/* DSPAM Context */
	int mode;
	int flags;
	mu_message_t msg;
	mu_stream_t msgstr, instr;
	const char *msgbuf;
	size_t msgsize;
	unsigned prec;
	mu_transport_t trans[2];
	mu_list_t config = 
#line 407
get_config(env,env_get_builtin_priv(env,DSPAM_CONFIG_id));
	int tokenizer;
	
	/* Prepare message buffer */
	msg = bi_message_from_descr(env, nmsg);
	rc = mu_message_size(msg, &msgsize);
		if (!(rc == 0))
#line 413
		(
#line 413
	env_throw_bi(env, mfe_failure, "dspam", "mu_message_size: %s",mu_strerror(rc))
#line 413
)
#line 415
;
	
	rc = mu_memory_stream_create(&msgstr, MU_STREAM_RDWR);
		if (!(rc == 0))
#line 418
		(
#line 418
	env_throw_bi(env, mfe_failure, "dspam", "mu_static_memory_stream_create: %s",mu_strerror(rc))
#line 418
)
#line 421
;
	env_function_cleanup_add(env, msgstr, _builtin_stream_cleanup);

	rc = mu_message_get_streamref(msg, &instr);
		if (!(rc == 0))
#line 425
		(
#line 425
	env_throw_bi(env, mfe_failure, "dspam", "mu_message_get_streamref: %s",mu_strerror(rc))
#line 425
)
#line 428
;
	env_function_cleanup_add(env, instr, _builtin_stream_cleanup);

	rc = mu_stream_copy(msgstr, instr, msgsize, NULL);
		if (!(rc == 0))
#line 432
		(
#line 432
	env_throw_bi(env, mfe_failure, "dspam", "mu_stream_copy: %s",mu_strerror(rc))
#line 432
)
#line 435
;
	
	env_function_cleanup_flush(env, instr);

	mu_stream_ioctl(msgstr, MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET, trans);
	msgbuf = (const char*)trans[0];
	
	/* Prepare DSPAM context */
		if (!(_builtin_const_to_c(mode_trans, MU_ARRAY_SIZE(mode_trans),
#line 443
				      mode_flags & _MFL__DSM_MASK, &mode) == 0))
#line 443
		(
#line 443
	env_throw_bi(env, mfe_failure, "dspam", "bad dspam mode")
#line 443
)
#line 446
;
	flags = _builtin_const_to_bitmap(flag_trans, MU_ARRAY_SIZE(flag_trans),
					 mode_flags);
	
	/* Create the DSPAM context */
	ctx = dspam_create((char*)env_data_ref(env, mf_c_val(*env_data_ref(env, dspam_user_loc),size) ),
			   (char*)env_data_ref(env, mf_c_val(*env_data_ref(env, dspam_group_loc),size) ),
			   config_find_value(config, "Home"), mode,
			   flags);
		if (!(ctx != NULL))
#line 455
		(
#line 455
	env_throw_bi(env, mfe_failure, "dspam", "dspam_create failed")
#line 455
)
#line 457
;
	env_function_cleanup_add(env, ctx, ctx_cleanup);

	/* Use graham and robinson algorithms, graham's p-values */
	ctx->algorithms = DSA_GRAHAM | DSA_BURTON | DSP_GRAHAM;

	tokenizer = mode_flags & _MFL__DSZ_MASK;
	set_context_attributes(ctx, config, (char*)env_data_ref(env, mf_c_val(*env_data_ref(env, dspam_profile_loc),size) ),
			       tokenizer);
	
		if (!(dspam_attach(ctx, NULL) == 0))
#line 467
		(
#line 467
	env_throw_bi(env, mfe_failure, "dspam", "dspam_attach failed")
#line 467
)
#line 469
;
	
	/* Configure tokenizer */
	if (tokenizer)
			if (!(_builtin_const_to_c(tokenizer_trans,
#line 473
					      MU_ARRAY_SIZE(tokenizer_trans),
#line 473
					      tokenizer, &ctx->tokenizer) == 0))
#line 473
		(
#line 473
	env_throw_bi(env, mfe_failure, "dspam", "bad dspam tokenizer")
#line 473
)
#line 477
;
	
	/* Set training mode */
		if (!(_builtin_const_to_c(tmod_trans, MU_ARRAY_SIZE(tmod_trans),
#line 480
			    mode_flags & _MFL__DST_MASK, &ctx->training_mode)
#line 480
		  == 0))
#line 480
		(
#line 480
	env_throw_bi(env, mfe_failure, "dspam", "bad dspam training mode")
#line 480
)
#line 484
;
	
	/* Set up classification and source */
	if ((__bi_argcnt > 2)) {
			if (!(_builtin_const_to_c(class_trans,
#line 488
					      MU_ARRAY_SIZE(class_trans),
#line 488
					      class_src & _MFL__DSR_MASK,
#line 488
					      &ctx->classification) == 0))
#line 488
		(
#line 488
	env_throw_bi(env, mfe_failure, "dspam", "bad dspam classification flag")
#line 488
)
#line 493
;
			if (!(_builtin_const_to_c(source_trans,
#line 494
					      MU_ARRAY_SIZE(source_trans),
#line 494
					      class_src & _MFL__DSS_MASK,
#line 494
					      &ctx->source) == 0))
#line 494
		(
#line 494
	env_throw_bi(env, mfe_failure, "dspam", "bad dspam source flag")
#line 494
)
#line 499
;
	}
	
	/* Process the message */
		if (!(dspam_process(ctx, msgbuf) == 0))
#line 503
		(
#line 503
	env_throw_bi(env, mfe_failure, "dspam", "dspam_process failed")
#line 503
)
#line 505
;

	rc = mf_c_val(*env_data_ref(env, dspam_prec_loc),uint) ;
	prec = 1;
	while (rc--)
		prec *= 10;
	mf_c_val(*env_data_ref(env, dspam_probability_loc),ulong) = (ctx->probability * prec);
	mf_c_val(*env_data_ref(env, dspam_confidence_loc),ulong) = (ctx->confidence * prec);
	if (flags & DSF_SIGNATURE) {
		char signame[128];
		_ds_create_signature_id(ctx, signame, sizeof(signame));
		_ds_set_signature(ctx, ctx->signature, signame);
		
#line 517
{ size_t __off;
#line 517
  const char *__s = signame;
#line 517
  if (__s)
#line 517
     strcpy((char*)env_data_ref(env, __off = heap_reserve(env, strlen(__s) + 1)), __s);
#line 517
  else
#line 517
     __off = 0;
#line 517
  mf_c_val(*env_data_ref(env, dspam_signature_loc),size) = (__off); }
#line 517
;
	}
		if (!(_builtin_c_to_const(class_trans,
#line 519
				      MU_ARRAY_SIZE(class_trans),
#line 519
				      ctx->result,
#line 519
				      &rc) == 0))
#line 519
		(
#line 519
	env_throw_bi(env, mfe_failure, "dspam", "unrecognized dspam result")
#line 519
)
#line 524
;
	env_function_cleanup_flush(env, ctx);
	
	/* FIXME: Any additional processing? */

	
#line 529
do {
#line 529
  push(env, (STKVAL)(mft_number)(rc));
#line 529
  goto endlab;
#line 529
} while (0);
}
endlab:
#line 531
        env_function_cleanup_flush(env, NULL);
#line 531
	return;
#line 531
}

#line 982 "../../src/builtin/snarf.m4"

#line 982

#line 982
#endif /* WITH_DSPAM */
#line 982

#line 982
void
#line 982
dspam_init_builtin(void)
#line 982
{
#line 982
	
#line 982
#ifdef WITH_DSPAM
#line 982
	pp_define("WITH_DSPAM");
#line 982
	#line 29 "dspam.bi"
	builtin_variable_install("dspam_user", dtype_string, SYM_VOLATILE|SYM_PRECIOUS, &dspam_user_loc);
#line 30 "dspam.bi"
	builtin_variable_install("dspam_group", dtype_string, SYM_VOLATILE|SYM_PRECIOUS, &dspam_group_loc);
#line 31 "dspam.bi"
	builtin_variable_install("dspam_config", dtype_string, SYM_VOLATILE|SYM_PRECIOUS, &dspam_config_loc);
#line 32 "dspam.bi"
	builtin_variable_install("dspam_profile", dtype_string, SYM_VOLATILE|SYM_PRECIOUS, &dspam_profile_loc);
#line 34 "dspam.bi"
	builtin_variable_install("dspam_signature", dtype_string, SYM_VOLATILE|SYM_PRECIOUS, &dspam_signature_loc);
#line 35 "dspam.bi"
	builtin_variable_install("dspam_probability", dtype_number, SYM_VOLATILE, &dspam_probability_loc);
#line 36 "dspam.bi"
	builtin_variable_install("dspam_confidence", dtype_number, SYM_VOLATILE, &dspam_confidence_loc);
#line 37 "dspam.bi"
	builtin_variable_install("dspam_prec", dtype_number, SYM_VOLATILE, &dspam_prec_loc);
#line 216 "dspam.bi"
DSPAM_CONFIG_id = builtin_priv_register(alloc_config, destroy_config,
#line 216
NULL);
#line 395 "dspam.bi"
va_builtin_install_ex("dspam", bi_dspam, 0, dtype_number, 3, 1, 0|0, dtype_number, dtype_number, dtype_number);

#line 982 "../../src/builtin/snarf.m4"
	
#line 982
#endif /* WITH_DSPAM */
#line 982
}
#line 982 "../../src/builtin/snarf.m4"

