diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2008-02-10 14:08:36 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2008-02-10 14:08:36 +0000 |
commit | a1871918823166cac9f0c12164ba27cb3b1f0c2c (patch) | |
tree | 7260d118aa5af6a8f006f9b2dac6cabde782db8c /pmult | |
parent | 455b645247bdc0f01239b2352a9d8f07f446024f (diff) | |
download | mailfromd-a1871918823166cac9f0c12164ba27cb3b1f0c2c.tar.gz mailfromd-a1871918823166cac9f0c12164ba27cb3b1f0c2c.tar.bz2 |
Merged HEAD from branches/gmach
git-svn-id: file:///svnroot/mailfromd/trunk@1612 7a8a7f39-df28-0410-adc6-e0d955640f24
Diffstat (limited to 'pmult')
-rw-r--r-- | pmult/Makefile.am | 44 | ||||
-rw-r--r-- | pmult/debugdef.m4 | 54 | ||||
-rw-r--r-- | pmult/pdbg.hm4 | 3 | ||||
-rw-r--r-- | pmult/pmult.c | 1715 |
4 files changed, 1816 insertions, 0 deletions
diff --git a/pmult/Makefile.am b/pmult/Makefile.am new file mode 100644 index 00000000..68c0f522 --- /dev/null +++ b/pmult/Makefile.am @@ -0,0 +1,44 @@ +# This file is part of mailfrom filter. +# Copyright (C) 2008 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/>. + +PMULT_PROG = pmult +sbin_PROGRAMS = @ENABLE_PMULT@ +EXTRA_PROGRAMS = pmult +EXTRA_DIST = pdbg.hm4 debugdef.m4 +BUILT_SOURCES = pdbg.h + +INCLUDES = \ + $(MAILUTILS_INCLUDES)\ + $(MU_COMMON_INCLUDES)\ + -I$(top_srcdir)/lib\ + -I$(top_srcdir)/gnu\ + -I../gnu\ + -I../gacopyz + +pmult_SOURCES = pmult.c pdbg.h +LDADD = \ + ../lib/libmf.a\ + $(MAILUTILS_LIBS)\ + ../gnu/libgnu.a\ + $(MILTER)\ + @META1_LIBS@ @PTHREAD_LIBRARIES@ + +pdbg.h: $(top_srcdir)/pmult/debugdef.m4 pdbg.hm4 + m4 $(top_srcdir)/pmult/debugdef.m4 pdbg.hm4 > pdbg.h + + + + diff --git a/pmult/debugdef.m4 b/pmult/debugdef.m4 new file mode 100644 index 00000000..7e4a0b6f --- /dev/null +++ b/pmult/debugdef.m4 @@ -0,0 +1,54 @@ +divert(-1) +# This file is part of Mailutils. +# Copyright (C) 2006, 2007, 2008 Free Software Foundation, Inc. +# +# Initially written by Sergey Poznyakoff for Mailfromd project. +# +# 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/>. + +changecom(/*,*/) + +define(`__arglist',`dnl +ifelse($1,$2,x$1, +`x$1, __arglist(incr($1), $2)')') + +define(`MKDEBUG',` +`#define __PMU_DEBUG'$1`(dbg, lev, fmt, '__arglist(1,$1)) \` + do \ + { \ + pthread_mutex_lock (&pmult_debug_mutex); \ + __MU_DEBUG'$1`(dbg, lev, fmt, '__arglist(1,$1)); `\ + pthread_mutex_unlock (&pmult_debug_mutex); \ + } \ + while (0) + +#define PMU_DEBUG'$1`(dbg, lev, fmt, '__arglist(1,$1)`) \ + do \ + { \ + if (mu_debug_check_level (dbg, lev)) \ + __PMU_DEBUG'$1`(dbg, lev, fmt, '__arglist(1,$1)`); \ + } \ + while(0) +'') + +define(`forloop', + `pushdef(`$1', `$2')_forloop(`$1', `$2', `$3', `$4')popdef(`$1')') +define(`_forloop', + `$4`'ifelse($1, `$3', , + `define(`$1', incr($1))_forloop(`$1', `$2', `$3', `$4')')') + +divert(0)dnl +/* -*- buffer-read-only: t -*- vi: set ro: + THIS FILE IS GENERATED AUTOMATICALLY. PLEASE DO NOT EDIT. +*/ diff --git a/pmult/pdbg.hm4 b/pmult/pdbg.hm4 new file mode 100644 index 00000000..a19506d4 --- /dev/null +++ b/pmult/pdbg.hm4 @@ -0,0 +1,3 @@ +#include <mailutils/debug.h> +#define PMU_DEBUG(d,l,s) PMU_DEBUG1(d,l,"%s",s) +forloop(`i',1,11,`MKDEBUG(i)') diff --git a/pmult/pmult.c b/pmult/pmult.c new file mode 100644 index 00000000..92cfc844 --- /dev/null +++ b/pmult/pmult.c @@ -0,0 +1,1715 @@ +/* This file is part of Mailfromd. + Copyright (C) 2008 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 HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <syslog.h> +#include <mailutils/mailutils.h> +#include <mailutils/daemon.h> +#include <mailutils/syslog.h> +#include <mailutils/libargp.h> +#include <sysexits.h> +#include <pthread.h> +#include <gacopyz.h> +#include "xalloc.h" +#include "libmf.h" + +/* FIXME */ +#include <inttypes.h> +const char *mu_umaxtostr (unsigned slot, uintmax_t val); + +#include "sm/error.h" +#include "sm/smreplycodes.h" +#include "sm/pmilter.h" +#include "sm/pmfdef.h" +#include "sm/pmfapi.h" + +#include "pdbg.h" + +const char *program_version = "pmult (" PACKAGE_STRING ")"; +const char *package_bugreport = "<" PACKAGE_BUGREPORT ">"; + +char *portspec; /* Communication socket */ +int log_to_stderr; /* Use stderr for logging */ +char *log_tag; /* override mu_log_tag */ +mu_log_level_t debug_level; /* Debug verbosity level */ +mu_debug_t pmult_debug; /* Debugging object */ +static pthread_mutex_t pmult_debug_mutex = PTHREAD_MUTEX_INITIALIZER; +char *pidfile; /* pidfile name */ +int no_sig_handler; /* Disable signal handler */ + +struct pmult_client +{ + int type; /* Type, unused so far */ + char *name; + struct timeval timeout[GACOPYZ_TO_COUNT]; + char *url; + int logmask; +}; + +/* List of configured clients: */ +mu_list_t /* of struct pmult_client */ client_list; + +enum pmult_msg_state + { + pmult_msg_state_initial, + pmult_msg_state_headers, + pmult_msg_state_cr1, + pmult_msg_state_crlf1, + pmult_msg_state_cr2, + pmult_msg_state_body + }; + +struct pmult_priv_data +{ + mu_debug_t debug; + mu_list_t /* of gacopyz_srv_t */ srvlist; + pthread_mutex_t mutex; + char *seid; + char *seid_c; + char *taid; + unsigned nrcpt; + unsigned nbadrcpts; + mu_stream_t hdrstream; + enum pmult_msg_state state; + mu_header_t hdr; +}; + +#define PRIV_SEID(p) ((p)->seid ? (p)->seid : "") +#define PRIV_SEID_C(p) ((p)->seid_c ? (p)->seid_c : pmult_seid_c (p)) +#define PRIV_LOCK(p) pthread_mutex_lock (&(p)->mutex) +#define PRIV_UNLOCK(p) pthread_mutex_unlock (&(p)->mutex) + +static char * +pmult_seid_c (struct pmult_priv_data *p) +{ + if (p->seid) + { + p->seid_c = malloc (strlen (p->seid) + 2); + if (p->seid_c) + { + strcpy (p->seid_c, p->seid); + strcat (p->seid_c, ":"); + return p->seid_c; + } + } + return ""; +} + + +static pthread_mutex_t pmult_mutex = PTHREAD_MUTEX_INITIALIZER; +#define protect() pthread_mutex_lock (&pmult_mutex) +#define unprotect() pthread_mutex_unlock (&pmult_mutex) + + +/* Logging */ +void +log_setup (int want_stderr) +{ + mu_debug_t debug; + + mu_diag_get_debug (&debug); + + if (log_tag) + mu_log_tag = log_tag; + + if (!want_stderr) + { + openlog (MU_LOG_TAG (), LOG_PID, mu_log_facility); + gacopyz_set_logger (gacopyz_syslog_log_printer); + mu_debug_set_print (debug, mu_diag_syslog_printer, NULL); + mu_debug_default_printer = mu_debug_syslog_printer; + } + else + { + gacopyz_set_logger (gacopyz_stderr_log_printer); + mu_debug_default_printer = mu_debug_stderr_printer; + } +} + +static int +stderr_closed_p() +{ + int fd = dup(0); + if (fd < 0) + return 1; + close(fd); + return fd <= 2; +} + + +static int +_cb_client_type (mu_debug_t debug, void *data, char *arg) +{ + if (strcmp (arg, "milter") == 0) + /* dobrze */; + else if (strcmp (arg, "pmilter") == 0) + mu_cfg_format_error (debug, MU_DEBUG_ERROR, + _("client type %s is not supported yet"), + arg); + else + mu_cfg_format_error (debug, MU_DEBUG_ERROR, + _("unknown client type %s"), + arg); + return 0; +} + +static int +cb_timeout (struct timeval *pt, mu_debug_t debug, char *arg) +{ + const char *endp; + time_t t; + if (parse_time_interval (arg, &t, &endp)) + { + mu_cfg_format_error (debug, MU_DEBUG_ERROR, + _("unrecognized time format (near `%s')"), + endp); + return 1; + } + pt->tv_usec = 0; + pt->tv_sec = t; + return 0; +} + +static int +_cb_write_timeout (mu_debug_t debug, void *data, char *arg) +{ + struct pmult_client *clt = data; + return cb_timeout (&clt->timeout[GACOPYZ_TO_WRITE], debug, arg); +} + +static int +_cb_read_timeout (mu_debug_t debug, void *data, char *arg) +{ + struct pmult_client *clt = data; + return cb_timeout (&clt->timeout[GACOPYZ_TO_READ], debug, arg); +} + +static int +_cb_eom_timeout (mu_debug_t debug, void *data, char *arg) +{ + struct pmult_client *clt = data; + return cb_timeout (&clt->timeout[GACOPYZ_TO_EOM], debug, arg); +} + +static int +_cb_connect_timeout (mu_debug_t debug, void *data, char *arg) +{ + struct pmult_client *clt = data; + return cb_timeout (&clt->timeout[GACOPYZ_TO_CONNECT], debug, arg); +} + +static int +_cb_log_level (mu_debug_t debug, void *data, char *arg) +{ + struct pmult_client *clt = data; + int argc, i; + char **argv; + int rc; + + rc = mu_argcv_get_np (arg, strlen (arg), ",", NULL, 0, &argc, &argv, NULL); + if (rc) + { + mu_cfg_format_error (debug, MU_DEBUG_ERROR, + "mu_argcv_get: %s", mu_strerror (rc)); + return 1; + } + + for (i = 0; i < argc; i++) + { + char *p = argv[i]; + int lev; + int revert = 0; + int upto = 0; + + if (*p == '!') + { + p++; + revert = 1; + } + if (*p == '<') + { + p++; + upto = 1; + } + + lev = gacopyz_string_to_log_level (p); + if (lev == -1) + { + mu_cfg_format_error (debug, MU_DEBUG_ERROR, + _("Invalid log level: %s"), arg); + return 1; + } + + if (revert) + { + if (upto) + clt->logmask &= ~SMI_LOG_UPTO (lev); + else + clt->logmask &= ~SMI_LOG_FROM (lev); + } + else + { + if (upto) + clt->logmask |= SMI_LOG_UPTO (lev); + else + clt->logmask |= SMI_LOG_FROM (lev); + } + } + + mu_argcv_free (argc, argv); + return 0; +} + +struct mu_cfg_param client_cfg_param[] = { + { "type", mu_cfg_callback, NULL, 0, _cb_client_type, + N_("Set remote milter type. Only `milter' is understood so far."), + N_("{milter [version: number]|pmilter}") }, + { "url", mu_cfg_string, NULL, mu_offsetof(struct pmult_client, url), NULL, + N_("Set remote client URL.") }, + { "write-timeout", mu_cfg_callback, NULL, 0, _cb_write_timeout, + N_("Set write timeout."), + N_("time") }, + { "read-timeout", mu_cfg_callback, NULL, 0, _cb_read_timeout, + N_("Set read timeout."), + N_("time") }, + { "eom-timeout", mu_cfg_callback, NULL, 0, _cb_eom_timeout, + N_("Set timeout for EOM."), + N_("time") }, + { "connect-timeout", mu_cfg_callback, NULL, 0, _cb_connect_timeout, + N_("Set connect timeout."), + N_("time") }, + { "log-level", mu_cfg_callback, NULL, 0, _cb_log_level, + N_("Set log verbosity level. Arg is a list of items separated by commas " + "or whitespace. Each item is a log level optionally prefixed with `!' " + "to indicate `any level except this', or '<', meaning `all levels up " + "to and including this'. Log levels in order of increasing priority " + "are: debug, info, warn, err, fatal."), + N_("arg: list") }, + { NULL } +}; + +static int +_cb_portspec (mu_debug_t debug, void *data, char *arg) +{ + char **pptr = data, *ptr; + char *proto = NULL; + char *port = NULL; + char *path = NULL; + size_t len; + + if (gacopyz_parse_connection (arg, &proto, &port, &path)) + { + mu_cfg_format_error (debug, MU_DEBUG_ERROR, _("Invalid URL: %s"), arg); + return 1; + } + + if (!proto) + { + if (port) + { + mu_cfg_format_error (debug, MU_DEBUG_ERROR, + _("Invalid URL: %s"), arg); + return 1; + } + ptr = xstrdup (path); + } + else + { + len = strlen (proto) + 1 + + (port ? strlen (port) + 1 : 0) + strlen (path) + 1; + ptr = xmalloc (len); + strcpy (ptr, proto); + strcat (ptr, ":"); + if (port) + { + strcat (ptr, port); + strcat (ptr, "@"); + } + strcat (ptr, path); + } + free (path); + free (port); + free (proto); + *pptr = ptr; + return 0; +} + +static int +_cb_debug (mu_debug_t debug, void *data, char *arg) +{ + int rc; + + rc = mu_debug_level_from_string (arg, &debug_level, debug); + if (rc) + return 0; + if (!pmult_debug) + mu_debug_create (&pmult_debug, NULL); + mu_debug_set_level (pmult_debug, debug_level); + return 0; +} + +struct mu_cfg_param pmult_cfg_param[] = { + { "listen", mu_cfg_callback, &portspec, 0, _cb_portspec, + N_("Listen for milter requests on the given URL."), + N_("url") }, + { "client", mu_cfg_section }, + { "debug", mu_cfg_callback, NULL, 0, _cb_debug, + N_("Set debug verbosity level.") }, + { "pidfile", mu_cfg_string, &pidfile, 0, NULL, + N_("Write PID to this file.") }, + { NULL } +}; + +static void +pmult_client_free (struct pmult_client *clt) +{ + free (clt->name); + free (clt); +} + +static int +client_block_begin (mu_debug_t debug, char *name, void **section_data) +{ + extern struct timeval default_gacopyz_timeout[GACOPYZ_TO_COUNT]; + struct pmult_client *clt = xcalloc (1, sizeof *clt); + clt->name = name ? xstrdup (name) : ""; + memcpy (clt->timeout, default_gacopyz_timeout, sizeof clt->timeout); + *section_data = clt; + return 0; +} + +static int +client_block_end (mu_debug_t debug, struct pmult_client *clt) +{ + gacopyz_srv_t gsrv; + int rc = gacopyz_srv_create (&gsrv, clt->name, clt->url, clt->logmask); + if (rc) + { + pmult_client_free (clt); + return 1; /* FIXME: error message */ + } + + gacopyz_srv_destroy (&gsrv); + if (!client_list) + MU_ASSERT (mu_list_create (&client_list)); + mu_list_append (client_list, clt); + return 0; +} + +static int +client_section_parser (enum mu_cfg_section_stage stage, + const mu_cfg_node_t *node, + const char *section_label, void **section_data, + void *call_data, + mu_cfg_tree_t *tree) +{ + switch (stage) + { + case mu_cfg_section_start: + return client_block_begin (tree->debug, node->tag_label, section_data); + + case mu_cfg_section_end: + return client_block_end (tree->debug, + (struct pmult_client *)*section_data); + } + return 0; +} + +static void +pmult_cfg_init () +{ + struct mu_cfg_section *section; + + if (mu_create_canned_section ("client", §ion)) + exit (EX_SOFTWARE); + section->parser = client_section_parser; + section->label = N_("ident"); + mu_cfg_section_add_params (section, client_cfg_param); +} + + +/* Command line parsing */ + +static char doc[] = N_("pmult -- pmilter multiplexer"); +static char args_doc[] = ""; + +enum { + OPTION_URL = 256, + OPTION_SYSLOG, + OPTION_LOG_TAG, + OPTION_NO_SIGNAL_HANDLER, +}; + +static struct argp_option options[] = { + { "url", OPTION_URL, N_("URL"), 0, + N_("Listen on the given URL"), 0 }, + { "syslog", OPTION_SYSLOG, NULL, 0, + N_("Log to syslog (default)"), }, + { "stderr", 's', NULL, 0, + N_("Log to stderr"), }, + { "log-tag", OPTION_LOG_TAG, N_("STRING"), 0, + N_("Set the identifier used in syslog messages to STRING"), }, + { "debug", 'x', N_("LEVEL"), 0, + N_("Set debug verbosity level.") }, + { "no-signal-handler", OPTION_NO_SIGNAL_HANDLER, NULL, 0, + N_("Disable signal handling in the main thread (use for debugging).") }, + { NULL } +}; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + static struct mu_argp_node_list lst; + + switch (key) + { + case OPTION_URL: + mu_argp_node_list_new (&lst, "listen", arg); + break; + + case OPTION_SYSLOG: + log_to_stderr = 0; + break; + + case OPTION_NO_SIGNAL_HANDLER: + no_sig_handler = 1; + break; + + case 's': + log_to_stderr = 1; + break; + + case 'x': + mu_argp_node_list_new (&lst, "debug", arg); + break; + + case OPTION_LOG_TAG: + log_tag = arg; + break; + + case ARGP_KEY_INIT: + mu_argp_node_list_init (&lst); + break; + + case ARGP_KEY_FINI: + mu_argp_node_list_finish (&lst, NULL, NULL); + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static const char *capa[] = { + "common", + "debug", + "logging", + NULL +}; + +static struct argp argp = { + options, + parse_opt, + args_doc, + doc, + NULL, + NULL, + NULL +}; + + +#define SM_ONERROR_ACTION(expr,onerr) do \ + { \ + sm_ret_T ret = expr; \ + if (sm_is_err (ret)) \ + { \ + char *cp = smerr2txt (ret); \ + if (cp) \ + mu_error ("%s:%d: " #expr " failed: %s", \ + __FILE__, __LINE__, cp); \ + else \ + mu_error ("%s:%d: " #expr " failed: %#x", \ + __FILE__, __LINE__, ret); \ + onerr; \ + } \ + } \ +while (0) + +#define SM_VERBOSE(expr) SM_ONERROR_ACTION(expr,) +#define SM_CHECK(expr) SM_ONERROR_ACTION(expr,return ret) +#define SM_ASSERT(expr) SM_ONERROR_ACTION(expr, exit (EX_CONFIG)) + + +#define MSG_TEMPFAIL "451 4.3.2 Please try again later\r\n" +#define MSG_REJECT "550 5.7.1 Command rejected\r\n" +#define MSG_SHUTDOWN "421 4.7.0 pmult closing connection\r\n" + +const char * +hdrcommname (int rc) +{ + switch (rc) + { + case SMFIR_CHGHEADER: + return "chgheader"; + + case SMFIR_ADDHEADER: + return "addheader"; + + case SMFIR_INSHEADER: + return "insheader"; + } + return "unknown"; +} + +static int +get_mod_index (struct pmult_priv_data *p, const char *header_name, + size_t pos, size_t *pidx) +{ + size_t i, count; + const char *name; + + if (mu_header_get_field_count (p->hdr, &count)) + return 1; + for (i = 1; i <= count; i++) + { + if (mu_header_sget_field_name (p->hdr, i, &name)) + continue; + if (strcmp (name, header_name) == 0 && --pos == 0) + { + *pidx = i - 1; + return 0; + } + } + return 1; +} + +static int +cb_reply (gacopyz_srv_t gsrv, int cmd, int rcmd, void *data) +{ + char *buf; + size_t size; + char *cp; + char *header; + uint type; + uint32_t idx = 0; + pmse_ctx_P pmse_ctx = data; + struct pmult_priv_data *p = sm_pmfi_get_ctx_se (pmse_ctx); + + switch (rcmd) + { + case SMFIR_CHGHEADER: + case SMFIR_ADDHEADER: + case SMFIR_INSHEADER: + gacopyz_srv_reply (gsrv, &buf, &size); + if (rcmd != SMFIR_ADDHEADER) + { + idx = ntohl (*(uint32_t*)buf); + buf += sizeof (uint32_t); + } + cp = memchr (buf, 0, size); + if (!cp) + { + mu_diag_output (MU_DIAG_NOTICE, "Malformed CHGHEADER command"); + break; + } + cp++; + PMU_DEBUG4 (p->debug, MU_DEBUG_TRACE2, + "%s=%u '%s','%s'\n", + hdrcommname (rcmd), idx, buf, cp); + + header = malloc (size + 5); + if (!header) + { + mu_error ("%s", mu_strerror (ENOMEM)); + return SMTP_R_ACCEPT; + } + strcpy (header, buf); + strcat (header, ": "); + strcat (header, cp); + strcat (header, "\r\n"); + + switch (rcmd) + { + case SMFIR_CHGHEADER: + { + /* Emulate milter semantics */ + size_t index; + + type = (*cp == 0) ? SM_HDRMOD_T_REMOVE : SM_HDRMOD_T_REPLACE; + if (get_mod_index (p, buf, idx, &index)) + { + PMU_DEBUG1 (p->debug, MU_DEBUG_TRACE1, + "no such header: %s\n", buf); + return 0; + } + idx = index; + } + break; + + case SMFIR_ADDHEADER: + type = SM_HDRMOD_T_APPEND; + break; + + case SMFIR_INSHEADER: + type = SM_HDRMOD_T_INSERT; + } + + SM_VERBOSE (sm_pmfi_hdr_mod (pmse_ctx, type, idx, header)); + free (header); + } + return 0; +} + +#define CRLF "\r\n" + +static char * +trailcrlf (char **pbuf) +{ + char *buf = *pbuf; + char *retp = NULL; + int len; + + if (!buf) + *pbuf = CRLF; + else if ((len = strlen (buf)) < 2 || memcmp (buf + len - 2, CRLF, 2)) + { + retp = malloc (len + 3); + if (!retp) + { + mu_error ("%s", mu_strerror (ENOMEM)); + *pbuf = MSG_TEMPFAIL; + } + strcat (strcpy (retp, buf), CRLF); + *pbuf = retp; + } + return retp; +} + +static int +pmult_std_reply (struct pmult_priv_data *p, pmse_ctx_P pmse_ctx, + gacopyz_srv_t gsrv, int rc, + const char *ident, const char *arg) +{ + char *buf, *tmp; + size_t size; + int status; + + if (!arg) + arg = ""; + + switch (rc) + { + case SMFIR_CONTINUE: + break; + + case SMFIR_REPLYCODE: + gacopyz_srv_reply (gsrv, &buf, &size); + PMU_DEBUG3 (p->debug, MU_DEBUG_TRACE2, + "%s=%s, reply=%s\n", + ident, arg, buf); + tmp = trailcrlf (&buf); + SM_VERBOSE (sm_pmfi_setreply (pmse_ctx, tmp)); + status = (buf[0] == '4') ? SMTP_R_TEMP : SMTP_R_PERM; + free (tmp); + return status; + + case SMFIR_REJECT: + PMU_DEBUG3 (p->debug, MU_DEBUG_TRACE2, + "%s=%s, reply=%s\n", + ident, arg, MSG_REJECT); + SM_VERBOSE (sm_pmfi_setreply (pmse_ctx, MSG_REJECT)); + return SMTP_R_PERM; + + case SMFIR_DISCARD: + PMU_DEBUG2 (p->debug, MU_DEBUG_TRACE2, + "%s=%s, discard\n", + ident, arg); + return SMTP_R_DISCARD; + + case SMFIR_TEMPFAIL: + PMU_DEBUG3 (p->debug, MU_DEBUG_TRACE2, + "%s=%s, reply=%s\n", + ident, arg, MSG_TEMPFAIL); + SM_VERBOSE (sm_pmfi_setreply (pmse_ctx, MSG_TEMPFAIL)); + return SMTP_R_TEMP; + + case SMFIR_SHUTDOWN: + PMU_DEBUG3 (p->debug, MU_DEBUG_TRACE2, + "%s=%s, reply=%s\n", + ident, arg, MSG_SHUTDOWN); + SM_VERBOSE (sm_pmfi_setreply (pmse_ctx, MSG_SHUTDOWN)); + return SMTP_R_SSD; + + case SMFIR_ADDRCPT: + gacopyz_srv_reply (gsrv, &buf, &size); + PMU_DEBUG3 (p->debug, MU_DEBUG_TRACE2, + "%s=%s, addrcpt=%s\n", + ident, arg, buf); + SM_VERBOSE (sm_pmfi_rcpt_add (pmse_ctx, buf, NULL)); + break; + + case SMFIR_DELRCPT: + gacopyz_srv_reply (gsrv, &buf, &size); + PMU_DEBUG3 (p->debug, MU_DEBUG_TRACE2, + "%s=%s, delrcpt=%s\n", + ident, arg, buf); + /* FIXME: Index is always 0. Should it be 1? */ + SM_VERBOSE (sm_pmfi_rcpt_del (pmse_ctx, buf, 0)); + break; + + case SMFIR_CHGHEADER: + case SMFIR_ADDHEADER: + case SMFIR_INSHEADER: + break; + + case SMFIR_REPLBODY: + /* FIXME */ + + default: + if (isascii (rc) && isprint (rc)) + mu_diag_output (MU_DIAG_WARNING, _("Unsupported reply code: %c (%d)"), + rc, rc); + else + mu_diag_output (MU_DIAG_WARNING, _("Unsupported reply code: (%d)"), + rc); + } + return SMTP_R_CONT; +} + +typedef sfsistat_T (*pmult_runfun_t) (pmse_ctx_P, gacopyz_srv_t, void *); + +static sfsistat_T +pmult_runlist0 (struct pmult_priv_data *p, pmult_runfun_t runfun, + pmse_ctx_P pmse_ctx, void *data, char **macros) +{ + sfsistat_T rc; + mu_iterator_t itr = NULL; + + mu_list_get_iterator (p->srvlist, &itr); + + for (mu_iterator_first (itr); !mu_iterator_is_done (itr); + mu_iterator_next (itr)) + { + int i; + + gacopyz_srv_t gsrv; + + mu_iterator_current (itr, (void**)&gsrv); + + if (macros) + for (i = 0; macros[i]; i += 2) + gacopyz_srv_define_macro (gsrv, macros[i], macros[i+1]); + + rc = runfun (pmse_ctx, gsrv, data); + if (rc != SMTP_R_CONT && rc != SMTP_R_OK) + break; + } + + mu_iterator_destroy (&itr); + return rc; +} + +static sfsistat_T +pmult_runlist (struct pmult_priv_data *p, pmult_runfun_t runfun, + pmse_ctx_P pmse_ctx, void *data, char **macros) +{ + sfsistat_T rc; + PRIV_LOCK (p); + rc = pmult_runlist0 (p, runfun, pmse_ctx, data, macros); + PRIV_UNLOCK (p); + return rc; +} + +void +pmult_free (struct pmult_priv_data *p) +{ + if (!p) + return; + + mu_list_destroy (&p->srvlist); + + free (p->taid); + free (p->seid); + free (p->seid_c); + + mu_stream_destroy (&p->hdrstream, NULL); + mu_header_destroy (&p->hdr, mu_header_get_owner (p->hdr)); + mu_debug_destroy (&p->debug, NULL); + pthread_mutex_destroy (&p->mutex); + free (p); +} + +void +pmult_clear (struct pmult_priv_data *p) +{ + PRIV_LOCK (p); + mu_stream_destroy (&p->hdrstream, NULL); + mu_header_destroy (&p->hdr, mu_header_get_owner (p->hdr)); + p->state = pmult_msg_state_initial; + p->nrcpt = 0; + p->nbadrcpts = 0; + PRIV_UNLOCK (p); +} + +void +pmult_shutdown (pmse_ctx_P pmse_ctx, struct pmult_priv_data *p) +{ + mu_iterator_t itr; + + if (!p) + return; + + PRIV_LOCK (p); + mu_list_get_iterator (p->srvlist, &itr); + + for (mu_iterator_first (itr); !mu_iterator_is_done (itr); + mu_iterator_next (itr)) + { + gacopyz_srv_t gsrv; + + mu_iterator_current (itr, (void**)&gsrv); + gacopyz_srv_quit (gsrv); + gacopyz_srv_close (gsrv); + gacopyz_srv_destroy (&gsrv); + } + mu_iterator_destroy (&itr); + sm_pmfi_set_ctx_se (pmse_ctx, NULL); + PRIV_UNLOCK (p); + pmult_free (p); +} + + +static void +version (FILE *stream, struct argp_state *state) +{ + mailfromd_version ("pmult", stream); +} + + +static sm_ret_T +pmult_negotiate (pmss_ctx_P pmss_ctx, + uint32_t srv_cap, uint32_t srv_fct, uint32_t srv_feat, + uint32_t srv_misc, uint32_t *pm_cap, uint32_t *pm_fct, + uint32_t *pm_feat, uint32_t *pm_misc) +{ + SM_CHECK (sm_pmfi_setmaclist (pmss_ctx, PM_SMST_CONNECT, PMM_SEID, PMM_END)); + SM_CHECK (sm_pmfi_setmaclist (pmss_ctx, PM_SMST_MAIL, PMM_MAIL_TAID, + PMM_END)); + return SM_SUCCESS; + +} + +static sfsistat_T +pmult_connect (pmse_ctx_P pmse_ctx, const char *hostname, + sm_sockaddr_T *hostaddr) +{ + mu_iterator_t itr; + struct pmult_priv_data *p; + mu_debug_t dbg = NULL; + int status = SMTP_R_CONT; + int rc; + char *tmp = NULL; + char *client_addr = NULL, *client_port = NULL; + + if (debug_level) + { + mu_debug_create (&dbg, NULL); + mu_debug_set_level (dbg, debug_level); + } + + if (mu_debug_check_level (dbg, MU_DEBUG_TRACE1)) + { + char *p = mu_sockaddr_to_astr (&hostaddr->sa, sizeof *hostaddr); + __PMU_DEBUG2 (dbg, MU_DEBUG_TRACE1, + "Connect from: %s, address %s\n", hostname, p); + free (p); + } + + p = calloc (1, sizeof *p); + if (!p) + { + mu_error ("%s: accept", mu_strerror (ENOMEM)); + mu_debug_destroy (&dbg, NULL); + return SMTP_R_ACCEPT; + } + p->debug = dbg; + pthread_mutex_init (&p->mutex, NULL); + + sm_pmfi_getmac (pmse_ctx, PMM_SEID, &tmp); + p->seid = strdup (tmp); + + rc = mu_list_create (&p->srvlist); + if (rc) + { + mu_error ("%smu_list_create: %s", PRIV_SEID_C (p), mu_strerror (rc)); + mu_debug_destroy (&dbg, NULL); + free (p); + return SMTP_R_ACCEPT; + } + + protect (); + mu_list_get_iterator (client_list, &itr); + + client_addr = strdup (inet_ntoa (hostaddr->sin.sin_addr)); + client_port = strdup (mu_umaxtostr (0, ntohs (hostaddr->sin.sin_port))); + unprotect (); + + PRIV_LOCK (p); + for (mu_iterator_first (itr); !mu_iterator_is_done (itr); + mu_iterator_next (itr)) + { + struct pmult_client *clt; + gacopyz_srv_t gsrv; + + mu_iterator_current (itr, (void**)&clt); + gacopyz_srv_create (&gsrv, clt->name, clt->url, clt->logmask); + gacopyz_srv_set_callback (gsrv, cb_reply); + gacopyz_srv_set_callback_data (gsrv, pmse_ctx); + gacopyz_srv_set_all_timeouts (gsrv, clt->timeout); + if (gacopyz_srv_connect (gsrv) != MI_SUCCESS) + { + mu_error (_("%sFailed to connect to %s (milter %s)"), + PRIV_SEID_C (p), clt->url, clt->name); + gacopyz_srv_destroy (&gsrv); + continue; + } + gacopyz_srv_negotiate (gsrv); + gacopyz_srv_define_macro (gsrv, "r", "SMTP"); + gacopyz_srv_define_macro (gsrv, "i", PRIV_SEID (p)); + + if (client_addr) + gacopyz_srv_define_macro (gsrv, "client_addr", client_addr); + gacopyz_srv_define_macro (gsrv, "client_name", hostname); + if (client_port) + gacopyz_srv_define_macro (gsrv, "client_port", client_port); + /* FIXME: client_ptr, client_resolve */ + + rc = gacopyz_srv_conn (gsrv, hostname, &hostaddr->sa); + status = pmult_std_reply (p, pmse_ctx, gsrv, rc, "connect", hostname); + if (status != SMTP_R_CONT) + break; + mu_list_append (p->srvlist, gsrv); + } + + free (client_addr); + free (client_port); + + protect (); + mu_iterator_destroy (&itr); + unprotect (); + + PRIV_UNLOCK (p); + if (status == SMTP_R_CONT) + sm_pmfi_set_ctx_se (pmse_ctx, p); + else + pmult_free (p); + return status; +} + +static sm_ret_T +pmult_close (pmse_ctx_P pmse_ctx) +{ + struct pmult_priv_data *p = sm_pmfi_get_ctx_se (pmse_ctx); + if (p) + { + PMU_DEBUG1 (p->debug, MU_DEBUG_TRACE1, "%sClosing connection\n", |