aboutsummaryrefslogtreecommitdiffstats
path: root/src/version.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/version.c')
-rw-r--r--src/version.c208
1 files changed, 131 insertions, 77 deletions
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)