📄 smtpappender.cpp
字号:
/*
* 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/helpers/stringhelper.h>
#include <log4cxx/helpers/stringtokenizer.h>
#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/helpers/synchronized.h>
#if !defined(LOG4CXX)
#define LOG4CXX 1
#endif
#include <log4cxx/private/log4cxx_private.h>
#include <apr_strings.h>
#include <vector>
using namespace log4cxx;
using namespace log4cxx::helpers;
using namespace log4cxx::net;
using namespace log4cxx::spi;
#if LOG4CXX_HAVE_LIBESMTP
#include <auth-client.h>
#include <libesmtp.h>
#endif
namespace log4cxx {
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,
Pool& p) : session(0), authctx(0),
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());
authctx = auth_create_context();
auth_set_mechanism_flags(authctx, AUTH_PLUGIN_PLAIN, 0);
auth_set_interact_cb(authctx, authinteract, (void*) this);
if (*user || *pwd) {
smtp_auth_set_context(session, authctx);
}
}
~SMTPSession() {
smtp_destroy_session(session);
auth_destroy_context(authctx);
}
void send(Pool& p) {
int status = smtp_start_session(session);
if (!status) {
size_t bufSize = 128;
char* buf = p.pstralloc(bufSize);
smtp_strerror(smtp_errno(), buf, bufSize);
throw Exception(buf);
}
}
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(LogString::const_iterator iter = str.begin();
iter != str.end();
iter++) {
unsigned int c = *iter;
if (c > 0x7F) {
c = '?';
}
*current++ = c;
}
*current = 0;
return buf;
}
private:
SMTPSession(SMTPSession&);
SMTPSession& operator=(SMTPSession&);
smtp_session_t session;
auth_context_t authctx;
char* user;
char* pwd;
/**
* This method is called if the SMTP server requests authentication.
*/
static int authinteract(auth_client_request_t request, char **result, int fields,
void *arg) {
SMTPSession* pThis = (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;
}
};
/**
* 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);
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;
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.
*/
static const char* toMessage(const LogString& str, Pool& p) {
//
// 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;
//
// iterator through message
//
for(LogString::const_iterator iter = str.begin();
iter != str.end();
iter++) {
unsigned int c = *iter;
//
// replace non-ASCII characters with '?'
//
if (c > 0x7F) {
*current++ = 0x3F; // '?'
} else if (c == 0x0A || c == 0x0D) {
//
// replace any stray CR or LF with CRLF
// reset start of line
*current++ = 0x0D;
*current++ = 0x0A;
startOfLine = current;
LogString::const_iterator next = iter + 1;
if (next != str.end() && (*next == 0x0A || *next == 0x0D)) {
iter++;
}
} else {
//
// truncate any lines to 1000 characters (including CRLF)
// as required by RFC.
if (current < startOfLine + 998) {
*current++ = (char) c;
}
}
}
*current = 0;
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 {
if (pThis->current) {
*len = strlen(pThis->current);
}
retval = pThis->current;
pThis->current = 0;
}
return retval;
}
};
#endif
class LOG4CXX_EXPORT DefaultEvaluator :
public virtual spi::TriggeringEventEvaluator,
public virtual helpers::ObjectImpl
{
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>.
*/
virtual bool isTriggeringEvent(const spi::LoggingEventPtr& event);
private:
DefaultEvaluator(const DefaultEvaluator&);
DefaultEvaluator& operator=(const DefaultEvaluator&);
}; // class DefaultEvaluator
}
}
IMPLEMENT_LOG4CXX_OBJECT(DefaultEvaluator)
IMPLEMENT_LOG4CXX_OBJECT(SMTPAppender)
DefaultEvaluator::DefaultEvaluator() {
}
bool DefaultEvaluator::isTriggeringEvent(const spi::LoggingEventPtr& event)
{
return event->getLevel()->isGreaterOrEqual(Level::getError());
}
SMTPAppender::SMTPAppender()
: smtpPort(25), bufferSize(512), locationInfo(false), cb(bufferSize),
evaluator(new DefaultEvaluator())
{
}
/**
Use <code>evaluator</code> passed as parameter as the
TriggeringEventEvaluator for this SMTPAppender. */
SMTPAppender::SMTPAppender(spi::TriggeringEventEvaluatorPtr evaluator)
: smtpPort(25), bufferSize(512), locationInfo(false), cb(bufferSize),
evaluator(evaluator)
{
}
SMTPAppender::~SMTPAppender()
{
finalize();
}
bool SMTPAppender::requiresLayout() const {
return true;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -