aboutsummaryrefslogtreecommitdiffstats
path: root/src/launcherutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/launcherutil.c')
-rw-r--r--src/launcherutil.c176
1 files changed, 176 insertions, 0 deletions
diff --git a/src/launcherutil.c b/src/launcherutil.c
index f7a38fd..2db6238 100644
--- a/src/launcherutil.c
+++ b/src/launcherutil.c
@@ -281,3 +281,179 @@ int l2_json_merge_objects(json_t *j1, json_t *j2)
return 0;
}
+
+int l2_launcher_check_integrity(FILE *fp, const 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;
+
+ if (!digest && sz == 0) return 1;
+
+ 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;
+ }
+}
+
+int l2_launcher_should_download(const char *path, const l2_sha1_digest_t *expectdigest, size_t expectsize)
+{
+ FILE *lfile = fopen(path, "rb");
+
+ if (!lfile) {
+ if (errno == ENOENT) {
+ return 1;
+ } else {
+ CMD_DEBUG("Failed to open %s for reading: %s", path, strerror(errno));
+ return -1;
+ }
+ }
+
+ int res = l2_launcher_check_integrity(lfile, expectdigest, expectsize);
+ fclose(lfile);
+
+ switch (res) {
+ case 0:
+ CMD_DEBUG("SHOULD redownload %s, the SHA1 or size doesn't match.", path);
+ return 1;
+ case 1:
+ CMD_DEBUG("SHOULDN'T redownload %s.", path);
+ return 0;
+ default:
+ return res;
+ }
+}
+
+struct l2_launcher__download_data {
+ l2_sha1_state_t digest_state;
+ size_t recv_size;
+ FILE *fp;
+};
+
+size_t l2_launcher__download_writecb(char *ptr, size_t size, size_t nmemb, void *user)
+{
+ struct l2_launcher__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;
+}
+
+int l2_launcher_download_checksummed(const char *url, const char *pathstr, l2_sha1_digest_t *expect_digest, size_t expect_size)
+{
+ int res = -1;
+ CURL *pc = NULL;
+
+ /* check if we even need to redownload the thing */
+ int rdres = l2_launcher_should_download(pathstr, expect_digest, expect_size);
+ if (rdres < 0) {
+ return -1;
+ } else if (!rdres) {
+ CMD_DEBUG("Not downloading %s", pathstr);
+ return 0;
+ }
+
+ if (!url) {
+ CMD_WARN("Cannot redownload %s, even though I need to! (no URL specified)", pathstr);
+ return -1;
+ }
+
+ /* redownload the file */
+ struct l2_launcher__download_data dldata;
+ char errbuf[CURL_ERROR_SIZE];
+
+ memset(&dldata, 0, sizeof(struct l2_launcher__download_data));
+ memset(&errbuf, 0, sizeof(errbuf));
+
+ l2_sha1_init(&dldata.digest_state);
+
+ if (l2_launcher_mkdir_parents_ex(pathstr, 1) < 0) {
+ goto cleanup;
+ }
+
+ dldata.fp = fopen(pathstr, "wb");
+ if (!dldata.fp) {
+ CMD_WARN("Failed to open %s for writing: %s", pathstr, strerror(errno));
+ goto cleanup;
+ }
+
+ pc = curl_easy_init();
+ if (!pc) {
+ 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_launcher__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: %s: %s", pathstr, curl_easy_strerror(cres), errbuf);
+ 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 (expect_digest && 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 has wrong digest! (expected: %s, got: %s)", pathstr, expstr, gotstr);
+ if (unlink(pathstr) < 0) {
+ CMD_WARN("Failed to delete %s: %s", pathstr, strerror(errno));
+ }
+ goto cleanup;
+ }
+
+ if (expect_size > 0 && expect_size != dldata.recv_size) {
+ CMD_WARN("Downloaded %s has wrong size! (expected: %zu bytes, got: %zu bytes)", pathstr, expect_size, dldata.recv_size);
+ if (unlink(pathstr) < 0) {
+ CMD_WARN("Failed to delete %s: %s", pathstr, strerror(errno));
+ }
+ goto cleanup;
+ }
+
+ CMD_INFO("Downloaded %s successfully.", pathstr);
+ curl_easy_cleanup(pc);
+ return 1;
+
+cleanup:
+ if (dldata.fp) fclose(dldata.fp);
+ if (pc) curl_easy_cleanup(pc);
+ return res;
+}