/* This file is part of Mailfromd.
   Copyright (C) 2005-2025 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/>. */

#include <sys/types.h>
#include <regex.h>
#include <mailutils/cctype.h>
#include <mailutils/mailutils.h>
#include <mfapi.h>
#include <dns.h>
#include <mailutils/alloc.h>
#include <sysexits.h>
#include "libmf.h"
#include "mfdb.h"
#include "bitmask.h"
#include "filenames.h"

/* A simplified version of the bitmask above, used for SMTP state mask */
#define STATMASK(n) ((unsigned)1<<(n))

#define SP(s) ((s) ? (s) : "(null)")

#ifndef INADDR_NONE
# define INADDR_NONE ((unsigned long) -1)
#endif

/* Exception codes. */
#include <mflib/exceptions.h>

/* SMTP states */
#include <mflib/milter.h>
enum smtp_state {
	smtp_state_none = _MFL_milter_state_none,
	smtp_state_startup = _MFL_milter_state_startup,
	smtp_state_shutdown = _MFL_milter_state_shutdown,
	smtp_state_begin = _MFL_milter_state_begin,
	smtp_state_end = _MFL_milter_state_end,
	smtp_state_connect = _MFL_milter_state_connect,
	smtp_state_helo = _MFL_milter_state_helo,
	smtp_state_envfrom = _MFL_milter_state_envfrom,
	smtp_state_envrcpt = _MFL_milter_state_envrcpt,
	smtp_state_data = _MFL_milter_state_data,
	smtp_state_header = _MFL_milter_state_header,
	smtp_state_eoh = _MFL_milter_state_eoh,
	smtp_state_body = _MFL_milter_state_body,
	smtp_state_eom = _MFL_milter_state_eom,

	smtp_state_action = _MFL_milter_action,

	smtp_state_count,
};

#define smtp_state_first smtp_state_connect
#define smtp_state_last smtp_state_eom

#define PROG_ACTION_NAME "prog action"

#define MFAM_STDIO 0
#define MFAM_UNIX  1
#define MFAM_INET  2
#define MFAM_INET6 3


mf_status dns_to_mf_status(dns_status s);

mf_status resolve_ipstr(const char *ipstr, char **hbuf);
mf_status resolve_ipstr_domain(const char *ipstr, const char *domain,
			       char **phbuf);
mf_status resolve_hostname(const char *host, int resolve_hostname, char **pipbuf);
	

/* Debugging macros */
extern mu_debug_handle_t mfd_debug_handle;
#include "mfd-dbgmod.h"


/* Syslog flavor */
#ifdef USE_SYSLOG_ASYNC
# include <syslog_async.h>
#endif


/* FIXME */
void enable_prog_trace(const char *modlist);
void disable_prog_trace(const char *modlist);

struct pathbuf {
	char *base;   /* Constructed path */
	int pos;      /* Insert position in base */
	int alloc;    /* 1 if base was allocated */
};

#define PATHBUF_INITIALIZER(p) \
	{ .base = p, .pos = 0, .alloc = 0 }

void pathbuf_add(struct pathbuf *pb, char const *dir);

/* Global variables declarations */
#define MAILFROMD_DAEMON        0
#define MAILFROMD_TEST          1
#define MAILFROMD_SHOW_DEFAULTS 2
#define MAILFROMD_RUN           3

extern char *script_file;
extern char *ext_pp_options;
extern int ext_pp_options_given;
extern int location_column_option;
extern int mode;
extern int mtasim_option;
extern unsigned optimization_level;
extern enum smtp_state test_state;
extern int regex_flags;
extern size_t variable_count;
extern int script_dump_macros;
extern int script_dump_xref;
extern int stack_trace_option;
extern const char *program_version;
extern char *callout_server_url;
extern struct pathbuf include_path;
extern struct pathbuf module_path;
extern char *mfmod_path;
extern char const *default_suffixes[];

