diff --git a/src/netops.c b/src/netops.c index 12738141f..fd788bc1d 100644 --- a/src/netops.c +++ b/src/netops.c @@ -578,11 +578,22 @@ int gitno_select_in(gitno_buffer *buf, long int sec, long int usec) return select((int)buf->socket->socket + 1, &fds, NULL, NULL, &tv); } -int gitno_extract_host_and_port(char **host, char **port, char **username, const char *url, const char *default_port) +int gitno_extract_url_parts( + char **host, + char **port, + char **username, + char **password, + const char *url, + const char *default_port) { - char *colon, *slash, *at, *delim; + char *colon, *slash, *at, *end; const char *start; + /* + * + * ==> [user[:pass]@]hostname.tld[:port]/resource + */ + colon = strchr(url, ':'); slash = strchr(url, '/'); at = strchr(url, '@'); @@ -592,6 +603,19 @@ int gitno_extract_host_and_port(char **host, char **port, char **username, const return -1; } + start = url; + if (at && at < slash) { + start = at+1; + *username = git__strndup(url, at - url); + } + + if (colon && colon < at) { + git__free(*username); + *username = git__strndup(url, colon-url); + *password = git__strndup(colon+1, at-colon-1); + colon = strchr(at, ':'); + } + if (colon == NULL) { *port = git__strdup(default_port); } else { @@ -599,15 +623,9 @@ int gitno_extract_host_and_port(char **host, char **port, char **username, const } GITERR_CHECK_ALLOC(*port); - delim = colon == NULL ? slash : colon; + end = colon == NULL ? slash : colon; - start = url; - if (at && at < slash) { - start = at+1; - *username = git__strndup(url, at - url); - } - - *host = git__strndup(start, delim - start); + *host = git__strndup(start, end - start); GITERR_CHECK_ALLOC(*host); return 0; diff --git a/src/netops.h b/src/netops.h index bb2624abe..d352bf3b6 100644 --- a/src/netops.h +++ b/src/netops.h @@ -66,6 +66,12 @@ int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags); int gitno_close(gitno_socket *s); int gitno_select_in(gitno_buffer *buf, long int sec, long int usec); -int gitno_extract_host_and_port(char **host, char **port, char **username, const char *url, const char *default_port); +int gitno_extract_url_parts( + char **host, + char **port, + char **username, + char **password, + const char *url, + const char *default_port); #endif diff --git a/src/transports/git.c b/src/transports/git.c index 5c816e127..21de4d789 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -179,7 +179,7 @@ static int _git_uploadpack_ls( const char *url, git_smart_subtransport_stream **stream) { - char *host, *port, *user; + char *host, *port, *user, *pass; git_stream *s; *stream = NULL; @@ -192,7 +192,7 @@ static int _git_uploadpack_ls( s = (git_stream *)*stream; - if (gitno_extract_host_and_port(&host, &port, &user, url, GIT_DEFAULT_PORT) < 0) + if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0) goto on_error; if (gitno_connect(&s->socket, host, port, 0) < 0) @@ -202,6 +202,7 @@ static int _git_uploadpack_ls( git__free(host); git__free(port); git__free(user); + git__free(pass); return 0; on_error: @@ -234,7 +235,7 @@ static int _git_receivepack_ls( const char *url, git_smart_subtransport_stream **stream) { - char *host, *port, *user; + char *host, *port, *user, *pass; git_stream *s; *stream = NULL; @@ -247,7 +248,7 @@ static int _git_receivepack_ls( s = (git_stream *)*stream; - if (gitno_extract_host_and_port(&host, &port, &user, url, GIT_DEFAULT_PORT) < 0) + if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0) goto on_error; if (gitno_connect(&s->socket, host, port, 0) < 0) @@ -257,6 +258,7 @@ static int _git_receivepack_ls( git__free(host); git__free(port); git__free(user); + git__free(pass); return 0; on_error: diff --git a/src/transports/http.c b/src/transports/http.c index 144906474..6c116d8e7 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -61,6 +61,7 @@ typedef struct { char *host; char *port; char *user_from_url; + char *pass_from_url; git_cred *cred; http_authmechanism_t auth_mechanism; unsigned connected : 1, @@ -744,8 +745,8 @@ static int http_action( if (!default_port) return -1; - if ((ret = gitno_extract_host_and_port(&t->host, &t->port, &t->user_from_url, - url, default_port)) < 0) + if ((ret = gitno_extract_url_parts(&t->host, &t->port, + &t->user_from_url, &t->pass_from_url, url, default_port)) < 0) return ret; t->path = strchr(url, '/'); @@ -821,6 +822,16 @@ static int http_close(git_smart_subtransport *subtransport) t->port = NULL; } + if (t->user_from_url) { + git__free(t->user_from_url); + t->user_from_url = NULL; + } + + if (t->pass_from_url) { + git__free(t->pass_from_url); + t->pass_from_url = NULL; + } + return 0; } diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 544e52f4d..4ac085ed3 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -788,7 +788,8 @@ static int winhttp_connect( t->use_ssl = 1; } - if ((ret = gitno_extract_host_and_port(&t->host, &t->port, &t->parent.user_from_url, url, default_port)) < 0) + if ((ret = gitno_extract_url_parts(&t->host, &t->port, &t->parent.user_from_url, + &t->parent.pass_from_url, url, default_port)) < 0) return ret; t->path = strchr(url, '/'); @@ -944,6 +945,16 @@ static int winhttp_close(git_smart_subtransport *subtransport) t->port = NULL; } + if (t->user_from_url) { + git__free(t->user_from_url); + t->user_from_url = NULL; + } + + if (t->pass_from_url) { + git__free(t->pass_from_url); + t->pass_from_url = NULL; + } + if (t->cred) { t->cred->free(t->cred); t->cred = NULL; diff --git a/tests-clar/network/urlparse.c b/tests-clar/network/urlparse.c new file mode 100644 index 000000000..29d0506df --- /dev/null +++ b/tests-clar/network/urlparse.c @@ -0,0 +1,78 @@ +#include "clar_libgit2.h" +#include "netops.h" + +void test_network_urlparse__trivial(void) +{ + char *host, *port, *user, *pass; + + cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, + "example.com/resource", "8080")); + cl_assert_equal_s(host, "example.com"); + cl_assert_equal_s(port, "8080"); + cl_assert_equal_sz(user, NULL); + cl_assert_equal_sz(pass, NULL); +} + +void test_network_urlparse__user(void) +{ + char *host, *port, *user, *pass; + + cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, + "user@example.com/resource", "8080")); + cl_assert_equal_s(host, "example.com"); + cl_assert_equal_s(port, "8080"); + cl_assert_equal_s(user, "user"); + cl_assert_equal_sz(pass, NULL); +} + +void test_network_urlparse__user_pass(void) +{ + char *host, *port, *user, *pass; + + /* user:pass@hostname.tld/resource */ + cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, + "user:pass@example.com/resource", "8080")); + cl_assert_equal_s(host, "example.com"); + cl_assert_equal_s(port, "8080"); + cl_assert_equal_s(user, "user"); + cl_assert_equal_s(pass, "pass"); +} + +void test_network_urlparse__port(void) +{ + char *host, *port, *user, *pass; + + /* hostname.tld:port/resource */ + cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, + "example.com:9191/resource", "8080")); + cl_assert_equal_s(host, "example.com"); + cl_assert_equal_s(port, "9191"); + cl_assert_equal_sz(user, NULL); + cl_assert_equal_sz(pass, NULL); +} + +void test_network_urlparse__user_port(void) +{ + char *host, *port, *user, *pass; + + /* user@hostname.tld:port/resource */ + cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, + "user@example.com:9191/resource", "8080")); + cl_assert_equal_s(host, "example.com"); + cl_assert_equal_s(port, "9191"); + cl_assert_equal_s(user, "user"); + cl_assert_equal_sz(pass, NULL); +} + +void test_network_urlparse__user_pass_port(void) +{ + char *host, *port, *user, *pass; + + /* user:pass@hostname.tld:port/resource */ + cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, + "user:pass@example.com:9191/resource", "8080")); + cl_assert_equal_s(host, "example.com"); + cl_assert_equal_s(port, "9191"); + cl_assert_equal_s(user, "user"); + cl_assert_equal_s(pass, "pass"); +} diff --git a/tests-clar/online/clone.c b/tests-clar/online/clone.c index 12b9e0388..0bc744013 100644 --- a/tests-clar/online/clone.c +++ b/tests-clar/online/clone.c @@ -7,6 +7,7 @@ #define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository" #define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository" #define BB_REPO_URL "https://libgit2@bitbucket.org/libgit2/testgitrepository.git" +#define BB_REPO_URL_WITH_PASS "https://libgit2:libgit2@bitbucket.org/libgit2/testgitrepository.git" static git_repository *g_repo; static git_clone_options g_options; @@ -167,4 +168,8 @@ void test_online_clone__bitbucket_style(void) cl_git_pass(git_clone(&g_repo, BB_REPO_URL, "./foo", &g_options)); git_repository_free(g_repo); g_repo = NULL; cl_fixture_cleanup("./foo"); + + cl_git_pass(git_clone(&g_repo, BB_REPO_URL_WITH_PASS, "./foo", &g_options)); + git_repository_free(g_repo); g_repo = NULL; + cl_fixture_cleanup("./foo"); }