/* $Id: milter-spamd.c,v 1.13 2006/10/05 04:27:35 ca Exp $ */
/*
* Copyright (c) 2004 Daniel Hartmeier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* Modified for MeTA1 pmilter
*/
static const char rcsid[] = "$Id: milter-spamd.c,v 1.13 2006/10/05 04:27:35 ca Exp $";
#include "sm/generic.h"
SM_RCSID("@(#)$Id: milter-spamd.c,v 1.13 2006/10/05 04:27:35 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/types.h"
#include "sm/ctype.h"
#include "sm/sysexits.h"
#include "sm/fcntl.h"
#include "sm/io.h"
#include "sm/regex.h"
#include "sm/signal.h"
#include "sm/syslog.h"
#include "sm/smreplycodes.h"
#include "sm/pmilter.h"
#include "sm/pmfdef.h"
#include "sm/pmfapi.h"
#if MTA_USE_PMILTER
#include <stdio.h>
#define MI_SUCCESS SM_PMI_SUCCESS
static const char *spamd_user = "";
static const char *ignore_connect = "";
static int debug = 0;
static int timeout = 30;
static regex_t re_ignore_connect;
/* when to add Spam: header: */
#define ADDHDR_NEVER 0 /* never */
#define ADDHDR_ONLY 1 /* only, don't reject */
#define ADDHDR_GT0 2 /* if score > 0 */
#define ADDHDR_ALWAYS 3 /* always */
static int AddHdr = ADDHDR_NEVER;
struct context {
char ctx_host[128];
char ctx_addr[64];
char ctx_helo[128];
#if 0
char hdr_from[128];
char hdr_to[128];
char hdr_subject[128];
#endif
char *ctx_ta_id;
int ctx_fd;
int ctx_state;
int ctx_flags;
int ctx_spam;
double ctx_score, ctx_threshold;
char ctx_symbols[128];
};
typedef struct context context_T, *context_P;
#define CTX_FL_NONE 0x0000
#define CTX_FL_FD_OPEN 0x0001 /* fd is ok to use */
#define CTX_FL_FD_FAIL 0x0002 /* don't try to open file */
#define CTX_FL_WR_FAIL 0x0004 /* write failed before */
#define CTX_SET_FLAG(context, fl) (context)->ctx_flags |= (fl)
#define CTX_CLR_FLAG(context, fl) (context)->ctx_flags &= ~(fl)
#define CTX_IS_FLAG(context, fl) (((context)->ctx_flags & (fl)) != 0)
static void usage(const char *);
static void msg(int, context_P, const char *, ...);
#define USER "_milter-spamd"
#define OCONN "/var/spool/milter-spamd/sock"
#define SPAMD_ADDR "127.0.0.1"
#define SPAMD_PORT 783
static short spamd_port = SPAMD_PORT;
#if SM_STAT_st_mtimespec
#define ST_MTIME st_mtimespec
#else
#define ST_MTIME st_mtime
#endif
static void
msg(int priority, context_P context, const char *fmt, ...)
{
va_list ap;
char msg[8192];
va_start(ap, fmt);
if (context != NULL)
snprintf(msg, sizeof(msg), "%s: ", context->ctx_addr);
else
msg[0] = 0;
vsnprintf(msg + strlen(msg), sizeof(msg) - strlen(msg), fmt, ap);
if (debug)
printf("syslog: %s\n", msg);
else
syslog(priority, "%s", msg);
va_end(ap);
}
static int
get_spamd_fd(context_P context)
{
int fd;
struct sockaddr_in sa;
if (CTX_IS_FLAG(context, CTX_FL_FD_OPEN) ||
CTX_IS_FLAG(context, CTX_FL_FD_FAIL))
return (-1);
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
msg(LOG_ERR, context, "get_spamd_fd: socket: %s",
strerror(errno));
return (-1);
}
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr(SPAMD_ADDR);
sa.sin_port = htons(spamd_port);
if (connect(fd, (struct sockaddr *)&sa, sizeof(sa))) {
msg(LOG_ERR, context, "get_spamd_fd: connect: %s",
strerror(errno));
close(fd);
CTX_SET_FLAG(context, CTX_FL_FD_FAIL);
return (-1);
}
CTX_SET_FLAG(context, CTX_FL_FD_OPEN);
return (fd);
}
static sm_ret_T
cb_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_ret_T ret;
*pm_cap = SM_SCAP_PM_ALL;
*pm_fct = 0;
*pm_feat = 0;
*pm_misc = 0;
ret = sm_pmfi_setmaclist(pmss_ctx, PM_SMST_CONNECT,
PMM_SRVHOSTNAME, PMM_END);
if (sm_is_err(ret))
fprintf(stderr,
"sev=ERROR, where=cb_negotiate, sm_pmfi_setmaclist=%#x, macro=hostname\n",
ret);
ret = sm_pmfi_setmaclist(pmss_ctx, PM_SMST_MAIL,
PMM_MAIL_TAID, PMM_END);
if (sm_is_err(ret))
fprintf(stderr,
"sev=ERROR, where=cb_negotiate, sm_pmfi_setmaclist=%#x, macro=taid\n",
ret);
return SM_SUCCESS;
}
static sfsistat_T
cb_connect(pmse_ctx_P pmse_ctx, const char *name, sm_sockaddr_T *sa)
{
context_P context;
char host[64];
context = calloc(1, sizeof(*context));
if (context == NULL) {
msg(LOG_ERR, NULL, "cb_connect: calloc: %s", strerror(errno));
return (SMFIS_ACCEPT);
}
context->ctx_fd = -1;
if (sm_pmfi_set_ctx_se(pmse_ctx, context) != MI_SUCCESS) {
free(context);
msg(LOG_ERR, NULL, "cb_connect: smfi_setpriv");
return (SMFIS_ACCEPT);
}
strlcpy(host, "unknown", sizeof(host));
switch (sa->sa.sa_family) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
if (inet_ntop(AF_INET, &sin->sin_addr.s_addr, host,
sizeof(host)) == NULL)
msg(LOG_ERR, NULL, "cb_connect: inet_ntop: %s",
strerror(errno));
break;
}
#if HAVE_INET6
case AF_INET6: {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
if (inet_ntop(AF_INET6, &sin6->sin6_addr, host,
sizeof(host)) == NULL)
msg(LOG_ERR, NULL, "cb_connect: inet_ntop: %s",
strerror(errno));
break;
}
#endif
}
strlcpy(context->ctx_host, name, sizeof(context->ctx_host));
strlcpy(context->ctx_addr, host, sizeof(context->ctx_addr));
msg(LOG_DEBUG, context, "cb_connect('%s', '%s')", name, host);
if (ignore_connect[0] && (
!regexec(&re_ignore_connect, name, 0, NULL, 0) ||
!regexec(&re_ignore_connect, host, 0, NULL, 0))) {
msg(LOG_DEBUG, context, "cb_connect: matches host ignore RE");
return (SMFIS_ACCEPT);
}
return (SMFIS_CONTINUE);
}
static sfsistat_T
cb_helo(pmse_ctx_P pmse_ctx, const char *arg, bool ehlo)
{
context_P context;
if ((context = (context_P)sm_pmfi_get_ctx_se(pmse_ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_helo: smfi_getpriv");
return (SMFIS_ACCEPT);
}
strlcpy(context->ctx_helo, arg, sizeof(context->ctx_helo));
msg(LOG_DEBUG, context, "cb_helo('%s')", arg);
return (SMFIS_CONTINUE);
}
#if MILTER_SPAMD_DEBUG
static sfsistat_T
cb_mail(pmse_ctx_P pmse_ctx, const char *mail, char **argv)
{
context_P context;
if ((context = (context_P)sm_pmfi_get_ctx_se(pmse_ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_envfrom: smfi_getpriv");
return (SMFIS_ACCEPT);
}
if (mail != NULL)
msg(LOG_DEBUG, context, "cb_envfrom('%s')", mail);
return (SMFIS_CONTINUE);
}
static sfsistat_T
cb_rcpt(pmse_ctx_P pmse_ctx, const char *rcpt, char **argv)
{
context_P context;
if ((context = (context_P)sm_pmfi_get_ctx_se(pmse_ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_envrcpt: smfi_getpriv");
return (SMFIS_ACCEPT);
}
if (rcpt != NULL)
msg(LOG_DEBUG, context, "cb_envrcpt('%s')", rcpt);
return (SMFIS_CONTINUE);
}
#endif /* MILTER_SPAMD_DEBUG */
static int
writefd(context_P context, unsigned char *chunk, size_t len)
{
extern sm_ret_T sm_write_wait(int _fd, int _timeout);
if (!CTX_IS_FLAG(context, CTX_FL_FD_OPEN) ||
CTX_IS_FLAG(context, CTX_FL_WR_FAIL))
return -1;
if (chunk == NULL || len <= 0)
return 0;
while (len > 0)
{
sm_ret_T r;
ssize_t written;
size_t wr;
r = sm_write_wait(context->ctx_fd, timeout);
if (r != 0)
return r;
written = write(context->ctx_fd, chunk, len);
if (written == -1)
{
sm_io_fprintf(smioerr,
"sev=ERROR, where=msg, write=%d\n",
(int) written);
CTX_SET_FLAG(context, CTX_FL_WR_FAIL);
close(context->ctx_fd);
context->ctx_fd = -1;
CTX_CLR_FLAG(context, CTX_FL_FD_OPEN);
return -1;
}
wr = (size_t) written;
assert(len >= wr);
len -= wr;
chunk += wr;
}
return 0;
}
static void
fdprintf(context_P context, const char *fmt, ...)
{
va_list ap;
char s[2048];
va_start(ap, fmt);
vsnprintf(s, sizeof(s), fmt, ap);
va_end(ap);
(void) writefd(context, (unsigned char *) s, strlen(s));
}
static sm_ret_T
cb_msg(pmse_ctx_P pmse_ctx, unsigned char *chunk, size_t size)
{
context_P context;
if ((context = (context_P)sm_pmfi_get_ctx_se(pmse_ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_msg: smfi_getpriv");
return (SMFIS_ACCEPT);
}
if (CTX_IS_FLAG(context, CTX_FL_FD_FAIL))
return -1;
/* first call? */
if (!CTX_IS_FLAG(context, CTX_FL_FD_OPEN) &&
!CTX_IS_FLAG(context, CTX_FL_WR_FAIL) &&
context->ctx_fd < 0) {
char *sendmail_name;
char *sendmail_queue;
char *sendmail_date;
sm_pmfi_getmac(pmse_ctx, PMM_SRVHOSTNAME, &sendmail_name);
sm_pmfi_getmac(pmse_ctx, PMM_MAIL_TAID, &sendmail_queue);
context->ctx_ta_id = sendmail_queue;
/* FIX: $b */
sendmail_date = NULL;
if ((context->ctx_fd = get_spamd_fd(context)) < 0)
return (SMFIS_ACCEPT);
fdprintf(context, "SYMBOLS SPAMC/1.2\r\n");
if (spamd_user[0])
fdprintf(context, "User: %s\r\n", spamd_user);
fdprintf(context, "\r\n");
/* send fake Received: header */
fdprintf(context, "Received: from %s (%s [%s])",
context->ctx_helo, context->ctx_host, context->ctx_addr);
if (sendmail_name != NULL && sendmail_name[0]) {
fdprintf(context, "\r\n\tby %s (milter-spamd)",
sendmail_name);
if (sendmail_queue != NULL && sendmail_queue[0])
fdprintf(context, " id %s", sendmail_queue);
}
if (sendmail_date != NULL && sendmail_date[0])
fdprintf(context, "; %s", sendmail_date);
else {
char d[128];
time_t t = time(NULL);
if (strftime(d, sizeof(d), "%a, %e %b %Y %H:%M:%S %z",
localtime(&t)))
fdprintf(context, "; %s", d);
}
fdprintf(context, "\r\n");
}
#if 0
fdprintf(context, "%s: %s\r\n", name, value);
if (!strcasecmp(name, "From"))
strlcpy(context->hdr_from, value, sizeof(context->hdr_from));
else if (!strcasecmp(name, "To"))
strlcpy(context->hdr_to, value, sizeof(context->hdr_to));
else if (!strcasecmp(name, "Subject"))
strlcpy(context->hdr_subject, value,
sizeof(context->hdr_subject));
#endif
if (CTX_IS_FLAG(context, CTX_FL_FD_OPEN) &&
!CTX_IS_FLAG(context, CTX_FL_WR_FAIL) &&
context->ctx_fd >= 0)
writefd(context, chunk, size);
return (SMFIS_CONTINUE);
}
static void
spamd_reply(const char *line, context_P context, sfsistat_T *action)
{
const char *p;
switch (context->ctx_state) {
case 0:
if (strncmp(line, "SPAMD/", 6)) {
msg(LOG_ERR, context, "spamd_reply: first reply "
"not SPAMD version: %s", line);
*action = SMFIS_ACCEPT;
break;
}
p = line + 6;
while (*p && *p != ' ')
++p;
while (*p == ' ')
++p;
if (strncmp(p, "0 EX_OK", 7)) {
msg(LOG_ERR, context, "spamd_reply: first reply "
"not 0 EX_OK: %s", line);
*action = SMFIS_ACCEPT;
break;
}
context->ctx_state = 1;
break;
case 1:
if (!strncmp(line, "Spam: ", 6)) {
char decision[16];
double score, threshold;
if (sscanf(line + 6, "%15s ; %lf / %lf", decision,
&score, &threshold) != 3) {
msg(LOG_ERR, context, "spamd_reply: malformed "
"decision reply: %s", line);
*action = SMFIS_ACCEPT;
break;
}
context->ctx_spam = !strcmp(decision, "True");
context->ctx_score = score;
context->ctx_threshold = threshold;
context->ctx_state = 2;
}
break;
case 2:
if (!line[0])
context->ctx_state = 3;
break;
case 3:
strlcat(context->ctx_symbols, line,
sizeof(context->ctx_symbols));
break;
default:
msg(LOG_ERR, context,
"spamd_reply: invalid context->ctx_state");
*action = SMFIS_ACCEPT;
}
}
static sfsistat_T
cb_eom(pmse_ctx_P pmse_ctx)
{
context_P context;
sfsistat_T action = SMFIS_CONTINUE;
char buf[2048];
int pos = 0, retry = 0;
if ((context = (context_P)sm_pmfi_get_ctx_se(pmse_ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_eom: smfi_getpriv");
return (SMFIS_ACCEPT);
}
msg(LOG_DEBUG, context, "cb_eom()");
if (context->ctx_fd < 0)
goto done;
context->ctx_symbols[0] = 0;
/* no more writing data to spamd, want to read result now */
if (shutdown(context->ctx_fd, SHUT_WR)) {
msg(LOG_ERR, context, "cb_eom: shutdown: %s", strerror(errno));
goto done;
}
if (fcntl(context->ctx_fd, F_SETFL, fcntl(context->ctx_fd, F_GETFL) |
O_NONBLOCK)) {
msg(LOG_ERR, context, "cb_eom: fcntl: %s", strerror(errno));
goto done;
}
/* try at most 6 times (10 seconds timeout each) */
while (action == SMFIS_CONTINUE && retry < 6) {
fd_set fds;
struct timeval tv;
int r, i;
char b[8192];
FD_ZERO(&fds);
FD_SET(context->ctx_fd, &fds);
tv.tv_sec = 10;
tv.tv_usec = 0;
r = select(context->ctx_fd + 1, &fds, NULL, NULL, &tv);
if (r < 0) {
if (errno != EINTR) {
msg(LOG_ERR, context, "cb_eom: select: %s",
strerror(errno));
break;
}
continue;
} else if (r == 0 || !FD_ISSET(context->ctx_fd, &fds)) {
retry++;
msg(LOG_DEBUG, context, "cb_eom: waiting for "
"spamd reply (retry %d)", retry);
continue;
}
r = read(context->ctx_fd, b, sizeof(b));
if (r < 0) {
if (errno != EINTR) {
msg(LOG_ERR, context, "cb_eom: read: %s",
strerror(errno));
break;
}
continue;
} else if (r == 0)
/* connection closed by spamd */
break;
for (i = 0; i < r; ++i)
if (b[i] == '\n' || pos == sizeof(buf) - 1) {
if (pos > 0 && buf[pos - 1] == '\r')
buf[pos - 1] = 0;
else
buf[pos] = 0;
/* sets action when done */
spamd_reply(buf, context, &action);
pos = 0;
} else
buf[pos++] = b[i];
}
if (retry == 6)
msg(LOG_ERR, context, "cb_eom: spamd connection timed out");
done:
if (context->ctx_fd >= 0) {
close(context->ctx_fd);
context->ctx_fd = -1;
}
/* either way, we don't want to continue */
if (action == SMFIS_CONTINUE)
{
action = (context->ctx_spam && ADDHDR_ONLY != AddHdr)
? SMFIS_REJECT : SMFIS_ACCEPT;
}
#if 0
msg(action == SMFIS_REJECT ? LOG_NOTICE : LOG_INFO, context,
"%s (%s %.1f/%.1f%s%s), From: %s, To: %s, Subject: %s",
(action == SMFIS_REJECT ? "REJECT" : "ACCEPT"),
(context->ctx_spam ? "SPAM" : "ham"), context->ctx_score,
context->ctx_threshold,
(context->ctx_symbols[0] ? " " : ""), context->ctx_symbols,
context->hdr_from, context->hdr_to, context->hdr_subject);
#else
msg(action == SMFIS_REJECT ? LOG_NOTICE : LOG_INFO, context,
"%s (%s %.1f/%.1f%s%s), ta_id=%s",
(action == SMFIS_REJECT ? "REJECT" : "ACCEPT"),
(context->ctx_spam ? "SPAM" : "ham"),
context->ctx_score, context->ctx_threshold,
(context->ctx_symbols[0] ? " " : ""), context->ctx_symbols,
context->ctx_ta_id);
#endif
if (action == SMFIS_REJECT) {
char m[64];
snprintf(m, sizeof(m), "554 5.7.1 Spam (score %.1f)\r\n",
context->ctx_score);
if (sm_pmfi_setreply(pmse_ctx, m) != MI_SUCCESS)
msg(LOG_ERR, context, "sm_pmfi_setreply");
}
if (ADDHDR_ALWAYS == AddHdr ||
ADDHDR_ONLY == AddHdr ||
(ADDHDR_GT0 == AddHdr && context->ctx_score > 0)) {
char m[64];
snprintf(m, sizeof(m), "X-Spam: score %.1f\r\n",
context->ctx_score);
if (sm_pmfi_hdr_mod(pmse_ctx, SM_HDRMOD_T_APPEND, 0,
(const uchar *) m) != MI_SUCCESS)
msg(LOG_ERR, context, "sm_pmfi_hdrmod");
}
#if 0
context->pos =
context->hdr_from[0] = context->hdr_to[0] =
context->hdr_subject[0] =
#endif
context->ctx_state = context->ctx_spam =
context->ctx_flags =
context->ctx_symbols[0] = 0;
context->ctx_score = context->ctx_threshold = 0.0;
return (action);
}
static sfsistat_T
cb_abort(pmse_ctx_P pmse_ctx)
{
context_P context;
if ((context = (context_P)sm_pmfi_get_ctx_se(pmse_ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_abort: smfi_getpriv");
return (SMFIS_ACCEPT);
}
msg(LOG_DEBUG, context, "cb_abort()");
if (context->ctx_fd >= 0) {
close(context->ctx_fd);
context->ctx_fd = -1;
}
#if 0
context->pos =
context->hdr_from[0] = context->hdr_to[0] =
context->hdr_subject[0] =
#endif
context->ctx_state = context->ctx_spam =
context->ctx_flags =
context->ctx_symbols[0] = 0;
context->ctx_score = context->ctx_threshold = 0.0;
return (SMFIS_CONTINUE);
}
static sfsistat_T
cb_close(pmse_ctx_P pmse_ctx)
{
context_P context;
context = (context_P)sm_pmfi_get_ctx_se(pmse_ctx);
msg(LOG_DEBUG, context, "cb_close()");
if (context != NULL) {
sm_pmfi_set_ctx_se(pmse_ctx, NULL);
if (context->ctx_fd >= 0) {
close(context->ctx_fd);
context->ctx_fd = -1;
}
free(context);
}
return (SMFIS_CONTINUE);
}
static pmilter_T
pmilter =
{
"milter-spamd", /* filter name */
LPMILTER_VERSION,
SM_SCAP_PM_CNNCT|SM_SCAP_PM_EHLO|SM_SCAP_PM_DATA|SM_SCAP_PM_MSG,
0,
0,
0,
cb_negotiate,
cb_connect,
cb_helo,
#if MILTER_SPAMD_DEBUG
cb_mail,
cb_rcpt,
#else
NULL,
NULL,
#endif
NULL,
cb_msg,
cb_eom,
cb_abort,
cb_close,
NULL, /* cb_unknown, */
NULL /* cb_signal */
};
static void
usage(const char *argv0)
{
fprintf(stderr, "usage: %s [-d] [-i RE] [-U spamd_user] "
"[-p spamd_port] [-p pipe]\n", argv0);
exit(1);
}
int
main(int argc, char **argv)
{
int ch;
const char *oconn = OCONN;
sfsistat_T ret = SM_PMI_FAILURE;
const char *ofile = NULL;
pmg_ctx_P pmg_ctx;
uint32_t major, minor, patchlevel;
char *prog;
prog = argv[0];
if (getuid() == 0 || geteuid() == 0)
{
fprintf(stderr,
"%s: ERROR: do not run this as super-user!\n",
prog);
exit(EX_USAGE);
}
tzset();
openlog("milter-spamd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
while ((ch = getopt(argc, argv, "di:P:p:t:U:")) != -1) {
switch (ch) {
case 'd':
debug = 1;
break;
case 'i': {
int r;
ignore_connect = optarg;
r = regcomp(&re_ignore_connect, ignore_connect,
REG_EXTENDED | REG_ICASE);
if (r) {
char e[8192];
regerror(r, &re_ignore_connect, e, sizeof(e));
fprintf(stderr, "regcomp: %s: %s\n",
ignore_connect, e);
usage(prog);
}
break;
}
case 'P':
spamd_port = (short) strtol(optarg, NULL, 0);
break;
case 'p':
oconn = optarg;
break;
case 't':
timeout = strtol(optarg, NULL, 0);
break;
case 'U':
spamd_user = optarg;
break;
default:
usage(prog);
}
}
if (argc != optind) {
fprintf(stderr, "unknown command line argument: %s ...",
argv[optind]);
usage(prog);
}
if (!strncmp(oconn, "unix:", 5))
ofile = oconn + 5;
else if (!strncmp(oconn, "local:", 6))
ofile = oconn + 6;
else
ofile = oconn;
if (ofile != NULL)
(void) unlink(ofile);
pmg_ctx = NULL;
ret = sm_pmfi_init(&pmg_ctx);
if (sm_is_err(ret))
{
fprintf(stderr, "sev=ERROR, sm_pmfi_init=%#x\n", ret);
goto done;
}
ret = sm_pmfi_version(pmg_ctx, &major, &minor, &patchlevel);
if (sm_is_err(ret))
{
fprintf(stderr, "sev=ERROR, sm_pmfi_version=%#x\n", ret);
goto done;
}
if (major != LPMILTER_VERSION_MAJOR)
{
fprintf(stderr,
"sev=ERROR, status=version_mismatch, compile_time=%d, run_time=%d\n"
, LPMILTER_VERSION_MAJOR, major);
goto done;
}
ret = sm_pmfi_setconn(pmg_ctx, (char *)ofile);
if (sm_is_err(ret))
{
fprintf(stderr, "sev=ERROR, sm_pmfi_setconn=%#x\n", ret);
goto done;
}
umask(0177);
signal(SIGPIPE, SIG_IGN);
msg(LOG_INFO, NULL, "started: %s", rcsid);
ret = sm_pmfi_start(pmg_ctx, &pmilter);
if (sm_is_err(ret))
{
fprintf(stderr, "sev=ERROR, sm_pmfi_start=%#x\n", ret);
goto done;
}
if (ret != MI_SUCCESS)
msg(LOG_ERR, NULL, "smfi_main: terminating due to error");
else
msg(LOG_INFO, NULL, "smfi_main: terminating without error");
done:
return ret;
}
#else /* MTA_USE_PMILTER */
int
main(int argc, char *argv[])
{
return 0;
}
#endif /* MTA_USE_PMILTER */
syntax highlighted by Code2HTML, v. 0.9.1