1 #include "cetlib/PluginTypeDeducer.h"
2 #include "fhiclcpp/ParameterSet.h"
4 #include "messagefacility/MessageService/ELdestination.h"
5 #include "messagefacility/Utilities/ELseverityLevel.h"
6 #if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
7 # include "messagefacility/MessageService/ELcontextSupplier.h"
8 # include "messagefacility/MessageLogger/MessageDrop.h"
10 # include "messagefacility/MessageService/MessageDrop.h"
12 #include "messagefacility/Utilities/exception.h"
20 #include <arpa/inet.h>
23 #include <netinet/in.h>
24 #include <boost/thread.hpp>
26 #include <QtCore/QString>
31 using mf::service::ELdestination;
32 using mf::ELseverityLevel;
34 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
35 using mf::service::ELcontextSupplier;
45 ELSMTP(
const fhicl::ParameterSet& pset);
50 while (sending_thread_active_) usleep(1000);
53 virtual void routePayload(
const std::ostringstream&,
const ErrorObj& msg
54 #
if MESSAGEFACILITY_HEX_VERSION < 0x20002
55 ,
const ELcontextSupplier&
61 std::string generateMessageId_()
const;
62 std::string dateTimeNow_();
63 std::string to_html(std::string msgString,
const ErrorObj& msg);
65 std::string smtp_host_;
67 std::vector<std::string> to_;
70 std::string message_prefix_;
74 std::string hostname_;
75 std::string hostaddr_;
79 std::string username_;
80 std::string password_;
81 bool ssl_verify_host_cert_;
83 std::atomic<bool> sending_thread_active_;
84 std::atomic<bool> abort_sleep_;
85 size_t send_interval_s_;
86 mutable std::mutex message_mutex_;
87 std::ostringstream message_contents_;
98 ELSMTP::ELSMTP(
const fhicl::ParameterSet& pset)
100 , smtp_host_(pset.get<std::string>(
"host",
"smtp.fnal.gov"))
101 , port_(pset.get<int>(
"port", 25))
102 , to_(pset.get<std::vector<std::string>>(
"to_addresses"))
103 , from_(pset.get<std::string>(
"from_address"))
104 , subject_(pset.get<std::string>(
"subject",
"MessageFacility SMTP Message Digest"))
105 , message_prefix_(pset.get<std::string>(
"message_header",
""))
106 , pid_(static_cast<long>(getpid()))
107 , use_ssl_(pset.get<bool>(
"use_smtps", false))
108 , username_(pset.get<std::string>(
"smtp_username",
""))
109 , password_(pset.get<std::string>(
"smtp_password",
""))
110 , ssl_verify_host_cert_(pset.get<bool>(
"verify_host_ssl_certificate", true))
111 , sending_thread_active_(false)
112 , abort_sleep_(false)
113 , send_interval_s_(pset.get<size_t>(
"email_send_interval_seconds", 15))
116 char hostname_c[1024];
117 hostname_ = (gethostname(hostname_c, 1023) == 0) ? hostname_c :
"Unkonwn Host";
120 hostent* host =
nullptr;
121 host = gethostbyname(hostname_c);
126 char* ip = inet_ntoa(*(
struct in_addr *)host->h_addr);
132 struct ifaddrs* ifAddrStruct =
nullptr;
133 struct ifaddrs* ifa =
nullptr;
134 void* tmpAddrPtr =
nullptr;
136 if (getifaddrs(&ifAddrStruct))
139 hostaddr_ =
"127.0.0.1";
144 for (ifa = ifAddrStruct; ifa !=
nullptr; ifa = ifa->ifa_next)
146 if (ifa->ifa_addr->sa_family == AF_INET)
149 tmpAddrPtr = &((
struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
150 char addressBuffer[INET_ADDRSTRLEN];
151 inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
152 hostaddr_ = addressBuffer;
155 else if (ifa->ifa_addr->sa_family == AF_INET6)
158 tmpAddrPtr = &((
struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
159 char addressBuffer[INET6_ADDRSTRLEN];
160 inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
161 hostaddr_ = addressBuffer;
165 if (!hostaddr_.empty()
166 && hostaddr_.compare(
"127.0.0.1")
167 && hostaddr_.compare(
"::1"))
171 if (hostaddr_.empty())
172 hostaddr_ =
"127.0.0.1";
178 std::ostringstream pid_ostr;
179 pid_ostr <<
"/proc/" << pid_ <<
"/exe";
180 exe = realpath(pid_ostr.str().c_str(), NULL);
182 size_t end = exe.find(
'\0');
183 size_t start = exe.find_last_of(
'/', end);
185 app_ = exe.substr(start + 1, end - start - 1);
189 std::string ELSMTP::to_html(std::string msgString,
const ErrorObj& msg)
191 # if MESSAGEFACILITY_HEX_VERSION >= 0x20002 // an indication of a switch from s48 to s50
192 auto sevid = msg.xid().severity().getLevel();
194 auto sevid = msg.xid().severity.getLevel();
197 QString text_ = QString(
"<font color=");
201 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
202 case mf::ELseverityLevel::ELsev_incidental:
204 case mf::ELseverityLevel::ELsev_success:
205 case mf::ELseverityLevel::ELsev_zeroSeverity:
206 case mf::ELseverityLevel::ELsev_unspecified:
207 text_ += QString(
"#505050>");
210 case mf::ELseverityLevel::ELsev_info:
211 text_ += QString(
"#008000>");
214 case mf::ELseverityLevel::ELsev_warning:
215 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
216 case mf::ELseverityLevel::ELsev_warning2:
218 text_ += QString(
"#E08000>");
221 case mf::ELseverityLevel::ELsev_error:
222 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
223 case mf::ELseverityLevel::ELsev_error2:
224 case mf::ELseverityLevel::ELsev_next:
225 case mf::ELseverityLevel::ELsev_severe2:
226 case mf::ELseverityLevel::ELsev_abort:
227 case mf::ELseverityLevel::ELsev_fatal:
229 case mf::ELseverityLevel::ELsev_severe:
230 case mf::ELseverityLevel::ELsev_highestSeverity:
231 text_ += QString(
"#FF0000>");
238 text_ += QString(
"<pre>")
239 + QString(msgString.c_str()).toHtmlEscaped()
242 text_ += QString(
"</font>");
243 return text_.toStdString();
249 void ELSMTP::routePayload(
const std::ostringstream& oss
250 #
if MESSAGEFACILITY_HEX_VERSION < 0x20002
251 ,
const ErrorObj& msg, ELcontextSupplier
const&
253 ,
const ErrorObj& msg
257 std::unique_lock<std::mutex>(message_mutex_);
258 message_contents_ << to_html(oss.str(), msg);
260 if (!sending_thread_active_)
262 sending_thread_active_ =
true;
263 boost::thread t([=] { send_message_(); });
268 void ELSMTP::send_message_()
271 while (!abort_sleep_ && slept < send_interval_s_ * 1000000)
279 std::unique_lock<std::mutex> lk(message_mutex_);
280 payload = message_contents_.str();
281 message_contents_.str(
"");
283 std::string destination = (use_ssl_ ?
"smtps://" :
"smtp://") + smtp_host_ +
":" + std::to_string(port_);
286 std::vector<const char*> to;
287 to.reserve(to_.size());
288 std::string toString;
289 for (
size_t i = 0; i < to_.size(); ++i)
291 to.push_back(to_[i].c_str());
293 if (i < to_.size() - 1)
299 std::ostringstream headers_builder;
301 headers_builder <<
"Date: " << dateTimeNow_() <<
"\r\n";
302 headers_builder <<
"To: " << toString <<
"\r\n";
303 headers_builder <<
"From: " << from_ <<
"\r\n";
304 headers_builder <<
"Message-ID: <" + generateMessageId_() +
"@" + from_.substr(from_.find(
'@') + 1) +
">\r\n";
305 headers_builder <<
"Subject: " << subject_ <<
" @ " << dateTimeNow_() <<
" from PID " << getpid() <<
"\r\n";
306 headers_builder <<
"Content-Type: text/html; charset=\"UTF-8\"\r\n";
307 headers_builder <<
"\r\n";
309 std::string headers = headers_builder.str();
310 std::ostringstream message_builder;
311 message_builder << headers <<
"<html><body><p>" << message_prefix_ <<
"</p>" << payload <<
"</body></html>";
312 std::string payloadWithHeaders = message_builder.str();
317 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_);
321 send_message(destination.c_str(), &to[0], to_.size(), from_.c_str(), payloadWithHeaders.c_str(), payloadWithHeaders.size());
323 sending_thread_active_ =
false;
327 std::string ELSMTP::generateMessageId_()
const
329 const size_t MESSAGE_ID_LEN = 37;
336 ret.resize(MESSAGE_ID_LEN);
337 size_t datelen = std::strftime(&ret[0], MESSAGE_ID_LEN,
"%Y%m%d%H%M%S", &t);
338 static const std::string alphaNum{
340 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
341 "abcdefghijklmnopqrstuvwxyz" };
343 std::uniform_int_distribution<> dis(0, alphaNum.length() - 1);
344 std::generate_n(ret.begin() + datelen, MESSAGE_ID_LEN - datelen, [&]() {
return alphaNum[dis(gen)]; });
348 std::string ELSMTP::dateTimeNow_()
350 const int RFC5322_TIME_LEN = 32;
353 ret.resize(RFC5322_TIME_LEN);
360 strftime(&ret[0], RFC5322_TIME_LEN,
"%a, %d %b %Y %H:%M:%S %z", t);
374 auto makePlugin(
const std::string&,
375 const fhicl::ParameterSet& pset)
377 return std::make_unique<mfplugins::ELSMTP>(pset);
381 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.