#ifndef _VALIDATE_H #define _VALIDATE_H #include #include #include #include #include #include #include #if !defined(_WIN32) #include #else #include #include // uses macros to #define a ton of symbols, // many of which interfere with our code here and down // the line in various extensions. #undef DELETE #undef ERROR #undef GetMessage #undef interface #undef TRUE #undef min #endif #include "google/protobuf/message.h" #include "google/protobuf/stubs/strutil.h" // for UTF8Len namespace pgv { using std::string; class UnimplementedException : public std::runtime_error { public: UnimplementedException() : std::runtime_error("not yet implemented") {} UnimplementedException(const std::string& message) : std::runtime_error(message) {} // Thrown by C++ validation code that is not yet implemented. }; using ValidationMsg = std::string; class BaseValidator { public: /** * Validate/check a generic message object with a registered validator for the concrete message * type. * @param m supplies the message to check. * @param err supplies the place to return error information. * @return true if the validation passes OR there is no registered validator for the concrete * message type. false is returned if validation explicitly fails. */ static bool AbstractCheckMessage(const google::protobuf::Message& m, ValidationMsg* err) { // Polymorphic lookup is used to see if there is a matching concrete validator. If so, call it. // Otherwise return success. auto it = abstractValidators().find(std::type_index(typeid(m))); if (it == abstractValidators().end()) { return true; } return it->second(m, err); } protected: // Used to implement AbstractCheckMessage() above. Every message that is linked into the binary // will register itself by type_index, allowing for polymorphic lookup later. static std::unordered_map>& abstractValidators() { static auto* validator_map = new std::unordered_map< std::type_index, std::function>(); return *validator_map; } }; template class Validator : public BaseValidator { public: Validator(std::function check) : check_(check) { abstractValidators()[std::type_index(typeid(T))] = [this](const google::protobuf::Message& m, ValidationMsg* err) -> bool { return check_(dynamic_cast(m), err); }; } private: std::function check_; }; static inline std::string String(const ValidationMsg& msg) { return std::string(msg); } static inline bool IsPrefix(const string& maybe_prefix, const string& search_in) { return search_in.compare(0, maybe_prefix.size(), maybe_prefix) == 0; } static inline bool IsSuffix(const string& maybe_suffix, const string& search_in) { return maybe_suffix.size() <= search_in.size() && search_in.compare(search_in.size() - maybe_suffix.size(), maybe_suffix.size(), maybe_suffix) == 0; } static inline bool Contains(const string& search_in, const string& to_find) { return search_in.find(to_find) != string::npos; } static inline bool NotContains(const string& search_in, const string& to_find) { return !Contains(search_in, to_find); } static inline bool IsIpv4(const string& to_validate) { struct sockaddr_in sa; return !(inet_pton(AF_INET, to_validate.c_str(), &sa.sin_addr) < 1); } static inline bool IsIpv6(const string& to_validate) { struct sockaddr_in6 sa_six; return !(inet_pton(AF_INET6, to_validate.c_str(), &sa_six.sin6_addr) < 1); } static inline bool IsIp(const string& to_validate) { return IsIpv4(to_validate) || IsIpv6(to_validate); } static inline bool IsHostname(const string& to_validate) { if (to_validate.length() > 253) { return false; } const std::regex dot_regex{"\\."}; const auto iter_end = std::sregex_token_iterator(); auto iter = std::sregex_token_iterator(to_validate.begin(), to_validate.end(), dot_regex, -1); for (; iter != iter_end; ++iter) { const std::string& part = *iter; if (part.empty() || part.length() > 63) { return false; } if (part.at(0) == '-') { return false; } if (part.at(part.length() - 1) == '-') { return false; } for (const auto& character : part) { if ((character < 'A' || character > 'Z') && (character < 'a' || character > 'z') && (character < '0' || character > '9') && character != '-') { return false; } } } return true; } static inline size_t Utf8Len(const string& narrow_string) { const char* str_char = narrow_string.c_str(); ptrdiff_t byte_len = narrow_string.length(); size_t unicode_len = 0; int char_len = 1; while (byte_len > 0 && char_len > 0) { char_len = google::protobuf::UTF8FirstLetterNumBytes(str_char, byte_len); str_char += char_len; byte_len -= char_len; ++unicode_len; } return unicode_len; } } // namespace pgv #endif // _VALIDATE_H