This is an automated email from the ASF dual-hosted git repository.

phrocker pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git


The following commit(s) were added to refs/heads/master by this push:
     new a4cd78b  WIP: Minificpp 1025 -- add metadata (#648)
a4cd78b is described below

commit a4cd78ba35e4c8ea10aa3c9046bc57646043f0c0
Author: Marc <[email protected]>
AuthorDate: Thu Sep 26 14:49:37 2019 -0400

    WIP: Minificpp 1025 -- add metadata (#648)
    
    * MINIFICPP-1025: Add message string functionality
    
    * minor update
    
    * updates
    
    * MINIFICPP-1025: Update CWEL
    
    * updates
    
    * update message type
    
    * update message type
    
    * MINIFICPP-1025: add multiple SID resolution test
---
 .../windows-event-log/ConsumeWindowsEventLog.cpp   | 122 ++++++++++---
 .../windows-event-log/ConsumeWindowsEventLog.h     |  18 +-
 .../tests/MetadataWalkerTests.cpp                  |  30 ++++
 .../tests/resources/multiplesids.xml               |  24 +++
 .../windows-event-log/wel/MetadataWalker.cpp       | 103 ++++++++++-
 extensions/windows-event-log/wel/MetadataWalker.h  |  36 ++--
 .../windows-event-log/wel/WindowsEventLog.cpp      | 200 +++++++++++++++++++++
 extensions/windows-event-log/wel/WindowsEventLog.h | 193 ++++++++++++++++++++
 libminifi/include/utils/Deleters.h                 |  40 +++++
 9 files changed, 728 insertions(+), 38 deletions(-)

diff --git a/extensions/windows-event-log/ConsumeWindowsEventLog.cpp 
b/extensions/windows-event-log/ConsumeWindowsEventLog.cpp
index b2d6114..6e7b2e0 100644
--- a/extensions/windows-event-log/ConsumeWindowsEventLog.cpp
+++ b/extensions/windows-event-log/ConsumeWindowsEventLog.cpp
@@ -36,7 +36,9 @@
 #include "core/ProcessContext.h"
 #include "core/ProcessSession.h"
 
+
 #pragma comment(lib, "wevtapi.lib")
+#pragma comment(lib, "ole32.lib")
 
 namespace org {
 namespace apache {
@@ -44,6 +46,8 @@ namespace nifi {
 namespace minifi {
 namespace processors {
 
+
+
 static std::string to_string(const wchar_t* pChar) {
   return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(pChar);
 }
@@ -108,6 +112,21 @@ core::Property ConsumeWindowsEventLog::ResolveAsAttributes(
        withDescription("If true, any metadata that is resolved ( such as IDs 
or keyword metadata ) will be placed into attributes, otherwise it will be 
replaced in the XML or text output")->
        build());
 
+
+core::Property ConsumeWindowsEventLog::EventHeaderDelimiter(
+       core::PropertyBuilder::createProperty("Event Header Delimiter")->
+       isRequired(false)->
+       withDescription("If set, the chosen delimiter will be used in the Event 
output header. Otherwise, a colon followed by spaces will be used.")->
+       build());
+
+
+core::Property ConsumeWindowsEventLog::EventHeader(
+       core::PropertyBuilder::createProperty("Event Header")->
+       isRequired(false)->
+       withDefaultValue("LOG_NAME=Log Name, SOURCE = Source, TIME_CREATED = 
Date,EVENT_RECORDID=Record ID,EVENTID = Event ID,TASK_CATEGORY = Task 
Category,LEVEL = Level,KEYWORDS = Keywords,USER = User,COMPUTER = Computer, 
EVENT_TYPE = EventType")->
+       withDescription("Comma seperated list of key/value pairs with the 
following keys LOG_NAME, SOURCE, 
TIME_CREATED,EVENT_RECORDID,EVENTID,TASK_CATEGORY,LEVEL,KEYWORDS,USER,COMPUTER, 
and EVENT_TYPE. Eliminating fields will remove them from the header.")->
+       build());
+
 core::Relationship ConsumeWindowsEventLog::Success("success", "Relationship 
for successfully consumed events.");
 
 ConsumeWindowsEventLog::ConsumeWindowsEventLog(const std::string& name, 
utils::Identifier uuid)
@@ -127,16 +146,53 @@ ConsumeWindowsEventLog::~ConsumeWindowsEventLog() {
 
 void ConsumeWindowsEventLog::initialize() {
   //! Set the supported properties
-  setSupportedProperties({Channel, Query, MaxBufferSize, 
InactiveDurationToReconnect, IdentifierMatcher, IdentifierFunction, 
ResolveAsAttributes });
+  setSupportedProperties({Channel, Query, MaxBufferSize, 
InactiveDurationToReconnect, IdentifierMatcher, IdentifierFunction, 
ResolveAsAttributes, EventHeaderDelimiter, EventHeader });
 
   //! Set the supported relationships
   setSupportedRelationships({Success});
 }
 
+bool ConsumeWindowsEventLog::insertHeaderName(wel::METADATA_NAMES &header, 
const std::string &key, const std::string & value) {
+       
+       wel::METADATA name = 
wel::WindowsEventLogMetadata::getMetadataFromString(key);
+               
+       if (name != wel::METADATA::UNKNOWN) {
+               header.emplace_back(std::make_pair(name, value));
+               return true;
+       }
+       else {
+               return false;
+       }
+}
+
 void ConsumeWindowsEventLog::onSchedule(const 
std::shared_ptr<core::ProcessContext> &context, const 
std::shared_ptr<core::ProcessSessionFactory> &sessionFactory) {
        context->getProperty(IdentifierMatcher.getName(), regex_);
        context->getProperty(ResolveAsAttributes.getName(), 
resolve_as_attributes_);
        context->getProperty(IdentifierFunction.getName(), 
apply_identifier_function_);
+       context->getProperty(EventHeaderDelimiter.getName(), header_delimiter_);
+
+       std::string header;
+       context->getProperty(EventHeader.getName(), header);
+       
+       auto keyValueSplit = utils::StringUtils::split(header, ",");
+       for (const auto &kv : keyValueSplit) {
+               auto splitKeyAndValue = utils::StringUtils::split(kv, "=");
+               if (splitKeyAndValue.size() == 2) {
+                       auto key = 
utils::StringUtils::trim(splitKeyAndValue.at(0));
+                       auto value = 
utils::StringUtils::trim(splitKeyAndValue.at(1));
+                       if (!insertHeaderName(header_names_, key, value)) {
+                               logger_->log_debug("%s is an invalid key for 
the header map", key);
+                       }
+               }
+               else if (splitKeyAndValue.size() == 1) {
+                       auto key = 
utils::StringUtils::trim(splitKeyAndValue.at(0));
+                       if (!insertHeaderName(header_names_, key, "")) {
+                               logger_->log_debug("%s is an invalid key for 
the header map", key);
+                       }
+               }
+
+       }
+       
   if (subscriptionHandle_) {
     logger_->log_error("Processor already subscribed to Event Log, expected 
cleanup to unsubscribe.");
   } else {
@@ -144,6 +200,7 @@ void ConsumeWindowsEventLog::onSchedule(const 
std::shared_ptr<core::ProcessConte
 
     subscribe(context);
   }
+
 }
 
 void ConsumeWindowsEventLog::onTrigger(const 
std::shared_ptr<core::ProcessContext> &context, const 
std::shared_ptr<core::ProcessSession> &session) {
@@ -169,7 +226,7 @@ void ConsumeWindowsEventLog::onTrigger(const 
std::shared_ptr<core::ProcessContex
   }
 }
 
-EVT_HANDLE ConsumeWindowsEventLog::getProvider(const std::string & name) {
+wel::WindowsEventLogHandler ConsumeWindowsEventLog::getEventLogHandler(const 
std::string & name) {
        std::lock_guard<std::mutex> lock(cache_mutex_);
        auto provider = providers_.find(name);
        if (provider != std::end(providers_)) {
@@ -179,14 +236,14 @@ EVT_HANDLE ConsumeWindowsEventLog::getProvider(const 
std::string & name) {
        std::wstring temp_wstring = std::wstring(name.begin(), name.end());
        LPCWSTR widechar = temp_wstring.c_str();
 
-       providers_[name] = EvtOpenPublisherMetadata(NULL, widechar, NULL, 0, 0);
+       providers_[name] = 
wel::WindowsEventLogHandler(EvtOpenPublisherMetadata(NULL, widechar, NULL, 0, 
0));
 
        return providers_[name];
 } 
 
+
 bool ConsumeWindowsEventLog::subscribe(const 
std::shared_ptr<core::ProcessContext> &context) {
-  std::string channel;
-  context->getProperty(Channel.getName(), channel);
+  context->getProperty(Channel.getName(), channel_);
 
   std::string query;
   context->getProperty(Query.getName(), query);
@@ -194,7 +251,7 @@ bool ConsumeWindowsEventLog::subscribe(const 
std::shared_ptr<core::ProcessContex
   context->getProperty(MaxBufferSize.getName(), maxBufferSize_);
   logger_->log_debug("ConsumeWindowsEventLog: maxBufferSize_ %lld", 
maxBufferSize_);
 
-  provenanceUri_ = "winlog://" + computerName_ + "/" + channel + "?" + query;
+  provenanceUri_ = "winlog://" + computerName_ + "/" + channel_ + "?" + query;
 
   std::string strInactiveDurationToReconnect;
   context->getProperty(InactiveDurationToReconnect.getName(), 
strInactiveDurationToReconnect);
@@ -209,37 +266,37 @@ bool ConsumeWindowsEventLog::subscribe(const 
std::shared_ptr<core::ProcessContex
   subscriptionHandle_ = EvtSubscribe(
       NULL,
       NULL,
-      std::wstring(channel.begin(), channel.end()).c_str(),
+      std::wstring(channel_.begin(), channel_.end()).c_str(),
       std::wstring(query.begin(), query.end()).c_str(),
       NULL,
       this,
-      [](EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID pContext, EVT_HANDLE hEvent)
+      [](EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID pContext, EVT_HANDLE 
eventHandle)
       {
+         
         auto pConsumeWindowsEventLog = 
static_cast<ConsumeWindowsEventLog*>(pContext);
 
         auto& logger = pConsumeWindowsEventLog->logger_;
 
         if (action == EvtSubscribeActionError) {
-          if (ERROR_EVT_QUERY_RESULT_STALE == (DWORD)hEvent) {
+          if (ERROR_EVT_QUERY_RESULT_STALE == (DWORD)eventHandle) {
             logger->log_error("Received missing event notification. Consider 
triggering processor more frequently or increasing queue size.");
           } else {
-            logger->log_error("Received the following Win32 error: %x", 
hEvent);
+            logger->log_error("Received the following Win32 error: %x", 
eventHandle);
           }
         } else if (action == EvtSubscribeActionDeliver) {
           DWORD size = 0;
           DWORD used = 0;
           DWORD propertyCount = 0;
-
-          if (!EvtRender(NULL, hEvent, EvtRenderEventXml, size, 0, &used, 
&propertyCount)) {
+          if (!EvtRender(NULL, eventHandle, EvtRenderEventXml, size, 0, &used, 
&propertyCount)) {
             if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
               if (used > pConsumeWindowsEventLog->maxBufferSize_) {
-                logger->log_error("Dropping event %x because it couldn't be 
rendered within %ll bytes.", hEvent, pConsumeWindowsEventLog->maxBufferSize_);
+                logger->log_error("Dropping event %x because it couldn't be 
rendered within %ll bytes.", eventHandle, 
pConsumeWindowsEventLog->maxBufferSize_);
                 return 0UL;
               }
 
               size = used;
               std::vector<wchar_t> buf(size/2 + 1);
-              if (EvtRender(NULL, hEvent, EvtRenderEventXml, size, &buf[0], 
&used, &propertyCount)) {
+              if (EvtRender(NULL, eventHandle, EvtRenderEventXml, size, 
&buf[0], &used, &propertyCount)) {
                 std::string xml = to_string(&buf[0]);
 
                                EventRender renderedData;
@@ -247,19 +304,37 @@ bool ConsumeWindowsEventLog::subscribe(const 
std::shared_ptr<core::ProcessContex
                                pugi::xml_document doc;
                                pugi::xml_parse_result result = 
doc.load_string(xml.c_str());
 
-
-
                                if (!result) {
-                                       logger->log_error("'loadXML' failed");
+                                       logger->log_error("Invalid XML 
produced");
                                        return 0UL;
                                }
+                               // this is a well known path. 
+                               std::string providerName = 
doc.child("Event").child("System").child("Provider").attribute("Name").value();
+                               
+                               auto handler = 
pConsumeWindowsEventLog->getEventLogHandler(providerName);
+                               auto message = 
handler.getEventMessage(eventHandle);
+
                                
-                               std::string providerName = 
doc.child("System").child("Provider").attribute("Name").value();
 
                                // resolve the event metadata
-                               wel::MetadataWalker 
walker(pConsumeWindowsEventLog->getProvider(providerName), hEvent, 
!pConsumeWindowsEventLog->resolve_as_attributes_, 
pConsumeWindowsEventLog->apply_identifier_function_, 
pConsumeWindowsEventLog->regex_);
+                               wel::MetadataWalker 
walker(pConsumeWindowsEventLog->getEventLogHandler(providerName).getMetadata(), 
pConsumeWindowsEventLog->channel_, eventHandle, 
!pConsumeWindowsEventLog->resolve_as_attributes_, 
pConsumeWindowsEventLog->apply_identifier_function_, 
pConsumeWindowsEventLog->regex_);
                                doc.traverse(walker);
 
+                               if (!message.empty())
+                               {       
+                                       for (const auto &mapEntry : 
walker.getIdentifiers()) {
+                                               // replace the identifiers with 
their translated strings.
+                                               
utils::StringUtils::replaceAll(message, mapEntry.first, mapEntry.second);
+                                       }
+                                       wel::WindowsEventLogHeader 
log_header(pConsumeWindowsEventLog->header_names_);
+                                       // set the delimiter
+                                       
log_header.setDelimiter(pConsumeWindowsEventLog->header_delimiter_);
+                                       // render the header.
+                                       renderedData.rendered_text_ = 
log_header.getEventHeader(&walker);
+                                       renderedData.rendered_text_ += 
"Message" + pConsumeWindowsEventLog->header_delimiter_ + " ";
+                                       renderedData.rendered_text_ += message;
+                               }
+
                                if 
(pConsumeWindowsEventLog->resolve_as_attributes_) {
                                        renderedData.matched_fields_ = 
walker.getFieldValues();
                                }
@@ -329,7 +404,14 @@ int ConsumeWindowsEventLog::processQueue(const 
std::shared_ptr<core::ProcessSess
     session->putAttribute(flowFile, FlowAttributeKey(MIME_TYPE), 
"application/xml");
     session->getProvenanceReporter()->receive(flowFile, provenanceUri_, 
getUUIDStr(), "Consume windows event logs", 0);
     session->transfer(flowFile, Success);
-    session->commit();
+
+       flowFile = session->create();
+
+       session->write(flowFile, &WriteCallback(evt.rendered_text_));
+       session->putAttribute(flowFile, FlowAttributeKey(MIME_TYPE), 
"text/plain");
+       session->getProvenanceReporter()->receive(flowFile, provenanceUri_, 
getUUIDStr(), "Consume windows event logs", 0);
+       session->transfer(flowFile, Success);
+       session->commit();
 
     flowFileCount++;
   }
diff --git a/extensions/windows-event-log/ConsumeWindowsEventLog.h 
b/extensions/windows-event-log/ConsumeWindowsEventLog.h
index 4d6196d..dae262c 100644
--- a/extensions/windows-event-log/ConsumeWindowsEventLog.h
+++ b/extensions/windows-event-log/ConsumeWindowsEventLog.h
@@ -21,6 +21,7 @@
 #pragma once
 
 #include "core/Core.h"
+#include "wel/WindowsEventLog.h"
 #include "FlowFileRecord.h"
 #include "concurrentqueue.h"
 #include "core/Processor.h"
@@ -31,6 +32,7 @@
 #include <regex>
 #include <codecvt>
 #include "utils/OsUtils.h"
+#include <Objbase.h>
 
 
 //#import <msxml6.dll>
@@ -44,10 +46,9 @@ namespace processors {
 struct EventRender {
        std::map<std::string, std::string> matched_fields_;
        std::string text_;
+       std::string rendered_text_;
 };
 
-
-
 //! ConsumeWindowsEventLog Class
 class ConsumeWindowsEventLog : public core::Processor
 {
@@ -73,6 +74,8 @@ public:
   static core::Property IdentifierMatcher;
   static core::Property IdentifierFunction;
   static core::Property ResolveAsAttributes;
+  static core::Property EventHeaderDelimiter;
+  static core::Property EventHeader;
 
   //! Supported Relationships
   static core::Relationship Success;
@@ -90,17 +93,24 @@ public:
   //! Initialize, overwrite by NiFi ConsumeWindowsEventLog
   virtual void initialize(void) override;
   virtual void notifyStop() override;
+  
 
 protected:
   bool subscribe(const std::shared_ptr<core::ProcessContext> &context);
   void unsubscribe();
   int processQueue(const std::shared_ptr<core::ProcessSession> &session);
   
-  EVT_HANDLE getProvider(const std::string & name);
+  wel::WindowsEventLogHandler getEventLogHandler(const std::string & name);
+
+  bool insertHeaderName(wel::METADATA_NAMES &header, const std::string &key, 
const std::string &value);
 
   void LogWindowsError();
 private:
+
   // Logger
+  wel::METADATA_NAMES header_names_;
+  std::string header_delimiter_;
+  std::string channel_;
   std::shared_ptr<logging::Logger> logger_;
   std::string regex_;
   bool resolve_as_attributes_;
@@ -114,7 +124,7 @@ private:
   DWORD lastActivityTimestamp_{};
   std::shared_ptr<core::ProcessSessionFactory> sessionFactory_;
   std::mutex cache_mutex_;
-  std::map<std::string, EVT_HANDLE > providers_;
+  std::map<std::string, wel::WindowsEventLogHandler > providers_;
 };
 
 REGISTER_RESOURCE(ConsumeWindowsEventLog, "Windows Event Log Subscribe 
Callback to receive FlowFiles from Events on Windows.");
diff --git a/extensions/windows-event-log/tests/MetadataWalkerTests.cpp 
b/extensions/windows-event-log/tests/MetadataWalkerTests.cpp
index aae70bf..b9531b0 100644
--- a/extensions/windows-event-log/tests/MetadataWalkerTests.cpp
+++ b/extensions/windows-event-log/tests/MetadataWalkerTests.cpp
@@ -93,4 +93,34 @@ TEST_CASE("TestUnknownSid", "[InvalidSet]") {
        REQUIRE(MetadataWalker::updateXmlMetadata(xml, 0x00, 0x00, false, true) 
== formatXml(xml));
 
 
+}
+
+
+
+TEST_CASE("TestMultipleSids", "[Resolutions]") {
+       std::ifstream unresolvedfile("resources/multiplesids.xml");
+       std::string xml((std::istreambuf_iterator<char>(unresolvedfile)),
+               std::istreambuf_iterator<char>());
+
+       std::string programmaticallyResolved;
+
+       pugi::xml_document doc;
+       xml = MetadataWalker::updateXmlMetadata(xml, 0x00, 0x00, false, true);
+       pugi::xml_parse_result result = doc.load_string(xml.c_str());
+
+       for (const auto &node : 
doc.child("Event").child("EventData").children())
+       {
+               auto name = node.attribute("Name").as_string();
+               if 
(utils::StringUtils::equalsIgnoreCase("GroupMembership",name)) {
+                       programmaticallyResolved = node.text().get();
+                       break;
+               }
+       }
+
+       std::string expected = "Nobody Everyone Null Authority";
+
+       // we are only testing mulitiple sid resolutions, not the resolution of 
other items. 
+       REQUIRE(expected == programmaticallyResolved);
+
+
 }
\ No newline at end of file
diff --git a/extensions/windows-event-log/tests/resources/multiplesids.xml 
b/extensions/windows-event-log/tests/resources/multiplesids.xml
new file mode 100644
index 0000000..b5c35b5
--- /dev/null
+++ b/extensions/windows-event-log/tests/resources/multiplesids.xml
@@ -0,0 +1,24 @@
+
+ <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event";>
+ <System>
+  <Provider Name="Microsoft-Windows-Security-Auditing" 
Guid="{54849625-5478-4994-a5ba-3e3b0328c30d}" /> 
+  <EventID>4672</EventID> 
+  <Version>0</Version> 
+  <Level>0</Level> 
+  <Task>12548</Task> 
+  <Opcode>0</Opcode> 
+  <Keywords>0x8020000000000000</Keywords> 
+  <TimeCreated SystemTime="2019-08-27T14:37:56.104234800Z" /> 
+  <EventRecordID>2575952</EventRecordID> 
+  <Correlation ActivityID="{47aa1c15-3cc3-0006-e859-7fa1025cd501}" /> 
+  <Execution ProcessID="840" ThreadID="11544" /> 
+  <Channel>Security</Channel> 
+  <Computer>TestComputer</Computer> 
+  <Security /> 
+  </System>
+ <EventData>
+  <Data Name="SubjectUserSid">S-1-0-0</Data> 
+  <Data Name="PrivilegeList">SeAssignPrimaryTokenPrivilege SeTcbPrivilege 
SeSecurityPrivilege SeTakeOwnershipPrivilege SeLoadDriverPrivilege 
SeBackupPrivilege SeRestorePrivilege SeDebugPrivilege SeAuditPrivilege 
SeSystemEnvironmentPrivilege SeImpersonatePrivilege 
SeDelegateSessionUserImpersonatePrivilege</Data>
+   <Data Name="GroupMembership">%{S-1-0-0} %{S-1-1-0} %{S-1-0}</Data>
+ </EventData>
+  </Event>
\ No newline at end of file
diff --git a/extensions/windows-event-log/wel/MetadataWalker.cpp 
b/extensions/windows-event-log/wel/MetadataWalker.cpp
index af57788..3c10006 100644
--- a/extensions/windows-event-log/wel/MetadataWalker.cpp
+++ b/extensions/windows-event-log/wel/MetadataWalker.cpp
@@ -1,5 +1,7 @@
+#include <windows.h>
 #include "MetadataWalker.h"
 #include "XMLString.h"
+#include <strsafe.h>
 
 namespace org {
 namespace apache {
@@ -17,21 +19,62 @@ bool MetadataWalker::for_each(pugi::xml_node &node) {
 
                        if (std::regex_match(attr.name(), regex_)) {
                                std::function<std::string(const std::string &)> 
idUpdate = [&](const std::string &input) -> std::string {
-                                       return resolve_ ? 
utils::OsUtils::userIdToUsername(input) : input;
+                                       if (resolve_) {
+                                               auto resolved = 
utils::OsUtils::userIdToUsername(input);
+                                               replaced_identifiers_[input] = 
resolved;
+                                               return resolved;
+                                       }
+                                       else {
+                                               replaced_identifiers_[input] = 
input;
+                                       }
                                };
                                updateText(node, attr.name(), 
std::move(idUpdate));
 
                        }
+
                        if (std::regex_match(attr.value(), regex_)) {
                                std::function<std::string(const std::string &)> 
idUpdate = [&](const std::string &input) -> std::string {
-                                       return resolve_ ? 
utils::OsUtils::userIdToUsername(input) : input;
+                                       if (resolve_) {
+                                               auto resolved = 
utils::OsUtils::userIdToUsername(input);
+                                               replaced_identifiers_[input] = 
resolved;
+                                               return resolved;
+                                       }
+                                       else {
+                                               replaced_identifiers_[input] = 
input;
+                                       }
                                };
                                updateText(node, attr.value(), 
std::move(idUpdate));
                        }
                }
+
+               if (resolve_) {
+                       std::string nodeText = node.text().get();
+                       std::vector<std::string> ids = getIdentifiers(nodeText);
+                       for (const auto &id : ids) {
+                               auto  resolved = 
utils::OsUtils::userIdToUsername(id);
+                               std::string replacement = "%{" + id + "}";
+                               replaced_identifiers_[id] = resolved;
+                               replaced_identifiers_[replacement] = resolved;
+                               nodeText = 
utils::StringUtils::replaceAll(nodeText, replacement, resolved);
+                       }
+                       node.text().set(nodeText.c_str());
+               }
+
+       }
+       else if (node_name == "TimeCreated") {
+               metadata_["TimeCreated"] = node.attribute("SystemTime").value();
+       }
+       else if (node_name == "EventRecordID") {
+               metadata_["EventRecordID"] = node.text().get();
+       }
+       else if (node_name == "Provider") {
+               metadata_["Provider"] = node.attribute("Name").value();
+       }
+       else if (node_name == "EventID") {
+               metadata_["EventID"] = node.text().get();
        }
        else {
-               static std::map<std::string, EVT_FORMAT_MESSAGE_FLAGS> 
formatFlagMap = { {"Channel", EvtFormatMessageChannel}, {"Keywords", 
EvtFormatMessageKeyword}, {"Level", EvtFormatMessageLevel}, {"Opcode", 
EvtFormatMessageOpcode} };
+               static std::map<std::string, EVT_FORMAT_MESSAGE_FLAGS> 
formatFlagMap = { {"Channel", EvtFormatMessageChannel}, {"Keywords", 
EvtFormatMessageKeyword}, {"Level", EvtFormatMessageLevel}, {"Opcode", 
EvtFormatMessageOpcode}, {"Task",EvtFormatMessageTask} };
                auto it = formatFlagMap.find(node_name);
                if (it != formatFlagMap.end()) {
                        std::function<std::string(const std::string &)> 
updateFunc = [&](const std::string &input) -> std::string {
@@ -53,13 +96,64 @@ bool MetadataWalker::for_each(pugi::xml_node &node) {
        return true;
 }
 
+std::vector<std::string> MetadataWalker::getIdentifiers(const std::string 
&text) const {
+       auto pos = text.find("%{");
+       std::vector<std::string> found_strings;
+       while (pos != std::string::npos)
+       {
+               auto next_pos = text.find("}", pos);
+               if (next_pos != std::string::npos) {
+                       auto potential_identifier = text.substr(pos + 2, 
next_pos - (pos + 2));
+                       std::smatch match;
+                       if (potential_identifier.find("S-") != 
std::string::npos){
+                               found_strings.push_back(potential_identifier);
+                       }
+               }
+               pos = text.find("%{", pos + 2);
+       }
+       return found_strings;
+}
+
+std::string MetadataWalker::getMetadata(METADATA metadata) const {
+               switch (metadata) {
+                       case LOG_NAME:
+                               return log_name_;
+                       case SOURCE:
+                               return getString(metadata_,"Provider");
+                       case TIME_CREATED:
+                               return event_timestamp_str_;
+                       case EVENTID:
+                               return getString(metadata_,"EventID");
+                       case EVENT_RECORDID:
+                               return getString(metadata_, "EventRecordID");
+                       case OPCODE:
+                               return getString(metadata_, "Opcode");
+                       case TASK_CATEGORY:
+                               return getString(metadata_,"Task");
+                       case LEVEL:
+                               return getString(metadata_,"Level");
+                       case KEYWORDS:
+                               return getString(metadata_,"Keywords");
+                       case EVENT_TYPE:
+                               return std::to_string(event_type_index_);
+                       case COMPUTER:
+                               return getComputerName();
+               };
+               return "N/A";
+}
+
 std::map<std::string, std::string> MetadataWalker::getFieldValues() const {
   return fields_values_;
 }
 
+std::map<std::string, std::string> MetadataWalker::getIdentifiers() const {
+       return replaced_identifiers_;
+}
+
+
 
 std::string MetadataWalker::updateXmlMetadata(const std::string &xml, 
EVT_HANDLE metadata_ptr, EVT_HANDLE event_ptr, bool update_xml, bool resolve, 
const std::string &regex) {
-       MetadataWalker walker(metadata_ptr, event_ptr, update_xml, resolve, 
regex);
+       MetadataWalker walker(metadata_ptr,"", event_ptr, update_xml, resolve, 
regex);
 
        pugi::xml_document doc;
        pugi::xml_parse_result result = doc.load_string(xml.c_str());
@@ -84,6 +178,7 @@ void MetadataWalker::updateText(pugi::xml_node &node, const 
std::string &field_n
   auto new_field_value = fn(previous_value);
   if (new_field_value != previous_value) {
 
+       metadata_[field_name] = new_field_value;
     if (update_xml_) {
       node.text().set(new_field_value.c_str());
     } else {
diff --git a/extensions/windows-event-log/wel/MetadataWalker.h 
b/extensions/windows-event-log/wel/MetadataWalker.h
index 984103a..cf491d7 100644
--- a/extensions/windows-event-log/wel/MetadataWalker.h
+++ b/extensions/windows-event-log/wel/MetadataWalker.h
@@ -20,6 +20,7 @@
 
 #pragma once
 
+#include "WindowsEventLog.h"
 #include "core/Core.h"
 #include "FlowFileRecord.h"
 #include "concurrentqueue.h"
@@ -38,15 +39,16 @@ namespace nifi {
 namespace minifi {
 namespace wel {
 
+
+
 /**
  * Defines a tree walker for the XML input
  *
  */
-class MetadataWalker : public pugi::xml_tree_walker {
+class MetadataWalker : public pugi::xml_tree_walker, public 
WindowsEventLogMetadata {
  public:
-  MetadataWalker(EVT_HANDLE metadata_ptr, EVT_HANDLE event_ptr, bool 
update_xml, bool resolve, const std::string &regex = "")
-      : metadata_ptr_(metadata_ptr),
-        event_ptr_(event_ptr),
+  MetadataWalker(EVT_HANDLE metadata_ptr, const std::string &log_name, 
EVT_HANDLE event_ptr, bool update_xml, bool resolve, const std::string &regex = 
"")
+      : WindowsEventLogMetadata(metadata_ptr, event_ptr, log_name),
         regex_(regex),
         regex_str_(regex),
         update_xml_(update_xml),
@@ -61,13 +63,27 @@ class MetadataWalker : public pugi::xml_tree_walker {
 
        static std::string updateXmlMetadata(const std::string &xml, EVT_HANDLE 
metadata_ptr, EVT_HANDLE event_ptr, bool update_xml, bool resolve, const 
std::string &regex = "");
        
-       std::map<std::string, std::string> getFieldValues() const;
+       virtual std::map<std::string, std::string> getFieldValues() const 
override;
+
+       virtual std::map<std::string, std::string> getIdentifiers() const 
override;
+
+       virtual std::string getMetadata(METADATA metadata) const override;
+
 
  private:
 
-       static std::string to_string(const wchar_t* pChar);
+        std::vector<std::string> getIdentifiers(const std::string &text) const 
;
 
-  /**
+
+        static std::string getString(const std::map<std::string, std::string> 
&map, const std::string &field) {
+                auto srch = map.find(field);
+                if (srch != std::end(map)) {
+                        return srch->second;
+                }
+                return "N/A";
+       }
+       static std::string to_string(const wchar_t* pChar);
+   /**
    * Updates text within the XML representation
    */
   void updateText(pugi::xml_node &node, const std::string &field_name, 
std::function<std::string(const std::string &)> &&fn);
@@ -76,14 +92,14 @@ class MetadataWalker : public pugi::xml_tree_walker {
    * Gets event data.
    */
   std::string getEventData(EVT_FORMAT_MESSAGE_FLAGS flags);
-
-  EVT_HANDLE metadata_ptr_;
-  EVT_HANDLE event_ptr_;
+  
   std::regex regex_;
   std::string regex_str_;
   bool update_xml_;
   bool resolve_;
+  std::map<std::string, std::string> metadata_;
   std::map<std::string, std::string> fields_values_;
+  std::map<std::string, std::string> replaced_identifiers_;
 
 };
 
diff --git a/extensions/windows-event-log/wel/WindowsEventLog.cpp 
b/extensions/windows-event-log/wel/WindowsEventLog.cpp
new file mode 100644
index 0000000..4b2b041
--- /dev/null
+++ b/extensions/windows-event-log/wel/WindowsEventLog.cpp
@@ -0,0 +1,200 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <winmeta.h>
+#include "WindowsEventLog.h"
+#include "utils/Deleters.h"
+#include <algorithm>
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace wel {
+
+
+void WindowsEventLogMetadata::renderMetadata() {
+       DWORD status = ERROR_SUCCESS;
+       DWORD dwBufferSize = 0;
+       DWORD dwBufferUsed = 0;
+       DWORD dwPropertyCount = 0;
+       std::unique_ptr< EVT_VARIANT, utils::FreeDeleter> rendered_values;
+
+       auto context = EvtCreateRenderContext(0, NULL, EvtRenderContextSystem);
+       if (!EvtRender(context, event_ptr_, EvtRenderEventValues, dwBufferSize, 
nullptr, &dwBufferUsed, &dwPropertyCount))
+       {
+               if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError()))
+               {
+                       dwBufferSize = dwBufferUsed;
+                       rendered_values = std::unique_ptr<EVT_VARIANT, 
utils::FreeDeleter>((PEVT_VARIANT)(malloc(dwBufferSize)));
+                       if (rendered_values)
+                       {
+                               EvtRender(context, event_ptr_, 
EvtRenderEventValues, dwBufferSize, rendered_values.get(), &dwBufferUsed, 
&dwPropertyCount);
+                       }
+               }
+               else {
+                       return;
+               }
+
+               if (ERROR_SUCCESS != (status = GetLastError()))
+               {
+                       return;
+               }
+       }
+
+       event_timestamp_ = static_cast<PEVT_VARIANT>( 
rendered_values.get())[EvtSystemTimeCreated].FileTimeVal;
+
+       SYSTEMTIME st;
+       FILETIME ft;
+
+       ft.dwHighDateTime = (DWORD)((event_timestamp_ >> 32) & 0xFFFFFFFF);
+       ft.dwLowDateTime = (DWORD)(event_timestamp_ & 0xFFFFFFFF);
+
+       FileTimeToSystemTime(&ft, &st);
+       std::stringstream datestr;
+
+       std::string period = "AM";
+       auto hour = st.wHour;
+       if (hour >= 12 && hour < 24)
+               period = "PM";
+       if (hour >= 12)
+               hour -= 12;
+       datestr << st.wMonth << "/" << st.wDay << "/" << st.wYear << " " << 
std::setfill('0') << std::setw(2) << hour << ":" << std::setfill('0') << 
std::setw(2) << st.wMinute << ":" << std::setfill('0') << std::setw(2) << 
st.wSecond << " " << period;
+       event_timestamp_str_ = datestr.str();
+       auto level = 
static_cast<PEVT_VARIANT>(rendered_values.get())[EvtSystemLevel];
+       auto keyword = 
static_cast<PEVT_VARIANT>(rendered_values.get())[EvtSystemKeywords];
+       if (level.Type == EvtVarTypeByte) {
+               switch (level.ByteVal)
+               {
+                       case WINEVENT_LEVEL_CRITICAL:
+                       case WINEVENT_LEVEL_ERROR:
+                               event_type_ = "Error";
+                               event_type_index_ = 1;
+                               break;
+                       case WINEVENT_LEVEL_WARNING:
+                               event_type_ = "Warning";
+                               event_type_index_ = 2;
+                               break;
+                       case WINEVENT_LEVEL_INFO:
+                       case WINEVENT_LEVEL_VERBOSE:
+                               event_type_ = "Information";
+                               event_type_index_ = 4;
+                               break;
+                       default:
+                               event_type_index_ = 0;
+               };
+
+       }
+       else {
+               event_type_ = "N/A";
+       }
+
+       if (keyword.UInt64Val & WINEVENT_KEYWORD_AUDIT_SUCCESS) {
+               event_type_ = "Success Audit";
+               event_type_index_ = 8;
+       } else if (keyword.UInt64Val & EVENTLOG_AUDIT_FAILURE) {
+               event_type_ = "Failure Audit";
+               event_type_index_ = 16;
+       }
+
+       
+
+}
+
+std::string WindowsEventLogHandler::getEventMessage(EVT_HANDLE eventHandle) 
const
+{
+       std::string returnValue;
+       std::unique_ptr<WCHAR, utils::FreeDeleter> pBuffer;
+       DWORD dwBufferSize = 0;
+       DWORD dwBufferUsed = 0;
+       DWORD status = 0;
+
+       EvtFormatMessage(metadata_provider_, eventHandle, 0, 0, NULL, 
EvtFormatMessageEvent, dwBufferSize, pBuffer.get(), &dwBufferUsed);
+
+       //  we need to get the size of the buffer
+       status = GetLastError();
+       if (ERROR_INSUFFICIENT_BUFFER == status) {
+               dwBufferSize = dwBufferUsed;
+
+               /* All C++ examples use malloc and even HeapAlloc in some 
cases. To avoid any problems ( with EvtFormatMessage calling
+                       free for example ) we will continue to use malloc and 
use a custom deleter with unique_ptr.
+               '*/
+               pBuffer = std::unique_ptr<WCHAR, 
utils::FreeDeleter>((LPWSTR)malloc(dwBufferSize * sizeof(WCHAR)));
+
+
+               if (pBuffer) {
+                       EvtFormatMessage(metadata_provider_, eventHandle, 0, 0, 
NULL, EvtFormatMessageEvent, dwBufferSize, pBuffer.get(), &dwBufferUsed);
+               }
+               else {
+                       return returnValue;
+               }
+       }
+       else if (ERROR_EVT_MESSAGE_NOT_FOUND == status || 
ERROR_EVT_MESSAGE_ID_NOT_FOUND == status) {
+               return returnValue;
+       }
+       else {
+               return returnValue;
+       }
+
+       // convert wstring to std::string
+       std::wstring message(pBuffer.get());
+       returnValue = std::string(message.begin(), message.end());
+       return returnValue;
+
+}
+
+void WindowsEventLogHeader::setDelimiter(const std::string &delim) {
+       delimiter_ = delim;
+}
+
+std::string WindowsEventLogHeader::createDefaultDelimiter(size_t max, size_t 
length) const {
+       if (max > length) {
+               return ":" + std::string(max - length, ' ');
+       }
+       else {
+               return ": ";
+       }
+}
+
+std::string WindowsEventLogHeader::getEventHeader(const 
WindowsEventLogMetadata * const metadata) const{
+       std::stringstream eventHeader;
+       size_t max = 1;
+       for (const auto &option : header_names_) {
+               max = (std::max(max, option.second.size()));
+       }
+       ++max; // increment by one to get space.
+       for (const auto &option : header_names_) {
+               auto name = option.second;
+               if (!name.empty()) {
+                       eventHeader << name << (delimiter_.empty() ? 
createDefaultDelimiter(max, name.size()) : delimiter_);
+               }
+               eventHeader << 
utils::StringUtils::trim(metadata->getMetadata(option.first)) << std::endl;
+       }
+
+       return eventHeader.str();
+}
+
+EVT_HANDLE WindowsEventLogHandler::getMetadata() const {
+       return metadata_provider_;
+}
+
+} /* namespace wel */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
diff --git a/extensions/windows-event-log/wel/WindowsEventLog.h 
b/extensions/windows-event-log/wel/WindowsEventLog.h
new file mode 100644
index 0000000..7f8004b
--- /dev/null
+++ b/extensions/windows-event-log/wel/WindowsEventLog.h
@@ -0,0 +1,193 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "core/Core.h"
+#include "FlowFileRecord.h"
+#include "concurrentqueue.h"
+#include "core/Processor.h"
+#include "core/ProcessSession.h"
+#include <pugixml.hpp>
+#include <winevt.h>
+#include <sstream>
+#include <regex>
+#include <codecvt>
+#include "utils/OsUtils.h"
+
+
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace wel {
+
+
+       enum METADATA {
+               LOG_NAME,
+               SOURCE,
+               TIME_CREATED,
+               EVENTID,
+               OPCODE,
+               EVENT_RECORDID,
+               EVENT_TYPE,
+               TASK_CATEGORY,
+               LEVEL,
+               KEYWORDS,
+               USER,
+               COMPUTER,
+               UNKNOWN
+       };
+
+
+// this is a continuous enum so we can rely on the array
+
+typedef std::vector<std::pair<METADATA, std::string>> METADATA_NAMES;
+
+class WindowsEventLogHandler
+{
+public:
+       WindowsEventLogHandler() : metadata_provider_(nullptr){
+       }
+       
+       explicit WindowsEventLogHandler(EVT_HANDLE metadataProvider) : 
metadata_provider_(metadataProvider) {
+       }
+       
+       std::string getEventMessage(EVT_HANDLE eventHandle) const;
+
+
+       EVT_HANDLE getMetadata() const;
+
+private:
+       EVT_HANDLE metadata_provider_;
+};
+
+class WindowsEventLogMetadata {
+public:
+       WindowsEventLogMetadata(EVT_HANDLE metadataProvider, EVT_HANDLE 
event_ptr, const std::string &log_name) : metadata_ptr_(metadataProvider), 
event_timestamp_(0), event_ptr_(event_ptr), log_name_(log_name) {
+               renderMetadata();
+       }
+
+       virtual std::map<std::string, std::string> getFieldValues() const = 0;
+
+       virtual std::map<std::string, std::string> getIdentifiers() const = 0;
+
+       virtual std::string getMetadata(METADATA metadata) const = 0;
+
+       
+       void renderMetadata();
+
+
+       static std::string getMetadataString(METADATA val) {
+               static std::map< METADATA, std::string> map = {
+                       {LOG_NAME,      "LOG_NAME" },
+               {SOURCE,"SOURCE"},
+               {TIME_CREATED,"TIME_CREATED" },
+               {EVENTID,"EVENTID"},
+               {OPCODE,"OPCODE"},
+               {EVENT_RECORDID,"EVENT_RECORDID"},
+               {EVENT_TYPE,"EVENT_TYPE"},
+               {TASK_CATEGORY, "TASK_CATEGORY"},
+               {LEVEL,"LEVEL"},
+               {KEYWORDS,"KEYWORDS"},
+               {USER,"USER"},
+               {COMPUTER,"COMPUTER"}
+               };
+
+               return map[val];
+       }
+
+
+       static METADATA getMetadataFromString(const std::string &val) {
+               static std::map< std::string, METADATA> map = {
+                       {"LOG_NAME",LOG_NAME},
+                       {"SOURCE",SOURCE},
+                       {"TIME_CREATED",TIME_CREATED },
+                       {"EVENTID",EVENTID},
+                       {"OPCODE",OPCODE},
+                       {"EVENT_RECORDID",EVENT_RECORDID},
+                       {"TASK_CATEGORY", TASK_CATEGORY},
+                       {"EVENT_TYPE",EVENT_TYPE},
+                       {"LEVEL",LEVEL},
+                       {"KEYWORDS",KEYWORDS},
+                       {"USER",USER},
+                       {"COMPUTER",COMPUTER}
+               };
+
+               auto enumVal = map.find(val);
+               if (enumVal != std::end(map)) {
+                       return enumVal->second;
+               }
+               else {
+                       return METADATA::UNKNOWN;
+               }
+       }
+
+       static std::string getComputerName() {
+               static std::string computer_name;
+               if (computer_name.empty()) {
+                       char buff[10248];
+                       DWORD size = sizeof(buff);
+                       if (GetComputerNameExA(ComputerNameDnsFullyQualified, 
buff, &size)) {
+                               computer_name = buff;
+                       }
+                       else {
+                               computer_name = "N/A";
+                       }
+               }
+               return computer_name;
+       }
+
+protected:
+       std::string log_name_;
+       uint64_t event_timestamp_;
+       std::string event_type_;
+       short event_type_index_;
+       std::string event_timestamp_str_;
+       EVT_HANDLE event_ptr_;
+       EVT_HANDLE metadata_ptr_;
+};
+
+
+class WindowsEventLogHeader {
+public:
+       explicit WindowsEventLogHeader(METADATA_NAMES header_names) : 
header_names_(header_names){
+
+       }
+
+       void setDelimiter(const std::string &delim);
+
+       std::string getEventHeader(const WindowsEventLogMetadata * const 
metadata) const;
+
+       
+
+private:
+
+       inline std::string createDefaultDelimiter(size_t max, size_t length) 
const;
+
+       std::string delimiter_;
+       METADATA_NAMES header_names_;
+};
+
+} /* namespace wel */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+
diff --git a/libminifi/include/utils/Deleters.h 
b/libminifi/include/utils/Deleters.h
new file mode 100644
index 0000000..5a56dfb
--- /dev/null
+++ b/libminifi/include/utils/Deleters.h
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef LIBMINIFI_INCLUDE_UTILS_DELETERS_H
+#define LIBMINIFI_INCLUDE_UTILS_DELETERS_H
+
+namespace org {
+namespace apache {
+namespace nifi {
+namespace minifi {
+namespace utils {
+
+class FreeDeleter
+{
+public:
+       void operator()(void* ptr) { 
+               // free(null) is guaranteed to be safe, so no need to be 
defensive.
+               free(ptr); 
+       }
+};
+
+} /* namespace utils */
+} /* namespace minifi */
+} /* namespace nifi */
+} /* namespace apache */
+} /* namespace org */
+#endif // LIBMINIFI_INCLUDE_UTILS_DELETERS_H
\ No newline at end of file

Reply via email to