From 8a35b429c6b1bc73635d9b6db482523619199a90 Mon Sep 17 00:00:00 2001 From: Curt Arnold Date: Tue, 11 Dec 2007 02:11:34 +0000 Subject: [PATCH] LOGCXX-1: Migrate SMTPAppender to libesmtp --- configure.in | 42 +- src/dependencies/esmtp/build.xml | 233 +++++++ src/main/cpp/class.cpp | 2 +- src/main/cpp/domconfigurator.cpp | 18 +- src/main/cpp/propertysetter.cpp | 5 +- src/main/cpp/smtpappender.cpp | 647 ++++++++++++------ src/main/include/log4cxx/net/smtpappender.h | 206 +++--- .../log4cxx/private/log4cxx_private.h.in | 2 +- .../include/log4cxx/xml/domconfigurator.h | 2 +- src/test/cpp/net/smtpappendertestcase.cpp | 71 +- .../resources/input/xml/smtpAppender1.xml | 39 ++ 11 files changed, 914 insertions(+), 353 deletions(-) create mode 100755 src/dependencies/esmtp/build.xml create mode 100644 src/test/resources/input/xml/smtpAppender1.xml diff --git a/configure.in b/configure.in index 680ac758c..4e52a7f5e 100644 --- a/configure.in +++ b/configure.in @@ -121,7 +121,7 @@ AC_SUBST(base_dir) AC_ARG_ENABLE(cppunit, AC_HELP_STRING(--enable-cppunit, - [enable test excution with cppunit (auto)])) + [enable test execution with cppunit (auto)])) enable_tests=yes @@ -198,12 +198,6 @@ fi AC_PROG_RANLIB -# for SocketAppender -AC_CHECK_FUNCS(gethostbyname,, - [AC_CHECK_LIB(nsl,gethostbyname,, - [AC_CHECK_LIB(socket,gethostbyname)])]) -AC_CHECK_FUNCS(setsockopt,,[AC_CHECK_LIB(socket,setsockopt)]) - #for ODBCAppender AC_MSG_CHECKING(for ODBC support) AC_ARG_WITH(ODBC, @@ -256,27 +250,21 @@ AC_CHECK_FUNCS(gettimeofday ftime setenv mbsnrtowcs wcsnrtombs) AC_MSG_CHECKING(for SMTP support) AC_ARG_WITH(SMTP, AC_HELP_STRING(--with-SMTP, [SMTP support. Accepted arguments : - libsmtp, CDO, no (default=no)]), + libesmtp, no (default=no)]), [ac_with_smtp=$withval], [ac_with_smtp=no]) case "$ac_with_smtp" in - CDO) - AC_MSG_RESULT(CDO) - AC_DEFINE(HAVE_CDO_SMTP, 1, SMTP support through Microsoft CDO.) - AC_DEFINE(HAVE_SMTP, 1, SMTP support) - ;; - libsmtp) - AC_MSG_RESULT(libsmtp) - AC_CHECK_LIB([smtp_mime], [libsmtp_session_initialize],, - AC_MSG_ERROR(libsmtp library not found !), - `glib-config --libs` -lsmtp) - AC_DEFINE(HAVE_LIBSMTP, 1, SMTP support through libsmtp library.) - AC_DEFINE(HAVE_SMTP, 1, SMTP support) - LIBS="`glib-config --libs` -lsmtp $LIBS" - CPPFLAGS="`glib-config --cflags` $CPPFLAGS" + libesmtp) + AC_MSG_RESULT(libesmtp) + AC_CHECK_LIB([esmtp], [smtp_create_session],, + AC_MSG_ERROR(libesmtp library not found !), + -lesmtp) + AC_SUBST(HAS_LIBESMTP, 1, SMTP support through libesmtp library.) + LIBS="-lesmtp $LIBS" ;; no) AC_MSG_RESULT(no) + AC_SUBST(HAS_LIBESMTP, 0, SMTP support through libesmtp library.) ;; *) AC_MSG_RESULT(???) @@ -284,16 +272,6 @@ case "$ac_with_smtp" in ;; esac -#for _T - -case "$host" in - *apple-darwin*) - AC_DEFINE(MUST_UNDEF_T, 1, Defined to 1 if macro _T has to be undefined) - ;; -esac - -CPPFLAGS="-DLOG4CXX $CPPFLAGS" - #for wchar_t AC_ARG_ENABLE(wchar_t, AC_HELP_STRING(--enable-wchar_t, diff --git a/src/dependencies/esmtp/build.xml b/src/dependencies/esmtp/build.xml new file mode 100755 index 000000000..b2d78418a --- /dev/null +++ b/src/dependencies/esmtp/build.xml @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/cpp/class.cpp b/src/main/cpp/class.cpp index 404b8b7dc..63f5373c8 100644 --- a/src/main/cpp/class.cpp +++ b/src/main/cpp/class.cpp @@ -114,7 +114,7 @@ const Class& Class::forName(const LogString& className) // const Class* clazz = getRegistry()[lowerName]; if (clazz == 0) { - LogString::size_type pos = className.find_last_of(LOG4CXX_STR('.')); + LogString::size_type pos = className.find_last_of(LOG4CXX_STR(".$")); if (pos != LogString::npos) { LogString terminalName(lowerName, pos + 1, LogString::npos); clazz = getRegistry()[terminalName]; diff --git a/src/main/cpp/domconfigurator.cpp b/src/main/cpp/domconfigurator.cpp index 9e5a15db0..9de2aed94 100644 --- a/src/main/cpp/domconfigurator.cpp +++ b/src/main/cpp/domconfigurator.cpp @@ -46,6 +46,7 @@ #include #include #include +#include using namespace log4cxx; using namespace log4cxx::xml; @@ -224,10 +225,16 @@ AppenderPtr DOMConfigurator::parseAppender(Pool& p, } else if (tagName == TRIGGERING_POLICY_TAG) { - TriggeringPolicyPtr triggerPolicy(parseTriggeringPolicy(p, utf8Decoder, currentElement)); + ObjectPtr policy(parseTriggeringPolicy(p, utf8Decoder, currentElement)); RollingFileAppenderPtr rfa(appender); if (rfa != NULL) { - rfa->setTriggeringPolicy(triggerPolicy); + rfa->setTriggeringPolicy(policy); + } else { + log4cxx::net::SMTPAppenderPtr smtpa(appender); + if (smtpa != NULL) { + log4cxx::spi::TriggeringEventEvaluatorPtr evaluator(policy); + smtpa->setEvaluator(evaluator); + } } } else if (tagName == APPENDER_REF_TAG) @@ -521,7 +528,7 @@ LayoutPtr DOMConfigurator::parseLayout ( /** Used internally to parse a triggering policy */ -TriggeringPolicyPtr DOMConfigurator::parseTriggeringPolicy ( +ObjectPtr DOMConfigurator::parseTriggeringPolicy ( log4cxx::helpers::Pool& p, log4cxx::helpers::CharsetDecoderPtr& utf8Decoder, apr_xml_elem* layout_element) @@ -531,8 +538,7 @@ TriggeringPolicyPtr DOMConfigurator::parseTriggeringPolicy ( try { ObjectPtr instance = Loader::loadClass(className).newInstance(); - TriggeringPolicyPtr layout = instance; - PropertySetter propSetter(layout); + PropertySetter propSetter(instance); for (apr_xml_elem* currentElement = layout_element->first_child; currentElement; @@ -557,7 +563,7 @@ TriggeringPolicyPtr DOMConfigurator::parseTriggeringPolicy ( } propSetter.activate(p); - return layout; + return instance; } catch (Exception& oops) { diff --git a/src/main/cpp/propertysetter.cpp b/src/main/cpp/propertysetter.cpp index 96882b631..2aa788036 100644 --- a/src/main/cpp/propertysetter.cpp +++ b/src/main/cpp/propertysetter.cpp @@ -69,6 +69,7 @@ void PropertySetter::setProperties(helpers::Properties& properties, LogString value = OptionConverter::findAndSubst(key, properties); key = key.substr(len); if (key == LOG4CXX_STR("layout") + && obj != 0 && obj->instanceof(Appender::getStaticClass())) { continue; @@ -86,7 +87,7 @@ void PropertySetter::setProperty(const LogString& option, if (value.empty()) return; - if (obj->instanceof(OptionHandler::getStaticClass())) + if (obj != 0 && obj->instanceof(OptionHandler::getStaticClass())) { LogLog::debug(LOG4CXX_STR("Setting option name=[") + option + LOG4CXX_STR("], value=[") + value + LOG4CXX_STR("]")); @@ -96,7 +97,7 @@ void PropertySetter::setProperty(const LogString& option, void PropertySetter::activate(Pool& p) { - if (obj->instanceof(OptionHandler::getStaticClass())) + if (obj != 0 && obj->instanceof(OptionHandler::getStaticClass())) { OptionHandlerPtr(obj)->activateOptions(p); } diff --git a/src/main/cpp/smtpappender.cpp b/src/main/cpp/smtpappender.cpp index 311bd20a1..2a97db912 100644 --- a/src/main/cpp/smtpappender.cpp +++ b/src/main/cpp/smtpappender.cpp @@ -16,7 +16,6 @@ */ #include -#if LOG4CXX_HAVE_SMTP #include #include @@ -28,14 +27,280 @@ #include #include -#include -#include + +#include +#include using namespace log4cxx; using namespace log4cxx::helpers; using namespace log4cxx::net; using namespace log4cxx::spi; +#if LOG4CXX_HAVE_LIBESMTP +#include +#include +#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(apr_itoa((apr_pool_t*) p.getAPRPool(), 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 = (char*) apr_palloc((apr_pool_t*) p.getAPRPool(), 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 = (char*) apr_palloc((apr_pool_t*) p.getAPRPool(), 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 = apr_pstrdup((apr_pool_t*) p.getAPRPool(), 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 = (char*) apr_palloc((apr_pool_t*) p.getAPRPool(), 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++ = '?'; + } else if (c == '\n' || c == '\r') { + // + // replace any stray CR or LF with CRLF + // reset start of line + *current++ = '\r'; + *current++ = '\n'; + startOfLine = current; + LogString::const_iterator next = iter + 1; + if (next != str.end() && (*next == '\n' || *next == '\r')) { + 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 event the e-mail triggering event? +

