Commit 9a682fbe2c64ce71685dc332f7d2758ede442224

Stefan Sperling 2020-03-19T14:43:38

add support for Git's SCP style URLs to got_fetch_parse_uri()

diff --git a/lib/fetch.c b/lib/fetch.c
index c82af73..0d45d17 100644
--- a/lib/fetch.c
+++ b/lib/fetch.c
@@ -229,58 +229,78 @@ got_fetch_parse_uri(char **proto, char **host, char **port,
 {
 	const struct got_error *err = NULL;
 	char *s, *p, *q;
-	int n, hasport;
+	int n;
 
 	*proto = *host = *port = *server_path = *repo_name = NULL;
 
 	p = strstr(uri, "://");
 	if (!p) {
-		return got_error(GOT_ERR_PARSE_URI);
-	}
-	*proto = strndup(uri, p - uri);
-	if (proto == NULL) {
-		err = got_error_from_errno("strndup");
-		goto done;
-	}
-
-	hasport = (strcmp(*proto, "git") == 0 ||
-	    strstr(*proto, "http") == *proto);
-	s = p + 3;
-	p = NULL;
-	if (!hasport) {
-		p = strstr(s, ":");
-		if (p != NULL)
-			p++;
-	}
-	if (p == NULL)
-		p = strstr(s, "/");
-	if (p == NULL || strlen(p) == 1) {
-		err = got_error(GOT_ERR_PARSE_URI);
-		goto done;
-	}
-
-	q = memchr(s, ':', p - s);
-	if (q) {
-		*host = strndup(s, q - s);
-		if (*host == NULL) {
-			err = got_error_from_errno("strndup");
+		/* Try parsing Git's "scp" style URL syntax. */
+		*proto = strdup("ssh");
+		if (proto == NULL) {
+			err = got_error_from_errno("strdup");
 			goto done;
 		}
-		*port = strndup(q + 1, p - (q + 1));
+		*port = strdup("22");
 		if (*port == NULL) {
+			err = got_error_from_errno("strdup");
+			goto done;
+		}
+		s = (char *)uri;
+		q = strchr(s, ':');
+		if (q == NULL) {
+			err = got_error(GOT_ERR_PARSE_URI);
+			goto done;
+		}
+		/* No slashes allowed before first colon. */
+		p = strchr(s, '/');
+		if (p && q > p) {
+			err = got_error(GOT_ERR_PARSE_URI);
+			goto done;
+		}
+		*host = strndup(s, q - s);
+		if (*host == NULL) {
 			err = got_error_from_errno("strndup");
 			goto done;
 		}
+		p = q + 1;
 	} else {
-		*host = strndup(s, p - s);
-		if (*host == NULL) {
+		*proto = strndup(uri, p - uri);
+		if (proto == NULL) {
 			err = got_error_from_errno("strndup");
 			goto done;
 		}
-		if (asprintf(port, "%u", GOT_DEFAULT_GIT_PORT) == -1) {
-			err = got_error_from_errno("asprintf");
+		s = p + 3;
+
+		p = strstr(s, "/");
+		if (p == NULL || strlen(p) == 1) {
+			err = got_error(GOT_ERR_PARSE_URI);
 			goto done;
 		}
+
+		q = memchr(s, ':', p - s);
+		if (q) {
+			*host = strndup(s, q - s);
+			if (*host == NULL) {
+				err = got_error_from_errno("strndup");
+				goto done;
+			}
+			*port = strndup(q + 1, p - (q + 1));
+			if (*port == NULL) {
+				err = got_error_from_errno("strndup");
+				goto done;
+			}
+		} else {
+			*host = strndup(s, p - s);
+			if (*host == NULL) {
+				err = got_error_from_errno("strndup");
+				goto done;
+			}
+			if (asprintf(port, "%u", GOT_DEFAULT_GIT_PORT) == -1) {
+				err = got_error_from_errno("asprintf");
+				goto done;
+			}
+		}
 	}
 
 	*server_path = strdup(p);
@@ -289,13 +309,17 @@ got_fetch_parse_uri(char **proto, char **host, char **port,
 		goto done;
 	}
 
-	p = strrchr(p, '/') + 1;
-	if (!p || strlen(p) == 0) {
-		//werrstr("missing repository in uri");
+	p = strrchr(p, '/');
+	if (!p || strlen(p) <= 1) {
 		err = got_error(GOT_ERR_PARSE_URI);
 		goto done;
 	}
+	p++;
 	n = strlen(p);
+	if (n == 0) {
+		err = got_error(GOT_ERR_PARSE_URI);
+		goto done;
+	}
 	if (hassuffix(p, ".git"))
 		n -= 4;
 	*repo_name = strndup(p, (p + n) - p);
diff --git a/regress/fetch/fetch_test.c b/regress/fetch/fetch_test.c
index a43ae11..ff64378 100644
--- a/regress/fetch/fetch_test.c
+++ b/regress/fetch/fetch_test.c
@@ -79,6 +79,8 @@ fetch_parse_uri(void)
 		    NULL, NULL, NULL, NULL, NULL, GOT_ERR_PARSE_URI },
 		{ "git:///127.0.0.1/git/",
 		    NULL, NULL, NULL, NULL, NULL, GOT_ERR_PARSE_URI },
+		{ "/127.0.0.1:/git/",
+		    NULL, NULL, NULL, NULL, NULL, GOT_ERR_PARSE_URI },
 
 		{ "git://127.0.0.1/git/myrepo",
 		    "git", "localhost", GOT_DEFAULT_GIT_PORT_STR, "git",
@@ -100,6 +102,16 @@ fetch_parse_uri(void)
 		    "https", "localhost", GOT_DEFAULT_GIT_PORT_STR,
 		    "git/repos/foo/../bar", "myrepo", GOT_ERR_OK },
 
+		{ "git+ssh://127.0.0.1:22/git/myrepo",
+		    "git+ssh", "localhost", "22", "git", "myrepo", GOT_ERR_OK },
+		{ "ssh://127.0.0.1:22/git/myrepo",
+		    "ssh", "localhost", "22", "git", "myrepo", GOT_ERR_OK },
+		{ "127.0.0.1:git/myrepo",
+		    "ssh", "localhost", "22", "git", "myrepo", GOT_ERR_OK },
+		{ "127.0.0.1:/git/myrepo",
+		    "ssh", "localhost", "22", "git", "myrepo", GOT_ERR_OK },
+		{ "127.0.0.1:22/git/myrepo",
+		    "ssh", "localhost", "22", "git", "myrepo", GOT_ERR_OK },
 	};
 	int i;