diff options
| author | 2024-01-02 23:47:50 -0600 | |
|---|---|---|
| committer | 2024-01-02 23:47:50 -0600 | |
| commit | eacff9bc34e2fea0999dd3c14b22d2e2fceeb2e8 (patch) | |
| tree | 127b43b00fedde11f9ace019941a0194ca3c456b /src/version.c | |
| parent | make library API better (diff) | |
downloads client jar
Diffstat (limited to 'src/version.c')
| -rw-r--r-- | src/version.c | 228 |
1 files changed, 180 insertions, 48 deletions
diff --git a/src/version.c b/src/version.c index 37ae3ba..7ca1487 100644 --- a/src/version.c +++ b/src/version.c @@ -564,7 +564,7 @@ cleanup: return res; } -int l2_version_check_integrity(FILE *fp, l2_sha1_digest_t *digest, size_t sz) +int l2_version_check_integrity(FILE *fp, const l2_sha1_digest_t *digest, size_t sz) { #define VER_READBUF_SZ (1024) @@ -599,7 +599,7 @@ unsigned l2_version__collect_libraries(json_t *jlibs, l2_subst_t *subst, struct void l2_version_free_libraries(struct l2_version_library *libs); int l2_version__get_library_artifact_path(json_t *lib, l2_subst_t *subst, const char *classifier, char **path); -int l2_version__library_should_download(struct l2_version_library *lib); +int l2_version__should_download(const char *path, const l2_sha1_digest_t *expectdigest, size_t expectsize); struct l2_version__library_dl_data { l2_sha1_state_t st; @@ -714,7 +714,7 @@ unsigned l2_version_download_libraries(struct l2_version_library *libs) } for (struct l2_version_library *cur = libs; cur; cur = cur->next) { - dlres = l2_version__library_should_download(cur); + dlres = l2_version__should_download(cur->fullpath, cur->has_digest ? &cur->digest : NULL, cur->size); if (dlres < 0) { res = VERSION_EDOWNLOAD; goto cleanup; @@ -778,28 +778,28 @@ cleanup: return res; } -int l2_version__library_should_download(struct l2_version_library *lib) +int l2_version__should_download(const char *path, const l2_sha1_digest_t *expectdigest, size_t expectsize) { - FILE *lfile = fopen(lib->fullpath, "rb"); + FILE *lfile = fopen(path, "rb"); if (!lfile) { if (errno == ENOENT) { return 1; } else { - CMD_DEBUG("Failed to open %s for reading: %s", lib->fullpath, strerror(errno)); + CMD_DEBUG("Failed to open %s for reading: %s", path, strerror(errno)); return -1; } } - int res = l2_version_check_integrity(lfile, lib->has_digest ? &lib->digest : NULL, lib->size); + int res = l2_version_check_integrity(lfile, expectdigest, expectsize); fclose(lfile); switch (res) { case 0: - CMD_DEBUG("Downloading library %s, the SHA1 or size doesn't match.", lib->url); + CMD_DEBUG("SHOULD redownload %s, the SHA1 or size doesn't match.", path); return 1; case 1: - CMD_DEBUG("Not downloading library %s.", lib->url); + CMD_DEBUG("SHOULDN'T redownload %s.", path); return 0; default: return res; @@ -1211,55 +1211,39 @@ int l2_version__get_library_artifact_path(json_t *lib, l2_subst_t *subst, const json_t *jname = json_object_get(lib, "name"); if (!json_is_string(jname)) return -1; - const char *name = json_string_value(jname); - const char *group_id_end = NULL; - const char *artifact_id = NULL; - int artifact_id_len = 0; - const char *version = NULL; - int version_len = 0; - const char *tclass = NULL; - char *basepath; - size_t basepathlen; - - for (const char *cur = name; *cur; ++cur) { - if (*cur == ':') { - if (!group_id_end) { - group_id_end = cur; - artifact_id = cur + 1; - } else if (!version) { - version = cur + 1; - artifact_id_len = (int)(cur - artifact_id); - } else if (!tclass) { - tclass = cur + 1; - version_len = (int)(cur - version); - } else { - return -1; - } - } - } + const char *cname = json_string_value(jname); + + char *name; + size_t size; - if (!group_id_end || !artifact_id || !version) return -1; + L2_ASTRDUP(name, size, cname); - basepathlen = group_id_end - name; - basepath = alloca(basepathlen + 1); - memcpy(basepath, name, basepathlen); - basepath[basepathlen] = '\0'; + char *nameparts[] = { name, NULL, NULL, NULL }; + size_t curpart = 1; - for (char *cur = basepath; *cur; ++cur) { - if (*cur == '.') *cur = '/'; + for (char *cur = name; *cur; ++cur) { + if (*cur == ':') { + if (curpart == 4) return -1; /* invalid name: too many colons! */ + + *cur = '\0'; + nameparts[curpart++] = cur + 1; + } else if (curpart == 1 && *cur == '.') { + *cur = '/'; + } } - size_t bufsz; + if (!nameparts[1] || !nameparts[2]) return -1; + char *buf; + size_t bufsz; int has_class = 0; - if (tclass || classifier) { - L2_ASPRINTF(buf, bufsz, "%s/%*.*s-%*.*s-%s.jar", basepath, - artifact_id_len, artifact_id_len, artifact_id, - version_len, version_len, version, tclass ? tclass : classifier); + if (nameparts[3] || classifier) { + const char *class = nameparts[3] ? nameparts[3] : classifier; + L2_ASPRINTF(buf, bufsz, "%s/%s/%s/%s-%s-%s.jar", nameparts[0], nameparts[1], nameparts[2], nameparts[1], nameparts[2], class); has_class = 1; } else { - L2_ASPRINTF(buf, bufsz, "%s/%*.*s-%s.jar", basepath, artifact_id_len, artifact_id_len, artifact_id, version); + L2_ASPRINTF(buf, bufsz, "%s/%s/%s/%s-%s.jar", nameparts[0], nameparts[1], nameparts[2], nameparts[1], nameparts[2]); } return l2_subst_apply(subst, buf, path) < 0 ? -1 : has_class; @@ -1381,3 +1365,151 @@ enum l2_version_check_result l2_version_check_rules(json_t *rules, l2_version_fe return res; } + +struct l2_version__download_data { + l2_sha1_state_t digest_state; + size_t recv_size; + FILE *fp; +}; + +size_t l2_version__download_writecb(char *ptr, size_t size, size_t nmemb, void *user) +{ + struct l2_version__download_data *data = user; + size_t realsz = size * nmemb; /* size should be 1 but whatever */ + + if (fwrite(ptr, size, nmemb, data->fp) < nmemb) { + return CURL_WRITEFUNC_ERROR; + } + + l2_sha1_update(&data->digest_state, ptr, realsz); + data->recv_size += realsz; + + return realsz; +} + +unsigned l2_version_download_jar(json_t *version, const char *specifier, char **path) +{ + const char *id = NULL; + const char *digeststr = NULL; + const char *url = NULL; + CURL *pc = NULL; + + json_int_t jexpectsize = 0; + + size_t expect_size; + l2_sha1_digest_t expect_digest; + + unsigned res = VERSION_SUCCESS; + + if (json_unpack(version, "{s:{s:{s?:s, s?:s, s?:I}}, s:s}", "downloads", specifier, "sha1", &digeststr, "url", &url, "size", &jexpectsize, "id", &id) < 0) { + return VERSION_EJSON; + } + + /* make sure values in version file make sense */ + if (jexpectsize < 0) return VERSION_EJSON; + if (digeststr && l2_sha1_digest_from_hex(&expect_digest, digeststr) < 0) return VERSION_EJSON; + expect_size = (size_t)jexpectsize; + + /* create the final path where the jar is placed */ + char *pathstr = l2_launcher_sprintf_alloc("%s/versions/%s/%s.jar", l2_state.paths.data, id, id); + if (!pathstr) return VERSION_EALLOC; + + /* check if we even need to redownload the thing */ + int rdres = l2_version__should_download(pathstr, digeststr ? &expect_digest : NULL, expect_size); + if (rdres < 0) { + return VERSION_EDOWNLOAD; + } else if (!rdres) { + CMD_DEBUG("Not downloading %s jar.", specifier); + *path = pathstr; + return VERSION_SUCCESS; + } + + if (!url) { + CMD_WARN("Cannot redownload %s jar, even though I need to! (no URL specified)", specifier); + return VERSION_EJSON; + } + + /* redownload the file */ + struct l2_version__download_data dldata; + char errbuf[CURL_ERROR_SIZE]; + + memset(&dldata, 0, sizeof(struct l2_version__download_data)); + memset(&errbuf, 0, sizeof(errbuf)); + + l2_sha1_init(&dldata.digest_state); + + if (l2_launcher_mkdir_parents_ex(pathstr, 1) < 0) { + res = VERSION_EDOWNLOAD; + goto cleanup; + } + + dldata.fp = fopen(pathstr, "wb"); + if (!dldata.fp) { + CMD_WARN("Failed to open %s for writing: %s", pathstr, strerror(errno)); + res = VERSION_EDOWNLOAD; + goto cleanup; + } + + pc = curl_easy_init(); + if (!pc) { + res = VERSION_EDOWNLOAD; + goto cleanup; + } + + curl_easy_setopt(pc, CURLOPT_USERAGENT, L2_USER_AGENT); + curl_easy_setopt(pc, CURLOPT_URL, url); + curl_easy_setopt(pc, CURLOPT_WRITEDATA, &dldata); + curl_easy_setopt(pc, CURLOPT_WRITEFUNCTION, &l2_version__download_writecb); + curl_easy_setopt(pc, CURLOPT_ERRORBUFFER, errbuf); + + CURLcode cres = curl_easy_perform(pc); + if (cres != CURLE_OK) { + CMD_WARN("Failed to download %s jar: %s: %s", specifier, curl_easy_strerror(cres), errbuf); + res = VERSION_EDOWNLOAD; + goto cleanup; + } + + fclose(dldata.fp); + dldata.fp = NULL; + + curl_easy_cleanup(pc); + pc = NULL; + + l2_sha1_digest_t recvdigest; + l2_sha1_finalize(&dldata.digest_state, &recvdigest); + + if (digeststr && l2_sha1_digest_compare(&recvdigest, &expect_digest)) { + char expstr[L2_SHA1_HEX_STRLEN + 1]; + char gotstr[L2_SHA1_HEX_STRLEN + 1]; + + l2_sha1_digest_to_hex(&expect_digest, expstr); + l2_sha1_digest_to_hex(&recvdigest, gotstr); + + CMD_WARN("Downloaded %s jar has wrong digest! (expected: %s, got: %s)", specifier, expstr, gotstr); + if (unlink(pathstr) < 0) { + CMD_WARN("Failed to delete %s jar: %s", specifier, strerror(errno)); + } + res = VERSION_EDOWNLOAD; + goto cleanup; + } + + if (expect_size > 0 && expect_size != dldata.recv_size) { + CMD_WARN("Downloaded %s jar has wrong size! (expected: %zu bytes, got: %zu bytes)", specifier, expect_size, dldata.recv_size); + if (unlink(pathstr) < 0) { + CMD_WARN("Failed to delete %s jar: %s", specifier, strerror(errno)); + } + res = VERSION_EDOWNLOAD; + goto cleanup; + } + + CMD_DEBUG("Downloaded %s jar successfully.", specifier); + curl_easy_cleanup(pc); + *path = pathstr; + return VERSION_SUCCESS; + +cleanup: + free(pathstr); + if (dldata.fp) fclose(dldata.fp); + if (pc) curl_easy_cleanup(pc); + return res; +} |
