diff options
Diffstat (limited to 'src/launcherutil.c')
| -rw-r--r-- | src/launcherutil.c | 176 |
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; +} |
