aboutsummaryrefslogtreecommitdiffstats
path: root/src/version.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/version.c')
-rw-r--r--src/version.c228
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;
+}