This method returns true, if the event level + has ERROR level or higher. Otherwise it returns + false. + */ + 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) @@ -49,8 +314,7 @@ bool DefaultEvaluator::isTriggeringEvent(const spi::LoggingEventPtr& event) SMTPAppender::SMTPAppender() : bufferSize(512), locationInfo(false), cb(bufferSize), -evaluator(new DefaultEvaluator()), session(0), -encoding(LOG4CXX_STR("7bit")), charset(LOG4CXX_STR("us-ascii")) +evaluator(new DefaultEvaluator()), smtpPort(25) { } @@ -59,8 +323,7 @@ Use evaluator passed as parameter as the TriggeringEventEvaluator for this SMTPAppender. */ SMTPAppender::SMTPAppender(spi::TriggeringEventEvaluatorPtr evaluator) : bufferSize(512), locationInfo(false), cb(bufferSize), -evaluator(evaluator), session(0), -encoding(LOG4CXX_STR("7bit")), charset(LOG4CXX_STR("us-ascii")) +evaluator(evaluator), smtpPort(25) { } @@ -69,6 +332,72 @@ SMTPAppender::~SMTPAppender() finalize(); } +bool SMTPAppender::requiresLayout() const { + return true; +} + + +LogString SMTPAppender::getFrom() const { + return from; +} + +void SMTPAppender::setFrom(const LogString& newVal) { + from = newVal; +} + + +LogString SMTPAppender::getSubject() const { + return subject; +} + +void SMTPAppender::setSubject(const LogString& newVal) { + subject = newVal; +} + +LogString SMTPAppender::getSMTPHost() const { + return smtpHost; +} + +void SMTPAppender::setSMTPHost(const LogString& newVal) { + smtpHost = newVal; +} + +int SMTPAppender::getSMTPPort() const { + return smtpPort; +} + +void SMTPAppender::setSMTPPort(int newVal) { + smtpPort = newVal; +} + +bool SMTPAppender::getLocationInfo() const { + return locationInfo; +} + +void SMTPAppender::setLocationInfo(bool newVal) { + locationInfo = newVal; +} + +LogString SMTPAppender::getSMTPUsername() const { + return smtpUsername; +} + +void SMTPAppender::setSMTPUsername(const LogString& newVal) { + smtpUsername = newVal; +} + +LogString SMTPAppender::getSMTPPassword() const { + return smtpPassword; +} + +void SMTPAppender::setSMTPPassword(const LogString& newVal) { + smtpPassword = newVal; +} + + + + + void SMTPAppender::setOption(const LogString& option, const LogString& value) { @@ -88,6 +417,14 @@ void SMTPAppender::setOption(const LogString& option, { 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); @@ -96,13 +433,17 @@ void SMTPAppender::setOption(const LogString& option, { setTo(value); } - else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("CHARSET"), LOG4CXX_STR("charset"))) + else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("CC"), LOG4CXX_STR("cc"))) { - setCharset(value); + setCc(value); } - else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("ENCODING"), LOG4CXX_STR("encoding"))) + else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BCC"), LOG4CXX_STR("bcc"))) { - setEncoding(value); + setBcc(value); + } + else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SMTPPORT"), LOG4CXX_STR("smtpport"))) + { + setSMTPPort(OptionConverter::toInt(value, 25)); } else { @@ -110,126 +451,55 @@ void SMTPAppender::setOption(const LogString& option, } } + +bool SMTPAppender::asciiCheck(const LogString& value, const logchar* field) { + for(LogString::const_iterator iter = value.begin(); + iter != value.end(); + iter++) { + if (0x7F < (unsigned int) *iter) { + LogLog::warn(LogString(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(Pool& p) { - session = ::libsmtp_session_initialize(); - if (session == 0) - { - LogLog::error(LOG4CXX_STR("Could not intialize session.")); - return; + bool activate = true; + if (layout == 0) { + LogLog::error(LOG4CXX_STR("No layout set for appender named [") +name+ LOG4CXX_STR("].")); + activate = false; } - - LOG4CXX_ENCODE_CHAR(ansiFrom, from); - LOG4CXX_ENCODE_CHAR(ansiSubject, subject); - ::libsmtp_set_environment( - const_cast(ansiFrom.c_str()), - const_cast(ansiSubject.c_str()), - 0, - (libsmtp_session_struct *)session); - - std::vector recipients = parseAddress(to); - std::vector::iterator i; - for (i = recipients.begin(); i != recipients.end(); i++) - { - LOG4CXX_ENCODE_CHAR(ansiTo, *i); - if (::libsmtp_add_recipient(LIBSMTP_REC_TO, - const_cast(ansiTo.c_str()), - (libsmtp_session_struct *)session) != 0) - { - LogLog::error(LOG4CXX_STR("Could not add recipient ")+ *i + LOG4CXX_STR(".")); - return; - } + if(evaluator == 0) { + LogLog::error(LOG4CXX_STR("No TriggeringEventEvaluator is set for appender [")+ + name+LOG4CXX_STR("].")); + activate = false; } - - // MIMEPART - if (layout != 0) - { - int mimeType = 0; - LogString contentType(layout->getContentType()); - if (contentType == LOG4CXX_STR("text/plain")) - { - mimeType = LIBSMTP_MIME_SUB_PLAIN; - } - else if (contentType == LOG4CXX_STR("text/html")) - { - mimeType = LIBSMTP_MIME_SUB_HTML; - } - else - { - LogLog::error(LOG4CXX_STR("invalid layout content type: ")+contentType+LOG4CXX_STR(".")); - return; - } - - int charset = 0; - if (this->charset == LOG4CXX_STR("us-ascii")) - { - charset = LIBSMTP_CHARSET_USASCII; - } - else if (this->charset == LOG4CXX_STR("iso8859_1")) - { - charset = LIBSMTP_CHARSET_ISO8859_1; - } - else if (this->charset == LOG4CXX_STR("iso8859_2")) - { - charset = LIBSMTP_CHARSET_ISO8859_2; - } - else if (this->charset == LOG4CXX_STR("iso8859_3")) - { - charset = LIBSMTP_CHARSET_ISO8859_3; - } - else - { - LogLog::error(LOG4CXX_STR("invalid charset: ")+this->charset+LOG4CXX_STR(".")); - return; - } - - int encoding = 0; - if (this->encoding == LOG4CXX_STR("7bit")) - { - encoding = LIBSMTP_ENC_7BIT; - } - else if (this->encoding == LOG4CXX_STR("8bit")) - { - encoding = LIBSMTP_ENC_8BIT; - } - else if (this->encoding == LOG4CXX_STR("binary")) - { - encoding = LIBSMTP_ENC_BINARY; - } - else if (this->encoding == LOG4CXX_STR("base64")) - { - encoding = LIBSMTP_ENC_BASE64; - } - else if (this->encoding == LOG4CXX_STR("quoted")) - { - encoding = LIBSMTP_ENC_QUOTED; - } - else - { - LogLog::error(LOG4CXX_STR("invalid encoding: ")+this->encoding+LOG4CXX_STR(".")); - return; - } - - libsmtp_part_struct * part = 0; - part = ::libsmtp_part_new( - 0, - LIBSMTP_MIME_TEXT, - mimeType, - encoding, - charset, - "content", - (libsmtp_session_struct *)session); - if (part == 0) - { - LogLog::error(LOG4CXX_STR("Error adding part.")); - } + if(smtpHost.empty()) { + LogLog::error(LOG4CXX_STR("No smtpHost is set for appender [")+ + name+LOG4CXX_STR("].")); + activate = false; } - else - { - LogLog::error(LOG4CXX_STR("Layout not set !")); + if(to.empty() && cc.empty() && bcc.empty()) { + LogLog::error(LOG4CXX_STR("No recipient address is set for appender [")+ + name+LOG4CXX_STR("].")); + activate = false; + } + activate &= asciiCheck(to, LOG4CXX_STR("to")); + activate &= asciiCheck(cc, LOG4CXX_STR("cc")); + activate &= asciiCheck(bcc, LOG4CXX_STR("bcc")); + activate &= asciiCheck(from, LOG4CXX_STR("from")); + +#if !LOG4CXX_HAS_LIBESMTP + LogLog::error(LOG4CXX_STR("log4cxx built without SMTP support.")); + activate = false; +#endif + if (activate) { + AppenderSkeleton::activateOptions(p); } } @@ -244,12 +514,11 @@ void SMTPAppender::append(const spi::LoggingEventPtr& event, Pool& p) return; } - event->getNDC(); - -/* if(locationInfo) - { - event.getLocationInformation(); - }*/ + LogString ndc; + event->getNDC(ndc); + event->getThreadName(); + // Get a copy of this thread's MDC. + event->getMDCCopy(); cb.add(event); @@ -266,7 +535,8 @@ there is a set layout. If these checks fail, then the boolean value false is returned. */ bool SMTPAppender::checkEntryConditions() { - if(to.empty() || from.empty() || subject.empty() || smtpHost.empty()) +#if LOG4CXX_HAVE_LIBESMTP + if((to.empty() && cc.empty() && bcc.empty()) || from.empty() || smtpHost.empty()) { errorHandler->error(LOG4CXX_STR("Message not configured.")); return false; @@ -286,31 +556,39 @@ bool SMTPAppender::checkEntryConditions() return false; } return true; +#else + return false; +#endif } -void SMTPAppender::close() -{ - synchronized sync(this); - if (!this->closed && session != 0) - { - ::libsmtp_free((libsmtp_session_struct *)session); - session = 0; - } + +void SMTPAppender::close() { this->closed = true; } -std::vector SMTPAppender::parseAddress(const LogString& addressStr) -{ - std::vector addresses; +LogString SMTPAppender::getTo() const{ + return to; +} - StringTokenizer st(addressStr, LOG4CXX_STR(",")); - while (st.hasMoreTokens()) - { - addresses.push_back(st.nextToken()); - } +void SMTPAppender::setTo(const LogString& addressStr) { + to = addressStr; +} + +LogString SMTPAppender::getCc() const{ + return cc; +} + +void SMTPAppender::setCc(const LogString& addressStr) { + cc = addressStr; +} - return addresses; +LogString SMTPAppender::getBcc() const{ + return bcc; +} + +void SMTPAppender::setBcc(const LogString& addressStr) { + bcc = addressStr; } /** @@ -318,6 +596,7 @@ Send the contents of the cyclic buffer as an e-mail message. */ void SMTPAppender::sendBuffer(Pool& p) { +#if LOG4CXX_HAS_LIBESMTP // Note: this code already owns the monitor for this // appender. This frees us from needing to synchronize on 'cb'. try @@ -328,75 +607,25 @@ void SMTPAppender::sendBuffer(Pool& p) int len = cb.length(); for(int i = 0; i < len; i++) { - //sbuf.append(MimeUtility.encodeText(layout.format(cb.get()))); LoggingEventPtr event = cb.get(); layout->format(sbuf, event, p); } layout->appendFooter(sbuf, p); - LOG4CXX_ENCODE_CHAR(aSmtpHost, smtpHost); - - /* This starts the SMTP connection */ - if (::libsmtp_connect( - const_cast(aSmtpHost.c_str()), - 0, - 0, - (libsmtp_session_struct *)session) != 0) - { - LogLog::error(LOG4CXX_STR("Error occured while starting the SMTP connection.")); - return; - } - - /* This will conduct the SMTP dialogue */ - if (::libsmtp_dialogue((libsmtp_session_struct *)session) != 0) - { - LogLog::error(LOG4CXX_STR("Error occured while conducting the SMTP dialogue.")); - return; - } - - /* Now lets send the headers */ - if (::libsmtp_headers((libsmtp_session_struct *)session) != 0) - { - LogLog::error(LOG4CXX_STR("Error occured while sending the headers.")); - return; - } - - /* Now lets send the MIME headers */ - if (::libsmtp_mime_headers((libsmtp_session_struct *)session) != 0) - { - LogLog::error(LOG4CXX_STR("Error occured while sending the MIME headers.")); - return; - } - - LOG4CXX_ENCODE_CHAR(s, sbuf); - if (::libsmtp_part_send( - const_cast(s.c_str()), - s.length(), - (libsmtp_session_struct *)session) != 0) - { - LogLog::error(LOG4CXX_STR("Error occured while sending the message body.")); - } - - /* This ends the body part */ - if (::libsmtp_body_end((libsmtp_session_struct *)session) != 0) - { - LogLog::error(LOG4CXX_STR("Error occured while ending the body part.")); - return; - } - - /* This ends the connection gracefully */ - if (::libsmtp_quit((libsmtp_session_struct *)session) != 0) - { - LogLog::error(LOG4CXX_STR("Error occured while ending the connection.")); - return; - } + SMTPSession session(smtpHost, smtpPort, smtpUsername, smtpPassword, p); + + SMTPMessage message(session, from, to, cc, + bcc, subject, sbuf, p); + + session.send(p); } catch(std::exception& e) { LogLog::error(LOG4CXX_STR("Error occured while sending e-mail notification."), e); } +#endif } /** @@ -407,6 +636,14 @@ LogString SMTPAppender::getEvaluatorClass() return evaluator == 0 ? LogString() : evaluator->getClass().getName(); } +log4cxx::spi::TriggeringEventEvaluatorPtr SMTPAppender::getEvaluator() const { + return evaluator; +} + +void SMTPAppender::setEvaluator(log4cxx::spi::TriggeringEventEvaluatorPtr& trigger) { + evaluator = trigger; +} + /** The BufferSize option takes a positive integer representing the maximum number of logging events to collect in a @@ -433,5 +670,3 @@ void SMTPAppender::setEvaluatorClass(const LogString& value) TriggeringEventEvaluator::getStaticClass(), evaluator); } -#endif //LOG4CXX_HAVE_SMTP - diff --git a/src/main/include/log4cxx/net/smtpappender.h b/src/main/include/log4cxx/net/smtpappender.h index 59396c6ac..64d43d4b1 100644 --- a/src/main/include/log4cxx/net/smtpappender.h +++ b/src/main/include/log4cxx/net/smtpappender.h @@ -23,14 +23,10 @@ #include #include - namespace log4cxx { namespace net { - class SMTPAppender; - typedef helpers::ObjectPtrT SMTPAppenderPtr; - /** Send an e-mail when a specific logging event occurs, typically on errors or fatal errors. @@ -44,18 +40,30 @@ namespace log4cxx class LOG4CXX_EXPORT SMTPAppender : public AppenderSkeleton { private: + + private: + SMTPAppender(const SMTPAppender&); + SMTPAppender& operator=(const SMTPAppender&); + static bool asciiCheck(const LogString&, const logchar*); + /** + This method determines if there is a sense in attempting to append. +

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 false is returned. */ + bool checkEntryConditions(); + 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; - void * session; - LogString encoding; - LogString charset; - - protected: spi::TriggeringEventEvaluatorPtr evaluator; public: @@ -97,45 +105,35 @@ namespace log4cxx an e-mail to be sent. */ virtual void append(const spi::LoggingEventPtr& event, log4cxx::helpers::Pool& p); - /** - This method determines if there is a sense in attempting to append. -

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 false is returned. */ - bool checkEntryConditions(); virtual void close(); - std::vector parseAddress(const LogString& addressStr); - /** Returns value of the To option. */ - inline const LogString& getTo() const - { return to; } + LogString getTo() const; /** - The SMTPAppender requires a {@link - Layout layout}. */ - virtual bool requiresLayout() const - { return true; } + Returns value of the cc option. + */ + LogString getCc() const; /** - Send the contents of the cyclic buffer as an e-mail message. + Returns value of the bcc option. */ - void sendBuffer(log4cxx::helpers::Pool& p); + LogString getBcc() const; + /** - Returns value of the Charset option. - */ - inline const LogString& getCharset() const - { return charset; } + The SMTPAppender requires a {@link + Layout layout}. */ + virtual bool requiresLayout() const; /** - Returns value of the Encoding option. + Send the contents of the cyclic buffer as an e-mail message. */ - inline const LogString& getEncoding() const - { return encoding; } + void sendBuffer(log4cxx::helpers::Pool& p); + /** Returns value of the EvaluatorClass option. @@ -145,44 +143,25 @@ namespace log4cxx /** Returns value of the From option. */ - inline const LogString& getFrom() const - { return from; } + LogString getFrom() const; /** Returns value of the Subject option. */ - inline const LogString& getSubject() const - { return subject; } + LogString getSubject() const; - /** - The Charset option takes a string value which should be the - charset of the mail (us-ascii, iso8859_1, - iso8859_2, iso8859_3). - */ - inline void setCharset(const LogString& charset) - { this->charset.assign(charset); } - - /** - The Encoding option takes a string value which should be the - encoding type of the mail (7bit, 8bit, - base64, binary, quoted). - */ - inline void setEncoding(const LogString& charset) - { this->encoding.assign(encoding); } /** The From option takes a string value which should be a e-mail address of the sender. */ - inline void setFrom(const LogString& from) - { this->from.assign(from); } + void setFrom(const LogString& from); /** The Subject option takes a string value which should be a the subject of the e-mail message. */ - inline void setSubject(const LogString& subject) - { this->subject.assign(subject); } + void setSubject(const LogString& subject); /** The BufferSize option takes a positive integer @@ -197,21 +176,64 @@ namespace log4cxx The SMTPHost option takes a string value which should be a the host name of the SMTP server that will send the e-mail message. */ - inline void setSMTPHost(const LogString& smtpHost) - { this->smtpHost.assign(smtpHost); } + void setSMTPHost(const LogString& smtpHost); + + /** + Returns value of the SMTPHost option. + */ + LogString getSMTPHost() const; + + /** + The SMTPPort option takes a string value which should be a + the port of the SMTP server that will send the e-mail message. + */ + void setSMTPPort(int port); /** Returns value of the SMTPHost option. */ - inline const LogString& getSMTPHost() const - { return smtpHost; } + int getSMTPPort() const; /** The To option takes a string value which should be a comma separated list of e-mail address of the recipients. */ - inline void setTo(const LogString& to) - { this->to.assign(to); } + void setTo(const LogString& to); + + /** + The Cc option takes a string value which should be a + comma separated list of e-mail address of the cc'd recipients. + */ + void setCc(const LogString& to); + + /** + The Bcc option takes a string value which should be a + comma separated list of e-mail address of the bcc'd recipients. + */ + void setBcc(const LogString& to); + + + /** + The SMTPUsername option takes a string value which should be a + the user name for the SMTP server. + */ + void setSMTPUsername(const LogString& newVal); + + /** + Returns value of the SMTPUsername option. + */ + LogString getSMTPUsername() const; + + /** + The SMTPPassword option takes a string value which should be a + the password for the SMTP server. + */ + void setSMTPPassword(const LogString& newVal); + + /** + Returns value of the SMTPPassword option. + */ + LogString getSMTPPassword() const; /** Returns value of the BufferSize option. @@ -219,6 +241,19 @@ namespace log4cxx inline int getBufferSize() const { return bufferSize; } + + /** + * Gets the current triggering evaluator. + * @return triggering evaluator. + */ + log4cxx::spi::TriggeringEventEvaluatorPtr getEvaluator() const; + + /** + * Sets the triggering evaluator. + * @param trigger triggering evaluator. + */ + void setEvaluator(log4cxx::spi::TriggeringEventEvaluatorPtr& trigger); + /** The EvaluatorClass option takes a string value representing the name of the class implementing the @@ -227,56 +262,21 @@ namespace log4cxx for the SMTPAppender. */ void setEvaluatorClass(const LogString& value); - + /** - The LocationInfo option takes a boolean value. By - default, it is set to false which means there will be no effort - to extract the location information related to the event. As a - result, the layout that formats the events as they are sent out - in an e-mail is likely to place the wrong location information - (if present in the format). - -

Location information extraction is comparatively very slow and - should be avoided unless performance is not a concern. + The LocationInfo option is provided for compatibility with log4j + and has no effect in log4cxx. */ - inline void setLocationInfo(bool locationInfo) - { this->locationInfo = locationInfo; } + void setLocationInfo(bool locationInfo); /** Returns value of the LocationInfo option. */ - inline bool getLocationInfo() const - { return locationInfo; } - - private: - SMTPAppender(const SMTPAppender&); - SMTPAppender& operator=(const SMTPAppender&); - + bool getLocationInfo() const; }; // class SMTPAppender + + typedef helpers::ObjectPtrT SMTPAppenderPtr; - class LOG4CXX_EXPORT DefaultEvaluator : - public virtual spi::TriggeringEventEvaluator, - public virtual helpers::ObjectImpl - { - public: - DECLARE_LOG4CXX_OBJECT(DefaultEvaluator) - BEGIN_LOG4CXX_CAST_MAP() - LOG4CXX_CAST_ENTRY(spi::TriggeringEventEvaluator) - END_LOG4CXX_CAST_MAP() - - DefaultEvaluator(); - - /** - Is this event the e-mail triggering event? -

This method returns true, if the event level - has ERROR level or higher. Otherwise it returns - false. - */ - virtual bool isTriggeringEvent(const spi::LoggingEventPtr& event); - private: - DefaultEvaluator(const DefaultEvaluator&); - DefaultEvaluator& operator=(const DefaultEvaluator&); - }; // class DefaultEvaluator } // namespace net } // namespace log4cxx diff --git a/src/main/include/log4cxx/private/log4cxx_private.h.in b/src/main/include/log4cxx/private/log4cxx_private.h.in index 3c65d63d7..58994071a 100644 --- a/src/main/include/log4cxx/private/log4cxx_private.h.in +++ b/src/main/include/log4cxx/private/log4cxx_private.h.in @@ -54,7 +54,7 @@ typedef int log4cxx_status_t; // Capabilities should be checked in client code by // attempting creation of the corresponding appender. // -#define LOG4CXX_HAVE_SMTP 0 +#define LOG4CXX_HAVE_LIBESMTP @HAS_LIBESMTP@ #define LOG4CXX_HAVE_SYSLOG @HAS_SYSLOG@ #endif diff --git a/src/main/include/log4cxx/xml/domconfigurator.h b/src/main/include/log4cxx/xml/domconfigurator.h index ff756cf59..73f8b01b7 100644 --- a/src/main/include/log4cxx/xml/domconfigurator.h +++ b/src/main/include/log4cxx/xml/domconfigurator.h @@ -168,7 +168,7 @@ namespace log4cxx /** Used internally to parse the logger factory element. */ - log4cxx::rolling::TriggeringPolicyPtr parseTriggeringPolicy( + log4cxx::helpers::ObjectPtr parseTriggeringPolicy( log4cxx::helpers::Pool& p, log4cxx::helpers::CharsetDecoderPtr& utf8Decoder, apr_xml_elem* factoryElement); diff --git a/src/test/cpp/net/smtpappendertestcase.cpp b/src/test/cpp/net/smtpappendertestcase.cpp index 7290fbeb8..04d718d69 100644 --- a/src/test/cpp/net/smtpappendertestcase.cpp +++ b/src/test/cpp/net/smtpappendertestcase.cpp @@ -24,9 +24,45 @@ #include #include #include "../appenderskeletontestcase.h" +#include +#include +#include using namespace log4cxx; using namespace log4cxx::helpers; +using namespace log4cxx::net; +using namespace log4cxx::xml; +using namespace log4cxx::spi; + +namespace log4cxx { + namespace net { + + class MockTriggeringEventEvaluator : + public virtual spi::TriggeringEventEvaluator, + public virtual helpers::ObjectImpl + { + public: + DECLARE_LOG4CXX_OBJECT(MockTriggeringEventEvaluator) + BEGIN_LOG4CXX_CAST_MAP() + LOG4CXX_CAST_ENTRY(MockTriggeringEventEvaluator) + LOG4CXX_CAST_ENTRY(spi::TriggeringEventEvaluator) + END_LOG4CXX_CAST_MAP() + + MockTriggeringEventEvaluator() { + } + + virtual bool isTriggeringEvent(const spi::LoggingEventPtr& event) { + return true; + } + private: + MockTriggeringEventEvaluator(const MockTriggeringEventEvaluator&); + MockTriggeringEventEvaluator& operator=(const MockTriggeringEventEvaluator&); + }; + } +} + +IMPLEMENT_LOG4CXX_OBJECT(MockTriggeringEventEvaluator) + /** Unit tests of log4cxx::SocketAppender @@ -39,7 +75,8 @@ class SMTPAppenderTestCase : public AppenderSkeletonTestCase // CPPUNIT_TEST(testDefaultThreshold); CPPUNIT_TEST(testSetOptionThreshold); - + CPPUNIT_TEST(testTrigger); + CPPUNIT_TEST(testInvalid); CPPUNIT_TEST_SUITE_END(); @@ -48,6 +85,38 @@ class SMTPAppenderTestCase : public AppenderSkeletonTestCase AppenderSkeleton* createAppenderSkeleton() const { return new log4cxx::net::SMTPAppender(); } + + void setUp() { + } + + void tearDown() { + LogManager::resetConfiguration(); + } + + /** + * Tests that triggeringPolicy element will set evaluator. + */ + void testTrigger() { + DOMConfigurator::configure("input/xml/smtpAppender1.xml"); + SMTPAppenderPtr appender(Logger::getRootLogger()->getAppender(LOG4CXX_STR("A1"))); + TriggeringEventEvaluatorPtr evaluator(appender->getEvaluator()); + CPPUNIT_ASSERT_EQUAL(true, evaluator->instanceof(MockTriggeringEventEvaluator::getStaticClass())); + } + + void testInvalid() { + SMTPAppenderPtr appender(new SMTPAppender()); + appender->setSMTPHost(LOG4CXX_STR("smtp.invalid")); + appender->setTo(LOG4CXX_STR("you@example.invalid")); + appender->setFrom(LOG4CXX_STR("me@example.invalid")); + appender->setLayout(new TTCCLayout()); + Pool p; + appender->activateOptions(p); + LoggerPtr root(Logger::getRootLogger()); + root->addAppender(appender); + LOG4CXX_INFO(root, "Hello, World."); + LOG4CXX_ERROR(root, "Sending Message"); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(SMTPAppenderTestCase); diff --git a/src/test/resources/input/xml/smtpAppender1.xml b/src/test/resources/input/xml/smtpAppender1.xml new file mode 100644 index 000000000..c1aebdf1c --- /dev/null +++ b/src/test/resources/input/xml/smtpAppender1.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + +