aboutsummaryrefslogtreecommitdiff
path: root/jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.c
diff options
context:
space:
mode:
authorMitchell Riedstra <mitch@riedstra.dev>2025-12-24 19:49:57 -0500
committerMitchell Riedstra <mitch@riedstra.dev>2025-12-24 19:49:57 -0500
commit939ac4319cb047a37ba46f84eff81948063f6954 (patch)
tree5112cf8aad73125a13f5b52c0290a7f26f948b52 /jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.c
parent3a1b5ba15b89c907f9bf66a0761ffdd73b32208b (diff)
downloadunixv4-939ac4319cb047a37ba46f84eff81948063f6954.tar.gz
unixv4-939ac4319cb047a37ba46f84eff81948063f6954.tar.xz
Add working webpage for unix v4
Diffstat (limited to 'jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.c')
-rw-r--r--jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.c625
1 files changed, 625 insertions, 0 deletions
diff --git a/jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.c b/jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.c
new file mode 100644
index 0000000..b4857b0
--- /dev/null
+++ b/jslinux-2019-12-21/tinyemu-2019-12-21/fs_wget.c
@@ -0,0 +1,625 @@
+/*
+ * HTTP file download
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <sys/time.h>
+#include <ctype.h>
+
+#include "cutils.h"
+#include "list.h"
+#include "fs.h"
+#include "fs_utils.h"
+#include "fs_wget.h"
+
+#if defined(EMSCRIPTEN)
+#include <emscripten.h>
+#else
+#include <curl/multi.h>
+#endif
+
+/***********************************************/
+/* HTTP get */
+
+#ifdef EMSCRIPTEN
+
+struct XHRState {
+ void *opaque;
+ WGetWriteCallback *cb;
+};
+
+static int downloading_count;
+
+void fs_wget_init(void)
+{
+}
+
+extern void fs_wget_update_downloading(int flag);
+
+static void fs_wget_update_downloading_count(int incr)
+{
+ int prev_state, state;
+ prev_state = (downloading_count > 0);
+ downloading_count += incr;
+ state = (downloading_count > 0);
+ if (prev_state != state)
+ fs_wget_update_downloading(state);
+}
+
+static void fs_wget_onerror(unsigned int handle, void *opaque, int status,
+ const char *status_text)
+{
+ XHRState *s = opaque;
+ if (status <= 0)
+ status = -404; /* HTTP not found error */
+ else
+ status = -status;
+ fs_wget_update_downloading_count(-1);
+ if (s->cb)
+ s->cb(s->opaque, status, NULL, 0);
+}
+
+static void fs_wget_onload(unsigned int handle,
+ void *opaque, void *data, unsigned int size)
+{
+ XHRState *s = opaque;
+ fs_wget_update_downloading_count(-1);
+ if (s->cb)
+ s->cb(s->opaque, 0, data, size);
+}
+
+extern int emscripten_async_wget3_data(const char* url, const char* requesttype, const char *user, const char *password, const uint8_t *post_data, int post_data_len, void *arg, int free, em_async_wget2_data_onload_func onload, em_async_wget2_data_onerror_func onerror, em_async_wget2_data_onprogress_func onprogress);
+
+XHRState *fs_wget2(const char *url, const char *user, const char *password,
+ WGetReadCallback *read_cb, uint64_t post_data_len,
+ void *opaque, WGetWriteCallback *cb, BOOL single_write)
+{
+ XHRState *s;
+ const char *request;
+ uint8_t *post_data;
+
+ s = mallocz(sizeof(*s));
+ s->opaque = opaque;
+ s->cb = cb;
+
+ if (post_data_len != 0) {
+ request = "POST";
+ post_data = malloc(post_data_len);
+ read_cb(opaque, post_data, post_data_len);
+ } else {
+ request = "GET";
+ post_data = NULL;
+ }
+ fs_wget_update_downloading_count(1);
+
+ emscripten_async_wget3_data(url, request, user, password,
+ post_data, post_data_len, s, 1, fs_wget_onload,
+ fs_wget_onerror, NULL);
+ if (post_data_len != 0)
+ free(post_data);
+ return s;
+}
+
+void fs_wget_free(XHRState *s)
+{
+ s->cb = NULL;
+ s->opaque = NULL;
+}
+
+#else
+
+struct XHRState {
+ struct list_head link;
+ CURL *eh;
+ void *opaque;
+ WGetWriteCallback *write_cb;
+ WGetReadCallback *read_cb;
+
+ BOOL single_write;
+ DynBuf dbuf; /* used if single_write */
+};
+
+typedef struct {
+ struct list_head link;
+ int64_t timeout;
+ void (*cb)(void *opaque);
+ void *opaque;
+} AsyncCallState;
+
+static CURLM *curl_multi_ctx;
+static struct list_head xhr_list; /* list of XHRState.link */
+
+void fs_wget_init(void)
+{
+ if (curl_multi_ctx)
+ return;
+ curl_global_init(CURL_GLOBAL_ALL);
+ curl_multi_ctx = curl_multi_init();
+ init_list_head(&xhr_list);
+}
+
+void fs_wget_end(void)
+{
+ curl_multi_cleanup(curl_multi_ctx);
+ curl_global_cleanup();
+}
+
+static size_t fs_wget_write_cb(char *ptr, size_t size, size_t nmemb,
+ void *userdata)
+{
+ XHRState *s = userdata;
+ size *= nmemb;
+
+ if (s->single_write) {
+ dbuf_write(&s->dbuf, s->dbuf.size, (void *)ptr, size);
+ } else {
+ s->write_cb(s->opaque, 1, ptr, size);
+ }
+ return size;
+}
+
+static size_t fs_wget_read_cb(char *ptr, size_t size, size_t nmemb,
+ void *userdata)
+{
+ XHRState *s = userdata;
+ size *= nmemb;
+ return s->read_cb(s->opaque, ptr, size);
+}
+
+XHRState *fs_wget2(const char *url, const char *user, const char *password,
+ WGetReadCallback *read_cb, uint64_t post_data_len,
+ void *opaque, WGetWriteCallback *write_cb, BOOL single_write)
+{
+ XHRState *s;
+ s = mallocz(sizeof(*s));
+ s->eh = curl_easy_init();
+ s->opaque = opaque;
+ s->write_cb = write_cb;
+ s->read_cb = read_cb;
+ s->single_write = single_write;
+ dbuf_init(&s->dbuf);
+
+ curl_easy_setopt(s->eh, CURLOPT_PRIVATE, s);
+ curl_easy_setopt(s->eh, CURLOPT_WRITEDATA, s);
+ curl_easy_setopt(s->eh, CURLOPT_WRITEFUNCTION, fs_wget_write_cb);
+ curl_easy_setopt(s->eh, CURLOPT_HEADER, 0);
+ curl_easy_setopt(s->eh, CURLOPT_URL, url);
+ curl_easy_setopt(s->eh, CURLOPT_VERBOSE, 0L);
+ curl_easy_setopt(s->eh, CURLOPT_ACCEPT_ENCODING, "");
+ if (user) {
+ curl_easy_setopt(s->eh, CURLOPT_USERNAME, user);
+ curl_easy_setopt(s->eh, CURLOPT_PASSWORD, password);
+ }
+ if (post_data_len != 0) {
+ struct curl_slist *headers = NULL;
+ headers = curl_slist_append(headers,
+ "Content-Type: application/octet-stream");
+ curl_easy_setopt(s->eh, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(s->eh, CURLOPT_POST, 1L);
+ curl_easy_setopt(s->eh, CURLOPT_POSTFIELDSIZE_LARGE,
+ (curl_off_t)post_data_len);
+ curl_easy_setopt(s->eh, CURLOPT_READDATA, s);
+ curl_easy_setopt(s->eh, CURLOPT_READFUNCTION, fs_wget_read_cb);
+ }
+ curl_multi_add_handle(curl_multi_ctx, s->eh);
+ list_add_tail(&s->link, &xhr_list);
+ return s;
+}
+
+void fs_wget_free(XHRState *s)
+{
+ dbuf_free(&s->dbuf);
+ curl_easy_cleanup(s->eh);
+ list_del(&s->link);
+ free(s);
+}
+
+/* timeout is in ms */
+void fs_net_set_fdset(int *pfd_max, fd_set *rfds, fd_set *wfds, fd_set *efds,
+ int *ptimeout)
+{
+ long timeout;
+ int n, fd_max;
+ CURLMsg *msg;
+
+ if (!curl_multi_ctx)
+ return;
+
+ curl_multi_perform(curl_multi_ctx, &n);
+
+ for(;;) {
+ msg = curl_multi_info_read(curl_multi_ctx, &n);
+ if (!msg)
+ break;
+ if (msg->msg == CURLMSG_DONE) {
+ XHRState *s;
+ long http_code;
+
+ curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char **)&s);
+ curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE,
+ &http_code);
+ /* signal the end of the transfer or error */
+ if (http_code == 200) {
+ if (s->single_write) {
+ s->write_cb(s->opaque, 0, s->dbuf.buf, s->dbuf.size);
+ } else {
+ s->write_cb(s->opaque, 0, NULL, 0);
+ }
+ } else {
+ s->write_cb(s->opaque, -http_code, NULL, 0);
+ }
+ curl_multi_remove_handle(curl_multi_ctx, s->eh);
+ curl_easy_cleanup(s->eh);
+ dbuf_free(&s->dbuf);
+ list_del(&s->link);
+ free(s);
+ }
+ }
+
+ curl_multi_fdset(curl_multi_ctx, rfds, wfds, efds, &fd_max);
+ *pfd_max = max_int(*pfd_max, fd_max);
+ curl_multi_timeout(curl_multi_ctx, &timeout);
+ if (timeout >= 0)
+ *ptimeout = min_int(*ptimeout, timeout);
+}
+
+void fs_net_event_loop(FSNetEventLoopCompletionFunc *cb, void *opaque)
+{
+ fd_set rfds, wfds, efds;
+ int timeout, fd_max;
+ struct timeval tv;
+
+ if (!curl_multi_ctx)
+ return;
+
+ for(;;) {
+ fd_max = -1;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&efds);
+ timeout = 10000;
+ fs_net_set_fdset(&fd_max, &rfds, &wfds, &efds, &timeout);
+ if (cb) {
+ if (cb(opaque))
+ break;
+ } else {
+ if (list_empty(&xhr_list))
+ break;
+ }
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+ select(fd_max + 1, &rfds, &wfds, &efds, &tv);
+ }
+}
+
+#endif /* !EMSCRIPTEN */
+
+XHRState *fs_wget(const char *url, const char *user, const char *password,
+ void *opaque, WGetWriteCallback *cb, BOOL single_write)
+{
+ return fs_wget2(url, user, password, NULL, 0, opaque, cb, single_write);
+}
+
+/***********************************************/
+/* file decryption */
+
+#define ENCRYPTED_FILE_HEADER_SIZE (4 + AES_BLOCK_SIZE)
+
+#define DEC_BUF_SIZE (256 * AES_BLOCK_SIZE)
+
+struct DecryptFileState {
+ DecryptFileCB *write_cb;
+ void *opaque;
+ int dec_state;
+ int dec_buf_pos;
+ AES_KEY *aes_state;
+ uint8_t iv[AES_BLOCK_SIZE];
+ uint8_t dec_buf[DEC_BUF_SIZE];
+};
+
+DecryptFileState *decrypt_file_init(AES_KEY *aes_state,
+ DecryptFileCB *write_cb,
+ void *opaque)
+{
+ DecryptFileState *s;
+ s = mallocz(sizeof(*s));
+ s->write_cb = write_cb;
+ s->opaque = opaque;
+ s->aes_state = aes_state;
+ return s;
+}
+
+int decrypt_file(DecryptFileState *s, const uint8_t *data,
+ size_t size)
+{
+ int l, len, ret;
+
+ while (size != 0) {
+ switch(s->dec_state) {
+ case 0:
+ l = min_int(size, ENCRYPTED_FILE_HEADER_SIZE - s->dec_buf_pos);
+ memcpy(s->dec_buf + s->dec_buf_pos, data, l);
+ s->dec_buf_pos += l;
+ if (s->dec_buf_pos >= ENCRYPTED_FILE_HEADER_SIZE) {
+ if (memcmp(s->dec_buf, encrypted_file_magic, 4) != 0)
+ return -1;
+ memcpy(s->iv, s->dec_buf + 4, AES_BLOCK_SIZE);
+ s->dec_state = 1;
+ s->dec_buf_pos = 0;
+ }
+ break;
+ case 1:
+ l = min_int(size, DEC_BUF_SIZE - s->dec_buf_pos);
+ memcpy(s->dec_buf + s->dec_buf_pos, data, l);
+ s->dec_buf_pos += l;
+ if (s->dec_buf_pos >= DEC_BUF_SIZE) {
+ /* keep one block in case it is the padding */
+ len = s->dec_buf_pos - AES_BLOCK_SIZE;
+ AES_cbc_encrypt(s->dec_buf, s->dec_buf, len,
+ s->aes_state, s->iv, FALSE);
+ ret = s->write_cb(s->opaque, s->dec_buf, len);
+ if (ret < 0)
+ return ret;
+ memcpy(s->dec_buf, s->dec_buf + s->dec_buf_pos - AES_BLOCK_SIZE,
+ AES_BLOCK_SIZE);
+ s->dec_buf_pos = AES_BLOCK_SIZE;
+ }
+ break;
+ default:
+ abort();
+ }
+ data += l;
+ size -= l;
+ }
+ return 0;
+}
+
+/* write last blocks */
+int decrypt_file_flush(DecryptFileState *s)
+{
+ int len, pad_len, ret;
+
+ if (s->dec_state != 1)
+ return -1;
+ len = s->dec_buf_pos;
+ if (len == 0 ||
+ (len % AES_BLOCK_SIZE) != 0)
+ return -1;
+ AES_cbc_encrypt(s->dec_buf, s->dec_buf, len,
+ s->aes_state, s->iv, FALSE);
+ pad_len = s->dec_buf[s->dec_buf_pos - 1];
+ if (pad_len < 1 || pad_len > AES_BLOCK_SIZE)
+ return -1;
+ len -= pad_len;
+ if (len != 0) {
+ ret = s->write_cb(s->opaque, s->dec_buf, len);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+void decrypt_file_end(DecryptFileState *s)
+{
+ free(s);
+}
+
+/* XHR file */
+
+typedef struct {
+ FSDevice *fs;
+ FSFile *f;
+ int64_t pos;
+ FSWGetFileCB *cb;
+ void *opaque;
+ FSFile *posted_file;
+ int64_t read_pos;
+ DecryptFileState *dec_state;
+} FSWGetFileState;
+
+static int fs_wget_file_write_cb(void *opaque, const uint8_t *data,
+ size_t size)
+{
+ FSWGetFileState *s = opaque;
+ FSDevice *fs = s->fs;
+ int ret;
+
+ ret = fs->fs_write(fs, s->f, s->pos, data, size);
+ if (ret < 0)
+ return ret;
+ s->pos += ret;
+ return ret;
+}
+
+static void fs_wget_file_on_load(void *opaque, int err, void *data, size_t size)
+{
+ FSWGetFileState *s = opaque;
+ FSDevice *fs = s->fs;
+ int ret;
+ int64_t ret_size;
+
+ // printf("err=%d size=%ld\n", err, size);
+ if (err < 0) {
+ ret_size = err;
+ goto done;
+ } else {
+ if (s->dec_state) {
+ ret = decrypt_file(s->dec_state, data, size);
+ if (ret >= 0 && err == 0) {
+ /* handle the end of file */
+ decrypt_file_flush(s->dec_state);
+ }
+ } else {
+ ret = fs_wget_file_write_cb(s, data, size);
+ }
+ if (ret < 0) {
+ ret_size = ret;
+ goto done;
+ } else if (err == 0) {
+ /* end of transfer */
+ ret_size = s->pos;
+ done:
+ s->cb(fs, s->f, ret_size, s->opaque);
+ if (s->dec_state)
+ decrypt_file_end(s->dec_state);
+ free(s);
+ }
+ }
+}
+
+static size_t fs_wget_file_read_cb(void *opaque, void *data, size_t size)
+{
+ FSWGetFileState *s = opaque;
+ FSDevice *fs = s->fs;
+ int ret;
+
+ if (!s->posted_file)
+ return 0;
+ ret = fs->fs_read(fs, s->posted_file, s->read_pos, data, size);
+ if (ret < 0)
+ return 0;
+ s->read_pos += ret;
+ return ret;
+}
+
+void fs_wget_file2(FSDevice *fs, FSFile *f, const char *url,
+ const char *user, const char *password,
+ FSFile *posted_file, uint64_t post_data_len,
+ FSWGetFileCB *cb, void *opaque,
+ AES_KEY *aes_state)
+{
+ FSWGetFileState *s;
+ s = mallocz(sizeof(*s));
+ s->fs = fs;
+ s->f = f;
+ s->pos = 0;
+ s->cb = cb;
+ s->opaque = opaque;
+ s->posted_file = posted_file;
+ s->read_pos = 0;
+ if (aes_state) {
+ s->dec_state = decrypt_file_init(aes_state, fs_wget_file_write_cb, s);
+ }
+
+ fs_wget2(url, user, password, fs_wget_file_read_cb, post_data_len,
+ s, fs_wget_file_on_load, FALSE);
+}
+
+/***********************************************/
+/* PBKDF2 */
+
+#ifdef USE_BUILTIN_CRYPTO
+
+#define HMAC_BLOCK_SIZE 64
+
+typedef struct {
+ SHA256_CTX ctx;
+ uint8_t K[HMAC_BLOCK_SIZE + SHA256_DIGEST_LENGTH];
+} HMAC_SHA256_CTX;
+
+void hmac_sha256_init(HMAC_SHA256_CTX *s, const uint8_t *key, int key_len)
+{
+ int i, l;
+
+ if (key_len > HMAC_BLOCK_SIZE) {
+ SHA256(key, key_len, s->K);
+ l = SHA256_DIGEST_LENGTH;
+ } else {
+ memcpy(s->K, key, key_len);
+ l = key_len;
+ }
+ memset(s->K + l, 0, HMAC_BLOCK_SIZE - l);
+ for(i = 0; i < HMAC_BLOCK_SIZE; i++)
+ s->K[i] ^= 0x36;
+ SHA256_Init(&s->ctx);
+ SHA256_Update(&s->ctx, s->K, HMAC_BLOCK_SIZE);
+}
+
+void hmac_sha256_update(HMAC_SHA256_CTX *s, const uint8_t *buf, int len)
+{
+ SHA256_Update(&s->ctx, buf, len);
+}
+
+/* out has a length of SHA256_DIGEST_LENGTH */
+void hmac_sha256_final(HMAC_SHA256_CTX *s, uint8_t *out)
+{
+ int i;
+
+ SHA256_Final(s->K + HMAC_BLOCK_SIZE, &s->ctx);
+ for(i = 0; i < HMAC_BLOCK_SIZE; i++)
+ s->K[i] ^= (0x36 ^ 0x5c);
+ SHA256(s->K, HMAC_BLOCK_SIZE + SHA256_DIGEST_LENGTH, out);
+}
+
+#define SALT_LEN_MAX 32
+
+void pbkdf2_hmac_sha256(const uint8_t *pwd, int pwd_len,
+ const uint8_t *salt, int salt_len,
+ int iter, int key_len, uint8_t *out)
+{
+ uint8_t F[SHA256_DIGEST_LENGTH], U[SALT_LEN_MAX + 4];
+ HMAC_SHA256_CTX ctx;
+ int it, U_len, j, l;
+ uint32_t i;
+
+ assert(salt_len <= SALT_LEN_MAX);
+ i = 1;
+ while (key_len > 0) {
+ memset(F, 0, SHA256_DIGEST_LENGTH);
+ memcpy(U, salt, salt_len);
+ U[salt_len] = i >> 24;
+ U[salt_len + 1] = i >> 16;
+ U[salt_len + 2] = i >> 8;
+ U[salt_len + 3] = i;
+ U_len = salt_len + 4;
+ for(it = 0; it < iter; it++) {
+ hmac_sha256_init(&ctx, pwd, pwd_len);
+ hmac_sha256_update(&ctx, U, U_len);
+ hmac_sha256_final(&ctx, U);
+ for(j = 0; j < SHA256_DIGEST_LENGTH; j++)
+ F[j] ^= U[j];
+ U_len = SHA256_DIGEST_LENGTH;
+ }
+ l = min_int(key_len, SHA256_DIGEST_LENGTH);
+ memcpy(out, F, l);
+ out += l;
+ key_len -= l;
+ i++;
+ }
+}
+
+#else
+
+void pbkdf2_hmac_sha256(const uint8_t *pwd, int pwd_len,
+ const uint8_t *salt, int salt_len,
+ int iter, int key_len, uint8_t *out)
+{
+ PKCS5_PBKDF2_HMAC((const char *)pwd, pwd_len, salt, salt_len,
+ iter, EVP_sha256(), key_len, out);
+}
+
+#endif /* !USE_BUILTIN_CRYPTO */