diff --git a/src/hex.h b/src/hex.h index 87a6aecb4..6f00a79bf 100644 --- a/src/hex.h +++ b/src/hex.h @@ -46,4 +46,17 @@ static inline std::string hex_encode(const std::string &data) return hex_encode(data.c_str(), data.size()); } +static inline bool hex_digit_decode(char hexdigit, unsigned char &value) +{ + if(hexdigit >= '0' && hexdigit <= '9') + value = hexdigit - '0'; + else if(hexdigit >= 'A' && hexdigit <= 'F') + value = hexdigit - 'A' + 10; + else if(hexdigit >= 'a' && hexdigit <= 'f') + value = hexdigit - 'a' + 10; + else + return false; + return true; +} + #endif diff --git a/src/test.cpp b/src/test.cpp index 5e025f2f5..c969c0951 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -171,6 +171,10 @@ struct TestUtilities: public TestBase UASSERT(removeStringEnd("bc", ends) == "b"); UASSERT(removeStringEnd("12c", ends) == "12"); UASSERT(removeStringEnd("foo", ends) == ""); + UASSERT(urlencode("\"Aardvarks lurk, OK?\"") + == "%22Aardvarks%20lurk%2C%20OK%3F%22"); + UASSERT(urldecode("%22Aardvarks%20lurk%2C%20OK%3F%22") + == "\"Aardvarks lurk, OK?\""); } }; diff --git a/src/util/string.cpp b/src/util/string.cpp index 39a14598b..925d0ea35 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -21,8 +21,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "pointer.h" #include "numeric.h" +#include +#include + #include "../sha1.h" #include "../base64.h" +#include "../hex.h" #include "../porting.h" std::wstring narrow_to_wide(const std::string& mbs) @@ -68,11 +72,40 @@ std::string translatePassword(std::string playername, std::wstring password) return pwd; } -size_t curl_write_data(char *ptr, size_t size, size_t nmemb, void *userdata) { - std::ostringstream *stream = (std::ostringstream*)userdata; - size_t count = size * nmemb; - stream->write(ptr, count); - return count; +std::string urlencode(std::string str) +{ + // Encodes non-unreserved URI characters by a percent sign + // followed by two hex digits. See RFC 3986, section 2.3. + static const char url_hex_chars[] = "0123456789ABCDEF"; + std::ostringstream oss(std::ios::binary); + for (u32 i = 0; i < str.size(); i++) { + unsigned char c = str[i]; + if (isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') + oss << c; + else + oss << "%" + << url_hex_chars[(c & 0xf0) >> 4] + << url_hex_chars[c & 0x0f]; + } + return oss.str(); +} + +std::string urldecode(std::string str) +{ + // Inverse of urlencode + std::ostringstream oss(std::ios::binary); + for (u32 i = 0; i < str.size(); i++) { + unsigned char highvalue, lowvalue; + if (str[i] == '%' && + hex_digit_decode(str[i+1], highvalue) && + hex_digit_decode(str[i+2], lowvalue)) { + oss << (char) ((highvalue << 4) | lowvalue); + i += 2; + } + else + oss << str[i]; + } + return oss.str(); } u32 readFlagString(std::string str, FlagDesc *flagdesc) { diff --git a/src/util/string.h b/src/util/string.h index 81855962f..e5a60bc47 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -317,7 +317,8 @@ inline std::string unescape_string(std::string &s) } std::string translatePassword(std::string playername, std::wstring password); -size_t curl_write_data(char *ptr, size_t size, size_t nmemb, void *userdata); +std::string urlencode(std::string str); +std::string urldecode(std::string str); u32 readFlagString(std::string str, FlagDesc *flagdesc); std::string writeFlagString(u32 flags, FlagDesc *flagdesc); char *mystrtok_r(char *s, const char *sep, char **lasts);