commit 82ebf666930f3ea5b44663d1fede65ed22282361 from: Stefan Sperling date: Wed Mar 18 16:10:34 2020 UTC tweak parse_uri() function, declare it as public API, and add a test for it 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 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 blob - /dev/null blob + 3235b8bc1059ed5309407ac94a0054ab55e72bb8 (mode 644) --- /dev/null +++ regress/fetch/fetch_test.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2020 Stefan Sperling + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +}