aboutsummaryrefslogtreecommitdiffstats
path: root/src/assets.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/assets.c')
-rw-r--r--src/assets.c273
1 files changed, 273 insertions, 0 deletions
diff --git a/src/assets.c b/src/assets.c
index 57624ae..7699d33 100644
--- a/src/assets.c
+++ b/src/assets.c
@@ -1,8 +1,281 @@
#include "assets.h"
+#include "digest/digest.h"
#include "version.h"
#include "l2su.h"
+#include "downloadpool.h"
+#include "macros.h"
+#include "command.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdio.h>
#include <jansson.h>
+#include <curl/curl.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+
+struct l2_assets__download_info {
+ char *url;
+ const char *name;
+ char *fullpath;
+
+ size_t expect_size;
+ l2_sha1_digest_t expect_digest;
+
+ /* initialized in init function */
+ FILE *fp;
+ l2_sha1_state_t digest_state;
+ size_t recv_size;
+
+ char errbuf[CURL_ERROR_SIZE];
+
+ struct l2_assets__download_info *next;
+};
+
+size_t l2_assets__download_asset_dlcb(char *buf, size_t size, size_t nmemb, void *user)
+{
+ struct l2_assets__download_info *info = user;
+ size_t realsz = size * nmemb;
+
+ if (fwrite(buf, size, nmemb, info->fp) < nmemb) {
+ return CURL_WRITEFUNC_ERROR;
+ }
+
+ l2_sha1_update(&info->digest_state, buf, realsz);
+ info->recv_size += realsz;
+ return realsz;
+}
+
+int l2_assets__download_asset_init(CURL *curl, void *user)
+{
+ struct l2_assets__download_info *info = user;
+
+ l2_sha1_init(&info->digest_state);
+
+ if (l2_launcher_mkdir_parents_ex(info->fullpath, 1) < 0) {
+ CMD_WARN("Error creating parent directories for %s (%s): %s", info->fullpath, info->name, strerror(errno));
+ return -1;
+ }
+
+ info->fp = fopen(info->fullpath, "wb");
+ if (!info->fp) {
+ CMD_WARN("Error opening %s (%s) for writing: %s", info->fullpath, info->name, strerror(errno));
+ return -1;
+ }
+
+ CMD_DEBUG("asset url %s", info->url);
+
+ curl_easy_setopt(curl, CURLOPT_URL, info->url);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, info);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &l2_assets__download_asset_dlcb);
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, info->errbuf);
+
+ return 0;
+}
+
+int l2_assets__download_asset_complete(CURL *curl, CURLcode code, void *user)
+{
+ struct l2_assets__download_info *info = user;
+ l2_sha1_digest_t dg;
+
+ fclose(info->fp);
+ info->fp = NULL;
+
+ if (code != CURLE_OK) {
+ CMD_WARN("Failed to download asset %s: %s", info->name, curl_easy_strerror(code));
+ CMD_DEBUG("Info for %s: %s", info->name, info->errbuf);
+ return -1;
+ }
+
+ long rescode;
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rescode);
+ if (rescode / 100 != 2) {
+ CMD_WARN("Failed to download asset %s: server responded with %ld", info->name, rescode);
+ return -1;
+ }
+
+ l2_sha1_finalize(&info->digest_state, &dg);
+ if (l2_sha1_digest_compare(&dg, &info->expect_digest)) {
+ char expstr[L2_SHA1_HEX_STRLEN + 1], gotstr[L2_SHA1_HEX_STRLEN + 1];
+ l2_sha1_digest_to_hex(&info->expect_digest, expstr);
+ l2_sha1_digest_to_hex(&dg, gotstr);
+
+ CMD_WARN("Resource %s SHA1 mismatch! (expected %s, got %s)", info->name, expstr, gotstr);
+ if (unlink(info->fullpath) < 0) {
+ CMD_WARN("Failed to delete resource %s: %s", info->name, strerror(errno));
+ }
+ return -1;
+ }
+
+ if (info->recv_size != info->expect_size) {
+ CMD_WARN("Resource %s has wrong size! (expected %zu bytes, got %zu bytes)", info->name, info->expect_size, info->recv_size);
+ if (unlink(info->fullpath) < 0) {
+ CMD_WARN("Failed to delete resource %s: %s", info->name, strerror(errno));
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+void l2_assets__free_download_info(struct l2_assets__download_info *info)
+{
+ if (info->fp) {
+ fclose(info->fp);
+ if (unlink(info->fullpath) < 0) {
+ CMD_WARN("Failed to delete partially downloaded asset %s (%s): %s", info->name, info->fullpath, strerror(errno));
+ }
+ }
+
+ free(info->url);
+ free(info->fullpath);
+ free(info);
+}
+
+int l2_assets__download_assets(json_t *asset_index, char **path)
+{
+ int res = -1;
+ json_t *objects;
+ int virtual = 0;
+ int map_to_resources = 0;
+ l2_dlpool_t *pool = NULL;
+
+ char *resbasepath = NULL;
+
+ const char *key;
+ json_t *asset;
+ struct l2_assets__download_info *dl_info_head, *dl_info_tail;
+
+ if (json_unpack(asset_index, "{s:o, s?:b, s?:b}", "objects", &objects, "virtual", &virtual, "map_to_resources", &map_to_resources) < 0) {
+ CMD_WARN0("Malformed asset index!");
+ return -1;
+ }
+
+ virtual |= map_to_resources; /* they're the same for our purposes */
+
+ dl_info_head = dl_info_tail = NULL;
+
+ pool = l2_dlpool_init(&l2_assets__download_asset_init, &l2_assets__download_asset_complete);
+ if (!pool) {
+ res = -1;
+ goto cleanup;
+ }
+
+ json_object_foreach(objects, key, asset) {
+ /* key is path */
+
+ const char *digeststr;
+ char urldgpart[L2_SHA1_HEX_STRLEN + 1];
+ json_int_t size;
+ l2_sha1_digest_t digest;
+
+ if (json_unpack(asset, "{s:s, s:I}", "hash", &digeststr, "size", &size) < 0) {
+ CMD_WARN("Malformed asset object %s!", key);
+ res = -1;
+ goto cleanup;
+ }
+
+ if (size <= 0) {
+ CMD_WARN("Asset object %s has nonpositive size (%" JSON_INTEGER_FORMAT ")!", key, size);
+ res = -1;
+ goto cleanup;
+ }
+
+ if ((res = l2_sha1_digest_from_hex(&digest, digeststr)) < 0) {
+ CMD_WARN("Asset object %s has invalid SHA1!", key);
+ res = -1;
+ goto cleanup;
+ }
+
+ l2_sha1_digest_to_hex(&digest, urldgpart);
+ char *path = l2_launcher_sprintf_alloc("%s/assets/objects/%2.2s/%s", l2_state.paths.data, urldgpart, urldgpart);
+ int shoulddl = l2_launcher_should_download(path, &digest, size);
+ if (shoulddl < 0) {
+ free(path);
+ res = -1;
+ goto cleanup;
+ } else if (!shoulddl) {
+ CMD_DEBUG("Not downloading asset %s", key);
+ free(path);
+ continue;
+ }
+
+ struct l2_assets__download_info *curinfo = calloc(1, sizeof(struct l2_assets__download_info));
+ if (!curinfo) {
+ free(path);
+ res = -1;
+ goto cleanup;
+ }
+
+ if (dl_info_tail) {
+ dl_info_tail->next = curinfo;
+ dl_info_tail = curinfo;
+ } else {
+ dl_info_head = dl_info_tail = curinfo;
+ }
+
+ l2_sha1_digest_copy(&curinfo->expect_digest, &digest);
+ curinfo->expect_size = (size_t)size;
+
+ curinfo->url = l2_launcher_sprintf_alloc("%s/%2.2s/%s", L2_URL_RESOURCES_BASE, urldgpart, urldgpart);
+ curinfo->fullpath = path;
+ if (!curinfo->url) {
+ res = -1;
+ goto cleanup;
+ }
+
+ curinfo->name = key;
+
+ if ((res = l2_dlpool_add(pool, curinfo)) <= 0) {
+ res = -1;
+ goto cleanup;
+ }
+ }
+
+ if (dl_info_head) {
+ if (l2_dlpool_perform(pool) <= 0) {
+ res = -1;
+ goto cleanup;
+ }
+ } else {
+ CMD_INFO0("No assets to download.");
+ res = 0;
+ }
+
+ if (virtual) {
+ resbasepath = l2_launcher_sprintf_alloc("%s/assets/virtual", l2_state.paths.data);
+
+ CMD_ERROR0("virtual resources are not yet implemented. please nag figboot");
+ res = -1;
+ goto cleanup;
+ } else {
+ resbasepath = l2_launcher_sprintf_alloc("%s/assets/objects", l2_state.paths.data);
+ }
+
+ if (!resbasepath) {
+ res = -1;
+ goto cleanup;
+ }
+
+ *path = resbasepath;
+ resbasepath = NULL; /* prevent out variable from being freed */
+
+ CMD_INFO0("Successfully downloaded assets.");
+ res = 0;
+
+cleanup:
+ if (resbasepath) free(resbasepath);
+ if (pool) l2_dlpool_cleanup(pool);
+ if (dl_info_head) {
+ for (struct l2_assets__download_info *cur = dl_info_head, *temp; cur; cur = temp) {
+ temp = cur->next;
+ l2_assets__free_download_info(cur);
+ }
+ }
+
+ return res;
+}
int l2_assets__download_index(json_t *version, char **path)
{