/* * InspIRCd -- Internet Relay Chat Daemon * * Copyright (C) 2013, 2021-2024 Sadie Powell * Copyright (C) 2013 Attila Molnar * Copyright (C) 2012 Robby * Copyright (C) 2008 Robin Burchell * Copyright (C) 2007 Dennis Friis * Copyright (C) 2007 Craig Edwards * * This file is part of InspIRCd. InspIRCd is free software: you can * redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation, version 2. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once namespace Base64 { /** The default table used when handling Base64-encoded strings. */ inline constexpr const char* TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** Decodes a Base64-encoded byte array. * @param data The byte array to decode from. * @param length The length of the byte array. * @param table The index table to use for decoding. * @return The decoded form of the specified data. */ CoreExport std::string Decode(const void* data, size_t length, const char* table = nullptr); /** Decodes a Base64-encoded string. * @param data The string to decode from. * @param table The index table to use for decoding. * @return The decoded form of the specified data. */ inline std::string Decode(const std::string_view& data, const char* table = nullptr) { return Decode(data.data(), data.length(), table); } /** Encodes a byte array using Base64. * @param data The byte array to encode from. * @param length The length of the byte array. * @param table The index table to use for encoding. * @param padding If non-zero then the character to pad encoded strings with. * @return The encoded form of the specified data. */ CoreExport std::string Encode(const void* data, size_t length, const char* table = nullptr, char padding = 0); /** Encodes a string using Base64. * @param data The string to encode from. * @param table The index table to use for encoding. * @param padding If non-zero then the character to pad encoded strings with. * @return The encoded form of the specified data. */ inline std::string Encode(const std::string_view& data, const char* table = nullptr, char padding = 0) { return Encode(data.data(), data.length(), table, padding); } } namespace Hex { /** The table used for encoding as a lower-case hexadecimal string. */ inline constexpr const char* TABLE_LOWER = "0123456789abcdef"; /** The table used for encoding as an upper-case hexadecimal string. */ inline constexpr const char* TABLE_UPPER = "0123456789ABCDEF"; /** Decodes a hexadecimal-encoded byte array. * @param data The byte array to decode from. * @param length The length of the byte array. * @param separator If non-zero then the character hexadecimal digits are separated with. * @param table The index table to use for decoding. * @return The decoded form of the specified data. */ CoreExport std::string Decode(const void* data, size_t length, const char* table = nullptr, char separator = 0); /** Decodes a hexadecimal-encoded string. * @param data The string to decode from. * @param table The index table to use for decoding. * @param separator If non-zero then the character hexadecimal digits are separated with. * @return The decoded form of the specified data. */ inline std::string Decode(const std::string_view& data, const char* table = nullptr, char separator = 0) { return Decode(data.data(), data.length(), table, separator); } /** Encodes a byte array using hexadecimal encoding. * @param data The byte array to encode from. * @param length The length of the byte array. * @param table The index table to use for encoding. * @param separator If non-zero then the character to separate hexadecimal digits with. * @return The encoded form of the specified data. */ CoreExport std::string Encode(const void* data, size_t length, const char* table = nullptr, char separator = 0); /** Encodes a string using Base64. * @param data The string to encode from. * @param table The index table to use for encoding. * @param separator If non-zero then the character to separate hexadecimal digits with. * @return The encoded form of the specified data. */ inline std::string Encode(const std::string_view& data, const char* table = nullptr, char separator = 0) { return Encode(data.data(), data.length(), table, separator); } } namespace Percent { /** The table used to determine what characters are safe within a percent-encoded string. */ inline constexpr const char* TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~"; /** Decodes a percent-encoded byte array. * @param data The byte array to decode from. * @param length The length of the byte array. * @return The decoded form of the specified data. */ CoreExport std::string Decode(const void* data, size_t length); /** Decodes a percent-encoded string. * @param data The string to decode from. * @return The decoded form of the specified data. */ inline std::string Decode(const std::string_view& data) { return Decode(data.data(), data.length()); } /** Encodes a byte array using percent encoding. * @param data The byte array to encode from. * @param length The length of the byte array. * @param table The table of characters that do not require escaping. * @param upper Whether to use upper or lower case. * @return The encoded form of the specified data. */ CoreExport std::string Encode(const void* data, size_t length, const char* table = nullptr, bool upper = true); /** Encodes a string using percent encoding. * @param data The string to encode from. * @param table The table of characters that do not require escaping. * @param upper Whether to use upper or lower case. * @return The encoded form of the specified data. */ inline std::string Encode(const std::string_view& data, const char* table = nullptr, bool upper = true) { return Encode(data.data(), data.length(), table, upper); } } namespace Template { /** A mapping of variable names to their values. */ using VariableMap = insp::flat_map; /** Replaces template variables like %foo% within a string. * @param str The string to template from. * @param vars The variables to replace within the string. * @return The specified string with all variables replaced within it. */ CoreExport std::string Replace(const std::string_view& str, const VariableMap& vars); } /** Alows tokens in the IRC wire format to be read from a string. */ class CoreExport MessageTokenizer final { private: /** The message to parse tokens from. */ std::string message; /** The current position in the message. */ std::string::size_type position = 0; public: /** Creates a message tokenizer and fills it with the provided data. * @param msg The messsage to parse tokens from. * @param start The index to start tokenizing from. * @param end The index to stop tokenizing at. */ MessageTokenizer(const std::string_view& msg, std::string::size_type start = 0, std::string::size_type end = std::string::npos); /** Determines whether the tokenizer at at the end of the message. */ inline auto AtEnd() const { return this->position >= this->message.length(); } /** Retrieves a reference to the whole message. */ auto& GetMessage() { return this->message; } /** Retrieves the next \ token in the message. * @param token The next token available, or an empty string if none remain. * @return True if a token was retrieved; otherwise, false. */ bool GetMiddle(std::string& token); /** Retrieves the next \ token in the message. * @param token The next token available, or an empty string view if none remain. * @return True if a token was retrieved; otherwise, false. */ bool GetMiddle(std::string_view& token); /** Retrieves all remaining \ tokens in the message. * @param tokens A vector to place all remaining \ tokens in. */ template void GetRemainingMiddle(std::vector& tokens) { tokens.clear(); for (Value token; GetMiddle(token); ) tokens.push_back(token); } /** Retrieves all remaining \ tokens in the message. * @param tokens A vector to place all remaining \ tokens in. */ template void GetRemainingTrailing(std::vector& tokens) { tokens.clear(); for (Value token; GetTrailing(token); ) tokens.push_back(token); } /** Retrieves the current position in the message. */ auto GetPosition() const { return this->position; } /** Retrieves the next \ token in the message. * @param token The next token available, or an empty string if none remain. * @return True if a token was retrieved; otherwise, false. */ bool GetTrailing(std::string& token); /** Retrieve the next \ token in the message. * @param token The next token available, or an empty string view if none remain. * @return True if a token was retrieved; otherwise, false. */ bool GetTrailing(std::string_view& token); }; class CoreExport StringSplitter final { private: /** The current position in the string. */ std::string::size_type position = 0; public: /** Whether to allow empty values in the string. */ const bool allow_empty; /** The value to split the string based on. */ const std::string::value_type separator; /** The string to parse tokens from. */ const std::string string; /** Creates a string splitter and fills it with the provided data. * @param str The string to split. * @param sep The value to split on. * @param ae Whether to allow empty tokens within the string. * @param start The index to start splitting from. * @param end The index to stop splitting at. */ StringSplitter(const std::string_view& str, std::string::value_type sep = ' ', bool ae = false, std::string::size_type start = 0, std::string::size_type end = std::string::npos); /** Determines whether the splitter at at the end of the message. */ inline auto AtEnd() const { return this->position >= this->string.length(); } /** Retrieves the current position in the string. */ inline auto GetPosition() const { return this->position; } /** Retrieves a view to the part of the string which has not been split yet. */ std::string_view GetRemaining() const; /** Retrieves the next token in the string. * @param token The next token available, or an empty string if none remain. * @return True if a token was retrieved; otherwise, false. */ bool GetToken(std::string& token); /** Retrieves the next token in the string. * @param token The next token available, or an empty string view if none remain. * @return True if a token was retrieved; otherwise, false. */ bool GetToken(std::string_view& token); /** Retrieves the next numeric token in the string. * @param token The next token available, or an default-initialised value if none remain. * @return True if a token was retrieved; otherwise, false. */ template bool GetToken(Numeric& token) { if (std::string_view sv; GetToken(sv)) { token = ConvToNum(sv); return true; } token = Numeric(); return true; } }; class CoreExport NumberRange final { private: /** If we are in a num1-num2 range then the start and end of the current span. */ std::pair current_span; /** If we are skipping duplicates then numbers that have already been seen. */ std::set seen; /** The splitter for separating comma-delimited ports. */ StringSplitter splitter; /** Retrieves a single number from the range between \p sv1 and \p sv2. * @param sv1 A string view containing the start of the range. * @param sv2 A string view containing the end of the range. * @param num The next number available, or an default-initialised value if none remain. * @param min The minimum valid value to return. * @param max The maximum valid value to return. * @return True if a number was retrieved; otherwise, false. */ bool GetRange(const std::string_view& sv1, const std::string_view& sv2, uintmax_t& num, uintmax_t min, uintmax_t max); /** Retrieves a single number from \p sv. * @param sv A string view containing a single number. * @param num The next number available, or an default-initialised value if none remain. * @param min The minimum valid value to return. * @param max The maximum valid value to return. * @return True if a number was retrieved; otherwise, false. */ bool GetSingle(const std::string_view& sv, uintmax_t& num, uintmax_t min, uintmax_t max); /** Retrieves a number from the range stored in \ref current_span. * @param num The next number available, or an default-initialised value if none remain. * @param min The minimum valid value to return. * @param max The maximum valid value to return. * @return True if a number was retrieved; otherwise, false. */ bool GetSpan(uintmax_t& num, uintmax_t min, uintmax_t max); /** Retrieves the next number in the range. * @param num The next number available, or an default-initialised value if none remain. * @param min The minimum valid value to return. * @param max The maximum valid value to return. * @return True if a number was retrieved; otherwise, false. */ bool GetToken(uintmax_t& num, uintmax_t min = std::numeric_limits::min(), uintmax_t max = std::numeric_limits::max()); /** Parses an integer. */ static bool Parse(const std::string_view& in, uintmax_t& out); /** Determines if the specified number has been seen already and marks it as seen if not. */ bool Seen(uintmax_t num); public: /** Whether to skip duplicates in the range. */ const bool skip_duplicates; /** Creates a number range and fills it with the provided data. * @param range A number range in the format 1,2,3-4,5. * @param sd Whether to skip duplicates in the range. * @param start The index to start tokenizing from. * @param end The index to stop tokenizing at. */ NumberRange(const std::string_view& range, bool sd = true, std::string::size_type start = 0, std::string::size_type end = std::string::npos); /** @copydoc NumberRange::GetToken */ template bool GetToken(Numeric& num, Numeric min = std::numeric_limits::min(), Numeric max = std::numeric_limits::max()) { if (uintmax_t n; GetToken(n, static_cast(min), static_cast(max))) { num = static_cast(n); return true; } num = Numeric(); return false; } };