diff --git a/contrib/epee/include/net/abstract_http_client.h b/contrib/epee/include/net/abstract_http_client.h index 29a7ce19b9..95dd9e72b2 100644 --- a/contrib/epee/include/net/abstract_http_client.h +++ b/contrib/epee/include/net/abstract_http_client.h @@ -34,27 +34,8 @@ namespace epee { namespace net_utils { - inline const char* get_hex_vals() - { - static constexpr const char hexVals[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; - return hexVals; - } - - inline const char* get_unsave_chars() - { - //static constexpr char unsave_chars[] = "\"<>%\\^[]`+$,@:;/!#?=&"; - static constexpr const char unsave_chars[] = "\"<>%\\^[]`+$,@:;!#&"; - return unsave_chars; - } - - bool is_unsafe(unsigned char compare_char); - std::string dec_to_hex(char num, int radix); - int get_index(const char *s, char c); - std::string hex_to_dec_2bytes(const char *s); - std::string convert(char val); - std::string conver_to_url_format(const std::string& uri); - std::string convert_from_url_format(const std::string& uri); - + std::string convert_to_url_format(std::string_view uri); + std::string convert_from_url_format(std::string_view uri); namespace http { class abstract_http_client diff --git a/contrib/epee/src/abstract_http_client.cpp b/contrib/epee/src/abstract_http_client.cpp index bd9cb0ddea..a7767118fb 100644 --- a/contrib/epee/src/abstract_http_client.cpp +++ b/contrib/epee/src/abstract_http_client.cpp @@ -1,3 +1,4 @@ +#include #include #include "net/abstract_http_client.h" #include "net/http_base.h" @@ -7,113 +8,70 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net.http" +namespace { +constexpr auto hex_byte_len = 2; // 00 to FF +constexpr char uri_escape_char = '%'; // placed before each byte i.e. %61 +// immediately invoked lambda expression that creates a lookup table at compile-time. +// each safe byte for URL is '\0\0', each unsafe byte has the hex representation of the byte. +constexpr auto encoding_table = []() -> std::array, 256> { + std::array, 256> temp{}; // everything is zeroed-out. + std::string_view unsafe{"\"<>%\\^[]`+$,@:;!#&"}; + for (std::size_t i = 0; i < temp.size(); ++i) { + if((i <= 32) || (i >= 123) || (unsafe.find(i) != unsafe.npos)) { + // encode the unsafe characters. + const auto first {i / 16}; + const auto second {i % 16}; + temp[i][0] = (first < 0xA) ? ('0' + first) : ('A' + (first - 0xA)); + temp[i][1] = (second < 0xA) ? ('0' + second) : ('A' + (second - 0xA)); + } + } + return temp; +}(); // note : all this is done at compile-time. +} // namespace + namespace epee { namespace net_utils { - //---------------------------------------------------------------------------------------------------- - bool is_unsafe(unsigned char compare_char) + std::string convert_to_url_format(std::string_view uri) { - if(compare_char <= 32 || compare_char >= 123) - return true; - - const char* punsave = get_unsave_chars(); - - for(int ichar_pos = 0; 0!=punsave[ichar_pos] ;ichar_pos++) - if(compare_char == punsave[ichar_pos]) - return true; - - return false; - } - //---------------------------------------------------------------------------------------------------- - std::string dec_to_hex(char num, int radix) - { - int temp=0; - std::string csTmp; - int num_char; - - num_char = (int) num; - if (num_char < 0) - num_char = 256 + num_char; - - while (num_char >= radix) - { - temp = num_char % radix; - num_char = (int)floor((float)num_char / (float)radix); - csTmp = get_hex_vals()[temp]; + std::string result (uri.size() * (hex_byte_len + 1), '\0'); // reserve the max possible size, shrink later. + auto write_it {result.begin()}; + for (const unsigned char character : uri) { + const std::array& encoding = encoding_table[character]; + if (encoding.front() == '\0') { + *(write_it++) = character; + } else { + *write_it = uri_escape_char; + write_it = std::copy(encoding.begin(), encoding.end(), write_it + 1); + } } - - csTmp += get_hex_vals()[num_char]; - - if(csTmp.size() < 2) - { - csTmp += '0'; - } - - std::reverse(csTmp.begin(), csTmp.end()); - //_mbsrev((unsigned char*)csTmp.data()); - - return csTmp; - } - //---------------------------------------------------------------------------------------------------- - int get_index(const char *s, char c) - { - const char *ptr = (const char*)memchr(s, c, 16); - return ptr ? ptr-s : -1; - } - //---------------------------------------------------------------------------------------------------- - std::string hex_to_dec_2bytes(const char *s) - { - const char *hex = get_hex_vals(); - int i0 = get_index(hex, toupper(s[0])); - int i1 = get_index(hex, toupper(s[1])); - if (i0 < 0 || i1 < 0) - return std::string("%") + std::string(1, s[0]) + std::string(1, s[1]); - return std::string(1, i0 * 16 | i1); - } - //---------------------------------------------------------------------------------------------------- - std::string convert(char val) - { - std::string csRet; - csRet += "%"; - csRet += dec_to_hex(val, 16); - return csRet; - } - //---------------------------------------------------------------------------------------------------- - std::string conver_to_url_format(const std::string& uri) - { - - std::string result; - - for(size_t i = 0; i!= uri.size(); i++) - { - if(is_unsafe(uri[i])) - result += convert(uri[i]); - else - result += uri[i]; - - } - + result.resize(std::distance(result.begin(), write_it)); return result; } //---------------------------------------------------------------------------------------------------- - std::string convert_from_url_format(const std::string& uri) + std::string convert_from_url_format(std::string_view uri) { - - std::string result; - - for(size_t i = 0; i!= uri.size(); i++) - { - if(uri[i] == '%' && i + 2 < uri.size()) - { - result += hex_to_dec_2bytes(uri.c_str() + i + 1); - i += 2; + std::string result (uri.size(), '\0'); // reserve the max possible size, shrink later. + auto write_it {result.begin()}; + for (auto it {uri.cbegin()}, end {uri.cend()}; it < end; ++it) { + const auto character {*it}; + if (character != uri_escape_char || std::distance(it, end) < (hex_byte_len + 1)) { + *(write_it++) = character; + continue; } - else - result += uri[i]; - + unsigned char decoded{}; + const auto hex_begin = it + 1; // skip '%' + const auto hex_end {hex_begin + hex_byte_len}; + const auto [ptr, ec] = std::from_chars(hex_begin, hex_end, decoded, 16); + if (ec == std::errc{} && ptr == hex_end) { + *(write_it++) = decoded; + } else { + write_it = std::copy(it, hex_end, write_it); // in case of failure, copy the original string's contents. + } + it += hex_byte_len; } - + result.resize(std::distance(result.begin(), write_it)); return result; } //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e149a910cc..26740d1a3c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -14850,12 +14850,12 @@ std::string wallet2::make_uri(const std::string &address, const std::string &pay if (!recipient_name.empty()) { - uri += (n_fields++ ? "&" : "?") + std::string("recipient_name=") + epee::net_utils::conver_to_url_format(recipient_name); + uri += (n_fields++ ? "&" : "?") + std::string("recipient_name=") + epee::net_utils::convert_to_url_format(recipient_name); } if (!tx_description.empty()) { - uri += (n_fields++ ? "&" : "?") + std::string("tx_description=") + epee::net_utils::conver_to_url_format(tx_description); + uri += (n_fields++ ? "&" : "?") + std::string("tx_description=") + epee::net_utils::convert_to_url_format(tx_description); } return uri;