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.