blob: 0dad1a434e490e9f8ac05d9cae5f35f4e39f3830 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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
*
* 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 <log4cxx/net/smtpappender.h>
#include <log4cxx/level.h>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/helpers/optionconverter.h>
#include <log4cxx/spi/loggingevent.h>
#include <log4cxx/private/string_c11.h>
#include <log4cxx/helpers/stringhelper.h>
#include <log4cxx/helpers/stringtokenizer.h>
#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/helpers/loader.h>
#if !defined(LOG4CXX)
#define LOG4CXX 1
#endif
#include <log4cxx/private/log4cxx_private.h>
#include <log4cxx/private/appenderskeleton_priv.h>
#include <apr_strings.h>
#include <vector>
using namespace LOG4CXX_NS;
using namespace LOG4CXX_NS::helpers;
using namespace LOG4CXX_NS::net;
using namespace LOG4CXX_NS::spi;
#if LOG4CXX_HAVE_LIBESMTP
#include <auth-client.h>
#include <libesmtp.h>
#endif
namespace
{
// RFC 5322 §2.1 defines header fields as CRLF-terminated lines, so an embedded
// CR or LF in a configured Subject/From/To/Cc/Bcc value would split it across
// header boundaries on the wire — a caller who controls a configured field
// (e.g. through ${...} property substitution from an environment variable)
// could inject arbitrary additional headers such as Bcc. The library already
// owns SMTP wire-format sanitization (see SMTPSession::toAscii, which silently
// rewrites non-ASCII to '?'); strip CR/LF in the public setters so the same
// boundary is enforced regardless of how the value reaches the appender.
LogString stripSmtpControl(const LogString& value, const logchar* field)
{
if (value.find_first_of(LOG4CXX_STR("\r\n")) == LogString::npos)
{
return value;
}
LogString warning(LOG4CXX_STR("SMTPAppender "));
warning.append(field);
warning.append(LOG4CXX_STR(" contains CR or LF; stripping to prevent SMTP header injection."));
LogLog::warn(warning);
LogString out;
out.reserve(value.size());
for (auto ch : value)
{
if (ch != 0x0D && ch != 0x0A)
{
out.append(1, ch);
}
}
return out;
}
} // namespace
namespace LOG4CXX_NS
{
namespace net
{
//
// The following two classes implement an C++ SMTP wrapper over libesmtp.
// The same signatures could be implemented over different SMTP implementations
// or libesmtp could be combined with libgmime to enable support for non-ASCII
// content.
#if LOG4CXX_HAVE_LIBESMTP
/**
* SMTP Session.
*/
class SMTPSession
{
public:
/**
* Create new instance.
*/
SMTPSession(const LogString& smtpHost,
int smtpPort,
const LogString& smtpUsername,
const LogString& smtpPassword,
bool allowPlainTextAuth,
Pool& p
)
: user{toAscii(smtpUsername, p)}
, pwd{toAscii(smtpPassword, p)}
{
auth_client_init();
session = smtp_create_session();
if (session == 0)
{
throw Exception("Could not initialize session.");
}
std::string host(toAscii(smtpHost, p));
host.append(1, ':');
host.append(p.itoa(smtpPort));
smtp_set_server(session, host.c_str());
smtp_set_monitorcb(session, monitor_cb, (void*)this, 1);
smtp_set_eventcb(session, event_cb, (void*)this);
authctx = auth_create_context();
auth_set_mechanism_flags(authctx, AUTH_PLUGIN_PLAIN, 0);
auth_set_interact_cb(authctx, authinteract, (void*) this);
if (*user || *pwd)
{
// Secure by default: never send AUTH credentials over an
// unencrypted connection. Require STARTTLS before
// authenticating unless the operator explicitly opted in
// to plain-text authentication via the
// AllowPlainTextAuthentication option.
if (!allowPlainTextAuth && !smtp_starttls_enable(session, Starttls_REQUIRED))
{
// The destructor does not run when a constructor throws
smtp_destroy_session(session);
auth_destroy_context(authctx);
throw Exception("SMTPAppender: STARTTLS is unavailable in this libESMTP build;"
" refusing to send SMTP credentials in clear text."
" Set AllowPlainTextAuthentication=true to override.");
}
smtp_auth_set_context(session, authctx);
}
}
~SMTPSession()
{
smtp_destroy_session(session);
auth_destroy_context(authctx);
}
void send()
{
int status = smtp_start_session(session);
if (!status)
{
static const size_t smtp_msgSize = 128;
char smtp_msg[smtp_msgSize];
smtp_strerror(smtp_errno(), smtp_msg, smtp_msgSize);
char msg[2 * smtp_msgSize];
snprintf(msg, sizeof (msg), "%s (sessionState %d isActive? %d tlsStarted? %d)"
, smtp_msg, sessionState, isActive, tlsStarted);
throw Exception(msg);
}
else if (incorrectAuthentication)
throw Exception("Incorrect authentication data");
else if (relayDenied)
throw Exception("Relay Denied");
else if (!certificateProblem.empty())
throw Exception(("X509 error: " + certificateProblem).c_str());
}
operator smtp_session_t()
{
return session;
}
static char* toAscii(const LogString& str, Pool& p)
{
char* buf = p.pstralloc(str.length() + 1);
char* current = buf;
for (unsigned int c : str)
{
if (c > 0x7F)
{
c = '?';
}
*current++ = c;
}
*current = 0;
return buf;
}
private:
SMTPSession(SMTPSession&);
SMTPSession& operator=(SMTPSession&);
smtp_session_t session{0};
auth_context_t authctx{0};
char* user;
char* pwd;
int sessionState{0};
bool isActive{false};
bool tlsStarted{false};
bool incorrectAuthentication{false};
bool relayDenied{false};
std::string certificateProblem;
/**
* This method is called if the SMTP server requests authentication.
*/
static int authinteract(auth_client_request_t request, char** result, int fields,
void* arg)
{
auto pThis = static_cast<SMTPSession*>(arg);
for (int i = 0; i < fields; i++)
{
int flag = request[i].flags & 0x07;
if (flag == AUTH_USER)
{
result[i] = pThis->user;
}
else if (flag == AUTH_PASS)
{
result[i] = pThis->pwd;
}
}
return 1;
}
static void monitor_cb(const char *buf, int buflen, int writing, void *arg)
{
auto pThis = static_cast<SMTPSession*>(arg);
if (writing)
pThis->isActive = true;
else if (auto smtp_response = atoi(buf))
{
if (535 == smtp_response)
pThis->incorrectAuthentication = true;
if (550 == smtp_response)
pThis->relayDenied = true;
}
if (LogLog::isDebugEnabled())
{
while (0 < buflen && std::isspace(buf[buflen - 1]))
--buflen;
std::string data(buf, buflen);
LOG4CXX_DECODE_CHAR(lsData, data);
LogString type = writing ? LOG4CXX_STR("send") : LOG4CXX_STR("recv");
LogLog::debug(LOG4CXX_STR("SMTP ") + type + LOG4CXX_STR(" [") + lsData + LOG4CXX_STR("]"));
}
}
static void event_cb (smtp_session_t session /* unused */, int event_no, void *arg,...)
{
auto pThis = static_cast<SMTPSession*>(arg);
va_list alist;
va_start(alist, arg);
switch (event_no)
{
case SMTP_EV_CONNECT:
case SMTP_EV_MAILSTATUS:
case SMTP_EV_RCPTSTATUS:
case SMTP_EV_MESSAGEDATA:
case SMTP_EV_MESSAGESENT:
case SMTP_EV_DISCONNECT:
pThis->sessionState = event_no;
break;
case SMTP_EV_WEAK_CIPHER:
{
auto bitsRequired = va_arg(alist, long);
pThis->certificateProblem = "weak cipher";
if (auto ok = va_arg(alist, int*))
*ok = 1; // Accept the problem
break;
}
case SMTP_EV_STARTTLS_OK:
pThis->tlsStarted = true;
break;
case SMTP_EV_INVALID_PEER_CERTIFICATE:
pThis->certificateProblem = get_X509_error(va_arg(alist, long));
if (auto ok = va_arg(alist, int*))
*ok = 1; // Accept the problem
break;
case SMTP_EV_NO_PEER_CERTIFICATE:
pThis->certificateProblem = "no peer certificate";
if (auto ok = va_arg(alist, int*))
*ok = 1;
break;
case SMTP_EV_WRONG_PEER_CERTIFICATE:
pThis->certificateProblem = "wrong peer certificate";
if (auto ok = va_arg(alist, int*))
*ok = 1; // Accept the problem
break;
case SMTP_EV_NO_CLIENT_CERTIFICATE:
pThis->certificateProblem = "no client certificate";
if (auto ok = va_arg(alist, int*))
*ok = 1; // Accept the problem
break;
}
va_end(alist);
}
static std::string get_X509_error(long verifyResult)
{
switch (verifyResult)
{
case 2: return "unable to get issuer cert";
case 3: return "unable to get crl";
case 4: return "unable to decrypt cert signature";
case 5: return "unable to decrypt crl signature";
case 6: return "unable to decode issuer public key";
case 7: return "cert signature failure";
case 8: return "crl signature failure";
case 9: return "cert not yet valid";
case 10: return "cert has expired";
case 11: return "crl not yet valid";
case 12: return "crl has expired";
case 13: return "error in cert not before field";
case 14: return "error in cert not after field";
case 15: return "error in crl last update field";
case 16: return "error in crl next update field";
case 17: return "out of mem";
case 18: return "depth zero self signed cert";
case 19: return "self signed cert in chain";
case 20: return "unable to get issuer cert locally";
case 21: return "unable to verify leaf signature";
case 22: return "cert chain too long";
case 23: return "cert revoked";
case 24: return "invalid ca";
case 25: return "path length exceeded";
case 26: return "invalid purpose";
case 27: return "cert untrusted";
case 28: return "cert rejected";
case 29: return "subject issuer mismatch";
case 30: return "akid skid mismatch";
case 31: return "akid issuer serial mismatch";
case 32: return "keyusage no certsign";
case 33: return "unable to get crl issuer";
case 34: return "unhandled critical extension";
case 35: return "keyusage no crl sign";
case 36: return "unhandled critical crl extension";
case 37: return "invalid non ca";
case 38: return "proxy path length exceeded";
case 39: return "keyusage no digital signature";
case 40: return "proxy certificates not allowed";
case 41: return "invalid extension";
case 42: return "invalid policy extension";
case 43: return "no explicit policy";
case 44: return "different crl scope";
case 45: return "unsupported extension feature";
case 46: return "unnested resource";
case 47: return "permitted violation";
case 48: return "excluded violation";
case 49: return "subtree minmax";
case 51: return "unsupported constraint type";
case 52: return "unsupported constraint syntax";
case 53: return "unsupported name syntax";
case 54: return "crl path validation error";
case 50: return "application verification";
}
return "unknown";
}
};
/**
* A message in an SMTP session.
*/
class SMTPMessage
{
public:
SMTPMessage(SMTPSession& session,
const LogString& from,
const LogString& to,
const LogString& cc,
const LogString& bcc,
const LogString& subject,
const LogString msg, Pool& p)
{
message = smtp_add_message(session);
body = current = toMessage(msg, p, current_len);
messagecbState = 0;
smtp_set_reverse_path(message, toAscii(from, p));
addRecipients(to, "To", p);
addRecipients(cc, "Cc", p);
addRecipients(bcc, "Bcc", p);
if (!subject.empty())
{
smtp_set_header(message, "Subject", toAscii(subject, p));
}
smtp_set_messagecb(message, messagecb, this);
}
~SMTPMessage()
{
}
private:
SMTPMessage(const SMTPMessage&);
SMTPMessage& operator=(const SMTPMessage&);
smtp_message_t message;
const char* body;
const char* current;
size_t current_len;
int messagecbState;
void addRecipients(const LogString& addresses, const char* field, Pool& p)
{
if (!addresses.empty())
{
char* str = p.pstrdup(toAscii(addresses, p));;
smtp_set_header(message, field, NULL, str);
char* last;
for (char* next = apr_strtok(str, ",", &last);
next;
next = apr_strtok(NULL, ",", &last))
{
smtp_add_recipient(message, next);
}
}
}
static const char* toAscii(const LogString& str, Pool& p)
{
return SMTPSession::toAscii(str, p);
}
/**
* Message bodies can only contain US-ASCII characters and
* CR and LFs can only occur together.
* On return \c lenOut holds the length of the converted body.
*/
static const char* toMessage(const LogString& str, Pool& p, size_t& lenOut)
{
//
// count the number of carriage returns and line feeds
//
int feedCount = 0;
for (size_t pos = str.find_first_of(LOG4CXX_STR("\n\r"));
pos != LogString::npos;
pos = str.find_first_of(LOG4CXX_STR("\n\r"), ++pos))
{
feedCount++;
}
//
// allocate sufficient space for the modified message
char* retval = p.pstralloc(str.length() + feedCount + 1);
char* current = retval;
char* startOfLine = current;
unsigned int ignoreChar = 0;
//
// iterator through message
//
for (unsigned int c : str)
{
//
// replace non-ASCII characters and embedded NULs with '?'
// (a NUL octet must never act as a body terminator)
//
if (c > 0x7F || c == 0)
{
*current++ = 0x3F; // '?'
}
else if (c == 0x0A || c == 0x0D)
{
//
// replace any stray CR or LF with CRLF
// reset start of line
if (c == ignoreChar && current == startOfLine)
ignoreChar = 0;
else
{
*current++ = 0x0D;
*current++ = 0x0A;
startOfLine = current;
ignoreChar = (c == 0x0A ? 0x0D : 0x0A);
}
}
else
{
//
// truncate any lines to 1000 characters (including CRLF)
// as required by RFC.
if (current < startOfLine + 998)
{
*current++ = (char) c;
}
}
}
*current = 0;
lenOut = current - retval;
return retval;
}
/**
* Callback for message.
*/
static const char* messagecb(void** ctx, int* len, void* arg)
{
*ctx = 0;
const char* retval = 0;
SMTPMessage* pThis = (SMTPMessage*) arg;
// rewind message
if (len == NULL)
{
pThis->current = pThis->body;
}
else
{
// we are asked for headers, but we don't have any
if ((pThis->messagecbState)++ == 0)
{
return NULL;
}
if (pThis->current)
{
// Use the stored post-conversion length: strnlen_s over the
// pre-conversion length truncates at an embedded NUL and
// undercounts the CRLF-expanded body, silently dropping the
// newest content from the alert email.
*len = static_cast<int>(pThis->current_len);
}
retval = pThis->current;
pThis->current = 0;
}
return retval;
}
};
#endif
class LOG4CXX_EXPORT DefaultEvaluator
: public spi::TriggeringEventEvaluator
{
public:
DECLARE_LOG4CXX_OBJECT(DefaultEvaluator)
BEGIN_LOG4CXX_CAST_MAP()
LOG4CXX_CAST_ENTRY(DefaultEvaluator)
LOG4CXX_CAST_ENTRY(spi::TriggeringEventEvaluator)
END_LOG4CXX_CAST_MAP()
DefaultEvaluator();
/**
Is this <code>event</code> the e-mail triggering event?
<p>This method returns <code>true</code>, if the event level
has ERROR level or higher. Otherwise it returns
<code>false</code>.
*/
bool isTriggeringEvent(const spi::LoggingEventPtr& event) override;
private:
DefaultEvaluator(const DefaultEvaluator&);
DefaultEvaluator& operator=(const DefaultEvaluator&);
}; // class DefaultEvaluator
}
}
IMPLEMENT_LOG4CXX_OBJECT(DefaultEvaluator)
IMPLEMENT_LOG4CXX_OBJECT(SMTPAppender)
struct SMTPAppender::SMTPPriv : public AppenderSkeletonPrivate
{
SMTPPriv() :
AppenderSkeletonPrivate(),
smtpPort(25),
bufferSize(512),
locationInfo(false),
cb(bufferSize),
evaluator(new DefaultEvaluator()) {}
SMTPPriv(const spi::TriggeringEventEvaluatorPtr& evaluator) :
AppenderSkeletonPrivate(),
smtpPort(25),
bufferSize(512),
locationInfo(false),
cb(bufferSize),
evaluator(evaluator) {}
LogString to;
LogString cc;
LogString bcc;
LogString from;
LogString subject;
LogString smtpHost;
LogString smtpUsername;
LogString smtpPassword;
int smtpPort;
int bufferSize; // 512
bool locationInfo;
helpers::CyclicBuffer cb;
spi::TriggeringEventEvaluatorPtr evaluator;
// Whether AUTH credentials may be sent without STARTTLS (see setOption)
bool allowPlainTextAuth{false};
};
#define _priv static_cast<SMTPPriv*>(m_priv.get())
DefaultEvaluator::DefaultEvaluator()
{
}
bool DefaultEvaluator::isTriggeringEvent(const spi::LoggingEventPtr& event)
{
return event->getLevel()->isGreaterOrEqual(Level::getError());
}
SMTPAppender::SMTPAppender()
: AppenderSkeleton (std::make_unique<SMTPPriv>())
{
}
/**
Use <code>evaluator</code> passed as parameter as the
TriggeringEventEvaluator for this SMTPAppender. */
SMTPAppender::SMTPAppender(const spi::TriggeringEventEvaluatorPtr& evaluator)
: AppenderSkeleton (std::make_unique<SMTPPriv>(evaluator))
{
}
SMTPAppender::~SMTPAppender()
{
_priv->setClosed();
}
bool SMTPAppender::requiresLayout() const
{
return true;
}
LogString SMTPAppender::getFrom() const
{
return _priv->from;
}
void SMTPAppender::setFrom(const LogString& newVal)
{
_priv->from = stripSmtpControl(newVal, LOG4CXX_STR("From"));
}
LogString SMTPAppender::getSubject() const
{
return _priv->subject;
}
void SMTPAppender::setSubject(const LogString& newVal)
{
_priv->subject = stripSmtpControl(newVal, LOG4CXX_STR("Subject"));
}
LogString SMTPAppender::getSMTPHost() const
{
return _priv->smtpHost;
}
void SMTPAppender::setSMTPHost(const LogString& newVal)
{
_priv->smtpHost = newVal;
}
int SMTPAppender::getSMTPPort() const
{
return _priv->smtpPort;
}
void SMTPAppender::setSMTPPort(int newVal)
{
_priv->smtpPort = newVal;
}
bool SMTPAppender::getLocationInfo() const
{
return _priv->locationInfo;
}
void SMTPAppender::setLocationInfo(bool newVal)
{
_priv->locationInfo = newVal;
}
LogString SMTPAppender::getSMTPUsername() const
{
return _priv->smtpUsername;
}
void SMTPAppender::setSMTPUsername(const LogString& newVal)
{
_priv->smtpUsername = newVal;
}
LogString SMTPAppender::getSMTPPassword() const
{
return _priv->smtpPassword;
}
void SMTPAppender::setSMTPPassword(const LogString& newVal)
{
_priv->smtpPassword = newVal;
}
void SMTPAppender::setOption(const LogString& option,
const LogString& value)
{
if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BUFFERSIZE"), LOG4CXX_STR("buffersize")))
{
setBufferSize(OptionConverter::toInt(value, 512));
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("EVALUATORCLASS"), LOG4CXX_STR("evaluatorclass")))
{
setEvaluatorClass(value);
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("FROM"), LOG4CXX_STR("from")))
{
setFrom(value);
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPHOST"), LOG4CXX_STR("smtphost")))
{
setSMTPHost(value);
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPUSERNAME"), LOG4CXX_STR("smtpusername")))
{
setSMTPUsername(value);
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPPASSWORD"), LOG4CXX_STR("smtppassword")))
{
setSMTPPassword(value);
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SUBJECT"), LOG4CXX_STR("subject")))
{
setSubject(value);
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("TO"), LOG4CXX_STR("to")))
{
setTo(value);
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("CC"), LOG4CXX_STR("cc")))
{
setCc(value);
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BCC"), LOG4CXX_STR("bcc")))
{
setBcc(value);
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPPORT"), LOG4CXX_STR("smtpport")))
{
setSMTPPort(OptionConverter::toInt(value, 25));
}
else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("ALLOWPLAINTEXTAUTHENTICATION"), LOG4CXX_STR("allowplaintextauthentication")))
{
// Explicit opt-out from the STARTTLS-before-AUTH requirement;
// only for servers that cannot offer TLS on a trusted network.
_priv->allowPlainTextAuth = OptionConverter::toBoolean(value, false);
}
else
{
AppenderSkeleton::setOption(option, value);
}
}
bool SMTPAppender::asciiCheck(const LogString& value, const LogString& field)
{
for (unsigned int item : value)
{
if (0x7F < item)
{
LogLog::warn(field + LOG4CXX_STR(" contains non-ASCII character"));
return false;
}
}
return true;
}
/**
Activate the specified options, such as the smtp host, the
recipient, from, etc. */
void SMTPAppender::activateOptions( )
{
if (_priv->layout == 0)
{
_priv->errorHandler->error(LOG4CXX_STR("No layout set for appender named [") + _priv->name + LOG4CXX_STR("]."));
}
if (_priv->evaluator == 0)
{
_priv->errorHandler->error(LOG4CXX_STR("No TriggeringEventEvaluator is set for appender [") +
_priv->name + LOG4CXX_STR("]."));
}
if (_priv->smtpHost.empty())
{
_priv->errorHandler->error(LOG4CXX_STR("No smtpHost is set for appender [") +
_priv->name + LOG4CXX_STR("]."));
}
if (_priv->to.empty() && _priv->cc.empty() && _priv->bcc.empty())
{
_priv->errorHandler->error(LOG4CXX_STR("No recipient address is set for appender [") +
_priv->name + LOG4CXX_STR("]."));
}
asciiCheck(_priv->to, LOG4CXX_STR("to"));
asciiCheck(_priv->cc, LOG4CXX_STR("cc"));
asciiCheck(_priv->bcc, LOG4CXX_STR("bcc"));
asciiCheck(_priv->from, LOG4CXX_STR("from"));
#if !LOG4CXX_HAVE_LIBESMTP
_priv->errorHandler->error(LOG4CXX_STR("log4cxx built without SMTP support."));
#endif
}
/**
Perform SMTPAppender specific appending actions, mainly adding
the event to a cyclic buffer and checking if the event triggers
an e-mail to be sent. */
void SMTPAppender::append( const spi::LoggingEventPtr& event )
{
if (!checkEntryConditions())
{
return;
}
// Get a copy of this thread's diagnostic context
event->LoadDC();
_priv->cb.add(event);
if (_priv->evaluator->isTriggeringEvent(event))
{
Pool p;
sendBuffer(p);
}
}
/**
This method determines if there is a sense in attempting to append.
<p>It checks whether there is a set output target and also if
there is a set layout. If these checks fail, then the boolean
value <code>false</code> is returned. */
bool SMTPAppender::checkEntryConditions()
{
#if LOG4CXX_HAVE_LIBESMTP
if ((_priv->to.empty() && _priv->cc.empty() && _priv->bcc.empty()) || _priv->from.empty() || _priv->smtpHost.empty())
{
_priv->errorHandler->error(LOG4CXX_STR("Required SMTP properties missing for appender [") +
_priv->name + LOG4CXX_STR("]."));
return false;
}
if (_priv->evaluator == 0)
{
_priv->errorHandler->error(LOG4CXX_STR("No TriggeringEventEvaluator is set for appender [") +
_priv->name + LOG4CXX_STR("]."));
return false;
}
if (_priv->layout == 0)
{
_priv->errorHandler->error(LOG4CXX_STR("No layout set for appender named [") + _priv->name + LOG4CXX_STR("]."));
return false;
}
return true;
#else
return false;
#endif
}
void SMTPAppender::close()
{
_priv->setClosed();
}
LogString SMTPAppender::getTo() const
{
return _priv->to;
}
void SMTPAppender::setTo(const LogString& addressStr)
{
_priv->to = stripSmtpControl(addressStr, LOG4CXX_STR("To"));
}
LogString SMTPAppender::getCc() const
{
return _priv->cc;
}
void SMTPAppender::setCc(const LogString& addressStr)
{
_priv->cc = stripSmtpControl(addressStr, LOG4CXX_STR("Cc"));
}
LogString SMTPAppender::getBcc() const
{
return _priv->bcc;
}
void SMTPAppender::setBcc(const LogString& addressStr)
{
_priv->bcc = stripSmtpControl(addressStr, LOG4CXX_STR("Bcc"));
}
/**
Send the contents of the cyclic buffer as an e-mail message.
*/
void SMTPAppender::sendBuffer(Pool& p)
{
#if LOG4CXX_HAVE_LIBESMTP
// This thread owns the mutex for this appender, hence no need to synchronize on 'cb'.
try
{
LogString sbuf;
_priv->layout->appendHeader(sbuf);
int len = _priv->cb.length();
for (int i = 0; i < len; i++)
{
LoggingEventPtr event = _priv->cb.get();
_priv->layout->format(sbuf, event);
}
_priv->layout->appendFooter(sbuf);
SMTPSession session(_priv->smtpHost, _priv->smtpPort, _priv->smtpUsername, _priv->smtpPassword, _priv->allowPlainTextAuth, p);
SMTPMessage message(session, _priv->from, _priv->to, _priv->cc,
_priv->bcc, _priv->subject, sbuf, p);
session.send();
}
catch (std::exception& e)
{
_priv->errorHandler->error(LOG4CXX_STR("Error occured while sending e-mail to [") + _priv->smtpHost + LOG4CXX_STR("]."), e, 0);
}
#endif
}
/**
Returns value of the <b>EvaluatorClass</b> option.
*/
LogString SMTPAppender::getEvaluatorClass()
{
return _priv->evaluator == 0 ? LogString() : _priv->evaluator->getClass().getName();
}
LOG4CXX_NS::spi::TriggeringEventEvaluatorPtr SMTPAppender::getEvaluator() const
{
return _priv->evaluator;
}
void SMTPAppender::setEvaluator(const spi::TriggeringEventEvaluatorPtr& trigger)
{
_priv->evaluator = trigger;
}
/**
The <b>BufferSize</b> option takes a positive integer
representing the maximum number of logging events to collect in a
cyclic buffer. When the <code>BufferSize</code> is reached,
oldest events are deleted as new events are added to the
buffer. By default the size of the cyclic buffer is 512 events.
*/
void SMTPAppender::setBufferSize(int sz)
{
if (sz < 1)
{
sz = 1;
}
_priv->bufferSize = sz;
_priv->cb.resize(sz);
}
/**
The <b>EvaluatorClass</b> option takes a string value
representing the name of the class implementing the {@link
TriggeringEventEvaluator} interface. A corresponding object will
be instantiated and assigned as the triggering event evaluator
for the SMTPAppender.
*/
void SMTPAppender::setEvaluatorClass(const LogString& value)
{
ObjectPtr obj = ObjectPtr(Loader::loadClass(value).newInstance());
_priv->evaluator = LOG4CXX_NS::cast<TriggeringEventEvaluator>(obj);
}
int SMTPAppender::getBufferSize() const
{
return _priv->bufferSize;
}