/* This file is part of Mailfromd. Copyright (C) 2005-2011, 2015-2017 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); mf_status getmx(const char *ipstr, struct mxbuf *mxbuf); mf_status getmxip(char *host, GACOPYZ_UINT32_T *ipbuf, size_t ipmax, size_t *pcount); /* 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 */ header_delete_nth, /* Delete Nth header */ header_replace_nth, /* Replace Nth header */ 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); const 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); unsigned long get_sendmail_header_count(eval_environ_t env); void set_sendmail_header_count(eval_environ_t env, unsigned long v); 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(eval_environ_t env, enum msgmod_opcode opcode, const char *name, const char *value, unsigned idx); void env_clear_msgmod(eval_environ_t env); 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);