extern size_t stack_size;
extern size_t stack_max_size;
extern size_t stack_expand_incr;
enum stack_expand_policy {
	stack_expand_twice,
	stack_expand_add
};
extern enum stack_expand_policy stack_expand_policy;

extern size_t max_match_mx;


/* Filter script parser */

typedef enum {
	dtype_unspecified,
	dtype_string,
	dtype_number,
	dtype_pointer,
	dtype_any,
} data_type_t;

/* Parse tree node */
typedef struct node NODE;

struct stmtlist {
	NODE *head;
	NODE *tail;
};

typedef struct mu_locus_range mf_yyltype_t;

struct if_node {            /* if statement */
	NODE *cond;         /* Condition */
	NODE *if_true;      /* True branch */
	NODE *if_false;     /* False branch */
};

struct poll_data {                 /* Poll test */
	NODE *email;               
	NODE *client_addr;
	NODE *ehlo;
	NODE *mailfrom;
};

/* Opcodes for available binary operations */
enum bin_opcode {
	bin_and,
	bin_or,
	bin_eq,
	bin_ne,
	bin_lt,
	bin_le,
	bin_gt,
	bin_ge,
	bin_match,
	bin_fnmatch,
	bin_add,
	bin_sub,
	bin_mul,
	bin_div,
	bin_mod,
	bin_logand,
	bin_logor,
	bin_logxor,
	bin_shl,
	bin_shr
};

#define QUALIFIER_MX      0x1

struct bin_node {                  /* A binary operation */
	enum bin_opcode opcode;    /* Operation code */
	int qualifier;             /* Used to indicate MX MATCHES/FNMATCHES */
	NODE *arg[2];              /* arg[0] - left side argument,
				      arg[1] - right side argument */
};

/* Unary operations */
enum un_opcode {
	unary_not,
	unary_minus,
	unary_lognot
};

struct un_node {                   /* A unary operation node */ 
	enum un_opcode opcode;     /* Operation code */
	data_type_t result_type;   /* Result type */
	NODE *arg;                 /* Argument */ 
};

/* Return action node: accept/reject/tempfail/continue */
struct return_node {              
	sfsistat stat;             /* Return status */
	NODE *code;                /* Code */
	NODE *xcode;               /* Extended code */
	NODE *message;             /* Textual message */
};

enum msgmod_opcode {               /* Message modification operation */
	header_add,                /* Add a header */
	header_replace,            /* Replace a header value */
	header_delete,             /* Delete a header */
	header_insert,             /* Insert a header */
	rcpt_add,                  /* Add a recipient */
	rcpt_delete,               /* Delete a recipient */
	quarantine,                /* Quarantine a message */
	body_repl,                 /* Replace message body */
	body_repl_fd,              /* Replace message body from file */ 
	set_from                   /* Change envelope sender */ 
};

struct msgmod_closure {              
	enum msgmod_opcode opcode;   /* Operation code */
	char *name;                  /* Object name */
	char *value;                 /* Object value */
	unsigned idx;                /* Object index */
};

struct header_node {              
	enum msgmod_opcode opcode;   /* Operation code */
	struct literal *name;        /* Header name */
	NODE *value;                 /* Header value */
};

struct builtin_node {              /* Call to a built-in function */
	const struct builtin *builtin; /* Buit-in function */ 
	NODE *args;                    /* Actual arguments */
};

struct concat_node {              /* Concatenation of arg[0] and arg[1] */ 
	NODE *arg[2];
};

struct asgn_node {                /* Assignment */ 
	struct variable *var;     /* Variable */
	size_t nframes;           /* Number of frames to skip */
	NODE *node;               /* Expression to be evaluated and assigned
				     to it */ 
};

enum lexical_context {
	context_none,        
	context_handler,
	context_catch,       
	context_function
};

struct catch_node {
	struct exmask *exmask;    /* Exception mask */
	enum lexical_context context; /* Context in which it occurs */
	NODE *node;               /* Subtree */
};

