diff --git a/libretro-common/include/net/net_http.h b/libretro-common/include/net/net_http.h index ccc53c2f93..be03761a77 100644 --- a/libretro-common/include/net/net_http.h +++ b/libretro-common/include/net/net_http.h @@ -68,6 +68,8 @@ uint8_t* net_http_data(struct http_t *state, size_t* len, bool accept_error); /* Cleans up all memory. */ void net_http_delete(struct http_t *state); +void net_http_urlencode_full(char **dest, const char *source); + RETRO_END_DECLS #endif diff --git a/libretro-common/net/net_http.c b/libretro-common/net/net_http.c index b7b23be673..4cbf5785c2 100644 --- a/libretro-common/net/net_http.c +++ b/libretro-common/net/net_http.c @@ -74,6 +74,45 @@ struct http_connection_t int port; }; +static char urlencode_lut[256]; +static bool urlencode_lut_inited = false; + +void urlencode_lut_init() +{ + unsigned i; + + urlencode_lut_inited = true; + + for (i = 0; i < 256; i++) + { + + urlencode_lut[i] = isalnum(i) || i == '*' || i == '-' || i == '.' || i == '_' ? i : (i == ' ') ? '+' : 0; + } +} + +/* caller is responsible for deleting the destination buffer */ +void net_http_urlencode_full(char **dest, const char *source) { + /* Assume every character will be encoded, so we need 3 times the space. */ + size_t len = strlen(source) * 3 + 1; + char *enc; + + if (!urlencode_lut_inited) + urlencode_lut_init(); + + enc = (char*)calloc(1, len); + + *dest = enc; + + for (; *source; source++) + { + if (urlencode_lut[(int)*source]) sprintf(enc, "%c", urlencode_lut[(int)*source]); + else sprintf(enc, "%%%02X", *source); + while (*++(enc)); + } + + (*dest)[len - 1] = '\0'; +} + static int net_http_new_socket(const char *domain, int port) { int ret; @@ -108,50 +147,16 @@ static void net_http_send_str(int fd, bool *error, const char *text) *error = true; } -static char* urlencode(const char* url) -{ - unsigned i; - unsigned outpos = 0; - unsigned outlen = 0; - char *ret = NULL; - - for (i = 0; url[i] != '\0'; i++) - { - outlen++; - if (url[i] == ' ') - outlen += 2; - } - - ret = (char*)malloc(outlen + 1); - if (!ret) - return NULL; - - for (i = 0; url[i]; i++) - { - if (url[i] == ' ') - { - ret[outpos++] = '%'; - ret[outpos++] = '2'; - ret[outpos++] = '0'; - } - else - ret[outpos++] = url[i]; - } - ret[outpos] = '\0'; - - return ret; -} - struct http_connection_t *net_http_connection_new(const char *url, const char *method, const char *data) { char **domain = NULL; - struct http_connection_t *conn = (struct http_connection_t*)calloc(1, + struct http_connection_t *conn = (struct http_connection_t*)calloc(1, sizeof(struct http_connection_t)); - if (!conn) + if (!conn || !url) return NULL; - conn->urlcopy = urlencode(url); + conn->urlcopy = strdup(url); if (method) conn->methodcopy = strdup(method); diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c index 3136e6ba2b..44b3f11ac9 100644 --- a/network/netplay/netplay_frontend.c +++ b/network/netplay/netplay_frontend.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "netplay_private.h" @@ -525,6 +526,15 @@ static void netplay_announce(void) rarch_system_info_t *system = NULL; settings_t *settings = config_get_ptr(); uint32_t *content_crc_ptr = NULL; + char *username; + char *corename; + char *gamename; + char *coreversion; + + net_http_urlencode_full(&username, settings->username); + net_http_urlencode_full(&corename, system->info.library_name); + net_http_urlencode_full(&gamename, !string_is_empty(path_basename(path_get(RARCH_PATH_BASENAME))) ? path_basename(path_get(RARCH_PATH_BASENAME)) : "N/A"); + net_http_urlencode_full(&coreversion, system->info.library_version); content_get_crc(&content_crc_ptr); @@ -534,11 +544,15 @@ static void netplay_announce(void) snprintf(buf, sizeof(buf), "username=%s&core_name=%s&core_version=%s&" "game_name=%s&game_crc=%d&port=%d&has_password=%d&has_spectate_password=%d", - settings->username, system->info.library_name, system->info.library_version, - !string_is_empty(path_basename(path_get(RARCH_PATH_BASENAME))) ? path_basename(path_get(RARCH_PATH_BASENAME)) : "N/A", *content_crc_ptr, + username, corename, coreversion, gamename, *content_crc_ptr, settings->netplay.port, settings->netplay.password ? 1 : 0, settings->netplay.spectate_password ? 1 : 0); task_push_http_post_transfer(url, buf, true, NULL, netplay_announce_cb, NULL); + + free(username); + free(corename); + free(gamename); + free(coreversion); } int16_t input_state_net(unsigned port, unsigned device,