1 #include "cetlib/PluginTypeDeducer.h"
2 #include "fhiclcpp/ParameterSet.h"
3 #include "fhiclcpp/types/ConfigurationTable.h"
4 #include "fhiclcpp/types/Sequence.h"
5 #include "fhiclcpp/types/TableFragment.h"
7 #include "messagefacility/MessageService/ELdestination.h"
8 #include "messagefacility/Utilities/ELseverityLevel.h"
9 #if MESSAGEFACILITY_HEX_VERSION < 0x20201 // v2_02_01 is s67
10 #include "messagefacility/MessageService/MessageDrop.h"
12 #include "messagefacility/MessageLogger/MessageLogger.h"
14 #include "messagefacility/Utilities/exception.h"
17 #include <arpa/inet.h>
20 #include <netinet/in.h>
23 #include <boost/thread.hpp>
28 #include <QtCore/QString>
29 #include "cetlib/compiler_macros.h"
33 using mf::ELseverityLevel;
35 using mf::service::ELdestination;
40 class ELSMTP :
public ELdestination {
47 using strings_t = fhicl::Sequence<std::string>::default_type;
51 fhicl::Atom<std::string>
host = fhicl::Atom<std::string>{fhicl::Name{
"host"}, fhicl::Comment{
"SMTP Server hostname"},
"smtp.fnal.gov"};
53 fhicl::Atom<int>
port = fhicl::Atom<int>{fhicl::Name{
"port"}, fhicl::Comment{
"SMTP Server port"}, 25};
55 fhicl::Sequence<std::string>
to = fhicl::Sequence<std::string>{
56 fhicl::Name{
"to_addresses"},
57 fhicl::Comment{
"The list of email addresses that SMTP mfPlugin should sent to"},
60 fhicl::Atom<std::string>
from =
61 fhicl::Atom<std::string>{fhicl::Name{
"from_address"}, fhicl::Comment{
"Source email address"}};
63 fhicl::Atom<std::string>
subject = fhicl::Atom<std::string>{
64 fhicl::Name{
"subject"}, fhicl::Comment{
"Subject of the email message"},
65 "MessageFacility SMTP Message Digest"};
68 fhicl::Name{
"message_header"},
69 fhicl::Comment{
"String to preface messages with in email body"},
""};
72 fhicl::Atom<bool>{fhicl::Name{
"use_smtps"}, fhicl::Comment{
"Use SMTPS protocol"},
false};
74 fhicl::Atom<std::string>
user =
75 fhicl::Atom<std::string>{fhicl::Name{
"smtp_username"}, fhicl::Comment{
"Username for SMTP server"},
""};
77 fhicl::Atom<std::string>
pw =
78 fhicl::Atom<std::string>{fhicl::Name{
"smtp_password"}, fhicl::Comment{
"Password for SMTP server"},
""};
81 fhicl::Atom<bool>{fhicl::Name{
"verify_host_ssl_certificate"},
82 fhicl::Comment{
"Whether to run full SSL verify on SMTP server in SMTPS mode"},
true};
84 fhicl::Atom<size_t>
sendInterval = fhicl::Atom<size_t>{fhicl::Name{
"email_send_interval_seconds"},
85 fhicl::Comment{
"Only send email every N seconds"}, 15};
99 while (sending_thread_active_) usleep(1000);
107 virtual void routePayload(
const std::ostringstream& o,
const ErrorObj& msg)
override;
110 void send_message_();
111 std::string generateMessageId_()
const;
112 std::string dateTimeNow_();
113 std::string to_html(std::string msgString,
const ErrorObj& msg);
115 std::string smtp_host_;
117 std::vector<std::string> to_;
119 std::string subject_;
120 std::string message_prefix_;
124 std::string hostname_;
125 std::string hostaddr_;
129 std::string username_;
130 std::string password_;
131 bool ssl_verify_host_cert_;
133 std::atomic<bool> sending_thread_active_;
134 std::atomic<bool> abort_sleep_;
135 size_t send_interval_s_;
136 mutable std::mutex message_mutex_;
137 std::ostringstream message_contents_;
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),
162 send_interval_s_(pset().sendInterval()) {
164 char hostname_c[1024];
165 hostname_ = (gethostname(hostname_c, 1023) == 0) ? hostname_c :
"Unkonwn Host";
168 hostent* host =
nullptr;
169 host = gethostbyname(hostname_c);
171 if (host !=
nullptr) {
173 char* ip = inet_ntoa(*(
struct in_addr*)host->h_addr);
177 struct ifaddrs* ifAddrStruct =
nullptr;
178 struct ifaddrs* ifa =
nullptr;
179 void* tmpAddrPtr =
nullptr;
181 if (getifaddrs(&ifAddrStruct)) {
183 hostaddr_ =
"127.0.0.1";
186 for (ifa = ifAddrStruct; ifa !=
nullptr; ifa = ifa->ifa_next) {
187 if (ifa->ifa_addr->sa_family == AF_INET) {
189 tmpAddrPtr = &((
struct sockaddr_in*)ifa->ifa_addr)->sin_addr;
190 char addressBuffer[INET_ADDRSTRLEN];
191 inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
192 hostaddr_ = addressBuffer;
195 else if (ifa->ifa_addr->sa_family == AF_INET6) {
197 tmpAddrPtr = &((
struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr;
198 char addressBuffer[INET6_ADDRSTRLEN];
199 inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
200 hostaddr_ = addressBuffer;
204 if (!hostaddr_.empty() && hostaddr_.compare(
"127.0.0.1") && hostaddr_.compare(
"::1"))
break;
207 if (hostaddr_.empty())
208 hostaddr_ =
"127.0.0.1";
214 std::ostringstream pid_ostr;
215 pid_ostr <<
"/proc/" << pid_ <<
"/exe";
216 exe = realpath(pid_ostr.str().c_str(), NULL);
218 size_t end = exe.find(
'\0');
219 size_t start = exe.find_last_of(
'/', end);
221 app_ = exe.substr(start + 1, end - start - 1);
224 std::string ELSMTP::to_html(std::string msgString,
const ErrorObj& msg) {
225 auto sevid = msg.xid().severity().getLevel();
227 QString text_ = QString(
"<font color=");
230 case mf::ELseverityLevel::ELsev_success:
231 case mf::ELseverityLevel::ELsev_zeroSeverity:
232 case mf::ELseverityLevel::ELsev_unspecified:
233 text_ += QString(
"#505050>");
236 case mf::ELseverityLevel::ELsev_info:
237 text_ += QString(
"#008000>");
240 case mf::ELseverityLevel::ELsev_warning:
241 text_ += QString(
"#E08000>");
244 case mf::ELseverityLevel::ELsev_error:
245 case mf::ELseverityLevel::ELsev_severe:
246 case mf::ELseverityLevel::ELsev_highestSeverity:
247 text_ += QString(
"#FF0000>");
255 text_ += QString(
"<pre>") + QString(msgString.c_str()).toHtmlEscaped()
258 text_ += QString(
"</font>");
259 return text_.toStdString();
266 std::lock_guard<std::mutex> lk(message_mutex_);
267 message_contents_ << to_html(oss.str(), msg);
269 if (!sending_thread_active_) {
270 sending_thread_active_ =
true;
271 boost::thread t([=] { send_message_(); });
276 void ELSMTP::send_message_() {
278 while (!abort_sleep_ && slept < send_interval_s_ * 1000000) {
285 std::lock_guard<std::mutex> lk(message_mutex_);
286 payload = message_contents_.str();
287 message_contents_.str(
"");
289 std::string destination = (use_ssl_ ?
"smtps://" :
"smtp://") + smtp_host_ +
":" + std::to_string(port_);
291 std::vector<const char*> to;
292 to.reserve(to_.size());
293 std::string toString;
294 for (
size_t i = 0; i < to_.size(); ++i) {
295 to.push_back(to_[i].c_str());
297 if (i < to_.size() - 1) {
302 std::ostringstream headers_builder;
304 headers_builder <<
"Date: " << dateTimeNow_() <<
"\r\n";
305 headers_builder <<
"To: " << toString <<
"\r\n";
306 headers_builder <<
"From: " << from_ <<
"\r\n";
307 headers_builder <<
"Message-ID: <" + generateMessageId_() +
"@" + from_.substr(from_.find(
'@') + 1) +
">\r\n";
308 headers_builder <<
"Subject: " << subject_ <<
" @ " << dateTimeNow_() <<
" from PID " << getpid() <<
"\r\n";
309 headers_builder <<
"Content-Type: text/html; charset=\"UTF-8\"\r\n";
310 headers_builder <<
"\r\n";
312 std::string headers = headers_builder.str();
313 std::ostringstream message_builder;
314 message_builder << headers <<
"<html><body><p>" << message_prefix_ <<
"</p>" << payload <<
"</body></html>";
315 std::string payloadWithHeaders = message_builder.str();
318 send_message_ssl(destination.c_str(), &to[0], to_.size(), from_.c_str(), payloadWithHeaders.c_str(),
319 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(),
322 payloadWithHeaders.size());
324 sending_thread_active_ =
false;
328 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_() {
349 const int RFC5322_TIME_LEN = 32;
352 ret.resize(RFC5322_TIME_LEN);
359 strftime(&ret[0], RFC5322_TIME_LEN,
"%a, %d %b %Y %H:%M:%S %z", t);
371 #ifndef EXTERN_C_FUNC_DECLARE_START
372 #define EXTERN_C_FUNC_DECLARE_START extern "C" {
375 EXTERN_C_FUNC_DECLARE_START
376 auto makePlugin(
const std::string&,
const fhicl::ParameterSet& pset) {
377 return std::make_unique<mfplugins::ELSMTP>(pset);
381 DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)
fhicl::Atom< std::string > from
"from_address" (REQUIRED): Source email address
fhicl::Atom< std::string > user
"smtp_username" (Default: ""): Username for SMTP server
fhicl::Sequence< std::string > to
"to_addresses" (Default: {}): The list of email addresses that SMTP mfPlugin should sent to ...
fhicl::Atom< size_t > sendInterval
"email_send_interval_seconds" (Default: 15): Only send email every N seconds
fhicl::WrappedTable< Config > Parameters
Used for ParameterSet validation.
fhicl::Atom< std::string > host
"host" (Default: "smtp.fnal.gov"): SMTP Server hostname
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.
fhicl::Sequence< std::string >::default_type strings_t
Array of strings.
SMTP Message Facility destination plugin (Using libcurl)
fhicl::Atom< std::string > messageHeader
"message_header" (Default: ""): String to preface messages with in email body
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.
fhicl::Atom< std::string > subject
"subject" (Default: "MessageFacility SMTP Message Digest"): Subject of the email message ...
virtual void routePayload(const std::ostringstream &o, const ErrorObj &msg) override
Serialize a MessageFacility message to the output.
fhicl::TableFragment< ELdestination::Config > elDestConfig
ELDestination common config parameters.
fhicl::Atom< bool > verifyCert
"verify_host_ssl_certificate" (Default: true): Whether to run full SSL verify on SMTP server in SMTPS...
ELSMTP(Parameters const &pset)
ELSMTP Constructor
fhicl::Atom< std::string > pw
"smtp_password" (Default: ""): Password for SMTP server
fhicl::Atom< bool > useSmtps
"use_smtps" (Default: false): Use SMTPS protocol
Configuration parameters for ELSMTP.
fhicl::Atom< int > port
"port" (Default: 25): SMTP Server port