struct try_node {
	NODE *node;               /* Subtree */
	NODE *catch;              /* Catch node */ 
};

struct function_call {
	struct function *func;    /* Function description */
	NODE *args;               /* List of actual arguments */
};

struct value {
	data_type_t type;
	union {
		long number;
		struct literal *literal;
	} v;
};

struct valist {
	struct valist *next;
	struct value value;
};

struct case_stmt {
	struct case_stmt *next;
	struct mu_locus_range locus;
	struct valist *valist;
	NODE *node;
};

struct switch_stmt {
	NODE *node;                 /* Condition */
	struct case_stmt *cases;    /* Switch branches */
	/* Auxiliary data for code generation */
	size_t tabsize;             /* Size of the XLAT table */
	size_t off;                 /* Offset of the table in DS */
	struct switch_stmt *next;   /* Link to the next switch statement */
};

struct cast_node {
	data_type_t data_type;
	NODE *node;
};

struct positional_arg {
	data_type_t data_type;
	int number;
};

struct progdecl {
	enum smtp_state tag;
	size_t auto_count;
	struct stmtlist tree;
};

struct funcdecl {
	struct function *func;
	size_t auto_count;
	NODE *tree;
};

struct throw_node {
	int code;
	NODE *expr;
};

struct var_ref {
	struct variable *variable;  /* Variable being referenced */
	size_t nframes;             /* Number of frames to skip */
};

struct regcomp_data {
	NODE *expr;
	int flags;
	size_t regind;
};

struct loop_node {
	struct literal *ident;
	NODE *stmt;
	NODE *for_stmt;
	NODE *beg_while;
	NODE *end_while;
	NODE *body;
};

struct argx_node {
	int nargs;
	NODE *node;
	data_type_t dtype;
};

struct sedcomp_node {
	size_t index;
	NODE *expr; /* Substitution expression */
	int flags;
};

struct sed_node {
	NODE *comp;
	NODE *arg;
};

struct argv_node {
	size_t nargs;   /* Number of fixed arguments */
	NODE *node;
};
	
#include "node-type.h"

/* Parse tree node */
struct node {
	NODE *next;                   /* Link to the next node */
	enum node_type type;          /* Node type */
	struct mu_locus_range locus;  /* Corresponding input location */
	int value_ignored;            /* Is the node value ignored; curently
					 only for node_type_funcall and
					 node_type_builtin */
	union {
		struct literal *literal;
		long number;
		struct if_node cond;
		struct bin_node bin;
		struct un_node un;
		struct return_node ret;
		struct header_node hdr;
		struct builtin_node builtin;
		NODE *node;
		struct concat_node concat;
		struct var_ref var_ref;
		struct asgn_node asgn;
		void *val;
		struct catch_node catch;
		struct try_node try;
		struct throw_node throw;
		struct function_call call;
		struct switch_stmt switch_stmt;
		struct cast_node cast;
		struct positional_arg arg;
		struct progdecl progdecl;
		struct funcdecl funcdecl;
		struct regcomp_data regcomp_data;
		struct sym_regex *regex;
		struct loop_node loop;
		struct argx_node argx;
		struct sedcomp_node sedcomp;
		struct sed_node sed;
		struct argv_node argv;
	} v;
};

struct exmask {
	struct exmask *next;
	struct bitmask bm;
	int all;                /* If 1, set all bits in the exmask */ 
	size_t off;             /* Offset of the table in DS */
};


/* Expressions, built-in functions and variables */

typedef struct eval_environ *eval_environ_t;  /* Evaluation environment */
typedef unsigned long prog_counter_t;         /* Program counter */  

/* Data types */

#define SYMENT_STRUCT(name)			\
	char *name;\
	unsigned refcnt

#define SYM_VOLATILE    0x01  /* Variable can change outside of the compiled
				 code */
#define SYM_REFERENCED  0x02  /* Variable is referenced */ 
#define SYM_INITIALIZED 0x04  /* Variable is initialized by set */
#define SYM_PRECIOUS    0x08  /* Variable is precious, i.e. is not affected
				 by rset */
