Commit 60e7848e1e3cb3a1a3e4f3aa08d353bab90d47c0

Edward Thomson 2018-03-03T20:13:30

gitno_extract_url_parts: use `git_buf`s Now that we can decode percent-encoded strings as part of `git_buf`s, use that decoder in `gitno_extract_url_parts`.

diff --git a/src/netops.c b/src/netops.c
index 0622622..26cb607 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -225,64 +225,95 @@ char* gitno_unescape(char *str)
 }
 
 int gitno_extract_url_parts(
-		char **host,
-		char **port,
-		char **path,
-		char **username,
-		char **password,
-		const char *url,
-		const char *default_port)
+	char **host_out,
+	char **port_out,
+	char **path_out,
+	char **username_out,
+	char **password_out,
+	const char *url,
+	const char *default_port)
 {
 	struct http_parser_url u = {0};
-	const char *_host, *_port, *_path, *_userinfo;
+	bool has_host, has_port, has_path, has_userinfo;
+	git_buf host = GIT_BUF_INIT,
+		port = GIT_BUF_INIT,
+		path = GIT_BUF_INIT,
+		username = GIT_BUF_INIT,
+		password = GIT_BUF_INIT;
+	int error = 0;
 
 	if (http_parser_parse_url(url, strlen(url), false, &u)) {
 		giterr_set(GITERR_NET, "malformed URL '%s'", url);
-		return GIT_EINVALIDSPEC;
+		error = GIT_EINVALIDSPEC;
+		goto done;
 	}
 
-	_host = url+u.field_data[UF_HOST].off;
-	_port = url+u.field_data[UF_PORT].off;
-	_path = url+u.field_data[UF_PATH].off;
-	_userinfo = url+u.field_data[UF_USERINFO].off;
+	has_host = !!(u.field_set & (1 << UF_HOST));
+	has_port = !!(u.field_set & (1 << UF_PORT));
+	has_path = !!(u.field_set & (1 << UF_PATH));
+	has_userinfo = !!(u.field_set & (1 << UF_USERINFO));
 
-	if (u.field_set & (1 << UF_HOST)) {
-		*host = git__substrdup(_host, u.field_data[UF_HOST].len);
-		GITERR_CHECK_ALLOC(*host);
+	if (has_host) {
+		const char *url_host = url + u.field_data[UF_HOST].off;
+		size_t url_host_len = u.field_data[UF_HOST].len;
+		git_buf_put(&host, url_host, url_host_len);
 	}
 
-	if (u.field_set & (1 << UF_PORT))
-		*port = git__substrdup(_port, u.field_data[UF_PORT].len);
-	else
-		*port = git__strdup(default_port);
-	GITERR_CHECK_ALLOC(*port);
+	if (has_port) {
+		const char *url_port = url + u.field_data[UF_PORT].off;
+		size_t url_port_len = u.field_data[UF_PORT].len;
+		git_buf_put(&port, url_port, url_port_len);
+	} else {
+		git_buf_puts(&port, default_port);
+	}
 
-	if (path) {
-		if (u.field_set & (1 << UF_PATH)) {
-			*path = git__substrdup(_path, u.field_data[UF_PATH].len);
-			GITERR_CHECK_ALLOC(*path);
-		} else {
-			git__free(*port);
-			*port = NULL;
-			git__free(*host);
-			*host = NULL;
-			giterr_set(GITERR_NET, "invalid url, missing path");
-			return GIT_EINVALIDSPEC;
-		}
+	if (has_path && path_out) {
+		const char *url_path = url + u.field_data[UF_PATH].off;
+		size_t url_path_len = u.field_data[UF_PATH].len;
+		git_buf_decode_percent(&path, url_path, url_path_len);
+	} else if (path_out) {
+		giterr_set(GITERR_NET, "invalid url, missing path");
+		error = GIT_EINVALIDSPEC;
+		goto done;
 	}
 
-	if (u.field_set & (1 << UF_USERINFO)) {
-		const char *colon = memchr(_userinfo, ':', u.field_data[UF_USERINFO].len);
+	if (has_userinfo) {
+		const char *url_userinfo = url + u.field_data[UF_USERINFO].off;
+		size_t url_userinfo_len = u.field_data[UF_USERINFO].len;
+		const char *colon = memchr(url_userinfo, ':', url_userinfo_len);
+
 		if (colon) {
-			*username = gitno_unescape(git__substrdup(_userinfo, colon - _userinfo));
-			*password = gitno_unescape(git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo)));
-			GITERR_CHECK_ALLOC(*password);
+			const char *url_username = url_userinfo;
+			size_t url_username_len = colon - url_userinfo;
+			const char *url_password = colon + 1;
+			size_t url_password_len = url_userinfo_len - (url_username_len + 1);
+
+			git_buf_decode_percent(&username, url_username, url_username_len);
+			git_buf_decode_percent(&password, url_password, url_password_len);
 		} else {
-			*username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len);
+			git_buf_decode_percent(&username, url_userinfo, url_userinfo_len);
 		}
-		GITERR_CHECK_ALLOC(*username);
-
 	}
 
-	return 0;
+	if (git_buf_oom(&host) ||
+		git_buf_oom(&port) ||
+		git_buf_oom(&path) ||
+		git_buf_oom(&username) ||
+		git_buf_oom(&password))
+		return -1;
+
+	*host_out = git_buf_detach(&host);
+	*port_out = git_buf_detach(&port);
+	if (path_out)
+		*path_out = git_buf_detach(&path);
+	*username_out = git_buf_detach(&username);
+	*password_out = git_buf_detach(&password);
+
+done:
+	git_buf_free(&host);
+	git_buf_free(&port);
+	git_buf_free(&path);
+	git_buf_free(&username);
+	git_buf_free(&password);
+	return error;
 }