Commit cf7038a65cb080a2946202fe6cbbe52aefae1fd4

Ben Straub 2013-01-31T14:04:21

Enhance url parsing to include passwords

diff --git a/src/netops.c b/src/netops.c
index 1273814..fd788bc 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;
-
-	start = url;
-	if (at && at < slash) {
-		start = at+1;
-		*username = git__strndup(url, at - url);
-	}
+	end = colon == NULL ? slash : colon;
 
-	*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 bb2624a..d352bf3 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 5c816e1..21de4d7 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 1449064..6c116d8 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 544e52f..4ac085e 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 0000000..29d0506
--- /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 12b9e03..0bc7440 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");
 }