From 752ee96a25b12d8cc9dd4b445989c4f056299c49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= <timo.teras@iki.fi> Date: Tue, 31 Dec 2024 17:28:58 +0200 Subject: [PATCH] db: refactor repository parsing and url printing - pregenerate the needed repository urls - get rid of apk_url_print and simplify url printing --- src/apk_database.h | 20 ++++- src/apk_print.h | 13 +-- src/app_fetch.c | 2 +- src/app_policy.c | 4 +- src/app_update.c | 6 +- src/app_version.c | 4 +- src/ctype.c | 2 +- src/database.c | 192 +++++++++++++++++++++--------------------- src/print.c | 44 +++++----- test/unit/apk_test.h | 14 +++ test/unit/blob_test.c | 22 +++++ test/unit/db_test.c | 27 ++++++ test/unit/meson.build | 1 + 13 files changed, 208 insertions(+), 143 deletions(-) create mode 100644 test/unit/db_test.c diff --git a/src/apk_database.h b/src/apk_database.h index 5842eed7..43f2f5ae 100644 --- a/src/apk_database.h +++ b/src/apk_database.h @@ -136,18 +136,30 @@ struct apk_name { char name[]; }; +enum { + APK_REPOTYPE_INVALID = 0, + APK_REPOTYPE_NDX, + APK_REPOTYPE_V2, +}; + +struct apk_repoline { + apk_blob_t tag, url; + unsigned int type; +}; + struct apk_repository { - const char *url; struct apk_digest hash; time_t mtime; unsigned short tag_mask; - unsigned short url_is_file : 1; unsigned short absolute_pkgname : 1; unsigned short is_remote : 1; unsigned short stale : 1; apk_blob_t description; apk_blob_t url_base; + apk_blob_t url_base_printable; + apk_blob_t url_index; + apk_blob_t url_index_printable; apk_blob_t pkgname_spec; }; @@ -279,9 +291,9 @@ int apk_db_repository_check(struct apk_database *db); unsigned int apk_db_get_pinning_mask_repos(struct apk_database *db, unsigned short pinning_mask); struct apk_repository *apk_db_select_repo(struct apk_database *db, struct apk_package *pkg); -int apk_repo_index_url(struct apk_database *db, struct apk_repository *repo, int *fd, char *buf, size_t len, struct apk_url_print *urlp); +bool apk_repo_parse_line(apk_blob_t line, struct apk_repoline *rl); int apk_repo_index_cache_url(struct apk_database *db, struct apk_repository *repo, int *fd, char *buf, size_t len); -int apk_repo_package_url(struct apk_database *db, struct apk_repository *repo, struct apk_package *pkg, int *fd, char *buf, size_t len, struct apk_url_print *urlp); +int apk_repo_package_url(struct apk_database *db, struct apk_repository *repo, struct apk_package *pkg, int *fd, char *buf, size_t len); int apk_cache_download(struct apk_database *db, struct apk_repository *repo, struct apk_package *pkg, struct apk_progress *prog); diff --git a/src/apk_print.h b/src/apk_print.h index 6221517c..4c157694 100644 --- a/src/apk_print.h +++ b/src/apk_print.h @@ -21,18 +21,7 @@ const char *apk_error_str(int error); int apk_get_human_size_unit(apk_blob_t b); const char *apk_get_human_size(off_t size, off_t *dest); const char *apk_last_path_segment(const char *); - -struct apk_url_print { - const char *url; - const char *pwmask; - const char *url_or_host; - size_t len_before_pw; -}; - -void apk_url_parse(struct apk_url_print *, const char *); - -#define URL_FMT "%.*s%s%s" -#define URL_PRINTF(u) (int)u.len_before_pw, u.url, u.pwmask, u.url_or_host +apk_blob_t apk_url_sanitize(apk_blob_t url, struct apk_atom_pool *atoms); struct apk_out { int verbosity, progress_fd; diff --git a/src/app_fetch.c b/src/app_fetch.c index 60106435..bd7461a5 100644 --- a/src/app_fetch.c +++ b/src/app_fetch.c @@ -173,7 +173,7 @@ static int fetch_package(struct apk_database *db, const char *match, struct apk_ return 0; } - r = apk_repo_package_url(db, repo, pkg, &pkg_fd, pkg_url, sizeof pkg_url, NULL); + r = apk_repo_package_url(db, repo, pkg, &pkg_fd, pkg_url, sizeof pkg_url); if (r < 0) goto err; if (ctx->flags & FETCH_URL) diff --git a/src/app_policy.c b/src/app_policy.c index e679d082..3a6aa27d 100644 --- a/src/app_policy.c +++ b/src/app_policy.c @@ -49,10 +49,10 @@ zlib1g policy: continue; for (j = 0; j < db->num_repo_tags; j++) { if (db->repo_tags[j].allowed_repos & p->pkg->repos) - apk_out(out, " "BLOB_FMT"%s%s", + apk_out(out, " " BLOB_FMT "%s" BLOB_FMT, BLOB_PRINTF(db->repo_tags[j].tag), j == 0 ? "" : " ", - repo->url); + BLOB_PRINTF(repo->url_base_printable)); } } } diff --git a/src/app_update.c b/src/app_update.c index 9e474809..0d01fc25 100644 --- a/src/app_update.c +++ b/src/app_update.c @@ -19,7 +19,6 @@ static int update_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *a struct apk_out *out = &ac->out; struct apk_database *db = ac->db; struct apk_repository *repo; - struct apk_url_print urlp; int i; const char *msg = "OK:"; char buf[64]; @@ -29,10 +28,9 @@ static int update_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *a for (i = APK_REPOSITORY_FIRST_CONFIGURED; i < db->num_repos; i++) { repo = &db->repos[i]; - apk_url_parse(&urlp, db->repos[i].url); - apk_msg(out, BLOB_FMT " [" URL_FMT "]", + apk_msg(out, BLOB_FMT " [" BLOB_FMT "]", BLOB_PRINTF(repo->description), - URL_PRINTF(urlp)); + BLOB_PRINTF(repo->url_base_printable)); } if (db->repositories.unavailable || db->repositories.stale) diff --git a/src/app_version.c b/src/app_version.c index 2c2dc0fd..da0109ac 100644 --- a/src/app_version.c +++ b/src/app_version.c @@ -30,9 +30,9 @@ static int ver_indexes(struct apk_ctx *ac, struct apk_string_array *args) for (i = APK_REPOSITORY_FIRST_CONFIGURED; i < db->num_repos; i++) { repo = &db->repos[i]; - apk_out(out, BLOB_FMT " [%s]", + apk_out(out, BLOB_FMT " [" BLOB_FMT "]", BLOB_PRINTF(repo->description), - db->repos[i].url); + BLOB_PRINTF(repo->url_base_printable)); } return 0; diff --git a/src/ctype.c b/src/ctype.c index 0fdcf5d6..a8e53359 100644 --- a/src/ctype.c +++ b/src/ctype.c @@ -26,7 +26,7 @@ static uint8_t apk_ctype[128] = { [','] = DEPNAME, ['-'] = PKGNAME, ['.'] = PKGNAME, - [':'] = REPOSEP|DEPNAME, + [':'] = DEPNAME, ['<'] = DEPCOMP, ['='] = DEPCOMP, ['>'] = DEPCOMP, diff --git a/src/database.c b/src/database.c index 53a6cfc0..d4d5ae6b 100644 --- a/src/database.c +++ b/src/database.c @@ -674,22 +674,6 @@ static int apk_repo_subst(void *ctx, apk_blob_t key, apk_blob_t *to) return 0; } -int apk_repo_index_url(struct apk_database *db, struct apk_repository *repo, - int *fd, char *buf, size_t len, struct apk_url_print *urlp) -{ - apk_blob_t uri = APK_BLOB_STR(repo->url); - int r; - - r = apk_repo_fd(db, repo, fd); - if (r < 0) return r; - - if (repo->url_is_file) r = apk_fmt(buf, len, BLOB_FMT, BLOB_PRINTF(uri)); - else r = apk_fmt(buf, len, BLOB_FMT "/" BLOB_FMT "/APKINDEX.tar.gz", BLOB_PRINTF(uri), BLOB_PRINTF(*db->arches->item[0])); - if (r < 0) return r; - if (urlp) apk_url_parse(urlp, buf); - return 0; -} - int apk_repo_index_cache_url(struct apk_database *db, struct apk_repository *repo, int *fd, char *buf, size_t len) { int r = apk_repo_fd(db, &db->repos[APK_REPOSITORY_CACHED], fd); @@ -698,7 +682,7 @@ int apk_repo_index_cache_url(struct apk_database *db, struct apk_repository *rep } int apk_repo_package_url(struct apk_database *db, struct apk_repository *repo, struct apk_package *pkg, - int *fd, char *buf, size_t len, struct apk_url_print *urlp) + int *fd, char *buf, size_t len) { struct apk_ctx *ac = db->ctx; int r; @@ -717,43 +701,43 @@ int apk_repo_package_url(struct apk_database *db, struct apk_repository *repo, s r = apk_blob_subst(&buf[r], len - r, repo->pkgname_spec, apk_pkg_subst, pkg); } if (r < 0) return r; - if (urlp) apk_url_parse(urlp, buf); return 0; } int apk_cache_download(struct apk_database *db, struct apk_repository *repo, struct apk_package *pkg, struct apk_progress *prog) { struct apk_out *out = &db->ctx->out; - struct apk_url_print urlp; struct apk_progress_istream pis; struct apk_istream *is; struct apk_ostream *os; struct apk_extract_ctx ectx; - char download_url[PATH_MAX], cache_url[NAME_MAX]; + const char *download_url; + char cache_url[NAME_MAX], package_url[PATH_MAX]; int r, download_fd, cache_fd, tee_flags = 0; - time_t mtime = 0; + time_t download_mtime = 0; if (pkg != NULL) { - r = apk_repo_package_url(db, &db->repos[APK_REPOSITORY_CACHED], pkg, &cache_fd, cache_url, sizeof cache_url, NULL); + r = apk_repo_package_url(db, &db->repos[APK_REPOSITORY_CACHED], pkg, &cache_fd, cache_url, sizeof cache_url); if (r < 0) return r; - r = apk_repo_package_url(db, repo, pkg, &download_fd, download_url, sizeof download_url, &urlp); + r = apk_repo_package_url(db, repo, pkg, &download_fd, package_url, sizeof package_url); if (r < 0) return r; tee_flags = APK_ISTREAM_TEE_COPY_META; + download_url = package_url; } else { r = apk_repo_index_cache_url(db, repo, &cache_fd, cache_url, sizeof cache_url); if (r < 0) return r; - r = apk_repo_index_url(db, repo, &download_fd, download_url, sizeof download_url, &urlp); - if (r < 0) return r; - mtime = repo->mtime; + download_mtime = repo->mtime; + download_fd = AT_FDCWD; + download_url = repo->url_index.ptr; } - if (!prog) apk_out_progress_note(out, "fetch " URL_FMT, URL_PRINTF(urlp)); + if (!pkg && !prog) apk_out_progress_note(out, "fetch " BLOB_FMT, BLOB_PRINTF(repo->url_index_printable)); if (db->ctx->flags & APK_SIMULATE) return 0; os = apk_ostream_to_file(cache_fd, cache_url, 0644); if (IS_ERR(os)) return PTR_ERR(os); - is = apk_istream_from_fd_url_if_modified(download_fd, download_url, apk_db_url_since(db, mtime)); + is = apk_istream_from_fd_url_if_modified(download_fd, download_url, apk_db_url_since(db, download_mtime)); is = apk_progress_istream(&pis, is, prog); is = apk_istream_tee(is, os, tee_flags); apk_extract_init(&ectx, db->ctx, NULL); @@ -1416,7 +1400,8 @@ static int load_v3index(struct apk_extract_ctx *ectx, struct adb_obj *ndx) } apk_pkgtmpl_free(&tmpl); - if (num_broken) apk_warn(out, "Repository %s has %d packages without hash", repo->url, num_broken); + if (num_broken) apk_warn(out, "Repository " BLOB_FMT " has %d packages without hash", + BLOB_PRINTF(repo->url_index_printable), num_broken); return r; } @@ -1451,60 +1436,88 @@ static bool is_index_stale(struct apk_database *db, struct apk_repository *repo) return (time(NULL) - st.st_mtime) > db->ctx->cache_max_age; } -static int add_repository(struct apk_database *db, apk_blob_t _repository) +static bool get_word(apk_blob_t *line, apk_blob_t *word) { - struct apk_repository *repo; - apk_blob_t brepo, btag, url_base, pkgname_spec; - int repo_num, r, tag_id = 0, url_is_file = 0, index_fd; - char index_url[PATH_MAX], *url; + apk_blob_cspn(*line, APK_CTYPE_REPOSITORY_SEPARATOR, word, line); + apk_blob_spn(*line, APK_CTYPE_REPOSITORY_SEPARATOR, NULL, line); + return word->len > 0; +} - brepo = _repository; - btag = APK_BLOB_NULL; - if (brepo.ptr == NULL || brepo.len == 0 || *brepo.ptr == '#') - return 0; +bool apk_repo_parse_line(apk_blob_t line, struct apk_repoline *rl) +{ + apk_blob_t word; - if (brepo.ptr[0] == '@') { - apk_blob_cspn(brepo, APK_CTYPE_REPOSITORY_SEPARATOR, &btag, &brepo); - apk_blob_spn(brepo, APK_CTYPE_REPOSITORY_SEPARATOR, NULL, &brepo); - tag_id = apk_db_get_tag_id(db, btag); + memset(rl, 0, sizeof *rl); + rl->type = APK_REPOTYPE_V2; + + if (!get_word(&line, &word)) return false; + if (word.ptr[0] == '@') { + rl->tag = word; + if (!get_word(&line, &word)) return false; } + if (apk_blob_ends_with(word, APK_BLOB_STRLIT(".adb"))) rl->type = APK_REPOTYPE_NDX; + rl->url = word; + return line.len == 0; +} - url = apk_blob_cstr(brepo); - for (repo_num = 0; repo_num < db->num_repos; repo_num++) { - repo = &db->repos[repo_num]; - if (strcmp(url, repo->url) == 0) { - repo->tag_mask |= BIT(tag_id); - free(url); - return 0; - } +static int add_repository(struct apk_database *db, apk_blob_t line) +{ + struct apk_out *out = &db->ctx->out; + struct apk_repository *repo; + struct apk_repoline rl; + apk_blob_t url_base, url_index, pkgname_spec, dot = APK_BLOB_STRLIT("."); + char buf[PATH_MAX]; + int tag_id = 0; + + if (!line.ptr || line.len == 0 || line.ptr[0] == '#') return 0; + if (!apk_repo_parse_line(line, &rl)) { + apk_warn(out, "Unable to parse repository: " BLOB_FMT, BLOB_PRINTF(line)); + return 0; } - if (db->num_repos >= APK_MAX_REPOS) { - free(url); - return -1; + if (rl.type == APK_REPOTYPE_INVALID) { + apk_warn(out, "Unsupported repository: " BLOB_FMT, BLOB_PRINTF(line)); + return 0; } + if (rl.tag.ptr) tag_id = apk_db_get_tag_id(db, rl.tag); - if (apk_blob_ends_with(brepo, APK_BLOB_STRLIT(".adb"))) { - if (!apk_blob_rsplit(brepo, '/', &url_base, NULL)) url_base = APK_BLOB_STRLIT("."); - pkgname_spec = db->ctx->default_pkgname_spec; - url_is_file = 1; - } else { - url_base = apk_blob_trim_end(brepo, '/'); + const char *index_file = NULL; + switch (rl.type) { + case APK_REPOTYPE_V2: + index_file = "APKINDEX.tar.gz"; + break; + } + if (index_file) { + url_base = apk_blob_trim_end(rl.url, '/'); + url_index = apk_blob_fmt(buf, sizeof buf, BLOB_FMT "/" BLOB_FMT "/%s", BLOB_PRINTF(url_base), BLOB_PRINTF(*db->arches->item[0]), index_file); pkgname_spec = db->ctx->default_reponame_spec; + } else { + if (!apk_blob_rsplit(rl.url, '/', &url_base, NULL)) url_base = dot; + url_index = rl.url; + pkgname_spec = db->ctx->default_pkgname_spec; } - repo_num = db->num_repos++; - repo = &db->repos[repo_num]; + for (repo = &db->repos[APK_REPOSITORY_FIRST_CONFIGURED]; repo < &db->repos[db->num_repos]; repo++) { + if (apk_blob_compare(url_base, repo->url_base) != 0) continue; + if (apk_blob_compare(url_index, repo->url_index) != 0) continue; + repo->tag_mask |= BIT(tag_id); + return 0; + } + url_index = *apk_atomize_dup0(&db->atoms, url_index); + // url base is a prefix of url_index or '.' + if (url_base.ptr != dot.ptr) url_base = APK_BLOB_PTR_LEN(url_index.ptr, url_base.len); + + if (db->num_repos >= APK_MAX_REPOS) return -1; + repo = &db->repos[db->num_repos++]; *repo = (struct apk_repository) { - .url = url, - .url_is_file = url_is_file, - .url_base = *apk_atomize_dup(&db->atoms, url_base), + .url_base = url_base, + .url_base_printable = apk_url_sanitize(url_base, &db->atoms), + .url_index = url_index, + .url_index_printable = apk_url_sanitize(url_index, &db->atoms), .pkgname_spec = pkgname_spec, - .is_remote = apk_url_local_file(url) == NULL, + .is_remote = apk_url_local_file(url_index.ptr) == NULL, .tag_mask = BIT(tag_id), }; - r = apk_repo_index_url(db, repo, &index_fd, index_url, sizeof index_url, NULL); - if (r < 0) return r; - apk_digest_calc(&repo->hash, APK_DIGEST_SHA256, index_url, strlen(index_url)); + apk_digest_calc(&repo->hash, APK_DIGEST_SHA256, url_index.ptr, url_index.len); if (is_index_stale(db, repo)) repo->stale = 1; return 0; } @@ -1513,22 +1526,18 @@ static void open_repository(struct apk_database *db, int repo_num) { struct apk_out *out = &db->ctx->out; struct apk_repository *repo = &db->repos[repo_num]; - struct apk_url_print urlp; const char *error_action = "constructing url"; unsigned int repo_mask = BIT(repo_num); unsigned int available_repos = 0; - char index_url[PATH_MAX]; - int r, update_error = 0, index_fd = AT_FDCWD; - - r = apk_repo_index_url(db, repo, &index_fd, index_url, sizeof index_url, &urlp); - if (r < 0) goto err; + char cache_url[NAME_MAX], *open_url = repo->url_index.ptr; + int r, update_error = 0, open_fd = AT_FDCWD; error_action = "opening"; if (!(db->ctx->flags & APK_NO_NETWORK)) available_repos = repo_mask; if (repo->is_remote) { if (db->ctx->flags & APK_NO_CACHE) { error_action = "fetching"; - apk_out_progress_note(out, "fetch " URL_FMT, URL_PRINTF(urlp)); + apk_out_progress_note(out, "fetch " BLOB_FMT, BLOB_PRINTF(repo->url_index_printable)); } else { error_action = "opening from cache"; if (repo->stale) { @@ -1543,27 +1552,27 @@ static void open_repository(struct apk_database *db, int repo_num) break; } } - r = apk_repo_index_cache_url(db, repo, &index_fd, index_url, sizeof index_url); + open_url = cache_url; + r = apk_repo_index_cache_url(db, repo, &open_fd, cache_url, sizeof cache_url); if (r < 0) goto err; } - } else if (strncmp(repo->url, "file://localhost/", 17) != 0) { + } else if (!apk_blob_starts_with(repo->url_base, APK_BLOB_STRLIT("file://localhost/"))) { available_repos = repo_mask; db->local_repos |= repo_mask; } - r = load_index(db, apk_istream_from_fd_url(index_fd, index_url, apk_db_url_since(db, 0)), repo_num); + r = load_index(db, apk_istream_from_fd_url(open_fd, open_url, apk_db_url_since(db, 0)), repo_num); err: if (r || update_error) { if (repo->is_remote) { if (r) db->repositories.unavailable++; else db->repositories.stale++; } - apk_url_parse(&urlp, repo->url); if (update_error) error_action = r ? "updating and opening" : "updating"; else update_error = r; - apk_warn(out, "%s " URL_FMT ": %s", error_action, URL_PRINTF(urlp), - apk_error_str(update_error)); + apk_warn(out, "%s " BLOB_FMT ": %s", + error_action, BLOB_PRINTF(repo->url_index_printable), apk_error_str(update_error)); } if (r == 0) { db->available_repos |= available_repos; @@ -1593,18 +1602,11 @@ static int add_repos_from_file(void *ctx, int dirfd, const char *file) return 0; } -static void apk_db_setup_repositories(struct apk_database *db, const char *cache_dir) +static void apk_db_setup_repositories(struct apk_database *db, apk_blob_t cache_dir) { - /* This is the SHA-1 of the string 'cache'. Repo hashes like this - * are truncated to APK_CACHE_CSUM_BYTES and always use SHA-1. */ db->repos[APK_REPOSITORY_CACHED] = (struct apk_repository) { - .hash.data = { - 0xb0,0x35,0x92,0x80,0x6e,0xfa,0xbf,0xee,0xb7,0x09, - 0xf5,0xa7,0x0a,0x7c,0x17,0x26,0x69,0xb0,0x05,0x38 }, - .hash.len = APK_DIGEST_LENGTH_SHA1, - .hash.alg = APK_DIGEST_SHA1, - .url = cache_dir, - .url_base = APK_BLOB_STR(cache_dir), + .url_base = cache_dir, + .url_base_printable = cache_dir, .pkgname_spec = db->ctx->default_cachename_spec, }; db->num_repos = APK_REPOSITORY_FIRST_CONFIGURED; @@ -1940,7 +1942,7 @@ int apk_db_open(struct apk_database *db, struct apk_ctx *ac) !(ac->flags & APK_NO_NETWORK)) db->autoupdate = 1; - apk_db_setup_repositories(db, ac->cache_dir); + apk_db_setup_repositories(db, APK_BLOB_STR(ac->cache_dir)); db->root_fd = apk_ctx_fd_root(ac); db->cache_fd = -APKE_CACHE_NOT_AVAILABLE; db->permanent = !detect_tmpfs_root(db); @@ -2249,8 +2251,6 @@ void apk_db_close(struct apk_database *db) apk_pkg_uninstall(NULL, ipkg->pkg); } - for (int i = APK_REPOSITORY_FIRST_CONFIGURED; i < db->num_repos; i++) - free((void*) db->repos[i].url); foreach_array_item(ppath, db->protected_paths) free(ppath->relative_pattern); apk_protected_path_array_free(&db->protected_paths); @@ -3060,7 +3060,7 @@ static int apk_db_unpack_pkg(struct apk_database *db, r = -APKE_PACKAGE_NOT_FOUND; goto err_msg; } - r = apk_repo_package_url(db, repo, pkg, &file_fd, file_url, sizeof file_url, NULL); + r = apk_repo_package_url(db, repo, pkg, &file_fd, file_url, sizeof file_url); if (r < 0) goto err_msg; if (!(pkg->repos & db->local_repos)) need_copy = TRUE; } else { @@ -3082,7 +3082,7 @@ static int apk_db_unpack_pkg(struct apk_database *db, is = apk_progress_istream(&pis, is, prog); if (need_copy) { struct apk_istream *origis = is; - r = apk_repo_package_url(db, &db->repos[APK_REPOSITORY_CACHED], pkg, &cache_fd, cache_url, sizeof cache_url, NULL); + r = apk_repo_package_url(db, &db->repos[APK_REPOSITORY_CACHED], pkg, &cache_fd, cache_url, sizeof cache_url); if (r == 0) is = apk_istream_tee(is, apk_ostream_to_file(cache_fd, cache_url, 0644), APK_ISTREAM_TEE_COPY_META|APK_ISTREAM_TEE_OPTIONAL); diff --git a/src/print.c b/src/print.c index b5e68c97..8814fe74 100644 --- a/src/print.c +++ b/src/print.c @@ -133,28 +133,30 @@ const char *apk_last_path_segment(const char *path) return last == NULL ? path : last + 1; } -void apk_url_parse(struct apk_url_print *urlp, const char *url) +apk_blob_t apk_url_sanitize(apk_blob_t url, struct apk_atom_pool *atoms) { - const char *authority, *path_or_host, *pw; - - *urlp = (struct apk_url_print) { - .url = "", - .pwmask = "", - .url_or_host = url, - }; - - if (!(authority = strstr(url, "://"))) return; - authority += 3; - path_or_host = strpbrk(authority, "/@"); - if (!path_or_host || *path_or_host == '/') return; - pw = strpbrk(authority, "@:"); - if (!pw || *pw == '@') return; - *urlp = (struct apk_url_print) { - .url = url, - .pwmask = "*", - .url_or_host = path_or_host, - .len_before_pw = pw - url + 1, - }; + char buf[PATH_MAX]; + int password_start = 0; + int authority = apk_blob_contains(url, APK_BLOB_STRLIT("://")); + if (authority < 0) return url; + + for (int i = authority + 3; i < url.len; i++) { + switch (url.ptr[i]) { + case '/': + return url; + case '@': + if (!password_start) return url; + // password_start ... i-1 is the password + return *apk_atomize_dup(atoms, + apk_blob_fmt(buf, sizeof buf, "%.*s*%.*s", + password_start, url.ptr, + (int)(url.len - i), &url.ptr[i])); + case ':': + if (!password_start) password_start = i + 1; + break; + } + } + return url; } void apk_out_reset(struct apk_out *out) diff --git a/test/unit/apk_test.h b/test/unit/apk_test.h index 4dc30818..255eb806 100644 --- a/test/unit/apk_test.h +++ b/test/unit/apk_test.h @@ -5,6 +5,20 @@ #define assert_ptr_ok(c) _assert_true(!IS_ERR(c), #c, __FILE__, __LINE__) +#define _assert_blob_equal(a, b, file, line) do { \ + _assert_int_equal(a.len, b.len, file, line); \ + _assert_memory_equal(a.ptr, b.ptr, a.len, file, line); \ + } while (0) +#define assert_blob_equal(a, b) _assert_blob_equal(a, b, __FILE__, __LINE__) + +#define _assert_blob_identical(a, b, file, line) do { \ + _assert_int_equal(a.len, b.len, file, line); \ + _assert_int_equal(cast_ptr_to_largest_integral_type(a.ptr), \ + cast_ptr_to_largest_integral_type(b.ptr), \ + file, line); \ + } while (0) +#define assert_blob_identical(a, b) _assert_blob_identical(a, b, __FILE__, __LINE__) + void test_register(const char *, UnitTestFunction); #define APK_TEST(test_name) \ diff --git a/test/unit/blob_test.c b/test/unit/blob_test.c index 80c418ea..2afce2bb 100644 --- a/test/unit/blob_test.c +++ b/test/unit/blob_test.c @@ -1,5 +1,7 @@ #include "apk_test.h" #include "apk_blob.h" +#include "apk_atom.h" +#include "apk_print.h" APK_TEST(blob_foreach_word_test) { int ch = 'a'; @@ -24,3 +26,23 @@ APK_TEST(blob_split) { assert_int_equal(0, apk_blob_compare(l, APK_BLOB_STRLIT("bar"))); assert_int_equal(0, apk_blob_compare(r, APK_BLOB_STRLIT("foo"))); } + +APK_TEST(blob_url_sanitize) { + struct { + const char *url, *sanitized; + } tests[] = { + { "http://example.com", NULL }, + { "http://foo@example.com", NULL }, + { "http://foo:pass@example.com", "http://foo:*@example.com" }, + { "http://example.com/foo:pass@bar", NULL }, + }; + struct apk_atom_pool atoms; + apk_atom_init(&atoms); + for (int i = 0; i < ARRAY_SIZE(tests); i++) { + apk_blob_t url = APK_BLOB_STR(tests[i].url); + apk_blob_t res = apk_url_sanitize(APK_BLOB_STR(tests[i].url), &atoms); + if (tests[i].sanitized) assert_blob_equal(APK_BLOB_STR(tests[i].sanitized), res); + else assert_blob_identical(url, res); + } + apk_atom_free(&atoms); +} diff --git a/test/unit/db_test.c b/test/unit/db_test.c new file mode 100644 index 00000000..4c6062c3 --- /dev/null +++ b/test/unit/db_test.c @@ -0,0 +1,27 @@ +#include "apk_test.h" +#include "apk_database.h" + +static void _assert_repoline(apk_blob_t line, apk_blob_t tag, unsigned int type, apk_blob_t url, const char *const file, int lineno) +{ + struct apk_repoline rl; + + _assert_true(apk_repo_parse_line(line, &rl), "", file, lineno); + _assert_blob_equal(tag, rl.tag, file, lineno); + _assert_int_equal(type, rl.type, file, lineno); + _assert_blob_equal(url, rl.url, file, lineno); +} +#define assert_repoline(line, tag, type, url) _assert_repoline(line, tag, type, url, __FILE__, __LINE__) + +APK_TEST(db_repo_parse) { + struct apk_repoline rl; + apk_blob_t tag = APK_BLOB_STRLIT("@tag"); + apk_blob_t url = APK_BLOB_STRLIT("http://example.com"); + apk_blob_t index = APK_BLOB_STRLIT("http://example.com/index.adb"); + + assert_repoline(url, APK_BLOB_NULL, APK_REPOTYPE_V2, url); + assert_repoline(APK_BLOB_STRLIT("@tag http://example.com"), tag, APK_REPOTYPE_V2, url); + assert_repoline(APK_BLOB_STRLIT("http://example.com/index.adb"), APK_BLOB_NULL, APK_REPOTYPE_NDX, index); + + assert_false(apk_repo_parse_line(APK_BLOB_STRLIT("http://example.com extra"), &rl)); + assert_false(apk_repo_parse_line(APK_BLOB_STRLIT("@tag v3 http://example.com extra"), &rl)); +} diff --git a/test/unit/meson.build b/test/unit/meson.build index f7fc3863..8481807c 100644 --- a/test/unit/meson.build +++ b/test/unit/meson.build @@ -4,6 +4,7 @@ if cmocka_dep.found() unit_test_src = [ 'blob_test.c', + 'db_test.c', 'package_test.c', 'process_test.c', 'version_test.c', -- GitLab