847 lines
28 KiB
C

/* ommail.c
*
* This is an implementation of a mail sending output module. So far, we
* only support direct SMTP, that is talking to a SMTP server. In the long
* term, support for using sendmail should also be implemented. Please note
* that the SMTP protocol implementation is a very bare one. We support
* RFC821/822 messages, without any authentication and any other nice
* features (no MIME, no nothing). It is assumed that proper firewalling
* and/or STMP server configuration is used together with this module.
*
* NOTE: read comments in module-template.h to understand how this file
* works!
*
* File begun on 2008-04-04 by RGerhards
*
* Copyright 2008-2014 Adiscon GmbH.
*
* This file is part of rsyslog.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* -or-
* see COPYING.ASL20 in the source distribution
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "config.h"
#include "rsyslog.h"
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <time.h>
#include <sys/socket.h>
#include "conf.h"
#include "syslogd-types.h"
#include "srUtils.h"
#include "cfsysline.h"
#include "module-template.h"
#include "errmsg.h"
#include "datetime.h"
#include "glbl.h"
#include "parserif.h"
MODULE_TYPE_OUTPUT;
MODULE_TYPE_NOKEEP;
MODULE_CNFNAME("ommail")
/* internal structures
*/
DEF_OMOD_STATIC_DATA;
DEFobjCurrIf(glbl) DEFobjCurrIf(datetime)
/* we add a little support for multiple recipients. We do this via a
* singly-linked list, enqueued from the top. -- rgerhards, 2008-08-04
*/
typedef struct toRcpt_s toRcpt_t;
struct toRcpt_s {
uchar *pszTo;
toRcpt_t *pNext;
};
typedef struct _instanceData {
uchar *tplName; /* format template to use */
uchar *constSubject; /* if non-NULL, constant string to be used as subject */
int8_t iMode; /* 0 - smtp, 1 - sendmail */
sbool bHaveSubject; /* is a subject configured? (if so, it is the second string provided by rsyslog core) */
sbool bEnableBody; /* is a body configured? (if so, it is the second string provided by rsyslog core) */
union {
struct {
uchar *pszSrv;
uchar *pszSrvPort;
uchar *pszFrom;
toRcpt_t *lstRcpt;
} smtp;
} md; /* mode-specific data */
} instanceData;
typedef struct wrkrInstanceData {
instanceData *pData;
union {
struct {
char RcvBuf[1024]; /* buffer for receiving server responses */
size_t lenRcvBuf;
size_t iRcvBuf; /* current index into the rcvBuf (buf empty if iRcvBuf == lenRcvBuf) */
int sock; /* socket to this server (most important when we do multiple msgs per mail) */
} smtp;
} md; /* mode-specific data */
} wrkrInstanceData_t;
typedef struct configSettings_s {
toRcpt_t *lstRcpt;
uchar *pszSrv;
uchar *pszSrvPort;
uchar *pszFrom;
uchar *pszSubject;
int bEnableBody; /* should a mail body be generated? (set to 0 eg for SMS gateways) */
} configSettings_t;
static configSettings_t cs;
/* tables for interfacing with the v6 config system */
/* action (instance) parameters */
static struct cnfparamdescr actpdescr[] = {{"server", eCmdHdlrGetWord, CNFPARAM_REQUIRED},
{"port", eCmdHdlrGetWord, CNFPARAM_REQUIRED},
{"mailfrom", eCmdHdlrGetWord, CNFPARAM_REQUIRED},
{"mailto", eCmdHdlrArray, CNFPARAM_REQUIRED},
{"subject.template", eCmdHdlrGetWord, 0},
{"subject.text", eCmdHdlrString, 0},
{"body.enable", eCmdHdlrBinary, 0},
{"template", eCmdHdlrGetWord, 0}};
static struct cnfparamblk actpblk = {CNFPARAMBLK_VERSION, sizeof(actpdescr) / sizeof(struct cnfparamdescr), actpdescr};
BEGINinitConfVars /* (re)set config variables to default values */
CODESTARTinitConfVars;
cs.lstRcpt = NULL;
cs.pszSrv = NULL;
cs.pszSrvPort = NULL;
cs.pszFrom = NULL;
cs.pszSubject = NULL;
cs.bEnableBody = 1; /* should a mail body be generated? (set to 0 eg for SMS gateways) */
ENDinitConfVars
/* forward definitions (as few as possible) */
static rsRetVal Send(int sock, const char *msg, size_t len);
static rsRetVal readResponse(wrkrInstanceData_t *pWrkrData, int *piState, int iExpected);
/* helpers for handling the recipient lists */
/* destroy a complete recipient list */
static void lstRcptDestruct(toRcpt_t *pRoot) {
toRcpt_t *pDel;
while (pRoot != NULL) {
pDel = pRoot;
pRoot = pRoot->pNext;
/* ready to disalloc */
free(pDel->pszTo);
free(pDel);
}
}
/* This function adds a recipient to the specified list.
* The recipient address storage is handed over -- the caller must NOT delete it.
*/
static rsRetVal addRcpt(toRcpt_t **ppLstRcpt, uchar *newRcpt) {
DEFiRet;
toRcpt_t *pNew = NULL;
CHKmalloc(pNew = calloc(1, sizeof(toRcpt_t)));
pNew->pszTo = newRcpt;
pNew->pNext = *ppLstRcpt;
*ppLstRcpt = pNew;
DBGPRINTF("ommail::addRcpt adds recipient %s\n", newRcpt);
finalize_it:
if (iRet != RS_RET_OK) {
free(pNew);
free(newRcpt); /* in any case, this is no longer needed */
}
RETiRet;
}
/* This function is called when a new recipient email address is to be
* added. rgerhards, 2008-08-04
*/
static rsRetVal legacyConfAddRcpt(void __attribute__((unused)) * pVal, uchar *pNewVal) {
return addRcpt(&cs.lstRcpt, pNewVal);
}
/* output the recipient list to the mail server
* iStatusToCheck < 0 means no checking should happen
*/
static rsRetVal WriteRcpts(wrkrInstanceData_t *pWrkrData, uchar *pszOp, size_t lenOp, int iStatusToCheck) {
toRcpt_t *pRcpt;
int iState;
DEFiRet;
assert(lenOp != 0);
for (pRcpt = pWrkrData->pData->md.smtp.lstRcpt; pRcpt != NULL; pRcpt = pRcpt->pNext) {
DBGPRINTF("Sending '%s: <%s>'\n", pszOp, pRcpt->pszTo);
CHKiRet(Send(pWrkrData->md.smtp.sock, (char *)pszOp, lenOp));
CHKiRet(Send(pWrkrData->md.smtp.sock, ":<", sizeof(":<") - 1));
CHKiRet(Send(pWrkrData->md.smtp.sock, (char *)pRcpt->pszTo, strlen((char *)pRcpt->pszTo)));
CHKiRet(Send(pWrkrData->md.smtp.sock, ">\r\n", sizeof(">\r\n") - 1));
if (iStatusToCheck >= 0) CHKiRet(readResponse(pWrkrData, &iState, iStatusToCheck));
}
finalize_it:
RETiRet;
}
/* output the recipient list in rfc2822 format
*/
static rsRetVal WriteTos(wrkrInstanceData_t *pWrkrData, uchar *pszOp, size_t lenOp) {
toRcpt_t *pRcpt;
int iTos;
DEFiRet;
assert(lenOp != 0);
CHKiRet(Send(pWrkrData->md.smtp.sock, (char *)pszOp, lenOp));
CHKiRet(Send(pWrkrData->md.smtp.sock, ": ", sizeof(": ") - 1));
for (pRcpt = pWrkrData->pData->md.smtp.lstRcpt, iTos = 0; pRcpt != NULL; pRcpt = pRcpt->pNext, iTos++) {
DBGPRINTF("Sending '%s: <%s>'\n", pszOp, pRcpt->pszTo);
if (iTos) CHKiRet(Send(pWrkrData->md.smtp.sock, ", ", sizeof(", ") - 1));
CHKiRet(Send(pWrkrData->md.smtp.sock, "<", sizeof("<") - 1));
CHKiRet(Send(pWrkrData->md.smtp.sock, (char *)pRcpt->pszTo, strlen((char *)pRcpt->pszTo)));
CHKiRet(Send(pWrkrData->md.smtp.sock, ">", sizeof(">") - 1));
}
CHKiRet(Send(pWrkrData->md.smtp.sock, "\r\n", sizeof("\r\n") - 1));
finalize_it:
RETiRet;
}
/* end helpers for handling the recipient lists */
BEGINcreateInstance
CODESTARTcreateInstance;
pData->constSubject = NULL;
pData->bEnableBody = 1;
ENDcreateInstance
BEGINcreateWrkrInstance
CODESTARTcreateWrkrInstance;
ENDcreateWrkrInstance
BEGINisCompatibleWithFeature
CODESTARTisCompatibleWithFeature;
if (eFeat == sFEATURERepeatedMsgReduction) iRet = RS_RET_OK;
ENDisCompatibleWithFeature
BEGINfreeInstance
CODESTARTfreeInstance;
free(pData->tplName);
if (pData->iMode == 0) {
free(pData->md.smtp.pszSrv);
free(pData->md.smtp.pszSrvPort);
free(pData->md.smtp.pszFrom);
lstRcptDestruct(pData->md.smtp.lstRcpt);
}
ENDfreeInstance
BEGINfreeWrkrInstance
CODESTARTfreeWrkrInstance;
ENDfreeWrkrInstance
BEGINdbgPrintInstInfo
CODESTARTdbgPrintInstInfo;
printf("mail"); /* TODO: extend! */
ENDdbgPrintInstInfo
/* TCP support code, should probably be moved to net.c or some place else... -- rgerhards, 2008-04-04 */
/* "receive" a character from the remote server. A single character
* is returned. Returns RS_RET_NO_MORE_DATA if the server has closed
* the connection and RS_RET_IO_ERROR if something goes wrong. This
* is a blocking read.
* rgerhards, 2008-04-04
*/
static rsRetVal getRcvChar(wrkrInstanceData_t *pWrkrData, char *pC) {
DEFiRet;
ssize_t lenBuf;
if (pWrkrData->md.smtp.iRcvBuf == pWrkrData->md.smtp.lenRcvBuf) { /* buffer empty? */
/* yes, we need to read the next server response */
do {
lenBuf = recv(pWrkrData->md.smtp.sock, pWrkrData->md.smtp.RcvBuf, sizeof(pWrkrData->md.smtp.RcvBuf), 0);
if (lenBuf == 0) {
ABORT_FINALIZE(RS_RET_NO_MORE_DATA);
} else if (lenBuf < 0) {
if (errno != EAGAIN) {
ABORT_FINALIZE(RS_RET_IO_ERROR);
}
} else {
/* good read */
pWrkrData->md.smtp.iRcvBuf = 0;
pWrkrData->md.smtp.lenRcvBuf = lenBuf;
}
} while (lenBuf < 1);
}
/* when we reach this point, we have a non-empty buffer */
*pC = pWrkrData->md.smtp.RcvBuf[pWrkrData->md.smtp.iRcvBuf++];
finalize_it:
RETiRet;
}
/* close the mail server connection
* rgerhards, 2008-04-08
*/
static rsRetVal serverDisconnect(wrkrInstanceData_t *pWrkrData) {
DEFiRet;
assert(pWrkrData != NULL);
if (pWrkrData->md.smtp.sock != -1) {
close(pWrkrData->md.smtp.sock);
pWrkrData->md.smtp.sock = -1;
}
RETiRet;
}
/* open a connection to the mail server
* rgerhards, 2008-04-04
*/
static rsRetVal serverConnect(wrkrInstanceData_t *pWrkrData) {
struct addrinfo *res = NULL;
struct addrinfo hints;
const char *smtpPort;
const char *smtpSrv;
char errStr[1024];
instanceData *pData;
DEFiRet;
pData = pWrkrData->pData;
if (pData->md.smtp.pszSrv == NULL)
smtpSrv = "127.0.0.1";
else
smtpSrv = (char *)pData->md.smtp.pszSrv;
if (pData->md.smtp.pszSrvPort == NULL)
smtpPort = "25";
else
smtpPort = (char *)pData->md.smtp.pszSrvPort;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; /* TODO: make configurable! */
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(smtpSrv, smtpPort, &hints, &res) != 0) {
DBGPRINTF("error %d in getaddrinfo\n", errno);
ABORT_FINALIZE(RS_RET_IO_ERROR);
}
if ((pWrkrData->md.smtp.sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) {
DBGPRINTF("couldn't create send socket, reason %s", rs_strerror_r(errno, errStr, sizeof(errStr)));
ABORT_FINALIZE(RS_RET_IO_ERROR);
}
if (connect(pWrkrData->md.smtp.sock, res->ai_addr, res->ai_addrlen) != 0) {
DBGPRINTF("create tcp connection failed, reason %s", rs_strerror_r(errno, errStr, sizeof(errStr)));
ABORT_FINALIZE(RS_RET_IO_ERROR);
}
finalize_it:
if (res != NULL) freeaddrinfo(res);
if (iRet != RS_RET_OK) {
if (pWrkrData->md.smtp.sock != -1) {
close(pWrkrData->md.smtp.sock);
pWrkrData->md.smtp.sock = -1;
}
}
RETiRet;
}
/* send text to the server, blocking send */
static rsRetVal Send(const int sock, const char *const __restrict__ msg, const size_t len) {
DEFiRet;
size_t offsBuf = 0;
ssize_t lenSend;
assert(msg != NULL);
if (len == 0) /* it's valid, but does not make much sense ;) */
FINALIZE;
do {
lenSend = send(sock, msg + offsBuf, len - offsBuf, 0);
if (lenSend == -1) {
if (errno != EAGAIN) {
DBGPRINTF("message not (smtp/tcp)send, errno %d", errno);
ABORT_FINALIZE(RS_RET_TCP_SEND_ERROR);
}
} else if (lenSend != (ssize_t)(len - offsBuf)) {
offsBuf += lenSend; /* on to next round... */
} else {
FINALIZE;
}
} while (1);
finalize_it:
RETiRet;
}
/* send body text to the server, blocking send
* The body is special in that we must escape a leading dot inside a line
*/
static rsRetVal bodySend(wrkrInstanceData_t *pWrkrData, char *msg, size_t len) {
DEFiRet;
char szBuf[2048];
size_t iSrc;
size_t iBuf = 0;
int bHadCR = 0;
int bInStartOfLine = 1;
assert(pWrkrData != NULL);
assert(msg != NULL);
for (iSrc = 0; iSrc < len; ++iSrc) {
if (iBuf >= sizeof(szBuf) - 1) { /* one is reserved for our extra dot */
CHKiRet(Send(pWrkrData->md.smtp.sock, szBuf, iBuf));
iBuf = 0;
}
szBuf[iBuf++] = msg[iSrc];
switch (msg[iSrc]) {
case '\r':
bHadCR = 1;
break;
case '\n':
if (bHadCR) bInStartOfLine = 1;
bHadCR = 0;
break;
case '.':
if (bInStartOfLine) szBuf[iBuf++] = '.'; /* space is always reserved for this! */
/*FALLTHROUGH*/
default:
bInStartOfLine = 0;
bHadCR = 0;
break;
}
}
if (iBuf > 0) { /* incomplete buffer to send (the *usual* case)? */
CHKiRet(Send(pWrkrData->md.smtp.sock, szBuf, iBuf));
}
finalize_it:
RETiRet;
}
/* read response line from server
*/
static rsRetVal readResponseLn(wrkrInstanceData_t *pWrkrData,
char *pLn,
size_t lenLn,
size_t *const __restrict__ respLen) {
DEFiRet;
size_t i = 0;
char c;
assert(pWrkrData != NULL);
assert(pLn != NULL);
do {
CHKiRet(getRcvChar(pWrkrData, &c));
if (c == '\n') break;
if (i < (lenLn - 1)) /* if line is too long, we simply discard the rest */
pLn[i++] = c;
} while (1);
DBGPRINTF("smtp server response: %s\n", pLn);
/* do not remove, this is helpful in troubleshooting SMTP probs! */
finalize_it:
pLn[i] = '\0';
*respLen = i;
RETiRet;
}
/* read numerical response code from server and compare it to requried response code.
* If they two don't match, return RS_RET_SMTP_ERROR.
* rgerhards, 2008-04-07
*/
static rsRetVal readResponse(wrkrInstanceData_t *pWrkrData, int *piState, int iExpected) {
DEFiRet;
int bCont;
char buf[128];
size_t respLen;
assert(pWrkrData != NULL);
assert(piState != NULL);
bCont = 1;
do {
CHKiRet(readResponseLn(pWrkrData, buf, sizeof(buf), &respLen));
if (respLen < 4) /* we treat too-short responses as error */
ABORT_FINALIZE(RS_RET_SMTP_ERROR);
if (buf[3] != '-') { /* last or only response line? */
bCont = 0;
*piState = buf[0] - '0';
*piState = *piState * 10 + buf[1] - '0';
*piState = *piState * 10 + buf[2] - '0';
if (*piState != iExpected) ABORT_FINALIZE(RS_RET_SMTP_ERROR);
}
} while (bCont);
finalize_it:
RETiRet;
}
/* create a timestamp suitable for use with the Date: SMTP body header
* rgerhards, 2008-04-08
*/
static void mkSMTPTimestamp(uchar *pszBuf, size_t lenBuf) {
time_t tCurr;
struct tm tmCurr;
static const char szDay[][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static const char szMonth[][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
datetime.GetTime(&tCurr);
gmtime_r(&tCurr, &tmCurr);
snprintf((char *)pszBuf, lenBuf, "Date: %s, %2d %s %4d %02d:%02d:%02d +0000\r\n", szDay[tmCurr.tm_wday],
tmCurr.tm_mday, szMonth[tmCurr.tm_mon], 1900 + tmCurr.tm_year, tmCurr.tm_hour, tmCurr.tm_min,
tmCurr.tm_sec);
}
/* send a message via SMTP
* rgerhards, 2008-04-04
*/
static rsRetVal sendSMTP(wrkrInstanceData_t *pWrkrData, uchar *body, uchar *subject) {
DEFiRet;
int iState; /* SMTP state */
instanceData *pData;
uchar szDateBuf[64];
pData = pWrkrData->pData;
CHKiRet(serverConnect(pWrkrData));
CHKiRet(readResponse(pWrkrData, &iState, 220));
CHKiRet(Send(pWrkrData->md.smtp.sock, "HELO ", 5));
CHKiRet(Send(pWrkrData->md.smtp.sock, (char *)glbl.GetLocalHostName(), strlen((char *)glbl.GetLocalHostName())));
CHKiRet(Send(pWrkrData->md.smtp.sock, "\r\n", sizeof("\r\n") - 1));
CHKiRet(readResponse(pWrkrData, &iState, 250));
CHKiRet(Send(pWrkrData->md.smtp.sock, "MAIL FROM:<", sizeof("MAIL FROM:<") - 1));
CHKiRet(Send(pWrkrData->md.smtp.sock, (char *)pData->md.smtp.pszFrom, strlen((char *)pData->md.smtp.pszFrom)));
CHKiRet(Send(pWrkrData->md.smtp.sock, ">\r\n", sizeof(">\r\n") - 1));
CHKiRet(readResponse(pWrkrData, &iState, 250));
CHKiRet(WriteRcpts(pWrkrData, (uchar *)"RCPT TO", sizeof("RCPT TO") - 1, 250));
CHKiRet(Send(pWrkrData->md.smtp.sock, "DATA\r\n", sizeof("DATA\r\n") - 1));
CHKiRet(readResponse(pWrkrData, &iState, 354));
/* now come the data part */
/* header */
mkSMTPTimestamp(szDateBuf, sizeof(szDateBuf));
CHKiRet(Send(pWrkrData->md.smtp.sock, (char *)szDateBuf, strlen((char *)szDateBuf)));
CHKiRet(Send(pWrkrData->md.smtp.sock, "From: <", sizeof("From: <") - 1));
CHKiRet(Send(pWrkrData->md.smtp.sock, (char *)pData->md.smtp.pszFrom, strlen((char *)pData->md.smtp.pszFrom)));
CHKiRet(Send(pWrkrData->md.smtp.sock, ">\r\n", sizeof(">\r\n") - 1));
CHKiRet(WriteTos(pWrkrData, (uchar *)"To", sizeof("To") - 1));
CHKiRet(Send(pWrkrData->md.smtp.sock, "Subject: ", sizeof("Subject: ") - 1));
CHKiRet(Send(pWrkrData->md.smtp.sock, (char *)subject, strlen((char *)subject)));
CHKiRet(Send(pWrkrData->md.smtp.sock, "\r\n", sizeof("\r\n") - 1));
CHKiRet(Send(pWrkrData->md.smtp.sock, "X-Mailer: rsyslog-ommail\r\n", sizeof("x-mailer: rsyslog-ommail\r\n") - 1));
CHKiRet(Send(pWrkrData->md.smtp.sock, "\r\n", sizeof("\r\n") - 1)); /* indicate end of header */
/* body */
if (pData->bEnableBody) CHKiRet(bodySend(pWrkrData, (char *)body, strlen((char *)body)));
/* end of data, back to envelope transaction */
CHKiRet(Send(pWrkrData->md.smtp.sock, "\r\n.\r\n", sizeof("\r\n.\r\n") - 1));
CHKiRet(readResponse(pWrkrData, &iState, 250));
CHKiRet(Send(pWrkrData->md.smtp.sock, "QUIT\r\n", sizeof("QUIT\r\n") - 1));
CHKiRet(readResponse(pWrkrData, &iState, 221));
/* we are finished, a new connection is created for each request, so let's close it now */
CHKiRet(serverDisconnect(pWrkrData));
finalize_it:
RETiRet;
}
/* in tryResume we check if we can connect to the server in question. If that is OK,
* we close the connection without doing any actual SMTP transaction. It will be
* reopened during the actual send process. This may not be the best way to do it if
* there is a problem inside the SMTP transaction. However, we can't find that out without
* actually initiating something, and that would be bad. The logic here helps us
* correctly recover from an unreachable/down mail server, which is probably the majority
* of problem cases. For SMTP transaction problems, we will do lots of retries, but if it
* is a temporary problem, it will be fixed anyhow. So I consider this implementation to
* be clean enough, especially as I think other approaches have other weaknesses.
* rgerhards, 2008-04-08
*/
BEGINtryResume
CODESTARTtryResume;
CHKiRet(serverConnect(pWrkrData));
CHKiRet(serverDisconnect(pWrkrData)); /* if we fail, we will never reach this line */
finalize_it:
if (iRet == RS_RET_IO_ERROR) iRet = RS_RET_SUSPENDED;
ENDtryResume
BEGINdoAction
uchar *subject;
const instanceData *const __restrict__ pData = pWrkrData->pData;
CODESTARTdoAction;
DBGPRINTF("ommail doAction()\n");
if (pData->constSubject != NULL)
subject = pData->constSubject;
else if (pData->bHaveSubject)
subject = ppString[1];
else
subject = (uchar *)"message from rsyslog";
iRet = sendSMTP(pWrkrData, ppString[0], subject);
if (iRet != RS_RET_OK) {
DBGPRINTF("error sending mail, suspending\n");
iRet = RS_RET_SUSPENDED;
}
ENDdoAction
static inline void setInstParamDefaults(instanceData *pData) {
pData->tplName = NULL;
pData->constSubject = NULL;
}
BEGINnewActInst
struct cnfparamvals *pvals;
uchar *tplSubject = NULL;
int i, j;
CODESTARTnewActInst;
if ((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) {
ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
}
CHKiRet(createInstance(&pData));
setInstParamDefaults(pData);
for (i = 0; i < actpblk.nParams; ++i) {
if (!pvals[i].bUsed) continue;
if (!strcmp(actpblk.descr[i].name, "server")) {
CHKmalloc(pData->md.smtp.pszSrv = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
} else if (!strcmp(actpblk.descr[i].name, "port")) {
CHKmalloc(pData->md.smtp.pszSrvPort = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
} else if (!strcmp(actpblk.descr[i].name, "mailfrom")) {
CHKmalloc(pData->md.smtp.pszFrom = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
} else if (!strcmp(actpblk.descr[i].name, "mailto")) {
for (j = 0; j < pvals[i].val.d.ar->nmemb; ++j) {
addRcpt(&(pData->md.smtp.lstRcpt), (uchar *)es_str2cstr(pvals[i].val.d.ar->arr[j], NULL));
}
} else if (!strcmp(actpblk.descr[i].name, "subject.template")) {
if (pData->constSubject != NULL) {
parser_errmsg(
"ommail: only one of subject.template, subject.text "
"can be set");
ABORT_FINALIZE(RS_RET_DUP_PARAM);
}
CHKmalloc(tplSubject = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
} else if (!strcmp(actpblk.descr[i].name, "subject.text")) {
if (tplSubject != NULL) {
parser_errmsg(
"ommail: only one of subject.template, subject.text "
"can be set");
ABORT_FINALIZE(RS_RET_DUP_PARAM);
}
CHKmalloc(pData->constSubject = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
} else if (!strcmp(actpblk.descr[i].name, "body.enable")) {
pData->bEnableBody = (int)pvals[i].val.d.n;
} else if (!strcmp(actpblk.descr[i].name, "template")) {
CHKmalloc(pData->tplName = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL));
} else {
DBGPRINTF(
"ommail: program error, non-handled "
"param '%s'\n",
actpblk.descr[i].name);
}
}
if (tplSubject == NULL) {
/* if no subject is configured, we need just one template string */
CODE_STD_STRING_REQUESTparseSelectorAct(1)
} else {
CODE_STD_STRING_REQUESTparseSelectorAct(2) pData->bHaveSubject = 1;
/* NOTE: tplSubject memory is *handed over* down here below - do NOT free() */
CHKiRet(OMSRsetEntry(*ppOMSR, 1, tplSubject, OMSR_NO_RQD_TPL_OPTS));
}
if (pData->tplName == NULL) {
CHKiRet(OMSRsetEntry(*ppOMSR, 0, (uchar *)strdup("RSYSLOG_FileFormat"), OMSR_NO_RQD_TPL_OPTS));
} else {
CHKiRet(OMSRsetEntry(*ppOMSR, 0, (uchar *)strdup((char *)pData->tplName), OMSR_NO_RQD_TPL_OPTS));
}
CODE_STD_FINALIZERnewActInst;
cnfparamvalsDestruct(pvals, &actpblk);
ENDnewActInst
BEGINparseSelectorAct
CODESTARTparseSelectorAct;
if (!strncmp((char *)p, ":ommail:", sizeof(":ommail:") - 1)) {
p += sizeof(":ommail:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
} else {
ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
}
/* ok, if we reach this point, we have something for us */
if ((iRet = createInstance(&pData)) != RS_RET_OK) FINALIZE;
/* TODO: check strdup() result */
if (cs.pszFrom == NULL) {
LogError(0, RS_RET_MAIL_NO_FROM, "no sender address given - specify $ActionMailFrom");
ABORT_FINALIZE(RS_RET_MAIL_NO_FROM);
}
if (cs.lstRcpt == NULL) {
LogError(0, RS_RET_MAIL_NO_TO, "no recipient address given - specify $ActionMailTo");
ABORT_FINALIZE(RS_RET_MAIL_NO_TO);
}
pData->md.smtp.pszFrom = (uchar *)strdup((char *)cs.pszFrom);
pData->md.smtp.lstRcpt = cs.lstRcpt; /* we "hand over" this memory */
cs.lstRcpt = NULL; /* note: this is different from pre-3.21.2 versions! */
if (cs.pszSubject == NULL) {
/* if no subject is configured, we need just one template string */
CODE_STD_STRING_REQUESTparseSelectorAct(1)
} else {
CODE_STD_STRING_REQUESTparseSelectorAct(2) pData->bHaveSubject = 1;
CHKiRet(OMSRsetEntry(*ppOMSR, 1, (uchar *)strdup((char *)cs.pszSubject), OMSR_NO_RQD_TPL_OPTS));
}
if (cs.pszSrv != NULL) pData->md.smtp.pszSrv = (uchar *)strdup((char *)cs.pszSrv);
if (cs.pszSrvPort != NULL) pData->md.smtp.pszSrvPort = (uchar *)strdup((char *)cs.pszSrvPort);
pData->bEnableBody = cs.bEnableBody;
/* process template */
iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar *)"RSYSLOG_FileFormat");
CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct
/* Free string config variables and reset them to NULL (not necessarily the default!) */
static rsRetVal freeConfigVariables(void) {
DEFiRet;
free(cs.pszSrv);
cs.pszSrv = NULL;
free(cs.pszSrvPort);
cs.pszSrvPort = NULL;
free(cs.pszFrom);
cs.pszFrom = NULL;
lstRcptDestruct(cs.lstRcpt);
cs.lstRcpt = NULL;
RETiRet;
}
BEGINmodExit
CODESTARTmodExit;
/* cleanup our allocations */
freeConfigVariables();
/* release what we no longer need */
objRelease(datetime, CORE_COMPONENT);
objRelease(glbl, CORE_COMPONENT);
ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt;
CODEqueryEtryPt_STD_OMOD_QUERIES;
CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES;
CODEqueryEtryPt_STD_OMOD8_QUERIES;
CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES;
ENDqueryEtryPt
/* Reset config variables for this module to default values.
*/
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) * pp, void __attribute__((unused)) * pVal) {
DEFiRet;
cs.bEnableBody = 1;
iRet = freeConfigVariables();
RETiRet;
}
BEGINmodInit()
CODESTARTmodInit;
INITLegCnfVars;
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
/* tell which objects we need */
CHKiRet(objUse(glbl, CORE_COMPONENT));
CHKiRet(objUse(datetime, CORE_COMPONENT));
DBGPRINTF("ommail version %s initializing\n", VERSION);
CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionmailsmtpserver", 0, eCmdHdlrGetWord, NULL, &cs.pszSrv,
STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionmailsmtpport", 0, eCmdHdlrGetWord, NULL, &cs.pszSrvPort,
STD_LOADABLE_MODULE_ID));
CHKiRet(
omsdRegCFSLineHdlr((uchar *)"actionmailfrom", 0, eCmdHdlrGetWord, NULL, &cs.pszFrom, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionmailto", 0, eCmdHdlrGetWord, legacyConfAddRcpt, NULL,
STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionmailsubject", 0, eCmdHdlrGetWord, NULL, &cs.pszSubject,
STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionmailenablebody", 0, eCmdHdlrBinary, NULL, &cs.bEnableBody,
STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL,
STD_LOADABLE_MODULE_ID));
ENDmodInit