Commit Diff


commit - ee61b6d3d5750355ac28ca4afae5c04deb1a1fa2
commit + 82ebf666930f3ea5b44663d1fede65ed22282361
blob - b666240f68350f4108c0af0dcc49d57b93cc1c42
blob + 33820f922ef8ee50b6236805c495361c960616a7
--- include/got_fetch.h
+++ include/got_fetch.h
@@ -14,4 +14,10 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+/* IANA assigned */
+#define GOT_DEFAULT_GIT_PORT		9418
+#define GOT_DEFAULT_GIT_PORT_STR	"9418"
+
+const struct got_error *got_fetch_parse_uri(char **, char **, char **,
+    char **, char **, const char *);
 const struct got_error* got_fetch(char *, char *, char *);
blob - 052353d32d46bd93e82ad56c5da026ac56999ec5
blob + b4ee27f466629f5422a50913c157a5910cd95fd3
--- lib/fetch.c
+++ lib/fetch.c
@@ -49,6 +49,7 @@
 #include "got_worktree.h"
 #include "got_object.h"
 #include "got_opentemp.h"
+#include "got_fetch.h"
 
 #include "got_lib_delta.h"
 #include "got_lib_inflate.h"
@@ -80,19 +81,6 @@ hassuffix(char *base, char *suf)
 	return 0;
 }
 
-static int
-grab(char *dst, int n, char *p, char *e)
-{
-	int l;
-
-	l = e - p;
-	if (l >= n) {
-		errno = ENAMETOOLONG;
-		return -1;
-	}
-	return strlcpy(dst, p, l + 1);
-}
-
 static const struct got_error *
 dial_ssh(int *fetchfd, char *host, char *port, char *path, char *direction)
 {
@@ -192,20 +180,28 @@ done:
 	return err;
 }
 
