#ifndef _ANTISPAM_PLUGIN_H
#define _ANTISPAM_PLUGIN_H

#include "lib.h"
#include "str.h"
#include "dovecot-version.h"
#if DOVECOT_IS_GE(2, 0)
#include "imap-client.h"
#else
#include "client.h"
#endif
#include "ostream.h"
#include "imap-search.h"
#include <stdlib.h>

#define __stringify_1(x)	#x
#define stringify(x)		__stringify_1(x)

#define __PLUGIN_FUNCTION(name, ioe) \
	name ## _plugin_ ## ioe
#define _PLUGIN_FUNCTION(name, ioe) \
	__PLUGIN_FUNCTION(name, ioe)
#define PLUGIN_FUNCTION(ioe)	\
	_PLUGIN_FUNCTION(antispam, ioe)

struct antispam_transaction_context;

enum classification {
	CLASS_NOTSPAM,
	CLASS_SPAM,
};

enum antispam_debug_target {
	ADT_NONE,
	ADT_STDERR,
	ADT_SYSLOG,
};

struct signature_config {
	const char *signature_hdr;
	bool signature_nosig_ignore;
};

struct antispam_debug_config {
	const char *prefix;
	enum antispam_debug_target target;
	int verbose;
};

struct antispam_config {
	/* the selected backend */
	struct backend *backend;

	struct antispam_debug_config dbgcfg;

	bool can_append_to_spam;
	bool need_keyword_hook;
	bool need_folder_hook;
	bool nosig_ignore;

	/*
	 * There are three different matches for each folder type
	 *
	 * type			usage
	 * ----------------------------
	 * MT_REG		plain strcmp()
	 * MT_PATTERN		case sensitive match with possible wildcards
	 * MT_PATTERN_IGNCASE	case insensitive match with possible wildcards
	 */
	char **trash_folders[3];  // = { NULL,		NULL, NULL };
	char **spam_folders[3];   // = { default_spam_folders,NULL, NULL };
	char **unsure_folders[3]; // = { NULL,		NULL, NULL };

	char **spam_keywords;

	const char *signature_hdr;

	pool_t mem_pool;

	union {
		struct {
			struct signature_config sigcfg;
			const char *binary;
			const char *result_header;
			char **result_bl;
			int result_bl_num;
			char **extra_args;
			int extra_args_num;
			char **extra_env;
			int extra_env_num;
		} dspam;
		struct {
			struct signature_config sigcfg;
			const char *reaver_binary;
			char **extra_args;
			int extra_args_num;
			char **extra_env;
			int extra_env_num;
		} crm;
		struct {
			char **spam_args;
			int spam_args_num;
			char **ham_args;
			int ham_args_num;
			const char *pipe_binary;// = "/usr/sbin/sendmail";
			const char *tmpdir;// = "/tmp";
			char **extra_args;
			int extra_args_num;
		} pipe;
		struct {
			const char *spamspool, *hamspool;
		} s2d;
	};
};

struct backend {
	void (*init)(struct antispam_config *cfg,
		     const char *(getenv)(const char *env, void *data),
		     void *getenv_data);
	/*
	 * Handle mail; parameters are
	 *  - t: transaction context
	 *  - ast: transaction context from backend_start()
	 *  - mail: the message
	 *  - wanted: the wanted classification determined by the user
	 */
	int (*handle_mail)(const struct antispam_config *cfg,
			   struct mailbox_transaction_context *t,
			   struct antispam_transaction_context *ast,
			   struct mail *mail, enum classification wanted);
	struct antispam_transaction_context *(*start)(const struct antispam_config *cfg,
						      struct mailbox *box);
	void (*rollback)(const struct antispam_config *cfg,
			 struct antispam_transaction_context *ast);
	int (*commit)(const struct antispam_config *cfg,
		      struct mailbox_transaction_context *ctx,
		      struct antispam_transaction_context *ast);
};

/* signature handling */
struct siglist {
	struct siglist *next;
	char *sig;
	enum classification wanted;
};

void signature_init(struct signature_config *cfg,
		    const struct antispam_debug_config *dbgcfg,
		    const char *(getenv)(const char *env, void *data),
		    void *getenv_data);
int signature_extract_to_list(const struct signature_config *cfg,
			      struct mailbox_transaction_context *t,
			      struct mail *mail, struct siglist **list,
			      enum classification wanted);
int signature_extract(const struct signature_config *cfg,
		      struct mailbox_transaction_context *t,
		      struct mail *mail, const char **signature);
void signature_list_free(struct siglist **list);


/* possible backends */
extern struct backend crm114_backend;
extern struct backend dspam_backend;
extern struct backend pipe_backend;
extern struct backend spool2dir_backend;

