2 Commits

Author SHA1 Message Date
d84b06f814 core: Add a helper for getting a property by string as a string. I've typed this too much.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 9m15s
2025-12-06 14:04:27 -05:00
3a3b889196 core: Inconsistent ready message in editonly mode. 2025-12-06 13:21:45 -05:00
7 changed files with 87 additions and 48 deletions

View File

@@ -168,7 +168,11 @@ exports.app_socket = async function socket(request, response) {
if (blobId) {
if (message.edit_only) {
response.send(
JSON.stringify({action: 'ready', edit_only: true}),
JSON.stringify({
action: 'ready',
version: version(),
edit_only: true,
}),
0x1
);
} else {

View File

@@ -7,7 +7,7 @@ author a message on one device without having received all messages authored
from another, your account may become irrecoverably forked. Other clients may
stop receiving your messages if this happens.
1. In Tilde Friends, the _identity_ app will let you export and import your identity as a series of twelve English words for copying and pasting. Keep these words secret!
1. In Tilde Friends, the _identity_ app will let you export and import your identity as a series of twelve English words for copying and pasting. Keep these words secret!
2. The _sneaker_ app will let you export and import your feed to a file, but it's likely easier and faster to use your initial account on the receiver as a throwaway account to connect, follow yourself, and do the initial replication.

View File

@@ -243,9 +243,44 @@ static void _httpd_app_kill_task(app_t* work)
}
}
typedef struct _app_hello_t
{
app_t* app;
JSValue message;
const char* path;
} app_hello_t;
static void _httpd_app_hello_work(tf_ssb_t* ssb, void* user_data)
{
app_hello_t* work = user_data;
tf_printf("%s\n", work->path);
}
static void _httpd_app_hello_after_work(tf_ssb_t* ssb, int status, void* user_data)
{
app_hello_t* work = user_data;
JSContext* context = tf_ssb_get_context(ssb);
tf_http_request_unref(work->app->request);
JS_FreeCString(context, work->path);
JS_FreeValue(context, work->message);
tf_free(work);
}
static void _httpd_app_message_hello(app_t* work, JSValue message)
{
/* TODO */
JSContext* context = work->request->context;
tf_task_t* task = tf_task_get(context);
tf_ssb_t* ssb = tf_task_get_ssb(task);
tf_http_request_ref(work->request);
JSValue path = JS_GetPropertyStr(context, message, "path");
app_hello_t* hello = tf_malloc(sizeof(app_hello_t));
*hello = (app_hello_t) {
.app = work,
.message = JS_DupValue(context, message),
.path = JS_ToCString(context, path),
};
JS_FreeValue(context, path);
tf_ssb_run_work(ssb, _httpd_app_hello_work, _httpd_app_hello_after_work, hello);
}
static bool _httpd_app_message_call_client_api(app_t* work, JSValue message, const char* action_string)
@@ -301,8 +336,6 @@ static void _httpd_app_on_message(tf_http_request_t* request, int op_code, const
/* BINARY */
case 0x2:
{
char* copy = tf_malloc(size + 1);
memcpy(copy, data, size);
JSValue message = JS_ParseJSON(context, data, size, NULL);
if (JS_IsException(message) || !JS_IsObject(message))
{

View File

@@ -1122,8 +1122,7 @@ static bool _tf_ssb_verify_and_strip_signature_internal(
const char* sigstr = JS_ToCString(context, sigval);
const char* sigkind = strstr(str, ".sig.ed25519");
JSValue authorval = JS_GetPropertyStr(context, val, "author");
const char* author = JS_ToCString(context, authorval);
const char* author = tf_util_get_property_as_string(context, val, "author");
const char* author_id = author && *author == '@' ? author + 1 : author;
const char* type = strstr(author_id, ".ed25519");
@@ -1172,7 +1171,6 @@ static bool _tf_ssb_verify_and_strip_signature_internal(
JS_FreeCString(context, sigstr);
JS_FreeCString(context, str);
JS_FreeValue(context, sigval);
JS_FreeValue(context, authorval);
if (verified)
{
JS_FreeValue(context, signature);
@@ -3046,25 +3044,21 @@ static void _tf_ssb_connection_tunnel_callback(
tf_ssb_connection_rpc_send(connection, flags, -request_number, NULL, (const uint8_t*)"false", strlen("false"), NULL, NULL, NULL);
JSContext* context = tf_ssb_connection_get_context(connection);
JSValue message_val = JS_GetPropertyStr(context, args, "message");
JSValue stack_val = JS_GetPropertyStr(context, args, "stack");
const char* message_string = tf_util_get_property_as_string(context, args, "message");
const char* stack_string = tf_util_get_property_as_string(context, args, "stack");
char buffer[1024];
if (!JS_IsUndefined(message_val))
if (message_string)
{
const char* message_string = JS_ToCString(context, message_val);
const char* stack_string = JS_ToCString(context, stack_val);
snprintf(buffer, sizeof(buffer), "Tunnel error: %s\n%s", message_string, stack_string);
JS_FreeCString(context, message_string);
JS_FreeCString(context, stack_string);
}
else
{
snprintf(buffer, sizeof(buffer), "Tunnel error: %.*s", (int)size, message);
}
JS_FreeValue(context, stack_val);
JS_FreeValue(context, message_val);
JS_FreeCString(context, message_string);
JS_FreeCString(context, stack_string);
tf_ssb_connection_close(tunnel, buffer);
}
@@ -4049,8 +4043,7 @@ void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int32_t sequ
if (!JS_IsUndefined(message_keys))
{
JSValue message = JS_GetPropertyStr(context, message_keys, "value");
JSValue author = JS_GetPropertyStr(context, message, "author");
const char* author_string = JS_ToCString(context, author);
const char* author_string = tf_util_get_property_as_string(context, message, "author");
for (tf_ssb_connection_t* connection = ssb->connections; connection; connection = connection->next)
{
@@ -4068,7 +4061,6 @@ void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int32_t sequ
}
JS_FreeCString(context, author_string);
JS_FreeValue(context, author);
JS_FreeValue(context, message);
}
}

View File

@@ -125,9 +125,7 @@ static void _tf_ssb_rpc_blobs_get(tf_ssb_connection_t* connection, uint8_t flags
}
else
{
JSValue key = JS_GetPropertyStr(context, arg, "key");
id = JS_ToCString(context, key);
JS_FreeValue(context, key);
id = tf_util_get_property_as_string(context, arg, "key");
}
blobs_get_work_t* work = tf_malloc(sizeof(blobs_get_work_t));
@@ -313,25 +311,20 @@ static void _tf_ssb_rpc_tunnel_callback(tf_ssb_connection_t* connection, uint8_t
tf_ssb_connection_remove_request(connection, request_number);
JSContext* context = tf_ssb_connection_get_context(connection);
JSValue message_val = JS_GetPropertyStr(context, args, "message");
JSValue stack_val = JS_GetPropertyStr(context, args, "stack");
const char* message_string = tf_util_get_property_as_string(context, args, "message");
const char* stack_string = tf_util_get_property_as_string(context, args, "stack");
char buffer[1024];
if (!JS_IsUndefined(message_val))
if (message_string)
{
const char* message_string = JS_ToCString(context, message_val);
const char* stack_string = JS_ToCString(context, stack_val);
snprintf(buffer, sizeof(buffer), "Error from tunnel: %s\n%s", message_string, stack_string);
JS_FreeCString(context, message_string);
JS_FreeCString(context, stack_string);
}
else
{
snprintf(buffer, sizeof(buffer), "Error from tunnel: %.*s", (int)size, message);
}
JS_FreeValue(context, stack_val);
JS_FreeValue(context, message_val);
JS_FreeCString(context, message_string);
JS_FreeCString(context, stack_string);
}
else
{
@@ -762,8 +755,7 @@ static void _tf_ssb_rpc_connection_room_attendants_callback(
{
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
JSContext* context = tf_ssb_get_context(ssb);
JSValue type = JS_GetPropertyStr(context, args, "type");
const char* type_string = JS_ToCString(context, type);
const char* type_string = tf_util_get_property_as_string(context, args, "type");
if (!type_string)
{
tf_ssb_connection_rpc_send_error(connection, flags, -request_number, "Missing type.");
@@ -788,25 +780,21 @@ static void _tf_ssb_rpc_connection_room_attendants_callback(
}
else if (strcmp(type_string, "joined") == 0)
{
JSValue id = JS_GetPropertyStr(context, args, "id");
const char* id_string = JS_ToCString(context, id);
const char* id_string = tf_util_get_property_as_string(context, args, "id");
if (id_string)
{
tf_ssb_connection_add_room_attendant(connection, id_string);
}
JS_FreeCString(context, id_string);
JS_FreeValue(context, id);
}
else if (strcmp(type_string, "left") == 0)
{
JSValue id = JS_GetPropertyStr(context, args, "id");
const char* id_string = JS_ToCString(context, id);
const char* id_string = tf_util_get_property_as_string(context, args, "id");
if (id_string)
{
tf_ssb_connection_remove_room_attendant(connection, id_string);
}
JS_FreeCString(context, id_string);
JS_FreeValue(context, id);
}
else
{
@@ -815,20 +803,17 @@ static void _tf_ssb_rpc_connection_room_attendants_callback(
tf_ssb_connection_rpc_send_error(connection, flags, -request_number, buffer);
}
JS_FreeCString(context, type_string);
JS_FreeValue(context, type);
}
static bool _is_error(JSContext* context, JSValue message)
{
JSValue name = JS_GetPropertyStr(context, message, "name");
const char* name_string = JS_ToCString(context, name);
const char* name_string = tf_util_get_property_as_string(context, message, "name");
bool is_error = false;
if (name_string && strcmp(name_string, "Error") == 0)
{
is_error = true;
}
JS_FreeCString(context, name_string);
JS_FreeValue(context, name);
return is_error;
}
@@ -1937,12 +1922,10 @@ static void _tf_ssb_rpc_invite_use(tf_ssb_connection_t* connection, uint8_t flag
JSContext* context = tf_ssb_connection_get_context(connection);
JSValue array = JS_GetPropertyStr(context, args, "args");
JSValue object = JS_GetPropertyUint32(context, array, 0);
JSValue feed = JS_GetPropertyStr(context, object, "feed");
tf_ssb_connection_get_id(connection, work->invite_public_key, sizeof(work->invite_public_key));
const char* id = JS_ToCString(context, feed);
const char* id = tf_util_get_property_as_string(context, object, "feed");
tf_string_set(work->id, sizeof(work->id), id);
JS_FreeCString(context, id);
JS_FreeValue(context, feed);
JS_FreeValue(context, object);
JS_FreeValue(context, array);
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_invite_use_work, _tf_ssb_rpc_invite_use_after_work, work);

View File

@@ -502,6 +502,24 @@ int tf_util_get_length(JSContext* context, JSValue value)
return result;
}
const char* tf_util_get_property_as_string(JSContext* context, JSValue object, const char* key)
{
if (!JS_IsObject(object))
{
return NULL;
}
JSValue value = JS_GetPropertyStr(context, object, key);
if (JS_IsUndefined(value))
{
return NULL;
}
const char* string = JS_ToCString(context, value);
JS_FreeValue(context, value);
return string;
}
int tf_util_insert_index(const void* key, const void* base, size_t count, size_t size, int (*compare)(const void*, const void*))
{
int lower = 0;

View File

@@ -71,6 +71,15 @@ bool tf_util_report_error(JSContext* context, JSValue value);
*/
int tf_util_get_length(JSContext* context, JSValue value);
/**
** Get an object property by string key as a C-style string.
** @param context The JS context.
** @param object The object.
** @param key The property key.
** @return A string to be freed with JS_FreeCString() or NULL.
*/
const char* tf_util_get_property_as_string(JSContext* context, JSValue object, const char* key);
/**
** Get the index at which to insert into an array in order to preserve sorted order.
** @param key The key being inserted.