#define SYM_EXTERN      0x10  /* Symbol is external (for future use) */
#define SYM_STATIC      0x20  /* Symbol is static */
#define SYM_PUBLIC      0x40  /* Symbol is public */

#define SYM_PASSTOGGLE  0x80  /* Symbol processing pass toggle, used
				 in _ds_variable_count_fun and friends. */

struct mf_symbol {
	SYMENT_STRUCT(name);       /* Variable name */
	struct mf_symbol *alias;
	struct module *module;     /* Backlink to the module where it is
				      defined */
	struct mu_locus_range locus;        /* Location of the definition */
	unsigned flags;            /* SYM_ flags */
};

#define MFD_BUILTIN_CAPTURE    0x01  /* Builtin needs message capturing */     
#define MFD_BUILTIN_VARIADIC   0x02  /* Builtin is variadic */
#define MFD_BUILTIN_NO_PROMOTE 0x04  /* For variadic functions:
					do not promote varargs to string */
#define MFD_BUILTIN_REGEX_FLAGS 0x08 /* The built-in takes the value of the
					regex_flags as the first (implicit)
					argument. */

struct builtin {
	SYMENT_STRUCT(name);
	void (*handler) (eval_environ_t); /* C handler */
	size_t parmcount;                 /* Number of parameters */
	data_type_t *parmtype;            /* Parameter types */
	size_t optcount;                  /* Number of optional parameters
					     (size_t) -1 for varargs */
	data_type_t rettype;              /* Return type */
	unsigned statemask;               /* States where the function can
					     be used */
	int flags;                        /* Flags */
};

struct function {      /* User-defined function */
	struct mf_symbol sym;
	NODE *node;              /* Function definition (syntax tree) */
	prog_counter_t entry;    /* Entry point to the function code */
	struct exmask *exmask;   /* Exception mask */
	unsigned statemask;      /* States where the function can
				    be used */
	size_t parmcount;        /* Number of parameters */
	size_t optcount;         /* Number of optional parameters */
	int varargs;             /* 1 if function takes variable number of
				    arguments */
	data_type_t vartype;     /* Type to promote varargs to */
	data_type_t *parmtype;   /* Parameter types */
	data_type_t rettype;     /* Return type */
	prog_counter_t refstart; /* Start of the unresolved entrypoint chain */ 
};	

typedef enum {
	storage_extern,
	storage_auto,
	storage_param
} storage_class_t;

struct variable {
	struct mf_symbol sym;
	data_type_t type;          /* Variable type */
	storage_class_t storage_class; /* Storage class */
	size_t off;                /* Offset in the data segment */
	size_t ord;                /* Ordinal number in the parmlist
				      (for parameters only) */
	size_t *addrptr;           /* Address pointer (for built-in vars) */ 
	struct variable *shadowed; /* Points to the variable shadowed by
				      this one */
	int initialized;           /* Is the variable initialized (for
				      automatic variables) */
	mu_list_t xref;            /* List of struct mu_locus_range */
	struct variable *next;     /* Next variable in this class */
};

struct literal {
	SYMENT_STRUCT(text);
	unsigned flags;            /* SYM_* flags */
	size_t off;                /* Offset in DS */
	struct sym_regex *regex;   /* Any corresponding regexes */
};

struct constant {
	struct mf_symbol sym;
	struct value value;        /* Constant value */
};

#define REG_EXTENDED_NAME "extended"
#define REG_ICASE_NAME "icase"
#define REG_NEWLINE_NAME "newline"

#define REGEX_STRING_BUFSIZE \
 (sizeof(REG_EXTENDED_NAME) + \
  sizeof(REG_ICASE_NAME) + \
  sizeof(REG_NEWLINE_NAME) + 1)

char *regex_flags_to_string(int flags, char *buf, size_t size);

int add_legacy_milter_port(const char *str);

