diff --git a/src/netops.c b/src/netops.c index 9653344fe..72172f39b 100644 --- a/src/netops.c +++ b/src/netops.c @@ -658,6 +658,29 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *d) git__free(d->pass); d->pass = NULL; } +static char unescape_hex(char *x) +{ + char digit; + digit = ((x[0] >= 'A') ? ((x[0] & 0xdf) - 'A')+10 : (x[0] - '0')); + digit *= 16; + digit += ((x[1] >= 'A') ? ((x[1] & 0xdf) - 'A')+10 : (x[1] - '0')); + return digit; +} + +static char* unescape(char *str) +{ + int x, y; + + for (x=y=0; str[x]; ++x, ++y) { + if ((str[x] = str[y]) == '%') { + str[x] = unescape_hex(str+y+1); + y += 2; + } + } + str[x] = '\0'; + return str; +} + int gitno_extract_url_parts( char **host, char **port, @@ -699,13 +722,14 @@ int gitno_extract_url_parts( if (u.field_data[UF_USERINFO].len) { const char *colon = strchr(_userinfo, ':'); if (colon && (colon - _userinfo) < u.field_data[UF_USERINFO].len) { - *username = git__substrdup(_userinfo, colon - _userinfo); - *password = git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo)); + *username = unescape(git__substrdup(_userinfo, colon - _userinfo)); + *password = unescape(git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo))); GITERR_CHECK_ALLOC(*password); } else { *username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len); } GITERR_CHECK_ALLOC(*username); + } return 0; diff --git a/tests-clar/network/urlparse.c b/tests-clar/network/urlparse.c index 4babb0fa7..3ec3a51a3 100644 --- a/tests-clar/network/urlparse.c +++ b/tests-clar/network/urlparse.c @@ -33,7 +33,7 @@ void test_network_urlparse__trivial(void) cl_assert_equal_p(pass, NULL); } -void test_network_urlparse__weird_url(void) +void test_network_urlparse__encoded_password(void) { cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, "https://user:pass%2fis%40bad@hostname.com:1234/", "1")); @@ -41,7 +41,7 @@ void test_network_urlparse__weird_url(void) cl_assert_equal_s(port, "1234"); cl_assert_equal_s(path, "/"); cl_assert_equal_s(user, "user"); - cl_assert_equal_s(pass, "pass%2fis%40bad"); + cl_assert_equal_s(pass, "pass/is@bad"); } void test_network_urlparse__user(void) @@ -127,6 +127,18 @@ void test_network_urlparse__connection_data_ssl(void) cl_assert_equal_i(conndata.use_ssl, true); } +void test_network_urlparse__encoded_username_password(void) +{ + cl_git_pass(gitno_connection_data_from_url(&conndata, + "https://user%2fname:pass%40word@example.com/foo/bar/baz", "bar/baz")); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "443"); + cl_assert_equal_s(conndata.path, "/foo/"); + cl_assert_equal_s(conndata.user, "user/name"); + cl_assert_equal_s(conndata.pass, "pass@word"); + cl_assert_equal_i(conndata.use_ssl, true); +} + void test_network_urlparse__connection_data_cross_host_redirect(void) { conndata.host = git__strdup("bar.com");