diff options
| author | 2024-01-03 05:18:11 -0600 | |
|---|---|---|
| committer | 2024-01-03 05:18:11 -0600 | |
| commit | ce87d368bd3e9b6c05ed9f6fdbb97580f105bbcf (patch) | |
| tree | 7b7441d6891aff7080cae3b0b08cd577761a1f1c /src | |
| parent | downloads client jar (diff) | |
refactor and download asset index
Diffstat (limited to 'src')
| -rw-r--r-- | src/assets.c | 64 | ||||
| -rw-r--r-- | src/assets.h | 4 | ||||
| -rw-r--r-- | src/cmd-version.c | 14 | ||||
| -rw-r--r-- | src/l2su.h | 5 | ||||
| -rw-r--r-- | src/launcherutil.c | 176 | ||||
| -rw-r--r-- | src/meson.build | 2 | ||||
| -rw-r--r-- | src/version.c | 160 | ||||
| -rw-r--r-- | src/version.h | 2 |
8 files changed, 271 insertions, 156 deletions
diff --git a/src/assets.c b/src/assets.c new file mode 100644 index 0000000..57624ae --- /dev/null +++ b/src/assets.c @@ -0,0 +1,64 @@ +#include "assets.h" +#include "version.h" +#include "l2su.h" + +#include <jansson.h> + +int l2_assets__download_index(json_t *version, char **path) +{ + const char *url = NULL; + const char *id = NULL; + const char *digeststr = NULL; + json_int_t jsize = 0; + + l2_sha1_digest_t expect_digest; + + if (json_unpack(version, "{s:{s?:s, s:s, s:I, s:s}}", "assetIndex", "url", &url, "id", &id, "size", &jsize, "sha1", &digeststr) < 0) { + return -1; + } + + if (jsize < 0) return -1; + if (l2_sha1_digest_from_hex(&expect_digest, digeststr) < 0) return -1; + + char *pathstr = l2_launcher_sprintf_alloc("%s/assets/indexes/%s.json", l2_state.paths.data, id); + if (!pathstr) return -1; + + int res = l2_launcher_download_checksummed(url, pathstr, &expect_digest, (size_t)jsize); + + if (res < 0) { + free(pathstr); + return res; + } + + *path = pathstr; + return res; +} + +int l2_assets__load_index(json_t *version, json_t **asset_index) +{ + char *path = NULL; + int res; + + res = l2_assets__download_index(version, &path); + + if (res < 0) { + goto cleanup; + } + + json_error_t jerr; + json_t *js; + if (!(js = json_load_file(path, JSON_REJECT_DUPLICATES, &jerr))) { + CMD_ERROR("JSON error parsing asset index: %s", jerr.text); + res = -1; + goto cleanup; + } + + free(path); + + *asset_index = js; + return res; + +cleanup: + free(path); + return res; +} diff --git a/src/assets.h b/src/assets.h new file mode 100644 index 0000000..6f8496f --- /dev/null +++ b/src/assets.h @@ -0,0 +1,4 @@ +#ifndef L2SU_ASSETS_H_INCLUDED +#define L2SU_ASSETS_H_INCLUDED + +#endif /* include guard */ diff --git a/src/cmd-version.c b/src/cmd-version.c index f96d6f3..b11e35c 100644 --- a/src/cmd-version.c +++ b/src/cmd-version.c @@ -7,6 +7,7 @@ #include <jansson.h> #include <stdio.h> +#include <unistd.h> unsigned cmd_version_list_remote(struct l2_context_node *ctx, char **args) { @@ -40,6 +41,8 @@ bool feat_match_cb(const char *name, json_t *js) { return false; } +int l2_assets__load_index(json_t *version, json_t **asset_index); + unsigned cmd_version_install(struct l2_context_node *ctx, char **args) { unsigned res = l2_version_load_remote(); @@ -71,6 +74,17 @@ unsigned cmd_version_install(struct l2_context_node *ctx, char **args) CMD_FATAL("Failed to download client jar: %s", l2_version_strerror(res)); } + CMD_INFO0("Downloading asset index..."); + json_t *assets = NULL; + if (l2_assets__load_index(js, &assets) < 0) { + CMD_FATAL0("Failed to load asset index."); + } + + json_dumpf(assets, stdout, JSON_INDENT(4)); + putchar('\n'); + + json_decref(assets); + l2_version_free_libraries(libs); free(jarpath); json_decref(js); @@ -60,6 +60,11 @@ void l2_launcher_download_cleanup(struct l2_dlbuf *buf); extern const curl_write_callback l2_dlcb; CURLcode l2_launcher_download(CURL *cd, const char *url, void **odata, size_t *osize); + +int l2_launcher_should_download(const char *path, const l2_sha1_digest_t *expectdigest, size_t expectsize); +int l2_launcher_check_integrity(FILE *fp, const l2_sha1_digest_t *digest, size_t sz); +int l2_launcher_download_checksummed(const char *url, const char *pathstr, l2_sha1_digest_t *expect_digest, size_t expect_size); + int l2_json_merge_objects(json_t *j1, json_t *j2); /* string substitute utility */ diff --git a/src/launcherutil.c b/src/launcherutil.c index f7a38fd..2db6238 100644 --- a/src/launcherutil.c +++ b/src/launcherutil.c @@ -281,3 +281,179 @@ int l2_json_merge_objects(json_t *j1, json_t *j2) return 0; } + +int l2_launcher_check_integrity(FILE *fp, const l2_sha1_digest_t *digest, size_t sz) +{ + #define VER_READBUF_SZ (1024) + + size_t len = 0, nread; + uint8_t buf[VER_READBUF_SZ]; + l2_sha1_digest_t rdigest; + l2_sha1_state_t st; + + if (!digest && sz == 0) return 1; + + l2_sha1_init(&st); + + while ((nread = fread(buf, 1, VER_READBUF_SZ, fp)) > 0) { + len += nread; + l2_sha1_update(&st, buf, nread); + } + + if (ferror(fp)) return -1; + + l2_sha1_finalize(&st, &rdigest); + + if (sz > 0 && sz != len) return 0; + + if (digest) { + return !l2_sha1_digest_compare(&rdigest, digest) ? 1 : 0; + } else { + return 1; + } +} + +int l2_launcher_should_download(const char *path, const l2_sha1_digest_t *expectdigest, size_t expectsize) +{ + FILE *lfile = fopen(path, "rb"); + + if (!lfile) { + if (errno == ENOENT) { + return 1; + } else { + CMD_DEBUG("Failed to open %s for reading: %s", path, strerror(errno)); + return -1; + } + } + + int res = l2_launcher_check_integrity(lfile, expectdigest, expectsize); + fclose(lfile); + + switch (res) { + case 0: + CMD_DEBUG("SHOULD redownload %s, the SHA1 or size doesn't match.", path); + return 1; + case 1: + CMD_DEBUG("SHOULDN'T redownload %s.", path); + return 0; + default: + return res; + } +} + +struct l2_launcher__download_data { + l2_sha1_state_t digest_state; + size_t recv_size; + FILE *fp; +}; + +size_t l2_launcher__download_writecb(char *ptr, size_t size, size_t nmemb, void *user) +{ + struct l2_launcher__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; +} + +int l2_launcher_download_checksummed(const char *url, const char *pathstr, l2_sha1_digest_t *expect_digest, size_t expect_size) +{ + int res = -1; + CURL *pc = NULL; + + /* check if we even need to redownload the thing */ + int rdres = l2_launcher_should_download(pathstr, expect_digest, expect_size); + if (rdres < 0) { + return -1; + } else if (!rdres) { + CMD_DEBUG("Not downloading %s", pathstr); + return 0; + } + + if (!url) { + CMD_WARN("Cannot redownload %s, even though I need to! (no URL specified)", pathstr); + return -1; + } + + /* redownload the file */ + struct l2_launcher__download_data dldata; + char errbuf[CURL_ERROR_SIZE]; + + memset(&dldata, 0, sizeof(struct l2_launcher__download_data)); + memset(&errbuf, 0, sizeof(errbuf)); + + l2_sha1_init(&dldata.digest_state); + + if (l2_launcher_mkdir_parents_ex(pathstr, 1) < 0) { + goto cleanup; + } + + dldata.fp = fopen(pathstr, "wb"); + if (!dldata.fp) { + CMD_WARN("Failed to open %s for writing: %s", pathstr, strerror(errno)); + goto cleanup; + } + + pc = curl_easy_init(); + if (!pc) { + 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_launcher__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: %s: %s", pathstr, curl_easy_strerror(cres), errbuf); + 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 (expect_digest && 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 has wrong digest! (expected: %s, got: %s)", pathstr, expstr, gotstr); + if (unlink(pathstr) < 0) { + CMD_WARN("Failed to delete %s: %s", pathstr, strerror(errno)); + } + goto cleanup; + } + + if (expect_size > 0 && expect_size != dldata.recv_size) { + CMD_WARN("Downloaded %s has wrong size! (expected: %zu bytes, got: %zu bytes)", pathstr, expect_size, dldata.recv_size); + if (unlink(pathstr) < 0) { + CMD_WARN("Failed to delete %s: %s", pathstr, strerror(errno)); + } + goto cleanup; + } + + CMD_INFO("Downloaded %s successfully.", pathstr); + curl_easy_cleanup(pc); + return 1; + +cleanup: + if (dldata.fp) fclose(dldata.fp); + if (pc) curl_easy_cleanup(pc); + return res; +} diff --git a/src/meson.build b/src/meson.build index 1f7d40f..86728a6 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,4 +1,4 @@ -launcher_srcs = files('l2su.c', 'command.c', 'cmd-instance.c', 'uuid/uuid.c', 'launcherutil.c', 'instance.c', 'cmd-version.c', 'digest/sha1.c', 'version.c', 'subst.c', 'downloadpool.c') +launcher_srcs = files('l2su.c', 'command.c', 'cmd-instance.c', 'uuid/uuid.c', 'launcherutil.c', 'instance.c', 'cmd-version.c', 'digest/sha1.c', 'version.c', 'subst.c', 'downloadpool.c', 'assets.c') configure_file(input : 'config.h.in', output : 'config.h', configuration : config_data) config_include_dir = include_directories('.') diff --git a/src/version.c b/src/version.c index 7ca1487..54bdc20 100644 --- a/src/version.c +++ b/src/version.c @@ -124,7 +124,7 @@ void l2_version__load_local(const char *id, size_t expectsz, l2_sha1_digest_t *d FILE *ver = fopen(path, "r"); if (!ver) return; - int res = l2_version_check_integrity(ver, digest, expectsz); + int res = l2_launcher_check_integrity(ver, digest, expectsz); if (res < 0) { CMD_WARN("Error checking version file integrity for %s", id); return; @@ -564,43 +564,10 @@ cleanup: return res; } -int l2_version_check_integrity(FILE *fp, const l2_sha1_digest_t *digest, size_t sz) -{ - #define VER_READBUF_SZ (1024) - - size_t len = 0, nread; - uint8_t buf[VER_READBUF_SZ]; - l2_sha1_digest_t rdigest; - l2_sha1_state_t st; - - if (!digest && sz == 0) return 1; - - l2_sha1_init(&st); - - while ((nread = fread(buf, 1, VER_READBUF_SZ, fp)) > 0) { - len += nread; - l2_sha1_update(&st, buf, nread); - } - - if (ferror(fp)) return -1; - - l2_sha1_finalize(&st, &rdigest); - - if (sz > 0 && sz != len) return 0; - - if (digest) { - return !l2_sha1_digest_compare(&rdigest, digest) ? 1 : 0; - } else { - return 1; - } -} - unsigned l2_version__collect_libraries(json_t *jlibs, l2_subst_t *subst, struct l2_version_library **libs, l2_version_feature_match_proc_t *feature_matcher); 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__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; size_t sz; @@ -714,7 +681,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__should_download(cur->fullpath, cur->has_digest ? &cur->digest : NULL, cur->size); + dlres = l2_launcher_should_download(cur->fullpath, cur->has_digest ? &cur->digest : NULL, cur->size); if (dlres < 0) { res = VERSION_EDOWNLOAD; goto cleanup; @@ -791,7 +758,7 @@ int l2_version__should_download(const char *path, const l2_sha1_digest_t *expect } } - int res = l2_version_check_integrity(lfile, expectdigest, expectsize); + int res = l2_launcher_check_integrity(lfile, expectdigest, expectsize); fclose(lfile); switch (res) { @@ -1366,41 +1333,17 @@ 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; } @@ -1414,102 +1357,13 @@ unsigned l2_version_download_jar(json_t *version, const char *specifier, char ** 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); + int dlres = l2_launcher_download_checksummed(url, pathstr, digeststr ? &expect_digest : NULL, expect_size); - 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 (dlres < 0) { + free(pathstr); + return VERSION_EDOWNLOAD; } - 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; } diff --git a/src/version.h b/src/version.h index b2f871b..c008481 100644 --- a/src/version.h +++ b/src/version.h @@ -88,8 +88,6 @@ unsigned l2_version_load_remote(void); unsigned l2_version_load_local(const char *name, json_t **ojs); -int l2_version_check_integrity(FILE *fp, const l2_sha1_digest_t *digest, size_t sz); - enum l2_version_check_result { RULE_CHECK_ERROR = -1, RULE_CHECK_DEFAULT, |