void mflog_setup(void);
void mflog_reopen(const char *tag);

void builtin_setup(void);
void pragma_setup(void);

void print_code(void);
void print_xref(void);
void print_used_macros(void);

/* symbols.c */
extern struct symtab *stab_module;
extern struct symtab *stab_builtin;
extern struct symtab *stab_pragma;
extern struct symtab *stab_literal;

void init_symbols(void);
void free_symbols(void);

struct mf_symbol *symbol_resolve_alias(struct mf_symbol *sp);
void va_builtin_install (char *name, void (*handler) (eval_environ_t),
			 data_type_t rettype, size_t argcount, ...);
void va_builtin_install_ex (char *name, void (*handler) (eval_environ_t),
			    unsigned statemsk, 
			    data_type_t rettype, size_t argcount,
			    size_t optcount, int flags, ...);

const struct builtin *builtin_lookup(const char *name);
struct variable *variable_install(const char *name);
struct variable *variable_lookup(const char *name);
struct variable *variable_replace(const char *name, struct variable *newvar);
void variable_remove(struct variable *var);
struct variable *builtin_variable_install(const char *name,
					  data_type_t type,
					  unsigned flags,
					  size_t *addrptr);
void defer_initialize_variable(const char *arg, const char *val,
			       struct mu_locus_range const *locus);

int variable_or_constant_lookup(const char *name, void **dptr);

struct function *function_install(const char *name,
				  size_t parmcnt, size_t optcnt, int varargs,
				  data_type_t vartype,
				  data_type_t *parmtypes,
				  data_type_t rettype,
				  const struct mu_locus_range *locus);
struct function *function_lookup(const char *name);
struct literal *literal_lookup(const char *text);
struct constant *define_constant(const char *name, struct value *value,
				 unsigned flags,
				 struct mu_locus_range const *loc);
const struct constant *constant_lookup(const char *name);
const struct value *constant_lookup_value(const char *name);

struct sym_regex {
	struct literal *lit;        /* Corresponding literal */
	int regflags;               /* Compilation flags */
	unsigned flags;             /* VAR_* flags */
	size_t index;               /* Index in the regtab */
	struct sym_regex *next; /* Next sym_regex with the same name, but
				   different regflags */
};

struct sym_regex *install_regex(struct literal *lit, unsigned regflags);

void install_alias(const char *name, struct function *fun,
		   const struct mu_locus_range *locus);


struct rt_regex {
	int compiled;
	regex_t re;
	size_t expr;
	int regflags;
};

void register_regex(struct sym_regex *rp);
void finalize_regex(void);

size_t next_transform_index(void);
void install_transform(size_t index, transform_t tp);
transform_t get_transform(size_t index);


struct pragma {
	SYMENT_STRUCT(name);
	int minargs;
	int maxargs;
	void (*handler) (int, char **, const char *);
};

void install_pragma(const char *name, int minargs, int maxargs,
		    void (*handler) (int, char **, const char *));
const struct pragma *lookup_pragma(const char *name);


enum import_type {
	import_literal,
	import_regex,
	import_transform
};

struct import_rule {
	struct import_rule *next;  /* Next rule */
	enum import_type type;     /* Type of this rule */
	struct literal *literal;   /* Literal */
	int neg;                   /* Negate regex */
	regex_t re;                /* Regular expression */
	transform_t xform;         /* Transform expression */  
};

struct import_rule_list {
	struct import_rule *head, *tail;
};

struct import_rule *import_rule_create(struct literal *lit);


enum module_namespace {
	namespace_function,
	namespace_variable,
	namespace_constant
};
#define MODULE_NAMESPACE_COUNT 3

struct module {
	SYMENT_STRUCT(name);
	const char *file;
	const char *dclname;
	unsigned flags;
	struct symtab *symtab[MODULE_NAMESPACE_COUNT];
	struct import_rule *import_rules;
	struct module_list *submodule_head, *submodule_tail;
	mu_list_t /* of struct input_file_ident */ incl_sources;
};

