aboutsummaryrefslogtreecommitdiffstats
path: root/src/version.c
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2023-12-31 02:39:11 -0600
committerLibravatar bigfoot547 <[email protected]>2023-12-31 02:39:11 -0600
commit7c95357610f722db4ee1f684086fb4478e3875c3 (patch)
treea9464f42f256467eaac96ef29ec75acfcbb7b046 /src/version.c
parentadd a little version stuff (diff)
downloads versions now
Diffstat (limited to 'src/version.c')
-rw-r--r--src/version.c204
1 files changed, 203 insertions, 1 deletions
diff --git a/src/version.c b/src/version.c
index d8f264c..a5e63a0 100644
--- a/src/version.c
+++ b/src/version.c
@@ -21,6 +21,8 @@ const char *const l2_version__messages[] = {
"Allocation failed",
"OS error",
"Error downloading version manifest",
+ "Version not found",
+ "Max recursion depth exceeded",
NULL
};
@@ -46,12 +48,183 @@ done:
return r;
}
-unsigned l2_version_load_local(const char *name)
+void l2_version__load_local(const char *id, size_t expectsz, l2_sha1_digest_t *digest, json_t **ojson);
+bool l2_version__download_remote(struct l2_version_remote *remote, json_t **ojson);
+
+unsigned l2_version__load_or_download(const char *name, json_t **ojs)
{
+ json_t *js = NULL;
+ struct l2_version_remote *remote = NULL;
+ for (struct l2_version_remote *cur = l2_state.ver_remote_head; cur; cur = cur->next) {
+ if (!strcmp(cur->id, name)) {
+ remote = cur;
+ break;
+ }
+ }
+
+ if (remote) {
+ l2_version__load_local(remote->id, 0, &remote->sha1, &js);
+ if (!js) {
+ CMD_DEBUG0("local version not found, downloading");
+ if (!l2_version__download_remote(remote, &js) || !js)
+ return VERSION_EDOWNLOAD;
+ }
+ } else {
+ l2_version__load_local(remote->id, 0, NULL, &js);
+ if (!js) return VERSION_ENOTFOUND;
+ }
+
+ *ojs = js;
return VERSION_SUCCESS;
}
+unsigned l2_version__load_recursive(const char *name, json_t *target, int depth)
+{
+ json_t *other = NULL;
+ json_t *inherit;
+
+ if (depth <= 0) return VERSION_ERECURSE;
+
+ unsigned res = l2_version__load_or_download(name, &other);
+
+ if (res != VERSION_SUCCESS) return res;
+ l2_json_merge_objects(target, other);
+
+ inherit = json_object_get(other, "inheritsFrom");
+ if (!json_is_string(inherit)) {
+ json_decref(other);
+ return VERSION_SUCCESS;
+ }
+
+ json_decref(other);
+ return l2_version__load_recursive(json_string_value(inherit), target, depth - 1);
+}
+
+unsigned l2_version_load_local(const char *name, json_t **ojson)
+{
+ json_t *js = json_object();
+ if (!js) return VERSION_EJSON;
+
+ unsigned res = l2_version__load_recursive(name, js, 10);
+ *ojson = js;
+ return res;
+}
+
+void l2_version__load_local(const char *id, size_t expectsz, l2_sha1_digest_t *digest, json_t **ojson)
+{
+ char *path;
+ size_t temp;
+ L2_ASPRINTF(path, temp, "%s/versions/%s/%s.json", l2_state.paths.data, id, id);
+
+ FILE *ver = fopen(path, "r");
+ if (!ver) return;
+
+ int res = l2_version_check_integrity(ver, digest, expectsz);
+ if (res < 0) {
+ CMD_WARN("Error checking version file integrity for %s", id);
+ return;
+ } else if (res == 0) {
+ CMD_DEBUG("Version %s (%s) has bad integrity", id, path);
+ return;
+ }
+
+ json_t *js;
+ json_error_t jerr;
+
+ rewind(ver);
+ js = json_loadf(ver, JSON_REJECT_DUPLICATES, &jerr);
+ if (!js) {
+ CMD_WARN("JSON error loading %s: %s", id, jerr.text);
+ return;
+ }
+
+ *ojson = js;
+}
+
+/* downloads a remote version */
+bool l2_version__download_remote(struct l2_version_remote *remote, json_t **ojson)
+{
+ char *dirname;
+ char *fname;
+ size_t temp;
+ FILE *save = NULL;
+ json_t *js = NULL;
+ L2_ASPRINTF(dirname, temp, "%s/versions/%s", l2_state.paths.data, remote->id);
+ L2_ASPRINTF(fname, temp, "%s/%s.json", dirname, remote->id);
+
+ if (l2_launcher_mkdir_parents(dirname) < 0) {
+ CMD_ERROR("Could not create directory for %s: %s", remote->id, strerror(errno));
+ return false;
+ }
+
+ CURL *cu = curl_easy_init();
+ l2_sha1_state_t st;
+ l2_sha1_digest_t dig;
+ json_error_t err;
+ if (!cu) return false;
+
+ void *data = NULL;
+ char errbuf[CURL_ERROR_SIZE];
+ size_t sz;
+
+ memset(errbuf, 0, CURL_ERROR_SIZE * sizeof(char));
+
+ curl_easy_setopt(cu, CURLOPT_ERRORBUFFER, errbuf);
+ CURLcode code = l2_launcher_download(cu, remote->url, &data, &sz);
+
+ if (code != CURLE_OK) {
+ CMD_WARN("Error downloading version %s: %s: %s", remote->id, curl_easy_strerror(code), errbuf);
+ goto cleanup;
+ }
+
+ l2_sha1_init(&st);
+ l2_sha1_update(&st, data, sz);
+ l2_sha1_finalize(&st, &dig);
+
+ if (l2_sha1_digest_compare(&dig, &remote->sha1)) {
+ char d1[L2_SHA1_DIGESTLEN + 1];
+ char d2[L2_SHA1_DIGESTLEN + 1];
+
+ l2_sha1_digest_to_hex(&dig, d1);
+ l2_sha1_digest_to_hex(&remote->sha1, d2);
+
+ CMD_WARN("Downloaded file for %s has incorrect hash: expected %s, got %s!", remote->id, d2, d1);
+ goto cleanup;
+ }
+
+ js = json_loadb(data, sz, JSON_REJECT_DUPLICATES, &err);
+ if (!js) {
+ CMD_WARN("JSON parse error reading version %s: %s", remote->id, err.text);
+ goto cleanup;
+ }
+
+ save = fopen(fname, "w");
+ if (!save) {
+ CMD_WARN("Error opening version file %s", fname);
+ goto cleanup;
+ }
+
+ if (fwrite(data, sz, 1, save) < 1) {
+ CMD_WARN("Error saving version info to %s", fname);
+ goto cleanup;
+ }
+
+ *ojson = js;
+
+ curl_easy_cleanup(cu);
+ fclose(save);
+ free(data);
+ return true;
+
+cleanup:
+ curl_easy_cleanup(cu);
+ free(data);
+ if (save) fclose(save);
+ if (js) json_decref(js);
+ return false;
+}
+
char *l2_version__read_etag(time_t *lastmod)
{
size_t dpathlen = strlen(l2_state.paths.data);
@@ -386,3 +559,32 @@ cleanup:
return res;
}
+
+int l2_version_check_integrity(FILE *fp, 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;
+
+ 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;
+ }
+}