diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/command.h | 3 | ||||
| -rw-r--r-- | src/digest/sha1.c | 12 | ||||
| -rw-r--r-- | src/downloadpool.c | 177 | ||||
| -rw-r--r-- | src/downloadpool.h | 23 | ||||
| -rw-r--r-- | src/l2su.h | 1 | ||||
| -rw-r--r-- | src/launcherutil.c | 42 | ||||
| -rw-r--r-- | src/macros.h | 14 | ||||
| -rw-r--r-- | src/meson.build | 2 | ||||
| -rw-r--r-- | src/version.c | 394 | ||||
| -rw-r--r-- | src/version.h | 1 |
10 files changed, 620 insertions, 49 deletions
diff --git a/src/command.h b/src/command.h index 76a9166..abd7d55 100644 --- a/src/command.h +++ b/src/command.h @@ -54,6 +54,9 @@ enum { #define CMD_WARN(_s, ...) CMD_MSG("warn", _s, __VA_ARGS__) #define CMD_WARN0(_s) CMD_MSG0("warn", _s) +#define CMD_INFO(_s, ...) CMD_MSG("info", _s, __VA_ARGS__) +#define CMD_INFO0(_s) CMD_MSG0("info", _s) + #ifdef NDEBUG #define CMD_DEBUG(_s, ...) #define CMD_DEBUG0(_s) diff --git a/src/digest/sha1.c b/src/digest/sha1.c index e563abb..b8f697e 100644 --- a/src/digest/sha1.c +++ b/src/digest/sha1.c @@ -89,15 +89,9 @@ void l2_sha1_update(l2_sha1_state_t *st, const void *data, size_t sz) const uint8_t *dbytes = data; size_t rem; st->totallen += sz; - - if (sz < L2_SHA1_BLOCKLEN - st->nchunk) { - memcpy(st->chunk + st->nchunk, dbytes, sz); - st->nchunk += sz; - return; - } while (sz >= (rem = (L2_SHA1_BLOCKLEN - st->nchunk))) { - memcpy(st->chunk, dbytes, rem); + memcpy(st->chunk + st->nchunk, dbytes, rem); l2_sha1__update_int(st); st->nchunk = 0; @@ -106,8 +100,8 @@ void l2_sha1_update(l2_sha1_state_t *st, const void *data, size_t sz) } if (sz > 0) { - memcpy(st->chunk, dbytes, sz); - st->nchunk = sz; + memcpy(st->chunk + st->nchunk, dbytes, sz); + st->nchunk += sz; } } diff --git a/src/downloadpool.c b/src/downloadpool.c new file mode 100644 index 0000000..5da2905 --- /dev/null +++ b/src/downloadpool.c @@ -0,0 +1,177 @@ +#include "downloadpool.h" +#include "macros.h" +#include <curl/curl.h> +#include <curl/multi.h> +#include <stdbool.h> +#include <stdlib.h> + +struct l2_dlpool__handle { + CURL *easy; + void *user; + + struct l2_dlpool__handle *next; + struct l2_dlpool__handle *prev; +}; + +struct l2_dlpool__pool_tag { + CURLM *multi; + + struct l2_dlpool__handle *head, *tail; + + l2_dlpool_download_init_proc_t *initproc; + l2_dlpool_one_downloaded_proc_t *dlproc; +}; + +l2_dlpool_t *l2_dlpool_init(l2_dlpool_download_init_proc_t *init, l2_dlpool_one_downloaded_proc_t *dlproc) +{ + l2_dlpool_t *ret = calloc(1, sizeof(l2_dlpool_t)); + if (!ret) return NULL; + + ret->multi = curl_multi_init(); + if (!ret->multi) { + goto cleanup; + } + + ret->initproc = init; + ret->dlproc = dlproc; + + return ret; + +cleanup: + if (ret) { + if (ret->multi) curl_multi_cleanup(ret->multi); + } + free(ret); + return NULL; +} + +int l2_dlpool_add(l2_dlpool_t *pool, void *user) +{ + int res = -1; + struct l2_dlpool__handle *newhandle = calloc(1, sizeof(struct l2_dlpool__handle)); + if (!newhandle) return -1; + + newhandle->easy = curl_easy_init(); + if (!newhandle->easy) goto cleanup; + + curl_easy_setopt(newhandle->easy, CURLOPT_USERAGENT, L2_USER_AGENT); + + if ((*pool->initproc)(newhandle->easy, user) < 0) { + res = 0; + goto cleanup; + } + + newhandle->user = user; + + CURLMcode mc = curl_multi_add_handle(pool->multi, newhandle->easy); + if (mc != CURLM_OK) { + if (mc == CURLM_OUT_OF_MEMORY) { + fputs("CURL out of memory. (You are doomed.)\n", stderr); + abort(); + } + goto cleanup; + } + + if (pool->tail) { + pool->tail->next = newhandle; + newhandle->prev = pool->tail; + pool->tail = newhandle; + } else { + pool->head = pool->tail = newhandle; + } + + return 1; + +cleanup: + if (newhandle) { + if (newhandle->easy) curl_easy_cleanup(newhandle->easy); + } + free(newhandle); + return res; +} + +int l2_dlpool__download_complete(l2_dlpool_t *pool, CURL *easy, CURLcode code) +{ + struct l2_dlpool__handle *handle = NULL; + int uret = 0; + for (struct l2_dlpool__handle *cur = pool->head; cur; cur = cur->next) { + if (cur->easy == easy) { + handle = cur; + continue; + } + } + + if (handle) { + uret = (*pool->dlproc)(easy, code, handle->user); + + if (handle->prev) { + handle->prev->next = handle->next; + } else { + pool->head = handle->next; + } + + if (handle->next) { + handle->next->prev = handle->prev; + } else { + pool->tail = handle->prev; + } + + free(handle); + handle = NULL; + } + + curl_multi_remove_handle(pool->multi, easy); + curl_easy_cleanup(easy); + + if (uret < 0) return -1; + return 0; +} + +int l2_dlpool_perform(l2_dlpool_t *pool) +{ + CURLMcode code; + int running = 0; + int queuelen = 0; + CURLMsg *msg; + + do { + code = curl_multi_perform(pool->multi, &running); + if (code != CURLM_OK) { + return -1; + } + + while ((msg = curl_multi_info_read(pool->multi, &queuelen))) { + if (msg->msg == CURLMSG_DONE) { + if (l2_dlpool__download_complete(pool, msg->easy_handle, msg->data.result) < 0) return -1; + } + } + + code = curl_multi_poll(pool->multi, NULL, 0, 1000, NULL); + if (code != CURLM_OK) { + return -1; + } + } while (running); + + return 0; +} + +void l2_dlpool_cleanup(l2_dlpool_t *pool) +{ + if (!pool) return; + + /* assumes pool->multi is valid (API must not return a handle where this isn't the case) */ + + /* curl docs say to remove handles BEFORE ANY HANDLES ARE CLEANED UP */ + for (struct l2_dlpool__handle *cur = pool->head; cur; cur = cur->next) { + curl_multi_remove_handle(pool->multi, cur->easy); /* assume it succeeds (if it fails, too bad we tried LOL) */ + } + + for (struct l2_dlpool__handle *cur = pool->head, *temp; cur; cur = temp) { + temp = cur->next; + curl_easy_cleanup(cur->easy); + free(cur); + } + + curl_multi_cleanup(pool->multi); + free(pool); +} diff --git a/src/downloadpool.h b/src/downloadpool.h new file mode 100644 index 0000000..480fc4f --- /dev/null +++ b/src/downloadpool.h @@ -0,0 +1,23 @@ +#ifndef L2SU_DOWNLOADPOOL_H_INCLUDED +#define L2SU_DOWNLOADPOOL_H_INCLUDED + +#include <curl/curl.h> + +typedef struct l2_dlpool__pool_tag l2_dlpool_t; + +/* returns <0 for "error" (this handle is not used) + * returns 0 for "ok I have made my changes" */ +typedef int (l2_dlpool_download_init_proc_t)(CURL * /*easy*/, void * /*user*/); + +/* returns <0 for "give up on downloading everything (error)" + * returns anything else for "download succeeded (proceed)" */ +typedef int (l2_dlpool_one_downloaded_proc_t)(CURL * /*easy*/, CURLcode /*code*/, void * /*user*/); + +l2_dlpool_t *l2_dlpool_init(l2_dlpool_download_init_proc_t *init, l2_dlpool_one_downloaded_proc_t *dlproc); + +/* returns >0 for a successful addition, 0 for aborted (init proc returned <0), <0 for error */ +int l2_dlpool_add(l2_dlpool_t *pool, void *user); +int l2_dlpool_perform(l2_dlpool_t *pool); +void l2_dlpool_cleanup(l2_dlpool_t *pool); + +#endif /* include guard */ @@ -43,6 +43,7 @@ char *l2_launcher_find_data_path(void); int l2_launcher_open_config(const char *path, int flags, mode_t mode); int l2_launcher_mkdir_parents(const char *path); +int l2_launcher_mkdir_parents_ex(const char *path, unsigned ignore); char *l2_launcher_parse_iso_time(const char *str, struct tm *ts); diff --git a/src/launcherutil.c b/src/launcherutil.c index b31b2df..f7a38fd 100644 --- a/src/launcherutil.c +++ b/src/launcherutil.c @@ -12,6 +12,7 @@ #include <errno.h> #include <sys/stat.h> #include <stdarg.h> +#include <alloca.h> /* handcoded string functions * @@ -44,13 +45,13 @@ char *l2_launcher_sprintf_alloc(const char *fmt, ...) size_t len = vsnprintf(NULL, 0, fmt, pva); va_end(pva); - char *ret = calloc(len, sizeof(char)); + char *ret = calloc(len + 1, sizeof(char)); if (!ret) { return ret; } va_start(pva, fmt); - vsnprintf(ret, len, fmt, pva); + vsnprintf(ret, len + 1, fmt, pva); va_end(pva); return ret; } @@ -129,16 +130,28 @@ int l2_launcher_open_config(const char *path, int flags, mode_t mode) return instfd; } -/* NOTE: There's no portable (or otherwise - see open(2) BUGS) way to do this without race conditions. */ int l2_launcher_mkdir_parents(const char *path) { + return l2_launcher_mkdir_parents_ex(path, 0); +} + +/* NOTE: There's no portable (or otherwise - see open(2) BUGS) way to do this without race conditions. */ +int l2_launcher_mkdir_parents_ex(const char *path, unsigned ignore) +{ if (*path != '/') return -1; - int reserrno = 0; + char *pathbuf; + size_t pathlen; + L2_ASTRDUP(pathbuf, pathlen, path); - char *pathbuf = strdup(path); char *pcurelem = pathbuf; - if (!pathbuf) return -1; + + for (char *cur = pathbuf + pathlen; ignore && cur != pathbuf; --cur) { + if (*cur == '/') { + --ignore; + *cur = '\0'; + } + } struct stat stbuf = { 0 }; @@ -155,29 +168,18 @@ int l2_launcher_mkdir_parents(const char *path) if (errno == EEXIST) { /* racy: stat the file and continue if it is a directory */ if (stat(pathbuf, &stbuf) < 0) { - reserrno = errno; - goto mdcleanup; + return -1; } if (!S_ISDIR(stbuf.st_mode)) { - reserrno = ENOTDIR; - goto mdcleanup; + return -1; } } else { - reserrno = errno; - goto mdcleanup; + return -1; } } } while (pcurelem); -mdcleanup: - - free(pathbuf); - if (reserrno != 0) { - errno = reserrno; - return -1; - } - return 0; } diff --git a/src/macros.h b/src/macros.h index 71aa137..f15cdd7 100644 --- a/src/macros.h +++ b/src/macros.h @@ -25,6 +25,20 @@ snprintf(_var, _tmp + 1, _fmt, __VA_ARGS__); \ } while (0) +/* astrdup is a gnu extension */ +/* _var: the variable to which the duplicated string is assigned + * _sz: the length of the string duplicated (contractually) + * _in: the NUL-terminated string to duplicate */ +#define L2_ASTRDUP(_var, _sz, _in) do { \ + _sz = strlen(_in); \ + _var = alloca(_sz + 1); \ + memcpy(_var, _in, _sz); \ + (_var)[(_sz)] = '\0'; \ +} while (0) + +#define L2_BADMIN(_v1, _v2) (((_v1) < (_v2)) ? (_v1) : (_v2)) +#define L2_BADMAX(_v1, _v2) (((_v1) > (_v2)) ? (_v1) : (_v2)) + #define L2_USER_AGENT PROJECT_NAME "/0.1.0 <[email protected]>" #define L2_URL_META_BASE "https://piston-meta.mojang.com" diff --git a/src/meson.build b/src/meson.build index fea440e..1f7d40f 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') +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') 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 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; diff --git a/src/version.h b/src/version.h index c3448eb..8ba428f 100644 --- a/src/version.h +++ b/src/version.h @@ -57,6 +57,7 @@ struct l2_version_download { struct l2_version_library { char *url; char *artifactpath; + char *fullpath; l2_sha1_digest_t digest; bool has_digest; |
