aboutsummaryrefslogtreecommitdiffstats
path: root/src/version.c
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2024-01-02 20:58:01 -0600
committerLibravatar bigfoot547 <[email protected]>2024-01-02 20:58:01 -0600
commite627848dcf58aa00f4eb5dc465c4a804d6576877 (patch)
tree83d4c1c6e1ef13ed38e337b687cc7e223e3d8064 /src/version.c
parentnow loads libraries (diff)
library downloads complete
Diffstat (limited to 'src/version.c')
-rw-r--r--src/version.c394
1 files changed, 375 insertions, 19 deletions
diff --git a/src/version.c b/src/version.c
index 9eb7d0e..a1a5e5c 100644
--- a/src/version.c
+++ b/src/version.c
@@ -5,6 +5,7 @@
#include "macros.h"
#include "endian.h"
#include "command.h"
+#include "downloadpool.h"
#include <curl/curl.h>
#include <jansson.h>
@@ -13,16 +14,17 @@
#include <string.h>
#include <stdint.h>
#include <alloca.h>
+#include <sys/stat.h>
#include <unistd.h>
#include <pcre2.h>
const char *const l2_version__messages[] = {
"Success",
- "Malformed version manifest",
- "Version manifest JSON error",
+ "Malformed version",
+ "Version JSON error",
"Allocation failed",
"OS error",
- "Error downloading version manifest",
+ "Error downloading version",
"Version not found",
"Max recursion depth exceeded",
NULL
@@ -571,6 +573,8 @@ int l2_version_check_integrity(FILE *fp, l2_sha1_digest_t *digest, size_t 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) {
@@ -595,30 +599,376 @@ 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);
-unsigned l2_version_download_libraries(json_t *jlibraries, l2_version_feature_match_proc_t *feature_matcher)
+int l2_version__library_should_download(struct l2_version_library *lib);
+
+struct l2_version__library_dl_data {
+ l2_sha1_state_t st;
+ size_t sz;
+
+ FILE *ofile;
+ struct l2_version_library *lib;
+ struct l2_version__library_dl_data *next;
+};
+
+size_t l2_version__lib_writecb(char *ptr, size_t size, size_t nmemb, void *user)
{
- struct l2_version_library *plib = NULL;
- l2_subst_t *sp;
+ struct l2_version__library_dl_data *data = user;
+ size_t realsz = size * nmemb;
+
+ if (fwrite(ptr, size, nmemb, data->ofile) < nmemb) {
+ CMD_DEBUG("Error writing to file %s", data->lib->fullpath);
+ return CURL_WRITEFUNC_ERROR;
+ }
+
+ data->sz += realsz;
+ l2_sha1_update(&data->st, ptr, realsz);
+ return realsz;
+}
+
+int l2_version__dlpool_libs_init(CURL *curl, void *user)
+{
+ struct l2_version__library_dl_data *data = user;
+
+ l2_sha1_init(&data->st);
+
+ if (l2_launcher_mkdir_parents_ex(data->lib->fullpath, 1) < 0) {
+ CMD_WARN("Failed to make directory for libraries: %s", strerror(errno));
+ return -1;
+ }
+
+ data->ofile = fopen(data->lib->fullpath, "wb");
+ if (!data->ofile) {
+ CMD_WARN("Failed to open %s for writing: %s", data->lib->fullpath, strerror(errno));
+ return -1;
+ }
- l2_subst_init(&sp);
- l2_subst_add(sp, "arch", L2SU_LAUNCHER_ARCH_BITS);
+ /* curl */
+ curl_easy_setopt(curl, CURLOPT_URL, data->lib->url);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &l2_version__lib_writecb);
- unsigned u = l2_version__collect_libraries(jlibraries, sp, &plib, feature_matcher);
- char hexdig[L2_SHA1_HEX_STRLEN + 1];
- for (struct l2_version_library *lib = plib; lib; lib = lib->next) {
- if (lib->has_digest) {
- l2_sha1_digest_to_hex(&lib->digest, hexdig);
+ return 0;
+}
+
+int l2_version__dlpool_libs_complete(CURL *curl, CURLcode code, void *user)
+{
+ long rescode;
+ struct l2_version__library_dl_data *data = user;
+
+ fclose(data->ofile);
+ data->ofile = NULL;
+
+ if (code != CURLE_OK) {
+ CMD_WARN("Failed to download %s: %s", data->lib->url, curl_easy_strerror(code));
+ return -1;
+ }
+
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rescode);
+ if (rescode / 100 != 2) {
+ CMD_WARN("Failed to download %s: server responded with %ld", data->lib->url, rescode);
+ return -1;
+ }
+
+ if (data->lib->has_digest) {
+ l2_sha1_digest_t dig;
+ l2_sha1_finalize(&data->st, &dig);
+ if (l2_sha1_digest_compare(&dig, &data->lib->digest)) {
+ char expected[L2_SHA1_HEX_STRLEN + 1], got[L2_SHA1_HEX_STRLEN + 1];
+ l2_sha1_digest_to_hex(&dig, got);
+ l2_sha1_digest_to_hex(&data->lib->digest, expected);
+
+ CMD_WARN("Failed to download %s: SHA1 mismatch (expect: %s, got: %s)", data->lib->url, expected, got);
+
+ if (unlink(data->lib->fullpath) < 0) {
+ CMD_WARN("Failed to delete %s: %s", data->lib->fullpath, strerror(errno));
+ }
+
+ return -1;
}
+ }
+
+ if (data->lib->size > 0 && data->sz != data->lib->size) {
+ CMD_WARN("Failed to download %s: size mismatch (expect: %zu bytes, got: %zu bytes)", data->lib->fullpath, data->lib->size, data->sz);
- CMD_DEBUG("Library: %s url: %s dig: %s", lib->artifactpath, lib->url, lib->has_digest ? hexdig : "(none)");
- if (lib->extract.extracttype != EXTRACT_NONE) {
- CMD_DEBUG("Extract: %s", lib->extract.extracttype == EXTRACT_NEW ? "new" : "old");
+ if (unlink(data->lib->fullpath) < 0) {
+ CMD_WARN("Failed to delete %s: %s", data->lib->fullpath, strerror(errno));
}
+ return -1;
}
- l2_version__free_libraries(plib);
- l2_subst_free(sp);
- return u;
+ CMD_DEBUG("Downloaded library %s.", data->lib->url);
+ return 0;
+}
+
+unsigned l2_version__actually_download_libraries(struct l2_version_library *libs)
+{
+ unsigned res = VERSION_SUCCESS;
+ struct l2_version__library_dl_data *head = NULL, *tail = NULL;
+ l2_dlpool_t *pool = NULL;
+ int dlres;
+
+ pool = l2_dlpool_init(&l2_version__dlpool_libs_init, &l2_version__dlpool_libs_complete);
+ if (!pool) {
+ res = VERSION_EALLOC;
+ goto cleanup;
+ }
+
+ for (struct l2_version_library *cur = libs; cur; cur = cur->next) {
+ dlres = l2_version__library_should_download(cur);
+ if (dlres < 0) {
+ res = VERSION_EDOWNLOAD;
+ goto cleanup;
+ }
+
+ if (!dlres) continue;
+
+ struct l2_version__library_dl_data *curdata = calloc(1, sizeof(struct l2_version__library_dl_data));
+ if (!curdata) {
+ res = VERSION_EALLOC;
+ goto cleanup;
+ }
+
+ curdata->lib = cur;
+
+ if ((dlres = l2_dlpool_add(pool, curdata)) < 0) {
+ free(curdata);
+ res = VERSION_EALLOC;
+ goto cleanup;
+ } else if (dlres == 0) {
+ free(curdata);
+ res = VERSION_EDOWNLOAD;
+ goto cleanup;
+ }
+
+ if (tail) {
+ tail->next = curdata;
+ tail = curdata;
+ } else {
+ head = tail = curdata;
+ }
+ }
+
+ if (head != NULL) {
+ if (l2_dlpool_perform(pool) < 0) {
+ CMD_WARN0("Error downloading libraries.");
+ res = VERSION_EDOWNLOAD;
+ } else {
+ CMD_INFO0("Successfully downloaded libraries.");
+ res = VERSION_SUCCESS;
+ }
+ } else {
+ CMD_INFO0("No libraries to download.");
+ res = VERSION_SUCCESS;
+ }
+
+cleanup:
+ for (struct l2_version__library_dl_data *cur = head, *temp; cur; cur = temp) {
+ temp = cur->next;
+
+ if (cur->ofile) {
+ fclose(cur->ofile);
+ if (unlink(cur->lib->fullpath) < 0) {
+ CMD_WARN("Error deleting partially downloaded file %s: %s", cur->lib->fullpath, strerror(errno));
+ }
+ }
+ free(cur);
+ }
+
+ if (pool) l2_dlpool_cleanup(pool);
+ return res;
+}
+
+int l2_version__library_should_download(struct l2_version_library *lib)
+{
+ FILE *lfile = fopen(lib->fullpath, "rb");
+
+ if (!lfile) {
+ if (errno == ENOENT) {
+ return 1;
+ } else {
+ CMD_DEBUG("Failed to open %s for reading: %s", lib->fullpath, strerror(errno));
+ return -1;
+ }
+ }
+
+ int res = l2_version_check_integrity(lfile, lib->has_digest ? &lib->digest : NULL, lib->size);
+ fclose(lfile);
+
+ switch (res) {
+ case 0:
+ CMD_DEBUG("Downloading library %s, the SHA1 or size doesn't match.", lib->url);
+ return 1;
+ case 1:
+ CMD_DEBUG("Not downloading library %s.", lib->url);
+ return 0;
+ default:
+ return res;
+ }
+}
+
+struct l2_version__integrity_data {
+ char *baseurl;
+ struct l2_version_library *lib; /* here for convenience */
+ char digest_hex[L2_SHA1_HEX_STRLEN + 1];
+ size_t len;
+};
+
+size_t l2_version__integ_writecb(char *ptr, size_t size, size_t nmemb, void *user)
+{
+ /* NOTE: this function does not check what it's reading (or anything after the 40th character).
+ * It could be argued that this is a problem (the file may have stuff after the hash), but idc LOL. */
+
+ struct l2_version__integrity_data *integ = user;
+ size_t realsz = size * nmemb;
+ size_t rem = L2_SHA1_HEX_STRLEN - integ->len;
+ if (rem <= 0) return realsz;
+
+ size_t copylen = L2_BADMIN(rem, realsz);
+
+ memcpy(integ->digest_hex, ptr, copylen);
+ integ->len += copylen;
+ return realsz;
+}
+
+int l2_version__integ_dl_init(CURL *curl, void *user)
+{
+ struct l2_version_library *lib = user;
+ char *fullurl;
+ size_t sz;
+
+ L2_ASPRINTF(fullurl, sz, "%s.sha1", lib->url);
+
+ curl_easy_setopt(curl, CURLOPT_URL, fullurl);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, user);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &l2_version__integ_writecb);
+
+ CMD_DEBUG("Downloading signature for library: %s", fullurl);
+
+ return 0;
+}
+
+int l2_version__integ_dl_finish(CURL *curl, CURLcode code, void *user)
+{
+ struct l2_version__integrity_data *integ = user;
+ long res;
+
+ if (code != CURLE_OK) {
+ CMD_WARN("Error downloading integrity (%s): %s", integ->baseurl, curl_easy_strerror(code));
+ return -1; /* vanilla considers this non-fatal */
+ }
+
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res);
+ if ((res / 100) != 2) {
+ CMD_WARN("Error downloading integrity (%s): server returned HTTP %ld", integ->baseurl, res);
+ CMD_WARN0("Proceeding under the assumption that this resource is not checksummed. Integrity will not be checked.");
+ return 0;
+ }
+
+ if (l2_sha1_digest_from_hex(&integ->lib->digest, integ->digest_hex) < 0) {
+ CMD_WARN("Error downloading integrity (%s): %s", integ->baseurl, "invalid sha1 digest returned");
+ return -1; /* vanilla considers this non-fatal (though the comparison will always* fail) */
+ }
+
+ CMD_DEBUG("Retrieved SHA1 information for %s", integ->baseurl);
+ integ->lib->has_digest = true;
+ return 0;
+}
+
+unsigned l2_version__retrieve_library_integ(struct l2_version_library *libs)
+{
+ unsigned res = VERSION_SUCCESS;
+ struct l2_version__integrity_data *integ = NULL;
+ l2_dlpool_t *pool = NULL;
+ size_t nlibs = 0;
+
+ for (struct l2_version_library *cur = libs; cur; cur = cur->next) {
+ if (!cur->has_digest) ++nlibs;
+ }
+
+ if (!nlibs) return VERSION_SUCCESS;
+
+ integ = calloc(nlibs, sizeof(struct l2_version__integrity_data));
+ if (!integ) {
+ res = VERSION_EALLOC;
+ goto cleanup;
+ }
+
+ pool = l2_dlpool_init(&l2_version__integ_dl_init, &l2_version__integ_dl_finish);
+ if (!pool) {
+ res = VERSION_EALLOC;
+ goto cleanup;
+ }
+
+ nlibs = 0;
+ int addres;
+ for (struct l2_version_library *cur = libs; cur; cur = cur->next) {
+ if (cur->has_digest) continue;
+
+ integ[nlibs].lib = cur;
+ integ[nlibs].baseurl = cur->url;
+ if ((addres = l2_dlpool_add(pool, integ + nlibs)) < 0) {
+ res = VERSION_EALLOC;
+ goto cleanup;
+ } else if (addres == 0) {
+ integ[nlibs].lib = NULL; /* signal that this one is invalid */
+ continue;
+ }
+
+ ++nlibs;
+ }
+
+ if (l2_dlpool_perform(pool) < 0) {
+ res = VERSION_EDOWNLOAD;
+ goto cleanup;
+ }
+
+ free(integ);
+ l2_dlpool_cleanup(pool);
+ return VERSION_SUCCESS;
+
+cleanup:
+ free(integ);
+ if (pool) l2_dlpool_cleanup(pool);
+ return res;
+}
+
+unsigned l2_version_download_libraries(json_t *jlibraries, l2_version_feature_match_proc_t *feature_matcher)
+{
+ struct l2_version_library *plib = NULL;
+ unsigned ret = VERSION_EDOWNLOAD;
+ l2_subst_t *sp = NULL;
+
+ if (l2_subst_init(&sp) < 0) {
+ ret = VERSION_EALLOC;
+ goto cleanup;
+ }
+
+ if (l2_subst_add(sp, "arch", L2SU_LAUNCHER_ARCH_BITS) < 0) {
+ ret = VERSION_EALLOC;
+ goto cleanup;
+ }
+
+ ret = l2_version__collect_libraries(jlibraries, sp, &plib, feature_matcher);
+ if (ret != VERSION_SUCCESS) goto cleanup;
+
+ ret = l2_version__retrieve_library_integ(plib);
+ if (ret != VERSION_SUCCESS) {
+ CMD_WARN0("Failed to retrieve integrity information on libraries for which it was missing!");
+ goto cleanup;
+ }
+
+ ret = l2_version__actually_download_libraries(plib);
+ if (ret != VERSION_SUCCESS) {
+ CMD_WARN0("Library downloads failed.");
+ goto cleanup;
+ }
+
+ /* TODO: return libraries */
+
+cleanup:
+ if (sp) l2_subst_free(sp);
+ if (plib) l2_version__free_libraries(plib);
+ return ret;
}
unsigned l2_version__library_get_classifier(json_t *lib, const char **class)
@@ -661,6 +1011,11 @@ unsigned l2_version__gen_one_library(json_t *lib, l2_subst_t *subst, struct l2_v
goto cleanup;
}
+ if (!(retlib->fullpath = l2_launcher_sprintf_alloc("%s/libraries/%s", l2_state.paths.data, retlib->artifactpath))) {
+ res = VERSION_EALLOC;
+ goto cleanup;
+ }
+
if (classifier) {
retlib->extract.extracttype = EXTRACT_OLD;
} else if (has_class) {
@@ -848,6 +1203,7 @@ void l2_version__free_libraries(struct l2_version_library *libs)
next = libs->next;
free(libs->artifactpath);
+ free(libs->fullpath);
free(libs->url);
excludecur = libs->extract.exclude;