struct module_list {
	struct module *module;
	struct module_list *next;
};

extern struct module *top_module;

#define MODULE_SYMTAB(m,ns) ((m)->symtab[ns])
#define TOP_MODULE_SYMTAB(ns) MODULE_SYMTAB(top_module,ns)
int set_top_module(const char *name, const char *file,
		   struct import_rule *import_rules,
		   struct mu_locus_range const *locus);
void pop_top_module(void);
void collect_modules(struct module ***pmodv, size_t *pmodc);
int module_symtab_enumerate(enum module_namespace ns, symtab_enumerator_t fun,
			    void *data);


/* Preprocessor functions */
struct preprocessor {
	int enabled;
	char *command;
	int pass_includes;
	int pass_defines;
	char *setup_file;
};

struct input_file_ident {
	ino_t i_node;
	dev_t device;
};

extern struct preprocessor preprocessor;

void preprocessor_cfg_init(void);
FILE *pp_extrn_start(const char *name, pid_t *ppid);
void pp_extrn_shutdown(FILE *file, pid_t pid);
int preprocess_input(void);
char const *string_preprocessor(void);
void preprocessor_finalize(void);

char *path_find_file(const char *path, const char *name, char const **suffixes);

void require_module(const char *modname, struct import_rule *import_rules);

int parse_include(const char *text, int once);
int parse_require(const char *text, mf_yyltype_t *loc);
int parse_line(char *text, struct mu_locus_point *pt);
int parse_line_cpp(char *text, struct mu_locus_point *pt);
int source_lookup(struct input_file_ident *idptr);

#define LEX_NONE   0
#define LEX_ONCE   1
#define LEX_MODULE 2

int lex_new_source(const char *name, int once);
void lex_bye(void);
void lex_drain_input(void);
void tie_in_onblock(int enable);

/* Parser functions */
int yyparse();
int yylex();
int yyerror(char const *s);
void free_parser_data(void);
int parse_program(char *name, int ydebug);
void parse_pragma(const char *text);
struct mu_locus_range const *get_locus(void);
const char *msgmod_opcode_str(enum msgmod_opcode opcode);
const char *sfsistat_str(sfsistat s);
int sfsistat_code(char const *stat);
const char *mf_exception_str(unsigned ex);

mf_exception  mf_status_to_exception(mf_status s);

const char *storage_class_str(storage_class_t sc);
enum smtp_state string_to_state(const char *name);
const char *state_to_string(enum smtp_state state);
const char *xstate_to_string(enum smtp_state state);
void parse_error(const char *fmt, ...) MU_PRINTFLIKE(1,2);
void parse_error_locus(struct mu_locus_range const *loc, const char *fmt, ...)
	               MU_PRINTFLIKE(2,3);
void parse_warning(const char *fmt, ...) MU_PRINTFLIKE(1,2);
void parse_warning_locus(struct mu_locus_range const *loc, const char *fmt, ...)
	                 MU_PRINTFLIKE(2,3);

void print_syntax_tree(void);
void print_macros(void);
const char *function_name(void);
void lex_setup(void);
struct literal *string_alloc(const char *str, size_t len);
void string_begin(void);
void string_add(const char *str, size_t len);
void string_add_char(unsigned char c);
struct literal *string_finish(void);
void free_string_space(void);
void init_string_space(void);
data_type_t builtin_const_value(const char *s, size_t len,
				const char **sval, long *nval);
const char *symbit_to_qualifier(unsigned f);
void advance_line(void);

char *mf_strdup(const char *str);

void regex_push(void);
void regex_pop(void);

void register_macro(enum smtp_state tag, const char *macro);
char *get_stage_macro_string(enum gacopyz_stage i);
struct exmask *exmask_create(void);

void variable_check_initialized(struct variable *var,
				struct mu_locus_range const *loc);


/* Data types and declarations for handling compiled configuration code */

