00001 #include "cetlib/PluginTypeDeducer.h"
00002 #include "fhiclcpp/ParameterSet.h"
00003
00004 #include "messagefacility/MessageService/ELdestination.h"
00005 #include "messagefacility/Utilities/ELseverityLevel.h"
00006 #if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
00007 # include "messagefacility/MessageService/ELcontextSupplier.h"
00008 # include "messagefacility/MessageLogger/MessageDrop.h"
00009 #else
00010 # include "messagefacility/MessageService/MessageDrop.h"
00011 #endif
00012 #include "messagefacility/Utilities/exception.h"
00013
00014
00015 #include <memory>
00016 #include <algorithm>
00017 #include <atomic>
00018 #include <mutex>
00019 #include <random>
00020 #include <arpa/inet.h>
00021 #include <ifaddrs.h>
00022 #include <netdb.h>
00023 #include <netinet/in.h>
00024 #include <boost/thread.hpp>
00025
00026 #include <QtCore/QString>
00027 #include "mfextensions/Destinations/detail/curl_send_message.h"
00028
00029 namespace mfplugins
00030 {
00031 using mf::service::ELdestination;
00032 using mf::ELseverityLevel;
00033 using mf::ErrorObj;
00034 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
00035 using mf::service::ELcontextSupplier;
00036 # endif
00037
00041 class ELSMTP : public ELdestination
00042 {
00043 public:
00044
00045 ELSMTP(const fhicl::ParameterSet& pset);
00046
00047 ~ELSMTP()
00048 {
00049 abort_sleep_ = true;
00050 while (sending_thread_active_) usleep(1000);
00051 }
00052
00053 virtual void routePayload(const std::ostringstream&, const ErrorObj& msg
00054 # if MESSAGEFACILITY_HEX_VERSION < 0x20002
00055 , const ELcontextSupplier&
00056 # endif
00057 ) override;
00058
00059 private:
00060 void send_message_();
00061 std::string generateMessageId_() const;
00062 std::string dateTimeNow_();
00063 std::string to_html(std::string msgString, const ErrorObj& msg);
00064
00065 std::string smtp_host_;
00066 int port_;
00067 std::vector<std::string> to_;
00068 std::string from_;
00069 std::string subject_;
00070 std::string message_prefix_;
00071
00072
00073 long pid_;
00074 std::string hostname_;
00075 std::string hostaddr_;
00076 std::string app_;
00077
00078 bool use_ssl_;
00079 std::string username_;
00080 std::string password_;
00081 bool ssl_verify_host_cert_;
00082
00083 std::atomic<bool> sending_thread_active_;
00084 std::atomic<bool> abort_sleep_;
00085 size_t send_interval_s_;
00086 mutable std::mutex message_mutex_;
00087 std::ostringstream message_contents_;
00088 };
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098 ELSMTP::ELSMTP(const fhicl::ParameterSet& pset)
00099 : ELdestination(pset)
00100 , smtp_host_(pset.get<std::string>("host", "smtp.fnal.gov"))
00101 , port_(pset.get<int>("port", 25))
00102 , to_(pset.get<std::vector<std::string>>("to_addresses"))
00103 , from_(pset.get<std::string>("from_address"))
00104 , subject_(pset.get<std::string>("subject", "MessageFacility SMTP Message Digest"))
00105 , message_prefix_(pset.get<std::string>("message_header", ""))
00106 , pid_(static_cast<long>(getpid()))
00107 , use_ssl_(pset.get<bool>("use_smtps", false))
00108 , username_(pset.get<std::string>("smtp_username", ""))
00109 , password_(pset.get<std::string>("smtp_password", ""))
00110 , ssl_verify_host_cert_(pset.get<bool>("verify_host_ssl_certificate", true))
00111 , sending_thread_active_(false)
00112 , abort_sleep_(false)
00113 , send_interval_s_(pset.get<size_t>("email_send_interval_seconds", 15))
00114 {
00115
00116 char hostname_c[1024];
00117 hostname_ = (gethostname(hostname_c, 1023) == 0) ? hostname_c : "Unkonwn Host";
00118
00119
00120 hostent* host = nullptr;
00121 host = gethostbyname(hostname_c);
00122
00123 if (host != nullptr)
00124 {
00125
00126 char* ip = inet_ntoa(*(struct in_addr *)host->h_addr);
00127 hostaddr_ = ip;
00128 }
00129 else
00130 {
00131
00132 struct ifaddrs* ifAddrStruct = nullptr;
00133 struct ifaddrs* ifa = nullptr;
00134 void* tmpAddrPtr = nullptr;
00135
00136 if (getifaddrs(&ifAddrStruct))
00137 {
00138
00139 hostaddr_ = "127.0.0.1";
00140 }
00141 else
00142 {
00143
00144 for (ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next)
00145 {
00146 if (ifa->ifa_addr->sa_family == AF_INET)
00147 {
00148
00149 tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
00150 char addressBuffer[INET_ADDRSTRLEN];
00151 inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
00152 hostaddr_ = addressBuffer;
00153 }
00154
00155 else if (ifa->ifa_addr->sa_family == AF_INET6)
00156 {
00157
00158 tmpAddrPtr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
00159 char addressBuffer[INET6_ADDRSTRLEN];
00160 inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
00161 hostaddr_ = addressBuffer;
00162 }
00163
00164
00165 if (!hostaddr_.empty()
00166 && hostaddr_.compare("127.0.0.1")
00167 && hostaddr_.compare("::1"))
00168 break;
00169 }
00170
00171 if (hostaddr_.empty())
00172 hostaddr_ = "127.0.0.1";
00173 }
00174 }
00175
00176
00177 std::string exe;
00178 std::ostringstream pid_ostr;
00179 pid_ostr << "/proc/" << pid_ << "/exe";
00180 exe = realpath(pid_ostr.str().c_str(), NULL);
00181
00182 size_t end = exe.find('\0');
00183 size_t start = exe.find_last_of('/', end);
00184
00185 app_ = exe.substr(start + 1, end - start - 1);
00186 }
00187
00188
00189 std::string ELSMTP::to_html(std::string msgString, const ErrorObj& msg)
00190 {
00191 # if MESSAGEFACILITY_HEX_VERSION >= 0x20002 // an indication of a switch from s48 to s50
00192 auto sevid = msg.xid().severity().getLevel();
00193 # else
00194 auto sevid = msg.xid().severity.getLevel();
00195 # endif
00196
00197 QString text_ = QString("<font color=");
00198
00199 switch (sevid)
00200 {
00201 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
00202 case mf::ELseverityLevel::ELsev_incidental:
00203 # endif
00204 case mf::ELseverityLevel::ELsev_success:
00205 case mf::ELseverityLevel::ELsev_zeroSeverity:
00206 case mf::ELseverityLevel::ELsev_unspecified:
00207 text_ += QString("#505050>");
00208 break;
00209
00210 case mf::ELseverityLevel::ELsev_info:
00211 text_ += QString("#008000>");
00212 break;
00213
00214 case mf::ELseverityLevel::ELsev_warning:
00215 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
00216 case mf::ELseverityLevel::ELsev_warning2:
00217 # endif
00218 text_ += QString("#E08000>");
00219 break;
00220
00221 case mf::ELseverityLevel::ELsev_error:
00222 # if MESSAGEFACILITY_HEX_VERSION < 0x20002 // v2_00_02 is s50, pre v2_00_02 is s48
00223 case mf::ELseverityLevel::ELsev_error2:
00224 case mf::ELseverityLevel::ELsev_next:
00225 case mf::ELseverityLevel::ELsev_severe2:
00226 case mf::ELseverityLevel::ELsev_abort:
00227 case mf::ELseverityLevel::ELsev_fatal:
00228 # endif
00229 case mf::ELseverityLevel::ELsev_severe:
00230 case mf::ELseverityLevel::ELsev_highestSeverity:
00231 text_ += QString("#FF0000>");
00232 break;
00233
00234 default: break;
00235 }
00236
00237
00238 text_ += QString("<pre>")
00239 + QString(msgString.c_str()).toHtmlEscaped()
00240 + QString("</pre>");
00241
00242 text_ += QString("</font>");
00243 return text_.toStdString();
00244 }
00245
00246
00247
00248
00249 void ELSMTP::routePayload(const std::ostringstream& oss
00250 #if MESSAGEFACILITY_HEX_VERSION < 0x20002
00251 , const ErrorObj& msg, ELcontextSupplier const&
00252 #else
00253 , const ErrorObj& msg
00254 #endif
00255 )
00256 {
00257 std::unique_lock<std::mutex>(message_mutex_);
00258 message_contents_ << to_html(oss.str(), msg);
00259
00260 if (!sending_thread_active_)
00261 {
00262 sending_thread_active_ = true;
00263 boost::thread t([=] { send_message_(); });
00264 t.detach();
00265 }
00266 }
00267
00268 void ELSMTP::send_message_()
00269 {
00270 size_t slept = 0;
00271 while (!abort_sleep_ && slept < send_interval_s_ * 1000000)
00272 {
00273 usleep(10000);
00274 slept += 10000;
00275 }
00276
00277 std::string payload;
00278 {
00279 std::unique_lock<std::mutex> lk(message_mutex_);
00280 payload = message_contents_.str();
00281 message_contents_.str("");
00282 }
00283 std::string destination = (use_ssl_ ? "smtps://" : "smtp://") + smtp_host_ + ":" + std::to_string(port_);
00284
00285
00286 std::vector<const char*> to;
00287 to.reserve(to_.size());
00288 std::string toString;
00289 for (size_t i = 0; i < to_.size(); ++i)
00290 {
00291 to.push_back(to_[i].c_str());
00292 toString += to_[i];
00293 if (i < to_.size() - 1)
00294 {
00295 toString += ", ";
00296 }
00297 }
00298
00299 std::ostringstream headers_builder;
00300
00301 headers_builder << "Date: " << dateTimeNow_() << "\r\n";
00302 headers_builder << "To: " << toString << "\r\n";
00303 headers_builder << "From: " << from_ << "\r\n";
00304 headers_builder << "Message-ID: <" + generateMessageId_() + "@" + from_.substr(from_.find('@') + 1) + ">\r\n";
00305 headers_builder << "Subject: " << subject_ << " @ " << dateTimeNow_() << " from PID " << getpid() << "\r\n";
00306 headers_builder << "Content-Type: text/html; charset=\"UTF-8\"\r\n";
00307 headers_builder << "\r\n";
00308
00309 std::string headers = headers_builder.str();
00310 std::ostringstream message_builder;
00311 message_builder << headers << "<html><body><p>" << message_prefix_ << "</p>" << payload << "</body></html>";
00312 std::string payloadWithHeaders = message_builder.str();
00313
00314
00315 if (use_ssl_)
00316 {
00317 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_);
00318 }
00319 else
00320 {
00321 send_message(destination.c_str(), &to[0], to_.size(), from_.c_str(), payloadWithHeaders.c_str(), payloadWithHeaders.size());
00322 }
00323 sending_thread_active_ = false;
00324 }
00325
00326
00327 std::string ELSMTP::generateMessageId_() const
00328 {
00329 const size_t MESSAGE_ID_LEN = 37;
00330 tm t;
00331 time_t tt;
00332 time(&tt);
00333 gmtime_r(&tt, &t);
00334
00335 std::string ret;
00336 ret.resize(MESSAGE_ID_LEN);
00337 size_t datelen = std::strftime(&ret[0], MESSAGE_ID_LEN, "%Y%m%d%H%M%S", &t);
00338 static const std::string alphaNum{
00339 "0123456789"
00340 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
00341 "abcdefghijklmnopqrstuvwxyz" };
00342 std::mt19937 gen;
00343 std::uniform_int_distribution<> dis(0, alphaNum.length() - 1);
00344 std::generate_n(ret.begin() + datelen, MESSAGE_ID_LEN - datelen, [&]() { return alphaNum[dis(gen)]; });
00345 return ret;
00346 }
00347
00348 std::string ELSMTP::dateTimeNow_()
00349 {
00350 const int RFC5322_TIME_LEN = 32;
00351
00352 std::string ret;
00353 ret.resize(RFC5322_TIME_LEN);
00354
00355 time_t tt;
00356 tm tv, *t = &tv;
00357 tt = time(&tt);
00358 localtime_r(&tt, t);
00359
00360 strftime(&ret[0], RFC5322_TIME_LEN, "%a, %d %b %Y %H:%M:%S %z", t);
00361
00362 return ret;
00363 }
00364 }
00365
00366
00367
00368
00369
00370
00371
00372 extern "C"
00373 {
00374 auto makePlugin(const std::string&,
00375 const fhicl::ParameterSet& pset)
00376 {
00377 return std::make_unique<mfplugins::ELSMTP>(pset);
00378 }
00379 }
00380
00381 DEFINE_BASIC_PLUGINTYPE_FUNC(mf::service::ELdestination)