1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
#ifndef _VALIDATE_H
#define _VALIDATE_H
#include <functional>
#include <regex>
#include <stdexcept>
#include <string>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
#if !defined(_WIN32)
#include <arpa/inet.h>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
// <windows.h> 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"
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<std::type_index,
std::function<bool(const google::protobuf::Message&, ValidationMsg*)>>&
abstractValidators() {
static auto* validator_map = new std::unordered_map<
std::type_index, std::function<bool(const google::protobuf::Message&, ValidationMsg*)>>();
return *validator_map;
}
};
template <typename T> class Validator : public BaseValidator {
public:
Validator(std::function<bool(const T&, ValidationMsg*)> check) : check_(check) {
abstractValidators()[std::type_index(typeid(T))] = [this](const google::protobuf::Message& m,
ValidationMsg* err) -> bool {
return check_(dynamic_cast<const T&>(m), err);
};
}
private:
std::function<bool(const T&, ValidationMsg*)> 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;
}
namespace {
inline int OneCharLen(const char* src) {
return "\1\1\1\1\1\1\1\1\1\1\1\1\2\2\3\4"[(*src & 0xFF) >> 4];
}
inline int UTF8FirstLetterNumBytes(const char *utf8_str, int str_len) {
if (str_len == 0)
return 0;
return OneCharLen(utf8_str);
}
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 = UTF8FirstLetterNumBytes(str_char, byte_len);
str_char += char_len;
byte_len -= char_len;
++unicode_len;
}
return unicode_len;
}
} // namespace
} // namespace pgv
#endif // _VALIDATE_H
|