/* This file is part of Mailfromd.
Copyright (C) 2005-2020 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 . */
#include
#include
#include
#include
#include
#include
#include
#include
#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. */
typedef enum mf_exception_code {
#include
mf_exception_count
} mf_exception;
/* SMTP states */
enum smtp_state {
smtp_state_none,
smtp_state_begin,
smtp_state_first=smtp_state_begin,
smtp_state_connect,
smtp_state_helo,
smtp_state_envfrom,
smtp_state_envrcpt,
smtp_state_data,
smtp_state_header,
smtp_state_eoh,
smtp_state_body,
smtp_state_eom,
smtp_state_end,
smtp_state_count
};
#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, char **pipbuf);
/* Debugging macros */
extern mu_debug_handle_t mfd_debug_handle;
#include "mfd-dbgmod.h"
/* Syslog flavor */
#ifdef USE_SYSLOG_ASYNC
# include
#endif
/* FIXME */
void enable_prog_trace(const char *modlist);
void disable_prog_trace(const char *modlist);
/* 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;
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 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
} data_type_t;
/* Parse tree node */
typedef struct node NODE;
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;
NODE *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;
};
#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 */
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;
} v;
};
struct stmtlist {
NODE *head;
NODE *tail;
};
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 */
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 *parmtype; /* Parameter types */
data_type_t rettype; /* Return type */
};
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 */
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 correspondig 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 *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);
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 input_file_ident {
ino_t i_node;
dev_t device;
};
void include_path_setup(void);
FILE *pp_extrn_start(const char *name, pid_t *ppid);
void pp_extrn_shutdown(FILE *file, pid_t pid);
int preprocess_input(void);
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);
void alloc_ext_pp(void);
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);
void add_include_dir(const char *dir);
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);
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);
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);
/* Data types and declarations for handling compiled configuration code */
/* Entry points for each SMTP state */
extern prog_counter_t entry_point[smtp_state_count];
/* 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_make_frame0(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);
/* 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);
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 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);