Index: ossp-pkg/l2/Makefile.in RCS File: /v/ossp/cvs/ossp-pkg/l2/Makefile.in,v rcsdiff -q -kk '-r1.18' '-r1.19' -u '/v/ossp/cvs/ossp-pkg/l2/Makefile.in,v' 2>/dev/null --- Makefile.in 2001/09/09 15:42:25 1.18 +++ Makefile.in 2001/09/09 15:57:10 1.19 @@ -70,6 +70,7 @@ l2_ch_pipe.lo \ l2_ch_syslog.lo \ l2_ch_socket.lo \ + l2_ch_smtp.lo \ l2_ch_null.lo \ l2_ch_filter.lo \ l2_ch_prefix.lo \ Index: ossp-pkg/l2/l2.h RCS File: /v/ossp/cvs/ossp-pkg/l2/Attic/l2.h,v rcsdiff -q -kk '-r1.20' '-r1.21' -u '/v/ossp/cvs/ossp-pkg/l2/Attic/l2.h,v' 2>/dev/null --- l2.h 2001/09/06 14:37:53 1.20 +++ l2.h 2001/09/09 15:57:10 1.21 @@ -85,6 +85,7 @@ L2_ERR_USE, /* invalid usage */ L2_ERR_MEM, /* no more memory available */ L2_ERR_SYS, /* system error (see errno) */ + L2_ERR_IO, /* input/output error */ L2_ERR_FMT, /* message formating error */ L2_ERR_INT /* internal error */ } l2_result_t; @@ -163,6 +164,7 @@ extern l2_handler_t l2_handler_pipe; extern l2_handler_t l2_handler_socket; extern l2_handler_t l2_handler_syslog; +extern l2_handler_t l2_handler_smtp; /* list of shipped (filter) channel handlers */ extern l2_handler_t l2_handler_filter; Index: ossp-pkg/l2/l2_ch_smtp.c RCS File: /v/ossp/cvs/ossp-pkg/l2/l2_ch_smtp.c,v co -q -kk -p'1.1' '/v/ossp/cvs/ossp-pkg/l2/l2_ch_smtp.c,v' | diff -u /dev/null - -L'ossp-pkg/l2/l2_ch_smtp.c' 2>/dev/null --- ossp-pkg/l2/l2_ch_smtp.c +++ - 2025-05-20 17:19:52.403153575 +0200 @@ -0,0 +1,287 @@ +/* +** L2 - OSSP Logging Library +** Copyright (c) 2001 The OSSP Project (http://www.ossp.org/) +** Copyright (c) 2001 Cable & Wireless Deutschland (http://www.cw.com/de/) +** +** This file is part of OSSP L2, a flexible logging library which +** can be found at http://www.ossp.org/pkg/l2/. +** +** Permission to use, copy, modify, and distribute this software for +** any purpose with or without fee is hereby granted, provided that +** the above copyright notice and this permission notice appear in all +** copies. +** +** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 AUTHORS AND COPYRIGHT HOLDERS AND THEIR +** 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. +** +** l2_ch_smtp.c: SMTP channel implementation +*/ + +#include "l2.h" +#include "l2_p.h" /* for l2_version */ + +#include +#include +#include +#include + +/* declare private channel configuration */ +typedef struct { + char *cpFrom; + char *cpRcpt; + char *cpSubject; + char *cpHost; + char *cpPort; + char *cpLocalProg; + char *cpLocalUser; + char *cpLocalHost; + long nTimeout; + sa_addr_t *saaServer; + sa_t *saServer; +} l2_ch_smtp_t; + +/* create channel */ +static l2_result_t hook_create(l2_context_t *ctx, l2_channel_t *ch) +{ + l2_ch_smtp_t *cfg; + struct utsname uts; + struct passwd *pw; + + /* allocate private channel configuration */ + if ((cfg = (l2_ch_smtp_t *)malloc(sizeof(l2_ch_smtp_t))) == NULL) + return L2_ERR_ARG; + + /* initialize configuration with reasonable defaults */ + cfg->cpLocalProg = NULL; + if ((pw = getpwuid(getuid())) != NULL) + cfg->cpLocalUser = strdup(pw->pw_name); + else + cfg->cpLocalUser = l2_util_asprintf("uid#%d", getuid()); + if (uname(&uts) == 0) + cfg->cpLocalHost = strdup(uts.nodename); + else + cfg->cpLocalHost = strdup("localhost"); + cfg->cpFrom = l2_util_asprintf("%s@%s", cfg->cpLocalUser, cfg->cpLocalHost); + cfg->cpRcpt = NULL; + cfg->cpSubject = l2_util_asprintf("[L2] log channel output on %s", cfg->cpLocalHost); + cfg->cpHost = NULL; + cfg->cpPort = strdup("smtp"); + cfg->nTimeout = 30; + cfg->saaServer = NULL; + cfg->saServer = NULL; + + /* link private channel configuration into channel context */ + ctx->vp = cfg; + + return L2_OK; +} + +/* configure channel */ +static l2_result_t hook_configure(l2_context_t *ctx, l2_channel_t *ch, const char *fmt, va_list ap) +{ + l2_ch_smtp_t *cfg = (l2_ch_smtp_t *)ctx->vp; + l2_param_t pa[10]; + l2_result_t rv; + + /* feed and call generic parameter parsing engine */ + L2_PARAM_SET(pa[0], progname, STRING, &cfg->cpLocalProg); + L2_PARAM_SET(pa[1], localhost, STRING, &cfg->cpLocalHost); + L2_PARAM_SET(pa[2], localuser, STRING, &cfg->cpLocalUser); + L2_PARAM_SET(pa[3], from, STRING, &cfg->cpFrom); + L2_PARAM_SET(pa[4], rcpt, STRING, &cfg->cpRcpt); + L2_PARAM_SET(pa[5], subject, STRING, &cfg->cpSubject); + L2_PARAM_SET(pa[6], host, STRING, &cfg->cpHost); + L2_PARAM_SET(pa[7], port, STRING, &cfg->cpPort); + L2_PARAM_SET(pa[8], timeout, INT, &cfg->nTimeout); + L2_PARAM_END(pa[9]); + rv = l2_util_setparams(pa, fmt, ap); + + return rv; +} + +/* open channel */ +static l2_result_t hook_open(l2_context_t *ctx, l2_channel_t *ch) +{ + l2_ch_smtp_t *cfg = (l2_ch_smtp_t *)ctx->vp; + sa_rc_t rc; + + /* make sure a path was set */ + if (cfg->cpHost == NULL || cfg->cpRcpt == NULL) + return L2_ERR_USE; + + /* create socket address */ + if ((rc = sa_u2a(&cfg->saaServer, "tcp://%s:%s", cfg->cpHost, cfg->cpPort)) != SA_OK) + return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT); + + /* create socket */ + if ((rc = sa_create(&cfg->saServer)) != SA_OK) + return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT); + + /* configure socket parameters */ + sa_timeout(cfg->saServer, cfg->nTimeout, 0); + sa_buffers(cfg->saServer, 1024, 1024); + + return L2_OK; +} + +/* write to channel */ +static l2_result_t hook_write(l2_context_t *ctx, l2_channel_t *ch, + const char *buf, size_t buf_size) +{ + l2_ch_smtp_t *cfg = (l2_ch_smtp_t *)ctx->vp; + char caLine[1024]; + int bSent; + sa_t *sa; + sa_addr_t *saa; + sa_rc_t rc; + size_t n; + + /* establish connection to server */ + saa = cfg->saaServer; + sa = cfg->saServer; + if ((rc = sa_connect(sa, saa)) != SA_OK) + return (rc == SA_ERR_SYS ? L2_ERR_SYS : L2_ERR_INT); + + /* + * Sample SMTP transaction for reference: + * + * | 220 en1.engelschall.com ESMTP SMTP Sendmail 8.11.0+ ready + * | HELO l2 + * | 250 en1.engelschall.com Hello en1.engelschall.com [141.1.129.1], pleased to meet you + * | MAIL From: + * | 250 2.1.0 ... Sender ok + * | RCPT To: + * | 250 2.1.5 ... Recipient ok + * | DATA + * | 354 Enter mail, end with "." on a line by itself + * | From: l2@localhost + * | To: rse@engelschall.com + * | Subject: [L2] SMTP channel output + * | + * | test log entry + * | . + * | 250 2.0.0 f88Aev847031 Message accepted for delivery + * | QUIT + * | 221 2.0.0 en1.engelschall.com closing connection + * + * For more details about SMTP, see RFC 2821: + * Simple Mail Transfer Protocol; J. Klensin; April 2001. + */ + + bSent = FALSE; + if (sa_readline(sa, caLine, sizeof(caLine), &n) == SA_OK + && n > 3 && atoi(caLine) == 220) { + sa_printf(sa, "HELO %s\r\n", cfg->cpLocalHost); + if (sa_readline(sa, caLine, sizeof(caLine), &n) == SA_OK + && n > 3 && atoi(caLine) == 250) { + sa_printf(sa, "MAIL FROM:<%s>\r\n", cfg->cpFrom); + if (sa_readline(sa, caLine, sizeof(caLine), &n) == SA_OK + && n > 3 && atoi(caLine) == 250) { + sa_printf(sa, "RCPT TO:<%s>\r\n", cfg->cpRcpt); + if (sa_readline(sa, caLine, sizeof(caLine), &n) == SA_OK + && n > 3 && atoi(caLine) == 250) { + sa_printf(sa, "DATA\r\n"); + if (sa_readline(sa, caLine, sizeof(caLine), &n) == SA_OK + && n > 3 && atoi(caLine) == 354) { + sa_printf(sa, "From: %s\r\n", cfg->cpFrom); + sa_printf(sa, "To: %s\r\n", cfg->cpRcpt); + sa_printf(sa, "Subject: %s\r\n", cfg->cpSubject); + if (cfg->cpLocalProg != NULL) + sa_printf(sa, "User-Agent: %s, %s\r\n", + cfg->cpLocalProg, l2_version.v_gnu); + else + sa_printf(sa, "User-Agent: %s\r\n", l2_version.v_gnu); + sa_printf(sa, "\r\n"); + if (cfg->cpLocalProg != NULL) + sa_printf(sa, "Program %s of user %s on host %s logged:\r\n", + cfg->cpLocalProg, cfg->cpLocalUser, cfg->cpLocalHost); + else + sa_printf(sa, "A program of user %s on host %s logged:\r\n", + cfg->cpLocalUser, cfg->cpLocalHost); + /* FIXME: dot-escaping and remove doubled newline */ + sa_write(sa, buf, buf_size, &n); + sa_printf(sa, "\r\n"); + sa_printf(sa, ".\r\n"); + if (sa_readline(sa, caLine, sizeof(caLine), &n) == SA_OK + && n > 3 && atoi(caLine) == 250) { + sa_printf(sa, "QUIT\r\n"); + sa_readline(sa, caLine, sizeof(caLine), &n); + bSent = TRUE; + } + } + } + } + } + } + + /* shutdown connection to server */ + sa_shutdown(sa, "rw"); + + return (bSent ? L2_OK : L2_ERR_IO); +} + +/* close channel */ +static l2_result_t hook_close(l2_context_t *ctx, l2_channel_t *ch) +{ + l2_ch_smtp_t *cfg = (l2_ch_smtp_t *)ctx->vp; + + /* destroy remote address */ + if (cfg->saServer != NULL) { + sa_destroy(cfg->saServer); + cfg->saServer = NULL; + } + if (cfg->saaServer != NULL) { + free(cfg->saaServer); + cfg->saaServer = NULL; + } + + return L2_OK; +} + +/* destroy channel */ +static l2_result_t hook_destroy(l2_context_t *ctx, l2_channel_t *ch) +{ + l2_ch_smtp_t *cfg = (l2_ch_smtp_t *)ctx->vp; + + /* destroy channel configuration */ + if (cfg->cpFrom != NULL) + free(cfg->cpFrom); + if (cfg->cpRcpt != NULL) + free(cfg->cpRcpt); + if (cfg->cpSubject != NULL) + free(cfg->cpSubject); + if (cfg->cpHost != NULL) + free(cfg->cpHost); + if (cfg->cpPort != NULL) + free(cfg->cpPort); + if (cfg->cpLocalHost != NULL) + free(cfg->cpLocalHost); + if (cfg->cpLocalUser != NULL) + free(cfg->cpLocalUser); + free(cfg); + + return L2_OK; +} + +/* exported channel handler structure */ +l2_handler_t l2_handler_smtp = { + L2_CHANNEL_OUTPUT, + hook_create, + hook_configure, + hook_open, + hook_write, + NULL, + hook_close, + hook_destroy +}; +