/*
* Copyright (c) 2005, 2006 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: example-pmilter-0.c,v 1.12 2006/10/05 04:27:37 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/types.h"
#include "sm/sysexits.h"
#include "sm/fcntl.h"
#include "sm/io.h"
#include "sm/ctype.h"
#include "sm/smreplycodes.h"
#include "sm/pmilter.h"
#include "sm/pmfdef.h"
#include "sm/pmfapi.h"
#if MTA_USE_PMILTER
/* global context */
typedef struct pmexg_ctx_S pmexg_ctx_T, *pmexg_ctx_P;
struct pmexg_ctx_S
{
uint32_t pmexg_cap;
uint32_t pmexg_flags;
};
/* context per SMTP server */
typedef struct pmexss_ctx_S pmexss_ctx_T, *pmexss_ctx_P;
struct pmexss_ctx_S
{
pmexg_ctx_P pmexss_pmexg_ctx;
uint32_t pmexss_flags;
};
/* context per SMTP session */
typedef struct pmexse_ctx_S pmexse_ctx_T, *pmexse_ctx_P;
struct pmexse_ctx_S
{
pmexss_ctx_P pmexse_pmexss_ctx;
uint32_t pmexse_flags;
int pmexse_fd;
char pmexse_fname[PATH_MAX];
};
#define PMSE_FL_NONE 0x0000
#define PMG_FL_W2F 0x0001 /* write to file */
#define PMSE_FL_W2F PMG_FL_W2F
#define PMSE_FL_FD_OPEN 0x0002 /* fd is ok to use */
#define PMSE_FL_FD_FAIL 0x0004 /* don't try to open file */
#define PMSE_FL_WR_FAIL 0x0008 /* write failed before */
#define PMSE_SET_FLAG(pmexse_ctx, fl) (pmexse_ctx)->pmexse_flags |= (fl)
#define PMSE_CLR_FLAG(pmexse_ctx, fl) (pmexse_ctx)->pmexse_flags &= ~(fl)
#define PMSE_IS_FLAG(pmexse_ctx, fl) (((pmexse_ctx)->pmexse_flags & (fl)) != 0)
static sm_ret_T
expm1_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;
pmexss_ctx_P pmexss_ctx;
pmexg_ctx_P pmexg_ctx;
pmexg_ctx = sm_pmfi_get_ctx_g_ss(pmss_ctx);
pmexss_ctx = (pmexss_ctx_P) malloc(sizeof(*pmexss_ctx));
if (pmexss_ctx == NULL)
return SMTP_R_TEMP;
sm_pmfi_set_ctx_ss(pmss_ctx, pmexss_ctx);
pmexss_ctx->pmexss_pmexg_ctx = pmexg_ctx;
pmexss_ctx->pmexss_flags = pmexg_ctx->pmexg_flags;
*pm_cap = pmexg_ctx->pmexg_cap;
*pm_fct = 0;
*pm_feat = 0;
*pm_misc = 0;
ret = sm_pmfi_setmaclist(pmss_ctx, PM_SMST_CONNECT,
PMM_SRVHOSTNAME, PMM_SEID, PMM_END);
if (sm_is_err(ret))
sm_io_fprintf(smioerr,
"sev=ERROR, where=expm1_negotiate, sm_pmfi_setmaclist=%#x, macro=hostname\n",
ret);
ret = sm_pmfi_setmaclist(pmss_ctx, PM_SMST_DOT,
PMM_DOT_MSGID, PMM_END);
if (sm_is_err(ret))
sm_io_fprintf(smioerr,
"sev=ERROR, where=expm1_negotiate, sm_pmfi_setmaclist=%#x, macro=msg_id\n",
ret);
sm_io_fprintf(smioerr, "where=expm1_negotiate, cap=%#X\n",
pmexg_ctx->pmexg_cap);
return SM_SUCCESS;
}
static sfsistat_T
expm1_connect(pmse_ctx_P pmse_ctx, const char *hostname, sm_sockaddr_T *hostaddr)
{
sm_ret_T ret;
pmexse_ctx_P pmexse_ctx;
pmexss_ctx_P pmexss_ctx;
char *srvhostname;
/* create session context */
pmexse_ctx = (pmexse_ctx_P) malloc(sizeof(*pmexse_ctx));
if (pmexse_ctx == NULL)
return SMTP_R_TEMP;
pmexss_ctx = sm_pmfi_get_ctx_ss_se(pmse_ctx);
pmexse_ctx->pmexse_pmexss_ctx = pmexss_ctx;
pmexse_ctx->pmexse_flags = pmexss_ctx->pmexss_flags;
sm_pmfi_set_ctx_se(pmse_ctx, pmexse_ctx);
sm_io_fprintf(smioerr, "client=%s\n", hostname);
ret = sm_pmfi_getmac(pmse_ctx, PMM_SRVHOSTNAME, &srvhostname);
sm_io_fprintf(smioerr, "srvhostname=%s, ret=%#X\n",
srvhostname, ret);
return SMTP_R_CONT;
}
static sm_ret_T
expm1_close(pmse_ctx_P pmse_ctx)
{
pmexse_ctx_P pmexse_ctx;
sm_io_fprintf(smioerr, "where=close\n");
pmexse_ctx = (pmexse_ctx_P) sm_pmfi_get_ctx_se(pmse_ctx);
if (pmexse_ctx != NULL)
sm_free(pmexse_ctx);
sm_pmfi_set_ctx_se(pmse_ctx, NULL);
return SM_SUCCESS;
}
static sfsistat_T
expm1_helo(pmse_ctx_P pmse_ctx, const char *helohost, bool ehlo)
{
sm_io_fprintf(smioerr, "helo=%s\n", helohost);
return SMTP_R_CONT;
}
static sfsistat_T
expm1_mail(pmse_ctx_P pmse_ctx, const char *mail, char **argv)
{
sm_io_fprintf(smioerr, "mail=%s, argv[0]=%s\n", mail,
(argv != NULL && argv[0] != NULL) ? argv[0] : "NULL");
return SMTP_R_CONT;
}
static sfsistat_T
expm1_rcpt(pmse_ctx_P pmse_ctx, const char *rcpt, char **argv)
{
sm_io_fprintf(smioerr, "rcpt=%s, argv[0]=%s\n", rcpt,
(argv != NULL && argv[0] != NULL) ? argv[0] : "NULL");
return SMTP_R_CONT;
}
static sfsistat_T
expm1_data(pmse_ctx_P pmse_ctx)
{
sm_io_fprintf(smioerr, "where=data\n");
return SMTP_R_CONT;
}
static void
pm_open(pmse_ctx_P pmse_ctx, pmexse_ctx_P pmexse_ctx)
{
if (PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_W2F) &&
!PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_FD_OPEN) &&
!PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_FD_FAIL))
{
(void) strlcpy(pmexse_ctx->pmexse_fname, "temp.XXXXXX",
sizeof(pmexse_ctx->pmexse_fname));
pmexse_ctx->pmexse_fd = mkstemp(pmexse_ctx->pmexse_fname);
if (pmexse_ctx->pmexse_fd >= 0)
PMSE_SET_FLAG(pmexse_ctx, PMSE_FL_FD_OPEN);
else
PMSE_SET_FLAG(pmexse_ctx, PMSE_FL_FD_FAIL);
sm_io_fprintf(smioerr, "where=open, mkstemp=%d\n",
pmexse_ctx->pmexse_fd);
}
}
static sfsistat_T
expm1_unknown(pmse_ctx_P pmse_ctx, const char *cmd)
{
sm_io_fprintf(smioerr, "where=unknown, cmd=%s\n", cmd);
return SMTP_R_CONT;
}
static sm_ret_T
expm1_abort(pmse_ctx_P pmse_ctx)
{
sm_io_fprintf(smioerr, "where=abort_ta\n");
return SM_SUCCESS;
}
static sfsistat_T
expm1_msg(pmse_ctx_P pmse_ctx, unsigned char *buf, size_t len)
{
pmexse_ctx_P pmexse_ctx;
pmexse_ctx = (pmexse_ctx_P) sm_pmfi_get_ctx_se(pmse_ctx);
pm_open(pmse_ctx, pmexse_ctx);
if (PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_W2F) &&
PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_FD_OPEN) &&
!PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_WR_FAIL) &&
buf != NULL && len > 0
)
{
ssize_t written;
written = write(pmexse_ctx->pmexse_fd, buf, len);
if (written == -1)
{
sm_io_fprintf(smioerr,
"sev=ERROR, where=msg, write=%d\n",
(int) written);
PMSE_SET_FLAG(pmexse_ctx, PMSE_FL_WR_FAIL);
close(pmexse_ctx->pmexse_fd);
pmexse_ctx->pmexse_fd = INVALID_FD;
PMSE_CLR_FLAG(pmexse_ctx, PMSE_FL_FD_OPEN);
}
}
sm_io_fprintf(smioerr, "where=msg, len=%lu\n", (unsigned long) len);
return SM_SUCCESS;
}
static sfsistat_T
expm1_eom(pmse_ctx_P pmse_ctx)
{
sm_ret_T ret;
bool writefailed;
pmexse_ctx_P pmexse_ctx;
char *msgid;
pmexse_ctx = (pmexse_ctx_P) sm_pmfi_get_ctx_se(pmse_ctx);
writefailed = PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_W2F) &&
PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_WR_FAIL);
if (PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_W2F) &&
PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_FD_OPEN) &&
!PMSE_IS_FLAG(pmexse_ctx, PMSE_FL_WR_FAIL))
{
int r;
r = close(pmexse_ctx->pmexse_fd);
sm_io_fprintf(smioerr, "where=dot, write=%d\n", r);
pmexse_ctx->pmexse_fd = INVALID_FD;
PMSE_CLR_FLAG(pmexse_ctx, PMSE_FL_FD_OPEN);
}
ret = sm_pmfi_getmac(pmse_ctx, PMM_DOT_MSGID, &msgid);
sm_io_fprintf(smioerr, "msgid=%s, ret=%#X\n", msgid, ret);
sm_io_fprintf(smioerr, "where=dot\n");
return SMTP_R_CONT;
}
static sm_ret_T
expm1_signal(pmg_ctx_P pmg_ctx, int sig)
{
sm_io_fprintf(smioerr, "signal=%d\n", sig);
return SM_SUCCESS;
}
/*
** USAGE -- Print usage message to smioerr
**
** Parameters:
** prg -- program name
**
** Returns:
** exits
*/
static void
usage(const char *prg)
{
sm_io_fprintf(smioerr, "usage: %s [options]\n"
"-f name write msg to file\n"
, prg
);
exit(EX_USAGE);
}
static pmilter_T
pmilter =
{
"example-pmilter-0",
LPMILTER_VERSION,
SM_SCAP_PM_ALL,
0,
0,
0,
expm1_negotiate,
expm1_connect,
expm1_helo,
expm1_mail,
expm1_rcpt,
expm1_data,
expm1_msg,
expm1_eom,
expm1_abort,
expm1_close,
expm1_unknown,
expm1_signal
};
/*
** MAIN -- PMILTER test server
**
** Parameters:
** argc -- number of arguments
** argv -- vector of arguments
**
** Returns:
** exit code
*/
int
main(int argc, char *argv[])
{
sm_ret_T ret;
int c;
uint32_t major, minor, patchlevel;
pmg_ctx_P pmg_ctx;
char *prg;
pmexg_ctx_T pmexg_ctx;
prg = argv[0];
pmg_ctx = NULL;
if (getuid() == 0 || geteuid() == 0)
{
sm_io_fprintf(smioerr,
"%s: ERROR: do not run this as super-user!\n",
prg);
exit(EX_USAGE);
}
/* initialize test context */
sm_memzero(&pmexg_ctx, sizeof(pmexg_ctx));
pmexg_ctx.pmexg_cap = SM_SCAP_PM_ALL;
while ((c = getopt(argc, argv, "fh")) != -1)
{
switch (c)
{
case 'f':
pmexg_ctx.pmexg_flags |= PMG_FL_W2F;
break;
default:
usage(prg);
break;
}
}
argc -= optind;
argv += optind;
ret = sm_pmfi_init(&pmg_ctx);
if (sm_is_err(ret))
{
sm_io_fprintf(smioerr, "sev=ERROR, sm_pmfi_init=%#x\n", ret);
goto error;
}
ret = sm_pmfi_version(pmg_ctx, &major, &minor, &patchlevel);
if (sm_is_err(ret))
{
sm_io_fprintf(smioerr, "sev=ERROR, sm_pmfi_version=%#x\n", ret);
goto error;
}
if (major != LPMILTER_VERSION_MAJOR)
{
sm_io_fprintf(smioerr,
"sev=ERROR, status=version_mismatch, compile_time=%d, run_time=%d\n"
, LPMILTER_VERSION_MAJOR, major);
goto error;
}
ret = sm_pmfi_setconn(pmg_ctx, "pmilter.sock");
if (sm_is_err(ret))
{
sm_io_fprintf(smioerr, "sev=ERROR, sm_pmfi_setconn=%#x\n", ret);
goto error;
}
ret = sm_pmfi_set_ctx_g(pmg_ctx, &pmexg_ctx);
ret = sm_pmfi_start(pmg_ctx, &pmilter);
if (sm_is_err(ret))
{
sm_io_fprintf(smioerr, "sev=ERROR, sm_pmfi_start=%#x\n", ret);
goto error;
}
return 0;
error:
/* select an appropriate error here... */
return sm_error_value(ret);
}
#else /* MTA_USE_PMILTER */
int
main(int argc, char *argv[])
{
sm_io_fprintf(smioerr, "sev=ERROR, MTA_USE_PMILTER=not_set\n");
return -1;
}
#endif /* MTA_USE_PMILTER */
syntax highlighted by Code2HTML, v. 0.9.1