/* Entry points for each SMTP state */
extern prog_counter_t entry_point[smtp_state_count];

void set_milter_state(eval_environ_t env, enum smtp_state state);

/* Functions for accessing %rcpt_count */
unsigned long get_rcpt_count(eval_environ_t env);
void clear_rcpt_count(eval_environ_t env);
void incr_rcpt_count(eval_environ_t env);

void set_last_poll_helo(eval_environ_t env, const char *text);
void set_last_poll_greeting(eval_environ_t env, const char *text);
void set_last_poll_host(eval_environ_t env, const char *host_addr);
void set_last_poll_sent(eval_environ_t env, const char *send);
void set_last_poll_recv(eval_environ_t env, const char *recv);

void set_milter_client_address(eval_environ_t env, milter_sockaddr_t *addr,
			       socklen_t len);
void set_milter_server_address(eval_environ_t env, milter_sockaddr_t *addr,
			       socklen_t len);
void set_milter_server_id(eval_environ_t env, const char *id);

void set_cache_used(eval_environ_t env, int value);

/* Evaluation environment functions */
eval_environ_t
create_environment(SMFICTX *ctx,
		   const char *(*getsym)(void *data, const char *str),
		   int (*setreply)(void *data, char *code, char *xcode,
				   char *message),
		   void (*msgmod)(void *data, struct msgmod_closure *op),
		   void *data);
void destroy_environment(eval_environ_t env);
void env_init(eval_environ_t env);
void env_init_dataseg(eval_environ_t env);
void env_save_catches(eval_environ_t env);

# ifndef ATTRIBUTE_NORETURN
#  define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__))
# endif

void env_throw(eval_environ_t env, mf_exception exception,
	       const char *fmt, ...)
	       MU_PRINTFLIKE(3,4) ATTRIBUTE_NORETURN;
void env_throw_bi(eval_environ_t env, mf_exception exception,
		  const char *biname,
		  const char *fmt, ...) MU_PRINTFLIKE(4,5) ATTRIBUTE_NORETURN;
void env_throw_0(eval_environ_t env, mf_exception exception,
		 size_t text_off) ATTRIBUTE_NORETURN;

void env_push_string(eval_environ_t env, char *arg);
void env_push_number(eval_environ_t env, long arg);
void env_push_pointer(eval_environ_t env, void *arg);
void env_make_frame(eval_environ_t env);
void env_leave_frame(eval_environ_t env, int nargs);
int env_set_variable(eval_environ_t env, char *ident, char *value);
int eval_environment(eval_environ_t env, prog_counter_t pc);
sfsistat environment_get_status(eval_environ_t env);
const char *environment_get_null_symbol(eval_environ_t env,
					struct mu_locus_range *locus);
SMFICTX *env_get_context(eval_environ_t env);
size_t env_get_line_count(eval_environ_t env);
int env_get_stream(eval_environ_t env, mu_stream_t *pstr);
int env_get_header(eval_environ_t env, mu_header_t *hdr);
void env_reposition(eval_environ_t env);
int env_capture_start(eval_environ_t env);
int env_capture_write(eval_environ_t env, const char *buf, size_t size);
int env_capture_write_args(eval_environ_t env, ...);
void env_final_gc(eval_environ_t env);

void env_msgmod_append(eval_environ_t env, enum msgmod_opcode opcode,
		       const char *name, const char *value, unsigned idx);
void env_msgmod_clear(eval_environ_t env);
size_t env_msgmod_count(eval_environ_t env);
int env_msgmod_apply(eval_environ_t env, mu_list_action_t fun, void *data);

void capture_on(void);
const char *env_get_macro(eval_environ_t env, const char *symbol);

void mfl_smtp_io_callback(void *data, const char *key, const char *value);

typedef void (*mfd_stack_framer_t) (prog_counter_t,
				    char const *, char const *,
				    unsigned, void *);
void env_stack_trace(eval_environ_t env, mfd_stack_framer_t framer,
		     void *data);