-int
-got_parse_uri(char *uri, char *proto, char *host, char *port, char *path, char *repo)
+const struct got_error *
+got_fetch_parse_uri(char **proto, char **host, char **port,
+    char **server_path, char **repo_name, const char *uri)
 {
+	const struct got_error *err = NULL;
 	char *s, *p, *q;
 	int n, hasport;
 
+	*proto = *host = *port = *server_path = *repo_name = NULL;
+
 	p = strstr(uri, "://");
 	if (!p) {
-		//werrstr("missing protocol");
-		return -1;
+		return got_error(GOT_ERR_PARSE_URI);
 	}
-	if (grab(proto, GOT_PROTOMAX, uri, p) == -1)
-		return -1;
-	hasport = (strcmp(proto, "git") == 0 || strstr(proto, "http") == proto);
+	*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) {
@@ -216,37 +212,74 @@ got_parse_uri(char *uri, char *proto, char *host, char
 	if (p == NULL)
 		p = strstr(s, "/");
 	if (p == NULL || strlen(p) == 1) {
-		//werrstr("missing path");
-		return -1;
+		err = got_error(GOT_ERR_PARSE_URI);
+		goto done;
 	}
 
 	q = memchr(s, ':', p - s);
 	if (q) {
-		grab(host, GOT_HOSTMAX, s, q);
-		grab(port, GOT_PORTMAX, q + 1, p);
-	}else{
-		grab(host, GOT_HOSTMAX, s, p);
-		snprintf(port, GOT_PORTMAX, "9418");
+		*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;
+		}
 	}
 
-	snprintf(path, GOT_PATHMAX, "%s", p);
+	*server_path = strdup(p);
+	if (*server_path == NULL) {
+		err = got_error_from_errno("strdup");
+		goto done;
+	}
+
 	p = strrchr(p, '/') + 1;
 	if (!p || strlen(p) == 0) {
 		//werrstr("missing repository in uri");
-		return -1;
+		err = got_error(GOT_ERR_PARSE_URI);
+		goto done;
 	}
 	n = strlen(p);
 	if (hassuffix(p, ".git"))
 		n -= 4;
-	grab(repo, GOT_REPOMAX, p, p + n);
-	return 0;
+	*repo_name = strndup(p, (p + n) - p);
+	if (*repo_name == NULL) {
+		err = got_error_from_errno("strndup");
+		goto done;
+	}
+done:
+	if (err) {
+		free(*proto);
+		*proto = NULL;
+		free(*host);
+		*host = NULL;
+		free(*port);
+		*port = NULL;
+		free(*server_path);
+		*server_path = NULL;
+		free(*repo_name);
+		*repo_name = NULL;
+	}
+	return err;
 }
 
 const struct got_error*
 got_fetch(char *uri, char *branch_filter, char *destdir)
 {
-	char proto[GOT_PROTOMAX], host[GOT_HOSTMAX], port[GOT_PORTMAX];
-	char repo_name[GOT_REPOMAX], server_path[GOT_PATHMAX];
+	char *proto, *host, *port, *repo_name, *server_path;
 	int imsg_fetchfds[2], imsg_idxfds[2], fetchfd = -1;
 	int packfd = -1, npackfd = -1, idxfd = -1, nidxfd = -1;
 	int status, done = 0;
@@ -265,8 +298,10 @@ got_fetch(char *uri, char *branch_filter, char *destdi
 	TAILQ_INIT(&symrefs);
 
 	fetchfd = -1;
-	if (got_parse_uri(uri, proto, host, port, server_path, repo_name) == -1)
-		return got_error(GOT_ERR_PARSE_URI);
+	err = got_fetch_parse_uri(&proto, &host, &port, &server_path,
+	    &repo_name, uri);
+	if (err)
+		return err;
 	if (destdir == NULL) {
 		if (asprintf(&default_destdir, "%s.git", repo_name) == -1)
 			return got_error_from_errno("asprintf");
blob - 060d69c317d74037521f5d1b4a7653dc63a4fb41
blob + 5c2026319bf17285a069070e877b8280c58877a5
--- regress/Makefile
+++ regress/Makefile
@@ -1,3 +1,3 @@
-SUBDIR = cmdline delta idset path
+SUBDIR = cmdline delta idset path fetch
 
 .include <bsd.subdir.mk>
blob - /dev/null
blob + 94a84612acbddadb06374e6a4344fe42b4b414f2 (mode 644)
--- /dev/null
+++ regress/fetch/Makefile
@@ -0,0 +1,14 @@
+.PATH:${.CURDIR}/../../lib
+
+PROG = fetch_test
+SRCS = error.c privsep.c reference.c sha1.c object.c object_parse.c path.c \
+	opentemp.c repository.c lockfile.c object_cache.c pack.c inflate.c \
+	deflate.c delta.c delta_cache.c object_idset.c object_create.c \
+	fetch.c fetch_test.c
+
+CPPFLAGS = -I${.CURDIR}/../../include -I${.CURDIR}/../../lib
+LDADD = -lutil -lz
+
+NOMAN = yes
+
+.include <bsd.regress.mk>
blob - /dev/null
blob + 3235b8bc1059ed5309407ac94a0054ab55e72bb8 (mode 644)
--- /dev/null
+++ regress/fetch/fetch_test.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2020 Stefan Sperling <stsp@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/queue.h>
+
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+#include <sha1.h>
+#include <zlib.h>
+#include <time.h>
+
+#include "got_error.h"
+#include "got_object.h"
+#include "got_fetch.h"
+
+#include "got_lib_object_idset.h"
+#include "got_lib_sha1.h"
+#include "got_lib_inflate.h"
+#include "got_lib_delta.h"
+
+#ifndef nitems
+#define nitems(_a)	(sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+static int verbose;
+
+void
+test_printf(char *fmt, ...)
+{
+	va_list ap;
+
+	if (!verbose)
+		return;
+
+	va_start(ap, fmt);
+	vprintf(fmt, ap);
+	va_end(ap);
+}
+
+static int
+fetch_parse_uri(void)
+{
+	const struct got_error *err = NULL;
+	struct parse_uri_test {
+		const char *uri;
+		const char *proto;
+		const char *host;
+		const char *port;
+		const char *server_path;
+		const char *repo_name;
+		int errcode;
+	} test_data[] = {
+		{ "", NULL, NULL, NULL, NULL, NULL, GOT_ERR_PARSE_URI },
+		{ "git:", NULL, NULL, NULL, NULL, NULL, GOT_ERR_PARSE_URI },
+		{ "git://localhost/",
+		    NULL, NULL, NULL, NULL, NULL, GOT_ERR_PARSE_URI },
+		{ "git://localhost////",
+		    NULL, NULL, NULL, NULL, NULL, GOT_ERR_PARSE_URI },
+		{ "git://127.0.0.1/git/",
+		    NULL, NULL, NULL, NULL, NULL, GOT_ERR_PARSE_URI },
+		{ "git:///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",
+		    "myrepo", GOT_ERR_OK },
+		{ "http://127.0.0.1/git/myrepo",
+		    "http", "localhost", GOT_DEFAULT_GIT_PORT_STR, "git",
+		    "myrepo", GOT_ERR_OK },
+		{ "gopher://127.0.0.1/git/myrepo",
+		    "gopher", "localhost", GOT_DEFAULT_GIT_PORT_STR, "git",
+		    "myrepo", GOT_ERR_OK },
+
+		{ "git://127.0.0.1:22/git/myrepo",
+		    "git", "localhost", "22", "git", "myrepo", GOT_ERR_OK },
+
+		{ "git://127.0.0.1/git/repos/foo/bar/myrepo.git",
+		    "git", "localhost", GOT_DEFAULT_GIT_PORT_STR,
+		    "git/repos/foo/bar", "myrepo", GOT_ERR_OK },
+		{ "https://127.0.0.1/git/repos/foo/../bar/myrepo.git",
+		    "https", "localhost", GOT_DEFAULT_GIT_PORT_STR,
+		    "git/repos/foo/../bar", "myrepo", GOT_ERR_OK },
+
+	};
+	int i;
+
+	for (i = 0; i < nitems(test_data); i++) {
+		const char *uri = test_data[i].uri;
+		const char *expected_proto = test_data[i].proto;
+		const char *expected_host = test_data[i].host;
+		const char *expected_port = test_data[i].port;
+		const char *expected_server_path = test_data[i].server_path;
+		const char *expected_repo_name = test_data[i].repo_name;
+		char *proto, *host, *port, *server_path, *repo_name;
+
+		err = got_fetch_parse_uri(&proto, &host, &port, &server_path,
+		    &repo_name, uri);
+		if (err && err->code != test_data[i].errcode) {
+			test_printf("%d: error code %d; expected %d\n",
+			    i, err->code, test_data[i].errcode);
+			return 0;
+		}
+
+		if (expected_proto == NULL && proto != NULL) {
+			test_printf("%d: proto %s; expected NULL\n", i, proto);
+			return 0;
+		}
+		if (expected_host == NULL && host != NULL) {
+			test_printf("%d: host %s; expected NULL\n", i, host);
+			return 0;
+		}
+		if (expected_port == NULL && port != NULL) {
+			test_printf("%d: port %s; expected NULL\n", i, port);
+			return 0;
+		}
+		if (expected_server_path == NULL && server_path != NULL) {
+			test_printf("%d: server path %s; expected NULL\n", i,
+			    server_path);
+			return 0;
+		}
+		if (expected_repo_name == NULL && repo_name != NULL) {
+			test_printf("%d: repo name %s; expected NULL\n", i,
+			    repo_name);
+			return 0;
+		}
+
+		if (expected_proto != NULL && proto == NULL) {
+			test_printf("%d: proto NULL; expected %s\n", i,
+			    expected_proto);
+			return 0;
+		}
+		if (expected_host != NULL && host == NULL) {
+			test_printf("%d: host NULL; expected %s\n", i,
+			    expected_host);
+			return 0;
+		}
+		if (expected_port != NULL && port == NULL) {
+			test_printf("%d: port NULL; expected %s\n", i,
+			    expected_port);
+			return 0;
+		}
+		if (expected_server_path != NULL && server_path == NULL) {
+			test_printf("%d: server path %s; expected %s\n", i,
+			    expected_server_path);
+			return 0;
+		}
+		if (expected_repo_name != NULL && repo_name == NULL) {
+			test_printf("%d: repo name NULL; expected %s\n", i,
+			    repo_name);
+			return 0;
+		}
+
+		if (expected_proto != NULL && strcmp(expected_proto, proto)) {
+			test_printf("%d: proto %s; expected %s\n", i, proto,
+			    expected_proto);
+			return 0;
+		}
+
+		free(proto);
+		proto = NULL;
+		free(host);
+		host = NULL;
+		free(port);
+		port = NULL;
+		free(server_path);
+		server_path = NULL;
+		free(repo_name);
+		repo_name = NULL;
+	}
+
+	return 1;
+}
+
+#define RUN_TEST(expr, name) \
+	{ test_ok = (expr);  \
+	printf("test_%s %s\n", (name), test_ok ? "ok" : "failed"); \
+	failure = (failure || !test_ok); }
+
+void
+usage(void)
+{
+	fprintf(stderr, "usage: fetch_test [-v]\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+	int test_ok = 0, failure = 0;
+	int ch;
+
+#ifndef PROFILE
+	if (pledge("stdio", NULL) == -1)
+		err(1, "pledge");
+#endif
+
+	while ((ch = getopt(argc, argv, "v")) != -1) {
+		switch (ch) {
+		case 'v':
+			verbose = 1;
+			break;
+		default:
+			usage();
+			return 1;
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	RUN_TEST(fetch_parse_uri(), "fetch_parse_uri");
+
+	return failure ? 1 : 0;
+}