From 958d3982c516902041921a6eb1fe4d7e4cf01f79 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Mon, 15 May 2017 16:02:20 +0200 Subject: [PATCH] Add 'mode' argument for match, regex and cidr_match --- doc/18-library-reference.md | 16 +++- lib/base/scriptutils.cpp | 141 +++++++++++++++++++++++++++++++++--- lib/base/scriptutils.hpp | 5 +- 3 files changed, 145 insertions(+), 17 deletions(-) diff --git a/doc/18-library-reference.md b/doc/18-library-reference.md index 2dd41d69f..4c82ab4c1 100644 --- a/doc/18-library-reference.md +++ b/doc/18-library-reference.md @@ -16,7 +16,10 @@ Signature: function regex(pattern, text) -Returns true if the regular expression matches the text, false otherwise. +Returns true if the regular expression matches the text, false otherwise. The mode argument is +optional and can be either MatchAll (in which case all elements for an array have to match) or MatchAny +(in which case at least one element has to match). The default mode is MatchAll. + **Tip**: In case you are looking for regular expression tests try [regex101](https://regex101.com). Example: @@ -34,9 +37,11 @@ Example: Signature: - function match(pattern, text) + function match(pattern, text, mode) -Returns true if the wildcard (`?*`) pattern matches the text, false otherwise. +Returns true if the wildcard (`?*`) pattern matches the text, false otherwise. The mode argument is +optional and can be either MatchAll (in which case all elements for an array have to match) or MatchAny +(in which case at least one element has to match). The default mode is MatchAll. Example: @@ -59,7 +64,10 @@ Signature: Returns true if the CIDR pattern matches the IP address, false otherwise. IPv4 addresses are converted to IPv4-mapped IPv6 addresses before being -matched against the pattern. +matched against the pattern. The mode argument is optional and can be +either MatchAll (in which case all elements for an array have to match) or MatchAny +(in which case at least one element has to match). The default mode is MatchAll. + Example: diff --git a/lib/base/scriptutils.cpp b/lib/base/scriptutils.cpp index 9cbdc1c5f..ef70eccbf 100644 --- a/lib/base/scriptutils.cpp +++ b/lib/base/scriptutils.cpp @@ -27,6 +27,7 @@ #include "base/configtype.hpp" #include "base/application.hpp" #include "base/dependencygraph.hpp" +#include "base/initialize.hpp" #include #include #include @@ -36,9 +37,9 @@ using namespace icinga; -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, regex, &ScriptUtils::Regex, "pattern:text"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, match, &Utility::Match, "pattern:text"); -REGISTER_SAFE_SCRIPTFUNCTION_NS(System, cidr_match, &Utility::CidrMatch, "pattern:ip"); +REGISTER_SAFE_SCRIPTFUNCTION_NS(System, regex, &ScriptUtils::Regex, "pattern:text:mode"); +REGISTER_SAFE_SCRIPTFUNCTION_NS(System, match, &ScriptUtils::Match, "pattern:text:mode"); +REGISTER_SAFE_SCRIPTFUNCTION_NS(System, cidr_match, &ScriptUtils::CidrMatch, "pattern:ip:mode"); REGISTER_SAFE_SCRIPTFUNCTION_NS(System, len, &ScriptUtils::Len, "value"); REGISTER_SAFE_SCRIPTFUNCTION_NS(System, union, &ScriptUtils::Union, ""); REGISTER_SAFE_SCRIPTFUNCTION_NS(System, intersection, &ScriptUtils::Intersection, ""); @@ -67,6 +68,20 @@ REGISTER_SAFE_SCRIPTFUNCTION_NS(System, escape_create_process_arg, &Utility::Esc REGISTER_SCRIPTFUNCTION_NS(System, ptr, &ScriptUtils::Ptr, "object"); REGISTER_SCRIPTFUNCTION_NS(System, sleep, &Utility::Sleep, "interval"); +INITIALIZE_ONCE(&ScriptUtils::StaticInitialize); + +enum MatchType +{ + MatchAll, + MatchAny +}; + +void ScriptUtils::StaticInitialize(void) +{ + ScriptGlobal::Set("MatchAll", MatchAll); + ScriptGlobal::Set("MatchAny", MatchAny); +} + String ScriptUtils::CastString(const Value& value) { return value; @@ -81,18 +96,120 @@ bool ScriptUtils::CastBool(const Value& value) { return value.ToBool(); } -bool ScriptUtils::Regex(const String& pattern, const String& text) + +bool ScriptUtils::Regex(const std::vector& args) { - bool res = false; - try { - boost::regex expr(pattern.GetData()); - boost::smatch what; - res = boost::regex_search(text.GetData(), what, expr); - } catch (boost::exception&) { - res = false; /* exception means something went terribly wrong */ + if (args.size() < 2) + BOOST_THROW_EXCEPTION(std::invalid_argument("Regular expression and text must be specified.")); + + Array::Ptr texts = new Array(); + + String pattern = args[0]; + Value argTexts = args[1]; + MatchType mode; + + if (args.size() > 2) + mode = static_cast(static_cast(args[2])); + else + mode = MatchAll; + + if (argTexts.IsObjectType()) + texts = argTexts; + else { + texts = new Array(); + texts->Add(argTexts); } - return res; + ObjectLock olock(texts); + for (const String& text : texts) { + bool res = false; + try { + boost::regex expr(pattern.GetData()); + boost::smatch what; + res = boost::regex_search(text.GetData(), what, expr); + } catch (boost::exception&) { + res = false; /* exception means something went terribly wrong */ + } + + if (mode == MatchAny && res) + return true; + else if (mode == MatchAll && !res) + return false; + } + + return mode == MatchAll; +} + +bool ScriptUtils::Match(const std::vector& args) +{ + if (args.size() < 2) + BOOST_THROW_EXCEPTION(std::invalid_argument("Pattern and text must be specified.")); + + Array::Ptr texts = new Array(); + + String pattern = args[0]; + Value argTexts = args[1]; + MatchType mode; + + if (args.size() > 2) + mode = static_cast(static_cast(args[2])); + else + mode = MatchAll; + + if (argTexts.IsObjectType()) + texts = argTexts; + else { + texts = new Array(); + texts->Add(argTexts); + } + + ObjectLock olock(texts); + for (const String& text : texts) { + bool res = Utility::Match(pattern, text); + + if (mode == MatchAny && res) + return true; + else if (mode == MatchAll && !res) + return false; + } + + return mode == MatchAll; +} + +bool ScriptUtils::CidrMatch(const std::vector& args) +{ + if (args.size() < 2) + BOOST_THROW_EXCEPTION(std::invalid_argument("CIDR and IP address must be specified.")); + + Array::Ptr ips = new Array(); + + String pattern = args[0]; + Value argIps = args[1]; + MatchType mode; + + if (args.size() > 2) + mode = static_cast(static_cast(args[2])); + else + mode = MatchAll; + + if (argIps.IsObjectType()) + ips = argIps; + else { + ips = new Array(); + ips->Add(argIps); + } + + ObjectLock olock(ips); + for (const String& ip : ips) { + bool res = Utility::CidrMatch(pattern, ip); + + if (mode == MatchAny && res) + return true; + else if (mode == MatchAll && !res) + return false; + } + + return mode == MatchAll; } double ScriptUtils::Len(const Value& value) diff --git a/lib/base/scriptutils.hpp b/lib/base/scriptutils.hpp index fd4333241..e605be5fa 100644 --- a/lib/base/scriptutils.hpp +++ b/lib/base/scriptutils.hpp @@ -36,10 +36,13 @@ namespace icinga class I2_BASE_API ScriptUtils { public: + static void StaticInitialize(void); static String CastString(const Value& value); static double CastNumber(const Value& value); static bool CastBool(const Value& value); - static bool Regex(const String& pattern, const String& text); + static bool Regex(const std::vector& args); + static bool Match(const std::vector& args); + static bool CidrMatch(const std::vector& args); static double Len(const Value& value); static Array::Ptr Union(const std::vector& arguments); static Array::Ptr Intersection(const std::vector& arguments);