/* Runtime functions */
const char *mailfromd_msgid(SMFICTX *ctx);
void log_status(sfsistat status, SMFICTX *ctx);
void trace(const char *fmt, ...) MU_PRINTFLIKE(1,2);
int convert_rate(const char *arg, double *rate);
void priv_setup(void);
void mailfromd_daemon(void);
void save_cmdline(int argc, char **argv);
void pp_define(const char *symbol);
void test_message_data_init(eval_environ_t env);

void mailfromd_test(int argc, char **argv);
void mailfromd_run(prog_counter_t entry, int argc, char **argv,
		   int macc, char **macv);

void milter_enable_state(enum smtp_state state);
int milter_session_server(const char *id,
			  int fd,
			  struct sockaddr const *sa, socklen_t len,
			  void *server_data, void *srvman_data);
int mfd_callout_session_server(const char *id, int fd,
			       struct sockaddr const *sa, socklen_t len,
			       void *server_data, void *srvman_data);
void milter_setlogmask(int mask);
void milter_settimeout(time_t t);

int xeval(eval_environ_t env, enum smtp_state tag);
int xeval_begin(eval_environ_t env);

int relayed_domain_p(char *name);
size_t mem_search(const char *str, int c, size_t size);



typedef struct mf_stack *mf_stack_t;
mf_stack_t mf_stack_create(size_t elsize, size_t count);
void mf_stack_destroy(mf_stack_t *pstk);
void mf_stack_push(mf_stack_t stk, void *item);
int mf_stack_pop(mf_stack_t stk, void *ptr);
int mf_stack_peek(mf_stack_t stk, size_t n, void *ptr);

size_t mf_stack_count(mf_stack_t stk);

typedef int (*mf_stack_enumerator) (void *item, void *data);

int mf_stack_enumerate_desc(mf_stack_t stk, mf_stack_enumerator fun,
			    void *data);
int mf_stack_enumerate_asc(mf_stack_t stk, mf_stack_enumerator fun,
			   void *data);
	

int mf_stream_to_message(mu_stream_t stream, mu_message_t *msg);
void mf_add_runtime_params(struct mu_cfg_param *p);
	

/* deprecation.c */
void deprecation_warning(const char *fmt, ...) MU_PRINTFLIKE(1,2);
void deprecation_warning_locus(const struct mu_locus_range *locus, const char *fmt, ...)
	MU_PRINTFLIKE(2,3);

void final_deprecation_warning(void);
int enable_deprecation_warnings(void);


enum fixup_op
{
	fixup_noop,
	fixup_delete_line,
	fixup_insert_line,
	fixup_append_line,
	fixup_replace_line,
	fixup_delete_chars,
	fixup_insert_chars,
	fixup_replace_chars,
	fixup_rename_file,
	fixup_end
};

void add_fixup_command(enum fixup_op opcode, mf_yyltype_t *locus, char *arg);
void add_fixup_command_fmt(enum fixup_op opcode, mf_yyltype_t *locus,
			   const char *fmt, ...)
	MU_PRINTFLIKE(3,4);
void add_fixup_command_locus(enum fixup_op opcode, const struct mu_locus_range *locus,
			     char *arg);
void fixup_create_script(void);

/* savsrv.c */
int callout_session_server(const char *id, int fd,
			   struct sockaddr const *sa, socklen_t len,
			   void *server_data, void *srvman_data);
void trimcrlf(char *buf);

/* savclt.c */
void schedule_callout(const char *email, const char *ehlo,
		      const char *mailfrom,
		      const char *client_addr);
extern struct sockaddr *callout_server_sa;
extern socklen_t callout_server_sa_len;

/* exclist.c */
void define_exception(struct literal *lit, struct mu_locus_range const *locus);
void enumerate_exceptions(int (*efn)(const struct constant *cp,
				     const struct literal *lit,
				     void *data),
			  void *data);
void fixup_exceptions(void);
void free_exceptions(void);