int debug_init(struct antispam_debug_config *cfg,
	       const char *(getenv)(const char *env, void *data),
	       void *getenv_data);
void debug(const struct antispam_debug_config *cfg,
	   const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
void debugv(const struct antispam_debug_config *cfg, char **args);
void debugv_not_stderr(const struct antispam_debug_config *cfg, char **args);
void debug_verbose(const struct antispam_debug_config *cfg,
		   const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));

bool mailbox_is_spam(const struct antispam_config *cfg, struct mailbox *box);
bool mailbox_is_trash(const struct antispam_config *cfg, struct mailbox *box);
bool mailbox_is_unsure(const struct antispam_config *cfg, struct mailbox *box);
bool keyword_is_spam(const struct antispam_config *cfg, const char *keyword);

struct antispam_config *
antispam_setup_config(const char *(getenv)(const char *env, void *data),
		      void *getenv_data);
void antispam_free_config(struct antispam_config *cfg);

/*
 * Dovecot version compat code
 */
#if DOVECOT_IS_EQ(1, 0)
#define module_arg		void
#define ATTR_UNUSED		__attr_unused__
#define mempool_unref(x)	pool_unref(*(x))
#define ME(err)
#define str_array_length(x)	strarray_length(x)

static inline const char *const *
get_mail_headers(struct mail *mail, const char *hdr)
{
	return mail_get_headers(mail, hdr);
}

static inline struct ostream *
o_stream_create_from_fd(int fd, pool_t pool)
{
	return o_stream_create_file(fd, pool, 0, TRUE);
}

static inline int _mail_get_stream(struct mail *mail,
				   struct message_size *hdr_size,
				   struct message_size *body_size,
				   struct istream **stream)
{
	struct istream *res = mail_get_stream(mail, hdr_size, body_size);
	if (res == NULL)
		return -1;
	*stream = res;
	return 0;
}
#define mail_get_stream _mail_get_stream

#define T_BEGIN \
	STMT_START { t_push();
#define T_END \
	t_pop(); } STMT_END
#elif DOVECOT_IS_EQ(1, 1)
#define mempool_unref		pool_unref
#define module_arg		void
#define ME(err)			MAIL_ERROR_ ##err,

static inline const char *const *
get_mail_headers(struct mail *mail, const char *hdr)
{
	const char *const *ret;
	if (mail_get_headers(mail, hdr, &ret) < 0)
		return NULL;
	return ret;
}

static inline struct ostream *
o_stream_create_from_fd(int fd, pool_t pool ATTR_UNUSED)
{
	return o_stream_create_fd(fd, 0, TRUE);
}
#elif DOVECOT_IS_EQ(1, 2)
#define mempool_unref		pool_unref
#define module_arg		void
#define ME(err)			MAIL_ERROR_ ##err,

static inline const char *const *
get_mail_headers(struct mail *mail, const char *hdr)
{
	const char *const *ret;
	if (mail_get_headers(mail, hdr, &ret) < 0)
		return NULL;
	return ret;
}

static inline struct ostream *
o_stream_create_from_fd(int fd, pool_t pool ATTR_UNUSED)
{
	return o_stream_create_fd(fd, 0, TRUE);
}
#elif DOVECOT_IS_EQ(2, 0) || DOVECOT_IS_EQ(2, 1) || DOVECOT_IS_EQ(2, 2)
#define mempool_unref		pool_unref
#define module_arg		struct module *
#define ME(err)			MAIL_ERROR_ ##err,

static inline const char *const *
get_mail_headers(struct mail *mail, const char *hdr)
{
	const char *const *ret;
	if (mail_get_headers(mail, hdr, &ret) < 0)
		return NULL;
	return ret;
}

static inline struct ostream *
o_stream_create_from_fd(int fd, pool_t pool ATTR_UNUSED)
{
	return o_stream_create_fd(fd, 0, TRUE);
}
#elif DOVECOT_IS_EQ(2, 3)
#define mempool_unref		pool_unref
#define module_arg		struct module *
#define ME(err)			MAIL_ERROR_ ##err,

static inline const char *const *
get_mail_headers(struct mail *mail, const char *hdr)
{
	const char *const *ret;
	if (mail_get_headers(mail, hdr, &ret) < 0)
		return NULL;
	return ret;
}

static inline struct ostream *
o_stream_create_from_fd(int fd, pool_t pool ATTR_UNUSED)
{
	return o_stream_create_fd_autoclose(&fd, 0);
}

#define t_malloc t_malloc0
#else
#error "Building against this dovecot version is not supported"
#endif
        
void antispam_storage_init(module_arg);
void antispam_storage_deinit(void);

#endif /* _ANTISPAM_PLUGIN_H */
