diff options
| author | 2023-12-28 22:01:38 -0600 | |
|---|---|---|
| committer | 2023-12-28 22:01:38 -0600 | |
| commit | 56ae85a352d8909014240e69290dfe69d747210e (patch) | |
| tree | b472f6430c8a80e99aea71e5ce7540f508138fbf /src | |
| parent | [wip] version stuff (diff) | |
download (but not parse) version manifest
Diffstat (limited to 'src')
| -rw-r--r-- | src/l2su.h | 2 | ||||
| -rw-r--r-- | src/launcherutil.c | 11 | ||||
| -rw-r--r-- | src/version.c | 208 | ||||
| -rw-r--r-- | src/version.h | 2 |
4 files changed, 139 insertions, 84 deletions
@@ -53,6 +53,6 @@ void l2_launcher_download_cleanup(struct l2_dlbuf *buf); //extern size_t (*const l2_dlcb)(char *, size_t, size_t, void *); extern const curl_write_callback l2_dlcb; -int l2_launcher_download(CURL *cd, const char *url, void **odata, size_t *osize); +CURLcode l2_launcher_download(CURL *cd, const char *url, void **odata, size_t *osize); #endif /* include guard */ diff --git a/src/launcherutil.c b/src/launcherutil.c index f73428a..446bff9 100644 --- a/src/launcherutil.c +++ b/src/launcherutil.c @@ -176,7 +176,7 @@ size_t l2_launcher__download_callback(char *data, size_t size, size_t nmemb, voi if (buf->size + realsz > buf->capacity) { size_t newcap = 16; - while (newcap && buf->size + realsz < newcap) + while (newcap && buf->size + realsz > newcap) newcap <<= 1; if (!newcap) return CURLE_WRITE_ERROR; @@ -212,9 +212,10 @@ void l2_launcher_download_cleanup(struct l2_dlbuf *buf) const curl_write_callback l2_dlcb = &l2_launcher__download_callback; -int l2_launcher_download(CURL *cd, const char *url, void **odata, size_t *osize) +CURLcode l2_launcher_download(CURL *cd, const char *url, void **odata, size_t *osize) { struct l2_dlbuf db; + CURLcode code; l2_launcher_download_init(&db); @@ -222,13 +223,13 @@ int l2_launcher_download(CURL *cd, const char *url, void **odata, size_t *osize) curl_easy_setopt(cd, CURLOPT_WRITEDATA, &db); curl_easy_setopt(cd, CURLOPT_WRITEFUNCTION, l2_dlcb); - if (curl_easy_perform(cd) != CURLE_OK) { + if ((code = curl_easy_perform(cd)) != CURLE_OK) { l2_launcher_download_cleanup(&db); - return -1; + return code; } *odata = l2_launcher_download_finalize(&db, osize); l2_launcher_download_cleanup(&db); /* not strictly necessary but ok */ - return 0; + return CURLE_OK; } diff --git a/src/version.c b/src/version.c index b442f85..1f7911b 100644 --- a/src/version.c +++ b/src/version.c @@ -6,14 +6,13 @@ #include "command.h" #include <curl/curl.h> -#include <curl/easy.h> -#include <curl/header.h> #include <jansson.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <alloca.h> +#include <unistd.h> const char *const l2_version__messages[] = { "Success", @@ -21,15 +20,15 @@ const char *const l2_version__messages[] = { "Version manifest JSON error", "Allocation failed", "OS error", - "An unspecified error has occurred", + "Error downloading version manifest", NULL }; #define L2_VERSION_MANIFEST_ETAG_PATH "/.l2_vermanifest_etag" -#define L2_VERSION_MANIFEST_PATH "version_manifest_v2.json" +#define L2_VERSION_MANIFEST_PATH "/version_manifest_v2.json" #define L2_VERSION_MANIFEST_EXP_TIME ((time_t)120) -unsigned l2_version__download_manifest(json_t **out_json); +unsigned l2_version__load_manifest(json_t **out_json); unsigned l2_version__load_all_from_json(json_t *json); unsigned l2_version__add_remote(json_t *obj); @@ -37,7 +36,9 @@ unsigned l2_version__add_remote(json_t *obj); unsigned l2_version_load_remote(void) { json_t *vers; - return l2_version__download_manifest(&vers); + unsigned r = l2_version__load_manifest(&vers); + json_decref(vers); + return r; } unsigned l2_version_load_local(void) @@ -57,9 +58,15 @@ char *l2_version__read_etag(time_t *lastmod) uint32_t buf; uint64_t ts; - if (!etagfp) return NULL; - if (fread(&buf, sizeof(uint32_t), 1, etagfp) < sizeof(uint32_t)) goto cleanup; - if (fread(&ts, sizeof(uint64_t), 1, etagfp) < sizeof(uint64_t)) goto cleanup; + CMD_DEBUG0("Reading etag file"); + + if (!etagfp) { + CMD_DEBUG0("Failed to open etag file"); + return NULL; + } + + if (fread(&buf, sizeof(uint32_t), 1, etagfp) < 1) goto cleanup; + if (fread(&ts, sizeof(uint64_t), 1, etagfp) < 1) goto cleanup; buf = l2_betoh32(buf); ts = l2_betoh64(ts); @@ -74,6 +81,8 @@ char *l2_version__read_etag(time_t *lastmod) fclose(etagfp); + CMD_DEBUG("Etag read: %s", etag); + return etag; cleanup: @@ -90,20 +99,33 @@ void l2_version__write_etag(time_t modtime, const char *etag) L2_ASTRCAT2(etagpath, l2_state.paths.data, dpathlen, L2_VERSION_MANIFEST_ETAG_PATH, L2_CSTRLEN(L2_VERSION_MANIFEST_ETAG_PATH)); - uint32_t etaglen = l2_betoh32((uint32_t)strlen(etag)); - uint64_t mt_write = l2_betoh64((uint64_t)modtime); + size_t etaglen = strlen(etag); + uint32_t etaglenbe = l2_htobe32((uint32_t)etaglen); + uint64_t mt_write = l2_htobe64((uint64_t)modtime); FILE *etagfp = fopen(etagpath, "wb"); if (!etagfp) return; - if (fwrite(&etaglen, sizeof(uint32_t), 1, etagfp) < sizeof(uint32_t)) goto cleanup; - if (fwrite(&mt_write, sizeof(uint64_t), 1, etagfp) < sizeof(uint64_t)) goto cleanup; + if (fwrite(&etaglenbe, sizeof(uint32_t), 1, etagfp) < 1) goto cleanup; + if (fwrite(&mt_write, sizeof(uint64_t), 1, etagfp) < 1) goto cleanup; if (fwrite(etag, 1, etaglen, etagfp) < etaglen) goto cleanup; cleanup: fclose(etagfp); } +void l2_version__delete_etag(void) +{ + size_t dpathlen = strlen(l2_state.paths.data); + char *etagpath; + + L2_ASTRCAT2(etagpath, l2_state.paths.data, dpathlen, L2_VERSION_MANIFEST_ETAG_PATH, L2_CSTRLEN(L2_VERSION_MANIFEST_ETAG_PATH)); + + if (unlink(etagpath) < 0) { + CMD_WARN("Failed to delete etag: %s", strerror(errno)); + } +} + int l2_version__read_manifest_file(json_t **manifest) { size_t dpathlen = strlen(l2_state.paths.data); @@ -114,7 +136,7 @@ int l2_version__read_manifest_file(json_t **manifest) json_error_t err; json_t *man = json_load_file(manpath, JSON_REJECT_DUPLICATES, &err); if (!man) { - CMD_WARN("Error loading manifest file: %s\n", err.text); + CMD_WARN("Error loading version manifest file: %s", err.text); return -1; } @@ -122,105 +144,137 @@ int l2_version__read_manifest_file(json_t **manifest) return 0; } -#if 0 -unsigned l2_version__download_manifest(json_t **out_versions) +int l2_version__write_manifest_file(void *data, size_t d) { - unsigned ret = VERSION_SUCCESS; - CURL *pc = NULL; - FILE *outfile = NULL; - void *verdata = NULL; - int en; + size_t dpathlen = strlen(l2_state.paths.data); + char *manpath; - struct curl_slist *headers = NULL; + L2_ASTRCAT2(manpath, l2_state.paths.data, dpathlen, L2_VERSION_MANIFEST_PATH, L2_CSTRLEN(L2_VERSION_MANIFEST_PATH)); - time_t lastmod; - char *etag = l2_version__read_etag(&lastmod); + FILE* fp = fopen(manpath, "w"); + if (!fp) return -2; - if (etag) { - if (lastmod + L2_VERSION_MANIFEST_EXP_TIME > time(NULL)) { - /* no need to redownload */ - *out_versions = NULL; - return VERSION_SUCCESS; + if (fwrite(data, 1, d, fp) < d) { + fclose(fp); + return -1; + } + + fclose(fp); + return 0; +} + +unsigned l2_version__load_manifest(json_t **omanifest) +{ + time_t lmod; + char *etag = l2_version__read_etag(&lmod); + time_t now = time(NULL); + + CMD_DEBUG("etag: %s, time: %jd", etag ? etag : "(null)", etag ? (uintmax_t)(now - lmod) : UINTMAX_C(0)); + if (etag && now - lmod < L2_VERSION_MANIFEST_EXP_TIME) { + CMD_DEBUG0("Not downloading (cached)."); + if (l2_version__read_manifest_file(omanifest) == 0) { + free(etag); + return VERSION_SUCCESS; /* if it failed, redownload the file */ } } - pc = curl_easy_init(); - struct l2_dlbuf dlbuf; - if (!pc) return VERSION_EUNSPEC; + CMD_DEBUG0("Downloading"); + CURL *cu = curl_easy_init(); + struct curl_slist *headers = NULL; + CURLcode cres; + long httpres; + unsigned retval = VERSION_EDOWNLOAD; + + char ebuf[CURL_ERROR_SIZE]; /* assuming CURL_ERROR_SIZE is small enough to fit on the stack... */ + memset(ebuf, 0, sizeof(ebuf)); + + void *data = NULL; + size_t dlen; - l2_launcher_download_init(&dlbuf); + if (!cu) { + return VERSION_EDOWNLOAD; + } if (etag) { + char *headerstr; size_t etaglen = strlen(etag); - char *etagheader; +#define H_INM "If-None-Match: " + L2_ASTRCAT2(headerstr, H_INM, L2_CSTRLEN(H_INM), etag, etaglen); +#undef H_INM -#define H_IFNONEMATCH "If-None-Match: " - L2_ASTRCAT2(etagheader, H_IFNONEMATCH, L2_CSTRLEN(H_IFNONEMATCH), etag, etaglen); -#undef H_IFNONEMATCH - - headers = curl_slist_append(NULL, etagheader); + headers = curl_slist_append(NULL, headerstr); if (!headers) { - ret = VERSION_EUNSPEC; + retval = VERSION_EDOWNLOAD; goto cleanup; } - curl_easy_setopt(pc, CURLOPT_HTTPHEADER, headers); + CMD_DEBUG("Adding etag header %s", etag); + curl_easy_setopt(cu, CURLOPT_HTTPHEADER, headers); } - curl_easy_setopt(pc, CURLOPT_URL, L2_URL_META_VERSION_MANIFEST); - curl_easy_setopt(pc, CURLOPT_WRITEFUNCTION, l2_dlcb); - curl_easy_setopt(pc, CURLOPT_WRITEDATA, &dlbuf); + curl_easy_setopt(cu, CURLOPT_ERRORBUFFER, ebuf); - if (curl_easy_perform(pc) != CURLE_OK) { - /* TODO: set error string */ - ret = VERSION_EUNSPEC; + if ((cres = l2_launcher_download(cu, L2_URL_META_VERSION_MANIFEST, &data, &dlen)) != CURLE_OK) { + CMD_ERROR("Error downloading version manifest: %s: %s", curl_easy_strerror(cres), ebuf); goto cleanup; } - long rescode = 0; - curl_easy_getinfo(pc, CURLINFO_RESPONSE_CODE, &rescode); - if (rescode == 200) { - char *mfpath; - size_t datalen; - verdata = l2_launcher_download_finalize(&dlbuf, &datalen); - - L2_ASTRCAT2(mfpath, l2_state.paths.data, pathlen, L2_VERSION_MANIFEST_PATH, L2_CSTRLEN(L2_VERSION_MANIFEST_PATH)); + curl_easy_getinfo(cu, CURLINFO_RESPONSE_CODE, &httpres); + if (httpres == 200) { + CMD_DEBUG0("manifest download OK"); + /* yay we're good and *data contains our info */ + if (l2_version__write_manifest_file(data, dlen) < 0) { + CMD_WARN0("Error writing version manifest"); + } else { + struct curl_header *outh; + if (curl_easy_header(cu, "etag", 0, CURLH_HEADER, 0, &outh) == CURLHE_OK) { + l2_version__write_etag(time(NULL), outh->value); + CMD_DEBUG("New etag returned: %s", outh->value); + } + } - outfile = fopen(mfpath, "w"); - if (!outfile) { - ret = VERSION_ERRNO; + json_error_t err; + json_t *js = json_loadb(data, dlen, JSON_REJECT_DUPLICATES, &err); + if (!js) { + CMD_ERROR("Failed to parse version manifest: %s", err.text); + retval = VERSION_EJSON; goto cleanup; } - if (fwrite(verdata, 1, datalen, outfile) < datalen) { - ret = VERSION_EUNSPEC; + *omanifest = js; + } else if (httpres == 304) { /* not modified */ + CMD_DEBUG0("manifest download not modified"); + if (l2_version__read_manifest_file(omanifest) < 0) { + CMD_WARN0("Could not read cached version manifest. Please try again."); + l2_version__delete_etag(); + + retval = VERSION_EJSON; goto cleanup; } - fclose(outfile); - outfile = NULL; + /* write the new time because we know the file is good (at least valid json) */ + CMD_DEBUG0("writing new etag (read success)"); + l2_version__write_etag(time(NULL), etag); /* same etag but we know the file is still good */ + } else { + CMD_ERROR("Received unexpected HTTP status code: %ld", httpres); + goto cleanup; } - struct curl_header *ch; - if (curl_easy_header(pc, "ETag", 0, CURLH_HEADER | CURLH_TRAILER | CURLH_1XX, 0, &ch) == CURLHE_OK) { - l2_version__write_etag(etagpath, time(NULL), ch->value); - } + free(data); + free(etag); + data = NULL; - ret = VERSION_SUCCESS; + if (headers) curl_slist_free_all(headers); + curl_easy_cleanup(cu); + return VERSION_SUCCESS; cleanup: - en = errno; - - if (pc) curl_easy_cleanup(pc); if (headers) curl_slist_free_all(headers); - if (outfile) fclose(outfile); - free(verdata); - l2_launcher_download_cleanup(&dlbuf); - - errno = en; - return ret; + if (cu) curl_easy_cleanup(cu); + if (data) free(data); + if (etag) free(etag); + return retval; } -#endif /* parses an instance of version_manifest_v2.json */ unsigned l2_version__load_all_from_json(json_t *json) diff --git a/src/version.h b/src/version.h index 246e346..2c92796 100644 --- a/src/version.h +++ b/src/version.h @@ -11,7 +11,7 @@ enum { VERSION_EJSON, VERSION_EALLOC, VERSION_ERRNO, - VERSION_EUNSPEC + VERSION_EDOWNLOAD }; struct l2_version_remote { |
