aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/command.h3
-rw-r--r--src/digest/sha1.c12
-rw-r--r--src/downloadpool.c177
-rw-r--r--src/downloadpool.h23
-rw-r--r--src/l2su.h1
-rw-r--r--src/launcherutil.c42
-rw-r--r--src/macros.h14
-rw-r--r--src/meson.build2
-rw-r--r--src/version.c394
-rw-r--r--src/version.h1
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 */
diff --git a/src/l2su.h b/src/l2su.h
index 44e9964..1adfc4c 100644
--- a/src/l2su.h
+++ b/src/l2su.h
@@ -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;