From c794c1b8854a6aa43dfb1e2cf57af76be246f29e Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Sun, 16 Feb 2025 13:37:25 -0500 Subject: [PATCH] admin: Global settings can be specified on the command-line. Removed some previous, less thorough ways of configuring things. #102 --- apps/admin.json | 2 +- apps/admin/script.js | 2 +- core/core.js | 1 - src/httpd.js.c | 48 +++++---------------- src/main.c | 41 +++++------------- src/ssb.db.c | 45 +++++++++++++++++++ src/ssb.db.h | 9 ++++ src/ssb.js.c | 7 +++ src/ssb.tests.c | 2 +- src/task.c | 100 ++++++++++++++++--------------------------- src/task.h | 21 --------- src/tests.c | 11 ++--- src/util.js.c | 61 +++++++++++++++++++++----- src/util.js.h | 24 +++++++++++ 14 files changed, 201 insertions(+), 173 deletions(-) diff --git a/apps/admin.json b/apps/admin.json index 457676d3..a468be8d 100644 --- a/apps/admin.json +++ b/apps/admin.json @@ -1,5 +1,5 @@ { "type": "tildefriends-app", "emoji": "🎛", - "previous": "&R49FywYF8CXPhoSEydLbSCgvCddeyTiBwGuDU/gqY+M=.sha256" + "previous": "&kmKNyb/uaXNb24gCinJtfS8iWx4cLUWdtl0y2DwEUas=.sha256" } diff --git a/apps/admin/script.js b/apps/admin/script.js index cf152ffc..4b97b542 100644 --- a/apps/admin/script.js +++ b/apps/admin/script.js @@ -72,7 +72,7 @@ ${description.value} `; - } else { + } else if (description.type != 'hidden') { return html`
  • diff --git a/core/core.js b/core/core.js index ea4e8a85..ef21b425 100644 --- a/core/core.js +++ b/core/core.js @@ -470,7 +470,6 @@ async function getProcessBlob(blobId, key, options) { imports.ssb = Object.fromEntries( Object.keys(ssb).map((key) => [key, ssb[key].bind(ssb)]) ); - imports.ssb.port = tildefriends.ssb_port; imports.ssb.createIdentity = () => process.createIdentity(); imports.ssb.addIdentity = function (id) { if ( diff --git a/src/httpd.js.c b/src/httpd.js.c index b046e4d2..ed7f3f13 100644 --- a/src/httpd.js.c +++ b/src/httpd.js.c @@ -2319,35 +2319,6 @@ static void _httpd_endpoint_app_socket(tf_http_request_t* request) JS_FreeValue(context, global); } -static int _tf_httpd_get_tildefriends_int(JSContext* context, const char* arg) -{ - JSValue global = JS_GetGlobalObject(context); - JSValue tildefriends = JS_GetPropertyStr(context, global, "tildefriends"); - JSValue arg_value = JS_GetPropertyStr(context, tildefriends, arg); - int value = 0; - JS_ToInt32(context, &value, arg_value); - JS_FreeValue(context, arg_value); - JS_FreeValue(context, tildefriends); - JS_FreeValue(context, global); - return value; -} - -static const char* _tf_httpd_get_tildefriends_arg_string(JSContext* context, const char* arg) -{ - JSValue global = JS_GetGlobalObject(context); - JSValue tildefriends = JS_GetPropertyStr(context, global, "tildefriends"); - JSValue args = JS_GetPropertyStr(context, tildefriends, "args"); - JSValue arg_value = JS_GetPropertyStr(context, args, arg); - const char* value = !JS_IsUndefined(arg_value) ? JS_ToCString(context, arg_value) : NULL; - const char* result = value ? tf_strdup(value) : NULL; - JS_FreeCString(context, value); - JS_FreeValue(context, arg_value); - JS_FreeValue(context, args); - JS_FreeValue(context, tildefriends); - JS_FreeValue(context, global); - return result; -} - static void _httpd_free_user_data(void* user_data) { tf_free(user_data); @@ -2394,10 +2365,6 @@ void tf_httpd_register(JSContext* context) fprintf(stderr, "Failed to register Request.\n"); } - int http_port = _tf_httpd_get_tildefriends_int(context, "http_port"); - int https_port = _tf_httpd_get_tildefriends_int(context, "https_port"); - const char* out_http_port_file = _tf_httpd_get_tildefriends_arg_string(context, "out_http_port_file"); - JSValue global = JS_GetGlobalObject(context); JSValue httpd = JS_NewObjectClass(context, _httpd_class_id); @@ -2408,6 +2375,15 @@ void tf_httpd_register(JSContext* context) tf_http_set_trace(http, tf_task_get_trace(task)); JS_SetOpaque(httpd, http); + int64_t http_port = 0; + int64_t https_port = 0; + char out_http_port_file[512] = ""; + sqlite3* db = tf_ssb_acquire_db_reader(ssb); + tf_ssb_db_get_global_setting_int64(db, "http_port", &http_port); + tf_ssb_db_get_global_setting_int64(db, "https_port", &https_port); + tf_ssb_db_get_global_setting_string(db, "out_http_port_file", out_http_port_file, sizeof(out_http_port_file)); + tf_ssb_release_db_reader(ssb, db); + if (https_port) { http_user_data_t* user_data = tf_http_get_user_data(http); @@ -2463,14 +2439,14 @@ void tf_httpd_register(JSContext* context) JS_SetPropertyStr(context, global, "httpd", httpd); JS_FreeValue(context, global); - if (http_port > 0 || out_http_port_file) + if (http_port > 0 || *out_http_port_file) { httpd_listener_t* listener = tf_malloc(sizeof(httpd_listener_t)); *listener = (httpd_listener_t) { 0 }; int assigned_port = tf_http_listen(http, http_port, NULL, _httpd_listener_cleanup, listener); tf_printf(CYAN "~😎 Tilde Friends" RESET " " YELLOW VERSION_NUMBER RESET " is now up at " MAGENTA "http://127.0.0.1:%d/" RESET ".\n", assigned_port); - if (out_http_port_file) + if (*out_http_port_file) { const char* actual_http_port_file = tf_task_get_path_with_root(task, out_http_port_file); FILE* file = fopen(actual_http_port_file, "wb"); @@ -2507,6 +2483,4 @@ void tf_httpd_register(JSContext* context) tf_free((char*)private_key); } } - - tf_free((void*)out_http_port_file); } diff --git a/src/main.c b/src/main.c index 8b0b4a26..4a403262 100644 --- a/src/main.c +++ b/src/main.c @@ -1217,9 +1217,6 @@ typedef struct tf_run_args_t { const char* script; const char* network_key; - int ssb_port; - int http_port; - int https_port; const char* db_path; int count; const char* args; @@ -1244,9 +1241,6 @@ static int _tf_run_task(const tf_run_args_t* args, int index) tf_printf("setting zip path to %s\n", args->zip); tf_task_set_zip_path(task, args->zip); tf_task_set_ssb_network_key(task, args->network_key); - tf_task_set_ssb_port(task, args->ssb_port ? args->ssb_port + index : 0); - tf_task_set_http_port(task, args->http_port ? args->http_port + index : 0); - tf_task_set_https_port(task, args->https_port ? args->https_port + index : 0); tf_task_set_args(task, args->args); tf_task_set_one_proc(task, args->one_proc); const char* db_path = args->db_path; @@ -1301,7 +1295,14 @@ static int _tf_run_task(const tf_run_args_t* args, int index) tf_task_activate(task); tf_ssb_set_verbose(tf_task_get_ssb(task), args->verbose); tf_ssb_start_periodic(tf_task_get_ssb(task)); - if (args->http_port || args->https_port) + tf_ssb_t* ssb = tf_task_get_ssb(task); + sqlite3* db = tf_ssb_acquire_db_reader(ssb); + int64_t http_port = 0; + int64_t https_port = 0; + tf_ssb_db_get_global_setting_int64(db, "http_port", &http_port); + tf_ssb_db_get_global_setting_int64(db, "https_port", &https_port); + tf_ssb_release_db_reader(ssb, db); + if (http_port || https_port) { if (args->zip) { @@ -1417,9 +1418,6 @@ static int _tf_command_run(const char* file, int argc, char* argv[]) const char* default_db_path = _get_db_path(); tf_run_args_t args = { .count = 1, - .http_port = 12345, - .https_port = 12346, - .ssb_port = 8008, .db_path = default_db_path, }; bool show_usage = false; @@ -1436,10 +1434,7 @@ static int _tf_command_run(const char* file, int argc, char* argv[]) { static const struct option k_options[] = { { "script", required_argument, NULL, 's' }, - { "ssb-port", required_argument, NULL, 'b' }, { "ssb-network-key", required_argument, NULL, 'k' }, - { "http-port", required_argument, NULL, 'p' }, - { "https-port", required_argument, NULL, 'q' }, { "db-path", required_argument, NULL, 'd' }, { "count", required_argument, NULL, 'n' }, { "args", required_argument, NULL, 'a' }, @@ -1448,7 +1443,7 @@ static int _tf_command_run(const char* file, int argc, char* argv[]) { "verbose", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, }; - int c = getopt_long(argc, argv, "s:b:k:p:q:d:n:a:oz:vh", k_options, NULL); + int c = getopt_long(argc, argv, "s:k:d:n:a:oz:vh", k_options, NULL); if (c == -1) { break; @@ -1467,15 +1462,6 @@ static int _tf_command_run(const char* file, int argc, char* argv[]) case 'k': args.network_key = optarg; break; - case 'b': - args.ssb_port = atoi(optarg); - break; - case 'p': - args.http_port = atoi(optarg); - break; - case 'q': - args.https_port = atoi(optarg); - break; case 'd': args.db_path = optarg; break; @@ -1502,13 +1488,11 @@ static int _tf_command_run(const char* file, int argc, char* argv[]) tf_printf("\n%s run [options]\n\n", file); tf_printf("options\n"); tf_printf(" -s, --script script Script to run (default: core/core.js).\n"); - tf_printf(" -b, --ssb-port port Port on which to run SSB (default: 8008, 0 disables).\n"); - tf_printf(" -p, --http-port port Port on which to run Tilde Friends web server (default: 12345).\n"); - tf_printf(" -q, --https-port port Port on which to run secure Tilde Friends web server (default: 12346).\n"); tf_printf(" -d, --db-path path SQLite database path (default: %s).\n", default_db_path); tf_printf(" -k, --ssb-network-key key SSB network key to use.\n"); tf_printf(" -n, --count count Number of instances to run.\n"); - tf_printf(" -a, --args args Arguments of the format key=value,foo=bar,verbose=true.\n"); + tf_printf(" -a, --args args Arguments of the format key=value,foo=bar,verbose=true (note: these are persisted to the database).\n"); + tf_util_document_settings(" "); tf_printf(" -o, --one-proc Run everything in one process (unsafely!).\n"); tf_printf(" -z, --zip path Zip archive from which to load files.\n"); tf_printf(" -v, --verbose Log raw messages.\n"); @@ -1847,9 +1831,6 @@ void tf_run_thread_start(const char* zip_path) tf_run_thread_data_t* data = tf_malloc(sizeof(tf_run_thread_data_t)); tf_run_args_t args = { .count = 1, - .http_port = 12345, - .https_port = 12346, - .ssb_port = 8008, .db_path = "db.sqlite", .one_proc = true, .zip = zip_path, diff --git a/src/ssb.db.c b/src/ssb.db.c index 689b4b59..7f011ad7 100644 --- a/src/ssb.db.c +++ b/src/ssb.db.c @@ -2126,6 +2126,51 @@ bool tf_ssb_db_get_global_setting_string(sqlite3* db, const char* name, char* ou return result; } +bool tf_ssb_db_set_global_setting_from_string(sqlite3* db, const char* name, char* value) +{ + tf_setting_kind_t kind = tf_util_get_global_setting_kind(name); + if (kind == k_kind_unknown) + { + return false; + } + + bool result = false; + sqlite3_stmt* statement; + if (sqlite3_prepare(db, + "INSERT INTO properties (id, key, value) VALUES ('core', 'settings', json_object(?1, ?2)) ON CONFLICT DO UPDATE SET value = json_set(value, '$.' || ?1, ?2)", -1, + &statement, NULL) == SQLITE_OK) + { + if (sqlite3_bind_text(statement, 1, name, -1, NULL) == SQLITE_OK) + { + bool bound = false; + switch (kind) + { + case k_kind_bool: + bound = sqlite3_bind_int(statement, 2, value && (strcmp(value, "true") == 0 || atoi(value))) == SQLITE_OK; + break; + case k_kind_int: + bound = sqlite3_bind_int(statement, 2, atoi(value)) == SQLITE_OK; + break; + case k_kind_string: + bound = sqlite3_bind_text(statement, 2, value, -1, NULL) == SQLITE_OK; + break; + case k_kind_unknown: + break; + } + if (bound && sqlite3_step(statement) == SQLITE_DONE) + { + result = sqlite3_changes(db) != 0; + } + } + sqlite3_finalize(statement); + } + else + { + tf_printf("prepare failed: %s\n", sqlite3_errmsg(db)); + } + return result; +} + const char* tf_ssb_db_get_profile(sqlite3* db, const char* id) { const char* result = NULL; diff --git a/src/ssb.db.h b/src/ssb.db.h index f184fcf1..ba5fdc11 100644 --- a/src/ssb.db.h +++ b/src/ssb.db.h @@ -486,6 +486,15 @@ bool tf_ssb_db_get_global_setting_int64(sqlite3* db, const char* name, int64_t* */ bool tf_ssb_db_get_global_setting_string(sqlite3* db, const char* name, char* out_value, size_t size); +/** +** Set a global setting from a string representation of its value. +** @param db The database. +** @param name The setting name. +** @param value The settinv value. +** @return true if the setting was set. +*/ +bool tf_ssb_db_set_global_setting_from_string(sqlite3* db, const char* name, char* value); + /** ** Get the latest profile information for the given identity. ** @param db The database. diff --git a/src/ssb.js.c b/src/ssb.js.c index 0fe2b743..e71e4219 100644 --- a/src/ssb.js.c +++ b/src/ssb.js.c @@ -2357,6 +2357,12 @@ static JSValue _tf_ssb_set_user_permission(JSContext* context, JSValueConst this return result; } +static JSValue _tf_ssb_port(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId); + return JS_NewInt32(context, tf_ssb_server_get_port(ssb)); +} + void tf_ssb_register(JSContext* context, tf_ssb_t* ssb) { JS_NewClassID(&_tf_ssb_classId); @@ -2403,6 +2409,7 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb) JS_SetPropertyStr(context, object, "createTunnel", JS_NewCFunction(context, _tf_ssb_createTunnel, "createTunnel", 3)); JS_SetPropertyStr(context, object, "following", JS_NewCFunction(context, _tf_ssb_following, "following", 2)); JS_SetPropertyStr(context, object, "sync", JS_NewCFunction(context, _tf_ssb_sync, "sync", 0)); + JS_SetPropertyStr(context, object, "port", JS_NewCFunction(context, _tf_ssb_port, "port", 0)); /* Write. */ JS_SetPropertyStr(context, object, "storeMessage", JS_NewCFunction(context, _tf_ssb_storeMessage, "storeMessage", 1)); JS_SetPropertyStr(context, object, "blobStore", JS_NewCFunction(context, _tf_ssb_blobStore, "blobStore", 1)); diff --git a/src/ssb.tests.c b/src/ssb.tests.c index 9b99be41..58080edc 100644 --- a/src/ssb.tests.c +++ b/src/ssb.tests.c @@ -913,7 +913,7 @@ static void _write_file(const char* path, const char* contents) fclose(file); } -#define TEST_ARGS " --ssb-port=0 --http-port=0 --https-port=0" +#define TEST_ARGS " --args=ssb_port=0,http_port=0,https_port=0" void tf_ssb_test_encrypt(const tf_test_options_t* options) { diff --git a/src/task.c b/src/task.c index 0d7ba272..be1cd971 100644 --- a/src/task.c +++ b/src/task.c @@ -9,6 +9,7 @@ #include "packetstream.h" #include "serialize.h" #include "socket.js.h" +#include "ssb.db.h" #include "ssb.h" #include "ssb.js.h" #include "taskstub.js.h" @@ -154,9 +155,6 @@ typedef struct _tf_task_t JSValue _loadedFiles; const char* _network_key; - int _ssb_port; - int _http_port; - int _https_port; char _db_path[256]; char _zip_path[256]; char _root_path[256]; @@ -398,6 +396,7 @@ static JSValue _tf_task_exit(JSContext* context, JSValueConst this_val, int argc int exitCode = 0; JS_ToInt32(task->_context, &exitCode, argv[0]); tf_trace_end(task->_trace); + tf_printf("EXIT %d\n", exitCode); exit(exitCode); return JS_UNDEFINED; } @@ -1680,38 +1679,6 @@ void tf_task_activate(tf_task_t* task) JS_DefinePropertyGetSet(context, global, atom, JS_NewCFunction(context, _tf_task_get_parent, "parent", 0), JS_UNDEFINED, 0); JS_FreeAtom(context, atom); - JSValue tildefriends = JS_NewObject(context); - JSValue args = JS_NewObject(context); - JS_SetPropertyStr(context, tildefriends, "args", args); - if (task->_args) - { - char* saveptr = NULL; - char* copy = tf_strdup(task->_args); - char* start = copy; - while (true) - { - char* assignment = strtok_r(start, ",", &saveptr); - start = NULL; - if (!assignment) - { - break; - } - char* equals = strchr(assignment, '='); - if (equals) - { - *equals = '\0'; - JS_SetPropertyStr(context, args, assignment, JS_NewString(context, equals + 1)); - } - else - { - tf_printf("Assignment missing '=': %s.\n", assignment); - exit(1); - } - } - tf_free(copy); - } - JS_SetPropertyStr(context, global, "tildefriends", tildefriends); - task->_trace = tf_trace_create(); if (task->_trusted) { @@ -1732,23 +1699,45 @@ void tf_task_activate(tf_task_t* task) tf_ssb_register(context, task->_ssb); tf_ssb_set_hitch_callback(task->_ssb, _tf_task_record_hitch, task); - int actual_ssb_port = task->_ssb_port; + if (task->_args) + { + sqlite3* db = tf_ssb_acquire_db_writer(task->_ssb); + char* saveptr = NULL; + char* copy = tf_strdup(task->_args); + char* start = copy; + while (true) + { + char* assignment = strtok_r(start, ",", &saveptr); + start = NULL; + if (!assignment) + { + break; + } + char* equals = strchr(assignment, '='); + if (equals) + { + *equals = '\0'; + tf_ssb_db_set_global_setting_from_string(db, assignment, equals + 1); + } + else + { + tf_printf("Assignment missing '=': %s.\n", assignment); + exit(1); + } + } + tf_free(copy); + tf_ssb_release_db_writer(task->_ssb, db); + } - if (task->_ssb_port) + int64_t ssb_port = 0; + sqlite3* db = tf_ssb_acquire_db_reader(task->_ssb); + tf_ssb_db_get_global_setting_int64(db, "ssb_port", &ssb_port); + tf_ssb_release_db_reader(task->_ssb, db); + if (ssb_port) { tf_ssb_broadcast_listener_start(task->_ssb, false); tf_ssb_broadcast_sender_start(task->_ssb); - actual_ssb_port = tf_ssb_server_open(task->_ssb, task->_ssb_port); - } - - JS_SetPropertyStr(context, tildefriends, "ssb_port", JS_NewInt32(context, actual_ssb_port)); - if (task->_http_port) - { - JS_SetPropertyStr(context, tildefriends, "http_port", JS_NewInt32(context, task->_http_port)); - } - if (task->_https_port) - { - JS_SetPropertyStr(context, tildefriends, "https_port", JS_NewInt32(context, task->_https_port)); + tf_ssb_server_open(task->_ssb, ssb_port); } JS_SetPropertyStr(context, global, "getStats", JS_NewCFunction(context, _tf_task_getStats, "getStats", 0)); @@ -1999,21 +1988,6 @@ void tf_task_set_ssb_network_key(tf_task_t* task, const char* network_key) task->_network_key = network_key; } -void tf_task_set_ssb_port(tf_task_t* task, int port) -{ - task->_ssb_port = port; -} - -void tf_task_set_http_port(tf_task_t* task, int port) -{ - task->_http_port = port; -} - -void tf_task_set_https_port(tf_task_t* task, int port) -{ - task->_https_port = port; -} - void tf_task_set_db_path(tf_task_t* task, const char* db_path) { snprintf(task->_db_path, sizeof(task->_db_path), "%s", db_path); diff --git a/src/task.h b/src/task.h index 1579ec81..3623f8e7 100644 --- a/src/task.h +++ b/src/task.h @@ -76,27 +76,6 @@ void tf_task_configure_from_fd(tf_task_t* task, int fd); */ void tf_task_set_ssb_network_key(tf_task_t* task, const char* network_key); -/** -** Set the port number on which to run an SSB secure handshake server. -** @param task The task. -** @param port The port number or 0 to disable. -*/ -void tf_task_set_ssb_port(tf_task_t* task, int port); - -/** -** Set the port number on which to run an HTTP server. -** @param task The task. -** @param port The port number of 0 to disable. -*/ -void tf_task_set_http_port(tf_task_t* task, int port); - -/** -** Set the port number on which to run an HTTPS server. -** @param task The task. -** @param port The port number of 0 to disable. -*/ -void tf_task_set_https_port(tf_task_t* task, int port); - /** ** Set the path to the SQLite database. ** @param task The task. diff --git a/src/tests.c b/src/tests.c index 84d38e6e..485e8371 100644 --- a/src/tests.c +++ b/src/tests.c @@ -32,7 +32,7 @@ #include #endif -#define TEST_ARGS " --ssb-port=0 --http-port=0 --https-port=0" +#define TEST_ARGS " --args=ssb_port=0,http_port=0,https_port=0" #if !TARGET_OS_IPHONE static void _write_file(const char* path, const char* contents) @@ -311,7 +311,7 @@ static void _test_database(const tf_test_options_t* options) " print('Expected but did not find: ' + JSON.stringify(expected));\n" " exit(4);\n" " }\n" - " if (JSON.stringify(await databases.list('%')) != '[\"testdb\"]') {\n" + " if (JSON.stringify(await databases.list('%')) != '[\"core\",\"testdb\"]') {\n" " exit(7);\n" " }\n" "}\n" @@ -864,9 +864,6 @@ static void _test_httpd(const tf_test_options_t* options) uv_loop_init(&loop); unlink("out/test_db0.sqlite"); - char command[256]; - snprintf(command, sizeof(command), "%s run -b 0 --db-path=out/test_db0.sqlite" TEST_ARGS, options->exe_path); - uv_stdio_container_t stdio[] = { [STDIN_FILENO] = { .flags = UV_IGNORE }, [STDOUT_FILENO] = { .flags = UV_INHERIT_FD }, @@ -876,7 +873,7 @@ static void _test_httpd(const tf_test_options_t* options) uv_spawn(&loop, &process, &(uv_process_options_t) { .file = options->exe_path, - .args = (char*[]) { (char*)options->exe_path, "run", "-b0", "--db-path=out/test_db0.sqlite", "--http-port=8080", "--https-port=0", NULL }, + .args = (char*[]) { (char*)options->exe_path, "run", "--db-path=out/test_db0.sqlite", "--args=ssb_port=0,http_port=8080,https_port=0", NULL }, .stdio_count = sizeof(stdio) / sizeof(*stdio), .stdio = stdio, }); @@ -960,7 +957,7 @@ static void _test_auto(const tf_test_options_t* options) unlink("out/selenium.sqlite-shm"); unlink("out/selenium.sqlite-wal"); - char* args[] = { executable, "run", "-d", "out/selenium.sqlite", "-b", "0", "-p", "8888", NULL }; + char* args[] = { executable, "run", "-d", "out/selenium.sqlite", "-a", "ssb_port=0,http_port=8888", NULL }; uv_stdio_container_t io[3] = { { .flags = UV_INHERIT_FD }, diff --git a/src/util.js.c b/src/util.js.c index e0e06a1f..d14a1d54 100644 --- a/src/util.js.c +++ b/src/util.js.c @@ -314,13 +314,6 @@ static JSValue _util_parseHttpResponse(JSContext* context, JSValueConst this_val return result; } -typedef enum _value_kind_t -{ - k_kind_bool, - k_kind_int, - k_kind_string, -} value_kind_t; - static const char* k_kind_name[] = { [k_kind_bool] = "bool", [k_kind_int] = "int", @@ -329,7 +322,7 @@ static const char* k_kind_name[] = { typedef struct _setting_value_t { - value_kind_t kind; + tf_setting_kind_t kind; union { bool bool_value; @@ -348,6 +341,13 @@ typedef struct _setting_t static const setting_t k_settings[] = { { .name = "code_of_conduct", .type = "textarea", .description = "Code of conduct presented at sign-in.", .default_value = { .kind = k_kind_string, .string_value = NULL } }, + { .name = "ssb_port", + .type = "integer", + .description = "Port on which to listen for SSB secure handshake connections.", + .default_value = { .kind = k_kind_int, .int_value = 8008 } }, + { .name = "http_port", .type = "integer", .description = "Port on which to listen for HTTP connections.", .default_value = { .kind = k_kind_int, .int_value = 12345 } }, + { .name = "https_port", .type = "integer", .description = "Port on which to listen for secure HTTP connections.", .default_value = { .kind = k_kind_int, .int_value = 0 } }, + { .name = "out_http_port_file", .type = "hidden", .description = "File to which to write bound HTTP port.", .default_value = { .kind = k_kind_string, .string_value = NULL } }, { .name = "blob_fetch_age_seconds", .type = "integer", .description = "Only blobs mentioned more recently than this age will be automatically fetched.", @@ -393,21 +393,31 @@ static const setting_t k_settings[] = { .type = "boolean", .description = "Whether connections are accepted from accounts that aren't in the replication range or otherwise already known.", .default_value = { .kind = k_kind_bool, .bool_value = true } }, + { .name = "autologin", .type = "boolean", .description = "Whether mobile autologin is supported.", .default_value = { .kind = k_kind_bool, .bool_value = TF_IS_MOBILE != 0 } }, }; -static const setting_t* _util_get_setting(const char* name, value_kind_t kind) +static const setting_t* _util_get_setting(const char* name, tf_setting_kind_t kind) { for (int i = 0; i < tf_countof(k_settings); i++) { - if (strcmp(k_settings[i].name, name) == 0 && k_settings[i].default_value.kind == kind) + if (strcmp(k_settings[i].name, name) == 0 && (kind == k_kind_unknown || k_settings[i].default_value.kind == kind)) { return &k_settings[i]; } } - tf_printf("Did not find global setting of type %s: %s.\n", k_kind_name[kind], name); + if (kind != k_kind_unknown) + { + tf_printf("Did not find global setting of type %s: %s.\n", k_kind_name[kind], name); + } return NULL; } +tf_setting_kind_t tf_util_get_global_setting_kind(const char* name) +{ + const setting_t* setting = _util_get_setting(name, k_kind_unknown); + return setting ? setting->default_value.kind : k_kind_unknown; +} + bool tf_util_get_default_global_setting_bool(const char* name) { const setting_t* setting = _util_get_setting(name, k_kind_bool); @@ -446,12 +456,41 @@ static JSValue _util_defaultGlobalSettings(JSContext* context, JSValueConst this JS_SetPropertyStr( context, entry, "default_value", k_settings[i].default_value.string_value ? JS_NewString(context, k_settings[i].default_value.string_value) : JS_UNDEFINED); break; + case k_kind_unknown: + break; } JS_SetPropertyStr(context, settings, k_settings[i].name, entry); } return settings; } +void tf_util_document_settings(const char* line_prefix) +{ + char buffer[32]; + for (int i = 0; i < tf_countof(k_settings); i++) + { + const char* default_value = NULL; + const char* quote = ""; + switch (k_settings[i].default_value.kind) + { + case k_kind_bool: + default_value = k_settings[i].default_value.bool_value ? "true" : "false"; + break; + case k_kind_string: + quote = "\""; + default_value = k_settings[i].default_value.string_value ? k_settings[i].default_value.string_value : ""; + break; + case k_kind_int: + snprintf(buffer, sizeof(buffer), "%d", k_settings[i].default_value.int_value); + default_value = buffer; + break; + case k_kind_unknown: + break; + } + tf_printf("%s%s (default: %s%s%s): %s\n", line_prefix, k_settings[i].name, quote, default_value, quote, k_settings[i].description); + } +} + JSValue tf_util_new_uint8_array(JSContext* context, const uint8_t* data, size_t size) { JSValue array_buffer = JS_NewArrayBufferCopy(context, data, size); diff --git a/src/util.js.h b/src/util.js.h index 1cb2be3a..70b60d0b 100644 --- a/src/util.js.h +++ b/src/util.js.h @@ -10,6 +10,17 @@ #include +/** +** Type of a setting. +*/ +typedef enum _tf_setting_kind_t +{ + k_kind_unknown, + k_kind_bool, + k_kind_int, + k_kind_string, +} tf_setting_kind_t; + /** An event loop. */ typedef struct uv_loop_s uv_loop_t; @@ -194,6 +205,19 @@ int tf_util_get_default_global_setting_int(const char* name); */ const char* tf_util_get_default_global_setting_string(const char* name); +/** +** Get the expected kind of a global setting. +** @param name The setting name. +** @return The setting kind or unknown if nonexistent. +*/ +tf_setting_kind_t tf_util_get_global_setting_kind(const char* name); + +/** +** Log documentation for the available settings. +** @param line_prefix Text to prefix each line with." +*/ +void tf_util_document_settings(const char* line_prefix); + /** ** Check if the app is running on a mobile device. ** @return true for iPhone/Android, false otherwise.