1 #include "cetlib/PluginTypeDeducer.h"
2 #include "fhiclcpp/ParameterSet.h"
3 #if MESSAGEFACILITY_HEX_VERSION >= 0x20103
4 #include "fhiclcpp/types/ConfigurationTable.h"
5 #include "fhiclcpp/types/Sequence.h"
6 #include "fhiclcpp/types/TableFragment.h"
10 #include "messagefacility/MessageService/ELdestination.h"
11 #include "messagefacility/Utilities/ELseverityLevel.h"
12 #if MESSAGEFACILITY_HEX_VERSION < 0x20201 // v2_02_01 is s67
13 # include "messagefacility/MessageService/MessageDrop.h"
15 # include "messagefacility/MessageLogger/MessageLogger.h"
17 #include "messagefacility/Utilities/exception.h"
25 #include <arpa/inet.h>
28 #include <netinet/in.h>
29 #include <boost/thread.hpp>
31 #include <QtCore/QString>
33 #include "cetlib/compiler_macros.h"
37 using mf::service::ELdestination;
38 using mf::ELseverityLevel;
46 #if MESSAGEFACILITY_HEX_VERSION >= 0x20103
49 using strings_t = fhicl::Sequence<std::string>::default_type;
51 fhicl::TableFragment<ELdestination::Config> elDestConfig;
52 fhicl::Atom<std::string> host{ fhicl::Name{
"host" },fhicl::Comment{
"SMTP Server hostname" },
"smtp.fnal.gov" };
53 fhicl::Atom<int> port{ fhicl::Name{
"port" },fhicl::Comment{
"SMTP Server port" },25 };
54 fhicl::Sequence<std::string> to{ fhicl::Name{
"to_addresses" }, fhicl::Comment{
"The list of email addresses that SMTP mfPlugin should sent to" } , strings_t {} };
55 fhicl::Atom<std::string> from{ fhicl::Name{
"from_address" },fhicl::Comment{
"Source email address" } };
56 fhicl::Atom<std::string> subject{ fhicl::Name{
"subject" },fhicl::Comment{
"Subject of the email message" },
"MessageFacility SMTP Message Digest" };
57 fhicl::Atom<std::string> messageHeader{ fhicl::Name{
"message_header" },fhicl::Comment{
"String to preface messages with in email body" },
"" };
58 fhicl::Atom<bool> useSmtps{ fhicl::Name{
"use_smtps" },fhicl::Comment{
"Use SMTPS protocol" },
false };
59 fhicl::Atom<std::string> user{ fhicl::Name{
"smtp_username" },fhicl::Comment{
"Username for SMTP server" },
"" };
60 fhicl::Atom<std::string> pw{ fhicl::Name{
"smtp_password" },fhicl::Comment{
"Password for SMTP server" },
"" };
61 fhicl::Atom<bool> verifyCert{ fhicl::Name{
"verify_host_ssl_certificate" },fhicl::Comment{
"Whether to run full SSL verify on SMTP server in SMTPS mode" },
true };
62 fhicl::Atom<size_t> sendInterval{ fhicl::Name{
"email_send_interval_seconds" },fhicl::Comment{
"Only send email every N seconds" }, 15 };
64 using Parameters = fhicl::WrappedTable<Config>;
71 #if MESSAGEFACILITY_HEX_VERSION < 0x20103 // v2_01_03 is s58, pre v2_01_03 is s50
72 ELSMTP(
const fhicl::ParameterSet& pset);
74 ELSMTP(Parameters
const& pset);
80 while (sending_thread_active_) usleep(1000);
88 virtual void routePayload(
const std::ostringstream& o,
const ErrorObj& msg)
override;
92 std::string generateMessageId_()
const;
93 std::string dateTimeNow_();
94 std::string to_html(std::string msgString,
const ErrorObj& msg);
96 std::string smtp_host_;
98 std::vector<std::string> to_;
100 std::string subject_;
101 std::string message_prefix_;
105 std::string hostname_;
106 std::string hostaddr_;
110 std::string username_;
111 std::string password_;
112 bool ssl_verify_host_cert_;
114 std::atomic<bool> sending_thread_active_;
115 std::atomic<bool> abort_sleep_;
116 size_t send_interval_s_;
117 mutable std::mutex message_mutex_;
118 std::ostringstream message_contents_;
129 #if MESSAGEFACILITY_HEX_VERSION < 0x20103 // v2_01_03 is s58, pre v2_01_03 is s50
131 : ELdestination(pset)
132 , smtp_host_(pset.get<std::string>(
"host",
"smtp.fnal.gov"))
133 , port_(pset.get<int>(
"port", 25))
134 , to_(pset.get<std::vector<std::string>>(
"to_addresses"))
135 , from_(pset.get<std::string>(
"from_address"))
136 , subject_(pset.get<std::string>(
"subject",
"MessageFacility SMTP Message Digest"))
137 , message_prefix_(pset.get<std::string>(
"message_header",
""))
138 , pid_(static_cast<long>(getpid()))
139 , use_ssl_(pset.get<bool>(
"use_smtps", false))
140 , username_(pset.get<std::string>(
"smtp_username",
""))
141 , password_(pset.get<std::string>(
"smtp_password",
""))
142 , ssl_verify_host_cert_(pset.get<bool>(
"verify_host_ssl_certificate", true))
143 , sending_thread_active_(false)
144 , abort_sleep_(false)
145 , send_interval_s_(pset.get<size_t>(
"email_send_interval_seconds", 15))
148 : ELdestination(pset().elDestConfig())
149 , smtp_host_(pset().host())
150 , port_(pset().port())
152 , from_(pset().from())
153 , subject_(pset().subject())
154 , message_prefix_(pset().messageHeader())
155 , pid_(static_cast<long>(getpid()))
156 , use_ssl_(pset().useSmtps())
157 , username_(pset().user())
158 , password_(pset().pw())
159 , ssl_verify_host_cert_(pset().verifyCert())
160 , sending_thread_active_(false)
161 , abort_sleep_(false)
162 , send_interval_s_(pset().sendInterval())
166 char hostname_c[1024];
167 hostname_ = (gethostname(hostname_c, 1023) == 0) ? hostname_c :
"Unkonwn Host";
170 hostent* host =
nullptr;
171 host = gethostbyname(hostname_c);
176 char* ip = inet_ntoa(*(
struct in_addr *)host->h_addr);
182 struct ifaddrs* ifAddrStruct =
nullptr;
183 struct ifaddrs* ifa =
nullptr;
184 void* tmpAddrPtr =
nullptr;
186 if (getifaddrs(&ifAddrStruct))
189 hostaddr_ =
"127.0.0.1";
194 for (ifa = ifAddrStruct; ifa !=
nullptr; ifa = ifa->ifa_next)
196 if (ifa->ifa_addr->sa_family == AF_INET)
199 tmpAddrPtr = &((
struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
200 char addressBuffer[INET_ADDRSTRLEN];
201 inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
202 hostaddr_ = addressBuffer;
205 else if (ifa->ifa_addr->sa_family == AF_INET6)
208 tmpAddrPtr = &((
struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
209 char addressBuffer[INET6_ADDRSTRLEN];
210 inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
211 hostaddr_ = addressBuffer;
215 if (!hostaddr_.empty()
216 && hostaddr_.compare(
"127.0.0.1")
217 && hostaddr_.compare(
"::1"))
221 if (hostaddr_.empty())
222 hostaddr_ =
"127.0.0.1";
228 std::ostringstream pid_ostr;
229 pid_ostr <<
"/proc/" << pid_ <<
"/exe";
230 exe = realpath(pid_ostr.str().c_str(), NULL);
232 size_t end = exe.find(
'\0');
233 size_t start = exe.find_last_of(
'/', end);
235 app_ = exe.substr(start + 1, end - start - 1);
239 std::string ELSMTP::to_html(std::string msgString,
const ErrorObj& msg)
241 auto sevid = msg.xid().severity().getLevel();
243 QString text_ = QString(
"<font color=");
247 case mf::ELseverityLevel::ELsev_success:
248 case mf::ELseverityLevel::ELsev_zeroSeverity:
249 case mf::ELseverityLevel::ELsev_unspecified:
250 text_ += QString(
"#505050>");
253 case mf::ELseverityLevel::ELsev_info:
254 text_ += QString(
"#008000>");
257 case mf::ELseverityLevel::ELsev_warning:
258 text_ += QString(
"#E08000>");
261 case mf::ELseverityLevel::ELsev_error:
262 case mf::ELseverityLevel::ELsev_severe:
263 case mf::ELseverityLevel::ELsev_highestSeverity:
264 text_ += QString(
"#FF0000>");
271 text_ += QString(
"<pre>")
272 + QString(msgString.c_str()).toHtmlEscaped()
275 text_ += QString(
"</font>");
276 return text_.toStdString();
284 std::unique_lock<std::mutex>(message_mutex_);
285 message_contents_ << to_html(oss.str(), msg);
287 if (!sending_thread_active_)
289 sending_thread_active_ =
true;
290 boost::thread t([=] { send_message_(); });
295 void ELSMTP::send_message_()
298 while (!abort_sleep_ && slept < send_interval_s_ * 1000000)
306 std::unique_lock<std::mutex> lk(message_mutex_);
307 payload = message_contents_.str();
308 message_contents_.str(
"");
310 std::string destination = (use_ssl_ ?
"smtps://" :
"smtp://") + smtp_host_ +
":" + std::to_string(port_);
313 std::vector<const char*> to;
314 to.reserve(to_.size());
315 std::string toString;
316 for (
size_t i = 0; i < to_.size(); ++i)
318 to.push_back(to_[i].c_str());
320 if (i < to_.size() - 1)
326 std::ostringstream headers_builder;
328 headers_builder <<
"Date: " << dateTimeNow_() <<
"\r\n";
329 headers_builder <<
"To: " << toString <<
"\r\n";
330 headers_builder <<
"From: " << from_ <<
"\r\n";
331 headers_builder <<
"Message-ID: <" + generateMessageId_() +
"@" + from_.substr(from_.find(
'@') + 1) +
">\r\n";
332 headers_builder <<
"Subject: " << subject_ <<
" @ " << dateTimeNow_() <<
" from PID " << getpid() <<
"\r\n";
333 headers_builder <<
"Content-Type: text/html; charset=\"UTF-8\"\r\n";
334 headers_builder <<
"\r\n";
336 std::string headers = headers_builder.str();
337 std::ostringstream message_builder;
338 message_builder << headers <<
"<html><body><p>" << message_prefix_ <<
"</p>" << payload <<
"</body></html>";
339 std::string payloadWithHeaders = message_builder.str();
344 send_message_ssl(destination.c_str(), &to[0], to_.size(), from_.c_str(), payloadWithHeaders.c_str(), payloadWithHeaders.size(), username_.c_str(), password_.c_str(), !ssl_verify_host_cert_);
348 send_message(destination.c_str(), &to[0], to_.size(), from_.c_str(), payloadWithHeaders.c_str(), payloadWithHeaders.size());
350 sending_thread_active_ =
false;
354 std::string ELSMTP::generateMessageId_()
const
356 const size_t MESSAGE_ID_LEN = 37;
363 ret.resize(MESSAGE_ID_LEN);
364 size_t datelen = std::strftime(&ret[0], MESSAGE_ID_LEN,
"%Y%m%d%H%M%S", &t);
365 static const std::string alphaNum{
367 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
368 "abcdefghijklmnopqrstuvwxyz" };
370 std::uniform_int_distribution<> dis(0, alphaNum.length() - 1);
371 std::generate_n(ret.begin() + datelen, MESSAGE_ID_LEN - datelen, [&]() {
return alphaNum[dis(gen)]; });
375 std::string ELSMTP::dateTimeNow_()
377 const int RFC5322_TIME_LEN = 32;
380 ret.resize(RFC5322_TIME_LEN);
387 strftime(&ret[0], RFC5322_TIME_LEN,
"%a, %d %b %Y %H:%M:%S %z", t);
399 #ifndef EXTERN_C_FUNC_DECLARE_START
400 #define EXTERN_C_FUNC_DECLARE_START extern "C" {
403 EXTERN_C_FUNC_DECLARE_START
404 auto makePlugin(
const std::string&,
405 const fhicl::ParameterSet& pset)
407 return std::make_unique<mfplugins::ELSMTP>(pset);
411 DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)
void send_message_ssl(const char *dest, const char *to[], size_t to_size, const char *from, const char *payload, size_t payload_size, const char *username, const char *pw, int disableVerify)
Sends a message to the given SMTP server, using SSL encryption.
SMTP Message Facility destination plugin (Using libcurl)
void send_message(const char *dest, const char *to[], size_t to_size, const char *from, const char *payload, size_t payload_size)
Sends a message to the given SMTP server.
virtual void routePayload(const std::ostringstream &o, const ErrorObj &msg) override
Serialize a MessageFacility message to the output.
ELSMTP(const fhicl::ParameterSet &pset)
ELSMTP Constructor