ssb.js is now entirely in C. Usual disclaimers about it not being amazingly well tested.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4111 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
53e4f4341c
commit
69253432b8
@ -342,7 +342,6 @@ async function getProcessBlob(blobId, key, options) {
|
||||
});
|
||||
}
|
||||
};
|
||||
delete imports.ssb.addRpc;
|
||||
|
||||
if (process.credentials &&
|
||||
process.credentials.session &&
|
||||
|
164
core/ssb.js
164
core/ssb.js
@ -1,164 +0,0 @@
|
||||
"use strict";
|
||||
var g_database = new Database('core');
|
||||
const k_use_create_history_stream = false;
|
||||
|
||||
function get_latest_sequence_for_author(author) {
|
||||
var sequence = 0;
|
||||
ssb.sqlStream(
|
||||
'SELECT MAX(sequence) AS sequence FROM messages WHERE author = ?1',
|
||||
[author],
|
||||
function(row) {
|
||||
if (row.sequence) {
|
||||
sequence = row.sequence;
|
||||
}
|
||||
});
|
||||
return sequence;
|
||||
}
|
||||
|
||||
function storeMessage(message) {
|
||||
var payload = message.message.value ? message.message.value : message.message;
|
||||
if (typeof(payload) == 'object') {
|
||||
ssb.storeMessage(payload);
|
||||
}
|
||||
}
|
||||
|
||||
ssb.addEventListener('connections', function on_connections_changed(change, connection) {
|
||||
if (change == 'add') {
|
||||
var sequence = get_latest_sequence_for_author(connection.id);
|
||||
if (k_use_create_history_stream) {
|
||||
connection.send_json({'name': ['createHistoryStream'], 'type': 'source', 'args': [{'id': connection.id, 'seq': sequence, 'live': true, 'keys': false}]}, storeMessage);
|
||||
var identities = ssb.getAllIdentities();
|
||||
let ids = ssb.followingDeep(identities, 2);
|
||||
for (let id of ids) {
|
||||
if (identities.indexOf(id) != -1) {
|
||||
continue;
|
||||
}
|
||||
var sequence = get_latest_sequence_for_author(id);
|
||||
connection.send_json({'name': ['createHistoryStream'], 'type': 'source', 'args': [{'id': id, 'seq': sequence, 'live': true, 'keys': false}]}, storeMessage);
|
||||
}
|
||||
} else {
|
||||
if (connection.is_client) {
|
||||
connection.send_json({"name": ["ebt", "replicate"], "args": [{"version": 3, "format": "classic"}], "type": "duplex"}, ebtReplicateClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function ebtReplicateSendClock(request, have) {
|
||||
var identities = ssb.getAllIdentities();
|
||||
var message = {};
|
||||
var last_sent = request.connection.sent_clock || {};
|
||||
var ids = ssb.followingDeep(identities, 2).concat([request.connection.id]);
|
||||
if (!Object.keys(last_sent).length) {
|
||||
for (let id of ids) {
|
||||
message[id] = get_latest_sequence_for_author(id);
|
||||
}
|
||||
}
|
||||
for (let id of Object.keys(have)) {
|
||||
if (message[id] === undefined) {
|
||||
var sequence = get_latest_sequence_for_author(id);
|
||||
message[id] = sequence ? sequence : -1;
|
||||
}
|
||||
}
|
||||
|
||||
var to_send = {}
|
||||
var offset = Math.floor(Math.random() * ids.length);
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var id = ids[(i + offset) % ids.length];
|
||||
if (last_sent[id] === undefined || message[id] > last_sent[id]) {
|
||||
last_sent[id] = to_send[id] = message[id] === -1 ? -1 : message[id] << 1;
|
||||
}
|
||||
if (Object.keys(to_send).length >= 32) {
|
||||
request.send_json(to_send);
|
||||
to_send = {};
|
||||
}
|
||||
}
|
||||
request.connection.sent_clock = last_sent;
|
||||
|
||||
if (Object.keys(to_send).length) {
|
||||
request.send_json(to_send);
|
||||
}
|
||||
}
|
||||
|
||||
function formatMessage(row) {
|
||||
if (row.sequence_before_author) {
|
||||
return {
|
||||
previous: row.previous,
|
||||
sequence: row.sequence,
|
||||
author: row.author,
|
||||
timestamp: row.timestamp,
|
||||
hash: row.hash,
|
||||
content: JSON.parse(row.content),
|
||||
signature: row.signature,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
previous: row.previous,
|
||||
author: row.author,
|
||||
sequence: row.sequence,
|
||||
timestamp: row.timestamp,
|
||||
hash: row.hash,
|
||||
content: JSON.parse(row.content),
|
||||
signature: row.signature,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function ebtReplicateRegisterMessageCallback(request) {
|
||||
ssb.addEventListener('message', function(message_id) {
|
||||
if (request.connection.send_clock) {
|
||||
ssb.sqlStream(
|
||||
'SELECT previous, author, id, sequence, timestamp, hash, content, signature FROM messages WHERE id = ?1',
|
||||
[message_id],
|
||||
function (row) {
|
||||
if (request.connection.send_clock[row.author] < row.sequence) {
|
||||
request.send_json(formatMessage(row));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function ebtReplicateCommon(request) {
|
||||
if (request.message.author) {
|
||||
storeMessage(request);
|
||||
} else {
|
||||
ebtReplicateSendClock(request, request.message);
|
||||
|
||||
if (!request.connection.send_clock) {
|
||||
request.connection.send_clock = {};
|
||||
}
|
||||
for (let id of Object.keys(request.message)) {
|
||||
if (request.message[id] >= 0 && (request.message[id] & 1) == 0) {
|
||||
request.connection.send_clock[id] = request.message[id] >> 1;
|
||||
ssb.sqlStream(
|
||||
'SELECT previous, author, id, sequence, timestamp, hash, content, signature FROM messages WHERE author = ?1 AND sequence >= ?2 ORDER BY sequence',
|
||||
[id, request.message[id] >> 1],
|
||||
function (row) {
|
||||
request.send_json(formatMessage(row));
|
||||
request.connection.send_clock[id] = row.sequence;
|
||||
});
|
||||
} else {
|
||||
delete request.connection.send_clock[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ebtReplicateClient(request) {
|
||||
if (request.message?.name !== 'Error') {
|
||||
if (!request.connection.message_registered) {
|
||||
ebtReplicateRegisterMessageCallback(request);
|
||||
request.connection.message_registered = true;
|
||||
}
|
||||
ebtReplicateCommon(request);
|
||||
}
|
||||
}
|
||||
|
||||
function ebtReplicateServer(request) {
|
||||
ebtReplicateRegisterMessageCallback(request);
|
||||
ebtReplicateSendClock(request, {});
|
||||
request.more(ebtReplicateCommon);
|
||||
}
|
||||
|
||||
ssb.addRpc(['ebt', 'replicate'], ebtReplicateServer);
|
135
src/ssb.c
135
src/ssb.c
@ -206,6 +206,9 @@ typedef struct _tf_ssb_connection_t
|
||||
int32_t tunnel_request_number;
|
||||
|
||||
tf_ssb_blob_wants_t blob_wants;
|
||||
bool sent_clock;
|
||||
int32_t ebt_request_number;
|
||||
JSValue ebt_send_clock;
|
||||
|
||||
JSValue object;
|
||||
|
||||
@ -521,6 +524,8 @@ void tf_ssb_connection_add_new_message_request(tf_ssb_connection_t* connection,
|
||||
int index = tf_util_insert_index(author, connection->message_requests, connection->message_requests_count, sizeof(tf_ssb_connection_message_request_t), _message_request_compare);
|
||||
if (index < connection->message_requests_count && strcmp(author, connection->message_requests[index].author) == 0)
|
||||
{
|
||||
connection->message_requests[index].request_number = request_number;
|
||||
connection->message_requests[index].keys = keys;
|
||||
return;
|
||||
}
|
||||
connection->message_requests = tf_resize_vec(connection->message_requests, sizeof(tf_ssb_connection_message_request_t) * (connection->message_requests_count + 1));
|
||||
@ -537,6 +542,16 @@ void tf_ssb_connection_add_new_message_request(tf_ssb_connection_t* connection,
|
||||
connection->message_requests_count++;
|
||||
}
|
||||
|
||||
void tf_ssb_connection_remove_new_message_request(tf_ssb_connection_t* connection, const char* author)
|
||||
{
|
||||
int index = tf_util_insert_index(author, connection->message_requests, connection->message_requests_count, sizeof(tf_ssb_connection_message_request_t), _message_request_compare);
|
||||
if (index < connection->message_requests_count && strcmp(author, connection->message_requests[index].author) == 0)
|
||||
{
|
||||
memmove(connection->message_requests + index, connection->message_requests + index + 1, sizeof(tf_ssb_connection_message_request_t) * (connection->message_requests_count - index));
|
||||
connection->message_requests_count--;
|
||||
}
|
||||
}
|
||||
|
||||
static void _tf_ssb_connection_remove_request(tf_ssb_connection_t* connection, int32_t request_number)
|
||||
{
|
||||
tf_ssb_request_t* request = bsearch(&request_number, connection->requests, connection->requests_count, sizeof(tf_ssb_request_t), _request_compare);
|
||||
@ -1593,6 +1608,8 @@ void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const char* rea
|
||||
!connection->tcp.data &&
|
||||
!connection->connect.data)
|
||||
{
|
||||
JS_FreeValue(ssb->context, connection->ebt_send_clock);
|
||||
connection->ebt_send_clock = JS_UNDEFINED;
|
||||
tf_free(connection->message_requests);
|
||||
connection->message_requests = NULL;
|
||||
connection->message_requests_count = 0;
|
||||
@ -2079,64 +2096,6 @@ static void _tf_ssb_connection_finalizer(JSRuntime* runtime, JSValue value)
|
||||
}
|
||||
}
|
||||
|
||||
static void _tf_ssb_connection_send_json_response(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
|
||||
{
|
||||
if (!user_data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void _tf_ssb_on_rpc(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data);
|
||||
_tf_ssb_on_rpc(connection, flags, request_number, args, message, size, user_data);
|
||||
}
|
||||
|
||||
static void _tf_ssb_connection_cleanup_value(tf_ssb_t* ssb, void* user_data)
|
||||
{
|
||||
if (user_data)
|
||||
{
|
||||
JSValue callback = JS_MKPTR(JS_TAG_OBJECT, user_data);
|
||||
JS_FreeValue(tf_ssb_get_context(ssb), callback);
|
||||
}
|
||||
}
|
||||
|
||||
static JSValue _tf_ssb_connection_send_json_internal(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int flags)
|
||||
{
|
||||
tf_ssb_connection_t* connection = JS_GetOpaque(this_val, _connection_class_id);
|
||||
if (!connection)
|
||||
{
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
uint32_t request_number = tf_ssb_connection_next_request_number(connection);
|
||||
|
||||
JSValue message_val = JS_JSONStringify(context, argv[0], JS_NULL, JS_NULL);
|
||||
size_t size;
|
||||
const char* message = JS_ToCStringLen(context, &size, message_val);
|
||||
JS_FreeValue(context, message_val);
|
||||
|
||||
tf_ssb_connection_rpc_send(
|
||||
connection,
|
||||
flags,
|
||||
request_number,
|
||||
(const uint8_t*)message,
|
||||
size,
|
||||
_tf_ssb_connection_send_json_response,
|
||||
_tf_ssb_connection_cleanup_value,
|
||||
JS_IsFunction(context, argv[1]) ? JS_VALUE_GET_PTR(JS_DupValue(context, argv[1])) : NULL);
|
||||
JS_FreeCString(context, message);
|
||||
return JS_NewInt32(context, request_number);
|
||||
}
|
||||
|
||||
static JSValue _tf_ssb_connection_send_json(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
return _tf_ssb_connection_send_json_internal(context, this_val, argc, argv, k_ssb_rpc_flag_json | k_ssb_rpc_flag_stream);
|
||||
}
|
||||
|
||||
static JSValue _tf_ssb_connection_send_json_async(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
return _tf_ssb_connection_send_json_internal(context, this_val, argc, argv, k_ssb_rpc_flag_json);
|
||||
}
|
||||
|
||||
static void _tf_ssb_connection_process_message_async(uv_async_t* async)
|
||||
{
|
||||
tf_ssb_connection_t* connection = async->data;
|
||||
@ -2181,8 +2140,6 @@ tf_ssb_connection_t* tf_ssb_connection_create(tf_ssb_t* ssb, const char* host, c
|
||||
|
||||
connection->object = JS_NewObjectClass(ssb->context, _connection_class_id);
|
||||
JS_SetOpaque(connection->object, connection);
|
||||
JS_SetPropertyStr(context, connection->object, "send_json", JS_NewCFunction(context, _tf_ssb_connection_send_json, "send_json", 2));
|
||||
JS_SetPropertyStr(context, connection->object, "send_json_async", JS_NewCFunction(context, _tf_ssb_connection_send_json_async, "send_json_async", 2));
|
||||
char public_key_str[k_id_base64_len] = { 0 };
|
||||
if (tf_ssb_id_bin_to_str(public_key_str, sizeof(public_key_str), public_key))
|
||||
{
|
||||
@ -2240,8 +2197,6 @@ tf_ssb_connection_t* tf_ssb_connection_tunnel_create(tf_ssb_t* ssb, const char*
|
||||
|
||||
tunnel->object = JS_NewObjectClass(ssb->context, _connection_class_id);
|
||||
JS_SetOpaque(tunnel->object, tunnel);
|
||||
JS_SetPropertyStr(context, tunnel->object, "send_json", JS_NewCFunction(context, _tf_ssb_connection_send_json, "send_json", 2));
|
||||
JS_SetPropertyStr(context, tunnel->object, "send_json_async", JS_NewCFunction(context, _tf_ssb_connection_send_json_async, "send_json_async", 2));
|
||||
JS_SetPropertyStr(context, tunnel->object, "id", JS_NewString(context, target_id));
|
||||
JS_SetPropertyStr(context, tunnel->object, "is_client", JS_TRUE);
|
||||
|
||||
@ -2340,8 +2295,6 @@ static void _tf_ssb_on_connection(uv_stream_t* stream, int status)
|
||||
uv_async_init(ssb->loop, &connection->async, _tf_ssb_connection_process_message_async);
|
||||
|
||||
connection->object = JS_NewObjectClass(ssb->context, _connection_class_id);
|
||||
JS_SetPropertyStr(ssb->context, connection->object, "send_json", JS_NewCFunction(ssb->context, _tf_ssb_connection_send_json, "send_json", 2));
|
||||
JS_SetPropertyStr(ssb->context, connection->object, "send_json_async", JS_NewCFunction(ssb->context, _tf_ssb_connection_send_json_async, "send_json_async", 2));
|
||||
JS_SetOpaque(connection->object, connection);
|
||||
|
||||
if (uv_tcp_init(ssb->loop, &connection->tcp) != 0)
|
||||
@ -3118,3 +3071,57 @@ tf_ssb_blob_wants_t* tf_ssb_connection_get_blob_wants_state(tf_ssb_connection_t*
|
||||
{
|
||||
return connection ? &connection->blob_wants : NULL;
|
||||
}
|
||||
|
||||
bool tf_ssb_verify_strip_and_store_message(tf_ssb_t* ssb, JSValue value)
|
||||
{
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
char signature[crypto_sign_BYTES + 128];
|
||||
char id[crypto_hash_sha256_BYTES * 2 + 1];
|
||||
bool sequence_before_author = false;
|
||||
if (tf_ssb_verify_and_strip_signature(context, value, id, sizeof(id), signature, sizeof(signature), &sequence_before_author))
|
||||
{
|
||||
if (tf_ssb_db_store_message(ssb, context, id, value, signature, sequence_before_author))
|
||||
{
|
||||
tf_ssb_notify_message_added(ssb, id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("failed to verify message\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tf_ssb_connection_get_sent_clock(tf_ssb_connection_t* connection)
|
||||
{
|
||||
return connection->sent_clock;
|
||||
}
|
||||
|
||||
void tf_ssb_connection_set_sent_clock(tf_ssb_connection_t* connection, bool sent_clock)
|
||||
{
|
||||
connection->sent_clock = sent_clock;
|
||||
}
|
||||
|
||||
int32_t tf_ssb_connection_get_ebt_request_number(tf_ssb_connection_t* connection)
|
||||
{
|
||||
return connection->ebt_request_number;
|
||||
}
|
||||
|
||||
void tf_ssb_connection_set_ebt_request_number(tf_ssb_connection_t* connection, int32_t request_number)
|
||||
{
|
||||
connection->ebt_request_number = request_number;
|
||||
}
|
||||
|
||||
JSValue tf_ssb_connection_get_ebt_send_clock(tf_ssb_connection_t* connection)
|
||||
{
|
||||
JSContext* context = connection->ssb->context;
|
||||
return JS_DupValue(context, connection->ebt_send_clock);
|
||||
}
|
||||
|
||||
void tf_ssb_connection_set_ebt_send_clock(tf_ssb_connection_t* connection, JSValue send_clock)
|
||||
{
|
||||
JSContext* context = connection->ssb->context;
|
||||
JS_FreeValue(context, connection->ebt_send_clock);
|
||||
connection->ebt_send_clock = JS_DupValue(context, send_clock);
|
||||
}
|
||||
|
28
src/ssb.db.c
28
src/ssb.db.c
@ -1102,6 +1102,34 @@ const char** tf_ssb_db_following_deep(tf_ssb_t* ssb, const char** ids, int count
|
||||
return (const char**)result;
|
||||
}
|
||||
|
||||
typedef struct _identities_t
|
||||
{
|
||||
const char** ids;
|
||||
int count;
|
||||
} identities_t;
|
||||
|
||||
static void _add_identity(const char* identity, void* user_data)
|
||||
{
|
||||
identities_t* identities = user_data;
|
||||
char full_id[k_id_base64_len];
|
||||
snprintf(full_id, sizeof(full_id), "@%s", identity);
|
||||
identities->ids = tf_resize_vec(identities->ids, sizeof(const char*) * (identities->count + 1));
|
||||
identities->ids[identities->count++] = tf_strdup(full_id);
|
||||
}
|
||||
|
||||
const char** tf_ssb_db_get_all_visible_identities(tf_ssb_t* ssb, int depth)
|
||||
{
|
||||
identities_t identities = { 0 };
|
||||
tf_ssb_db_identity_visit_all(ssb, _add_identity, &identities);
|
||||
const char** following = tf_ssb_db_following_deep(ssb, identities.ids, identities.count, depth);
|
||||
for (int i = 0; i < identities.count; i++)
|
||||
{
|
||||
tf_free((void*)identities.ids[i]);
|
||||
}
|
||||
tf_free(identities.ids);
|
||||
return following;
|
||||
}
|
||||
|
||||
static void _test_private(sqlite3* db, const uint8_t* private_key)
|
||||
{
|
||||
sqlite3_stmt* statement = NULL;
|
||||
|
@ -38,5 +38,6 @@ JSValue tf_ssb_format_message(
|
||||
const char* signature,
|
||||
bool sequence_before_author);
|
||||
const char** tf_ssb_db_following_deep(tf_ssb_t* ssb, const char** ids, int count, int depth);
|
||||
const char** tf_ssb_db_get_all_visible_identities(tf_ssb_t* ssb, int depth);
|
||||
|
||||
void tf_ssb_db_private(sqlite3* db);
|
||||
|
@ -106,6 +106,7 @@ bool tf_ssb_id_bin_to_str(char* str, size_t str_size, const uint8_t* bin);
|
||||
|
||||
bool tf_ssb_verify_and_strip_signature(JSContext* context, JSValue val, char* out_id, size_t out_id_size, char* out_signature, size_t out_signature_size, bool* out_sequence_before_author);
|
||||
void tf_ssb_calculate_message_id(JSContext* context, JSValue message, char* out_id, size_t out_id_size);
|
||||
bool tf_ssb_verify_strip_and_store_message(tf_ssb_t* ssb, JSValue value);
|
||||
|
||||
bool tf_ssb_connection_is_client(tf_ssb_connection_t* connection);
|
||||
const char* tf_ssb_connection_get_host(tf_ssb_connection_t* connection);
|
||||
@ -152,6 +153,7 @@ void tf_ssb_connection_rpc_send_error_method_not_allowed(tf_ssb_connection_t* co
|
||||
void tf_ssb_connection_add_request(tf_ssb_connection_t* connection, int32_t request_number, tf_ssb_rpc_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data, tf_ssb_connection_t* dependent_connection);
|
||||
|
||||
void tf_ssb_connection_add_new_message_request(tf_ssb_connection_t* connection, const char* author, int32_t request_number, bool keys);
|
||||
void tf_ssb_connection_remove_new_message_request(tf_ssb_connection_t* connection, const char* author);
|
||||
|
||||
bool tf_ssb_connection_is_attendant(tf_ssb_connection_t* connection);
|
||||
int32_t tf_ssb_connection_get_attendant_request_number(tf_ssb_connection_t* connection);
|
||||
@ -162,6 +164,13 @@ void tf_ssb_connection_remove_room_attendant(tf_ssb_connection_t* connection, co
|
||||
|
||||
tf_ssb_connection_t* tf_ssb_connection_tunnel_create(tf_ssb_t* ssb, const char* portal_id, int32_t request_number, const char* target_id);
|
||||
|
||||
int32_t tf_ssb_connection_get_ebt_request_number(tf_ssb_connection_t* connection);
|
||||
void tf_ssb_connection_set_ebt_request_number(tf_ssb_connection_t* connection, int32_t request_number);
|
||||
JSValue tf_ssb_connection_get_ebt_send_clock(tf_ssb_connection_t* connection);
|
||||
void tf_ssb_connection_set_ebt_send_clock(tf_ssb_connection_t* connection, JSValue send_clock);
|
||||
bool tf_ssb_connection_get_sent_clock(tf_ssb_connection_t* connection);
|
||||
void tf_ssb_connection_set_sent_clock(tf_ssb_connection_t* connection, bool sent_clock);
|
||||
|
||||
JSClassID tf_ssb_get_connection_class_id();
|
||||
|
||||
void tf_ssb_get_stats(tf_ssb_t* ssb, tf_ssb_stats_t* out_stats);
|
||||
|
213
src/ssb.js.c
213
src/ssb.js.c
@ -313,20 +313,7 @@ static JSValue _tf_ssb_sqlStream(JSContext* context, JSValueConst this_val, int
|
||||
static JSValue _tf_ssb_storeMessage(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
char signature[crypto_sign_BYTES + 128];
|
||||
char id[crypto_hash_sha256_BYTES * 2 + 1];
|
||||
bool sequence_before_author = false;
|
||||
if (tf_ssb_verify_and_strip_signature(context, argv[0], id, sizeof(id), signature, sizeof(signature), &sequence_before_author))
|
||||
{
|
||||
if (tf_ssb_db_store_message(ssb, context, id, argv[0], signature, sequence_before_author))
|
||||
{
|
||||
tf_ssb_notify_message_added(ssb, id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("failed to verify message\n");
|
||||
}
|
||||
tf_ssb_verify_strip_and_store_message(ssb, argv[0]);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
@ -416,205 +403,12 @@ static JSValue _tf_ssb_connect(JSContext* context, JSValueConst this_val, int ar
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue _tf_ssb_rpc_send_json(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
JSValue connection_val = JS_GetPropertyStr(context, this_val, "connection");
|
||||
tf_ssb_connection_t* connection = JS_GetOpaque(connection_val, tf_ssb_get_connection_class_id());
|
||||
JSValue request_val = JS_GetPropertyStr(context, this_val, "request_number");
|
||||
int32_t request_number;
|
||||
JS_ToInt32(context, &request_number, request_val);
|
||||
JS_FreeValue(context, request_val);
|
||||
|
||||
JSValue flags_val = JS_GetPropertyStr(context, this_val, "flags");
|
||||
int32_t flags_number;
|
||||
JS_ToInt32(context, &flags_number, flags_val);
|
||||
JS_FreeValue(context, flags_val);
|
||||
|
||||
JSValue message_val = JS_JSONStringify(context, argv[0], JS_NULL, JS_NULL);
|
||||
size_t size;
|
||||
const char* message = JS_ToCStringLen(context, &size, message_val);
|
||||
|
||||
tf_ssb_connection_rpc_send(
|
||||
connection,
|
||||
k_ssb_rpc_flag_json | (flags_number & ~k_ssb_rpc_mask_type),
|
||||
-request_number,
|
||||
(const uint8_t*)message,
|
||||
size,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
JS_FreeValue(context, connection_val);
|
||||
JS_FreeCString(context, message);
|
||||
JS_FreeValue(context, message_val);
|
||||
return JS_NewInt32(context, -request_number);
|
||||
}
|
||||
|
||||
static JSValue _tf_ssb_rpc_send_json_end(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
JSValue connection_val = JS_GetPropertyStr(context, this_val, "connection");
|
||||
tf_ssb_connection_t* connection = JS_GetOpaque(connection_val, tf_ssb_get_connection_class_id());
|
||||
JSValue request_val = JS_GetPropertyStr(context, this_val, "request_number");
|
||||
int32_t request_number;
|
||||
JS_ToInt32(context, &request_number, request_val);
|
||||
JS_FreeValue(context, request_val);
|
||||
|
||||
JSValue flags_val = JS_GetPropertyStr(context, this_val, "flags");
|
||||
int32_t flags_number;
|
||||
JS_ToInt32(context, &flags_number, flags_val);
|
||||
JS_FreeValue(context, flags_val);
|
||||
|
||||
JSValue message_val = JS_JSONStringify(context, argv[0], JS_NULL, JS_NULL);
|
||||
size_t size;
|
||||
const char* message = JS_ToCStringLen(context, &size, message_val);
|
||||
|
||||
tf_ssb_connection_rpc_send(
|
||||
connection,
|
||||
k_ssb_rpc_flag_json | (flags_number & ~k_ssb_rpc_mask_type) | k_ssb_rpc_flag_end_error,
|
||||
-request_number,
|
||||
(const uint8_t*)message,
|
||||
size,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
JS_FreeValue(context, connection_val);
|
||||
JS_FreeCString(context, message);
|
||||
JS_FreeValue(context, message_val);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static void _tf_ssb_cleanup_value(tf_ssb_t* ssb, void* user_data)
|
||||
{
|
||||
JSValue callback = JS_MKPTR(JS_TAG_OBJECT, user_data);
|
||||
JS_FreeValue(tf_ssb_get_context(ssb), callback);
|
||||
}
|
||||
|
||||
static JSValue _tf_ssb_rpc_more(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
JSValue connection_val = JS_GetPropertyStr(context, this_val, "connection");
|
||||
tf_ssb_connection_t* connection = JS_GetOpaque(connection_val, tf_ssb_get_connection_class_id());
|
||||
JSValue request_val = JS_GetPropertyStr(context, this_val, "request_number");
|
||||
int32_t request_number;
|
||||
JS_ToInt32(context, &request_number, request_val);
|
||||
JS_FreeValue(context, request_val);
|
||||
|
||||
tf_ssb_connection_add_request(connection, -request_number, _tf_ssb_on_rpc, _tf_ssb_cleanup_value, JS_VALUE_GET_PTR(JS_DupValue(context, argv[0])), NULL);
|
||||
|
||||
JS_FreeValue(context, connection_val);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue _tf_ssb_rpc_send_binary(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
JSValue connection_val = JS_GetPropertyStr(context, this_val, "connection");
|
||||
tf_ssb_connection_t* connection = JS_GetOpaque(connection_val, tf_ssb_get_connection_class_id());
|
||||
JSValue request_val = JS_GetPropertyStr(context, this_val, "request_number");
|
||||
int32_t request_number;
|
||||
JS_ToInt32(context, &request_number, request_val);
|
||||
JS_FreeValue(context, request_val);
|
||||
|
||||
size_t size;
|
||||
uint8_t* message = tf_util_try_get_array_buffer(context, &size, argv[0]);
|
||||
if (message)
|
||||
{
|
||||
tf_ssb_connection_rpc_send(
|
||||
connection,
|
||||
k_ssb_rpc_flag_binary | k_ssb_rpc_flag_stream,
|
||||
-request_number,
|
||||
(const uint8_t*)message,
|
||||
size,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t offset;
|
||||
size_t element_size;
|
||||
JSValue buffer = tf_util_try_get_typed_array_buffer(context, argv[0], &offset, &size, &element_size);
|
||||
if (!JS_IsException(buffer))
|
||||
{
|
||||
size_t total_size;
|
||||
message = tf_util_try_get_array_buffer(context, &total_size, buffer);
|
||||
if (message)
|
||||
{
|
||||
tf_ssb_connection_rpc_send(
|
||||
connection,
|
||||
k_ssb_rpc_flag_binary | k_ssb_rpc_flag_stream,
|
||||
-request_number,
|
||||
(const uint8_t*)message + offset,
|
||||
size,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
JS_FreeValue(context, buffer);
|
||||
}
|
||||
JS_FreeValue(context, connection_val);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void _tf_ssb_on_rpc(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
|
||||
{
|
||||
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
JSValue callback = JS_MKPTR(JS_TAG_OBJECT, user_data);
|
||||
JSValue object = JS_NewObject(context);
|
||||
JSValue connection_object = JS_DupValue(context, tf_ssb_connection_get_object(connection));
|
||||
JS_SetPropertyStr(context, object, "connection", connection_object);
|
||||
JS_SetPropertyStr(context, object, "flags", JS_NewUint32(context, flags));
|
||||
JS_SetPropertyStr(context, object, "request_number", JS_NewInt32(context, request_number));
|
||||
JS_SetPropertyStr(context, object, "args", JS_GetPropertyStr(context, args, "args"));
|
||||
JS_SetPropertyStr(context, object, "message", message && size ? JS_NewArrayBufferCopy(context, message, size) : JS_DupValue(context, args));
|
||||
JS_SetPropertyStr(context, object, "send_json", JS_NewCFunction(context, _tf_ssb_rpc_send_json, "send_json", 1));
|
||||
JS_SetPropertyStr(context, object, "send_binary", JS_NewCFunction(context, _tf_ssb_rpc_send_binary, "send_binary", 1));
|
||||
JS_SetPropertyStr(context, object, "send_json_end", JS_NewCFunction(context, _tf_ssb_rpc_send_json_end, "send_json_end", 1));
|
||||
JS_SetPropertyStr(context, object, "more", JS_NewCFunction(context, _tf_ssb_rpc_more, "more", 1));
|
||||
|
||||
JSValue result = JS_Call(context, callback, JS_UNDEFINED, 1, &object);
|
||||
tf_util_report_error(context, result);
|
||||
JS_FreeValue(context, result);
|
||||
JS_FreeValue(context, object);
|
||||
}
|
||||
|
||||
static JSValue _tf_ssb_add_rpc(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
if (!JS_IsArray(context, argv[0]))
|
||||
{
|
||||
return JS_ThrowTypeError(context, "Expected argument 1 to be an array of strings.");
|
||||
}
|
||||
if (!JS_IsFunction(context, argv[1]))
|
||||
{
|
||||
return JS_ThrowTypeError(context, "Expected argument 2 to be a function.");
|
||||
}
|
||||
|
||||
enum { k_max_name_parts = 16 };
|
||||
const char* name[k_max_name_parts + 1] = { 0 };
|
||||
|
||||
int length = tf_util_get_length(context, argv[0]);
|
||||
if (length >= k_max_name_parts)
|
||||
{
|
||||
return JS_ThrowInternalError(context, "Too many parts to RPC name.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
JSValue value = JS_GetPropertyUint32(context, argv[0], i);
|
||||
name[i] = JS_ToCString(context, value);
|
||||
JS_FreeValue(context, value);
|
||||
}
|
||||
|
||||
tf_ssb_add_rpc_callback(ssb, name, _tf_ssb_on_rpc, _tf_ssb_cleanup_value, JS_VALUE_GET_PTR(JS_DupValue(context, argv[1])));
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
JS_FreeCString(context, name[i]);
|
||||
}
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static void _tf_ssb_on_message_added_callback(tf_ssb_t* ssb, const char* id, void* user_data)
|
||||
{
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
@ -1023,13 +817,8 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb)
|
||||
JS_SetPropertyStr(context, object, "followingDeep", JS_NewCFunction(context, _tf_ssb_followingDeep, "followingDeep", 2));
|
||||
|
||||
/* Should be trusted only. */
|
||||
JS_SetPropertyStr(context, object, "addRpc", JS_NewCFunction(context, _tf_ssb_add_rpc, "addRpc", 2));
|
||||
JS_SetPropertyStr(context, object, "addEventListener", JS_NewCFunction(context, _tf_ssb_add_event_listener, "addEventListener", 2));
|
||||
JS_SetPropertyStr(context, object, "removeEventListener", JS_NewCFunction(context, _tf_ssb_remove_event_listener, "removeEventListener", 2));
|
||||
|
||||
JS_FreeValue(context, global);
|
||||
|
||||
tf_util_register(context);
|
||||
tf_database_register(context, tf_ssb_get_db(ssb));
|
||||
tf_ssb_run_file(context, "core/ssb.js");
|
||||
}
|
||||
|
267
src/ssb.rpc.c
267
src/ssb.rpc.c
@ -678,26 +678,10 @@ static void _tf_ssb_rpc_connection_tunnel_isRoom_callback(tf_ssb_connection_t* c
|
||||
}
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_createHistoryStream(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
|
||||
static void _tf_ssb_connection_send_history_stream(tf_ssb_connection_t* connection, int32_t request_number, const char* author, int64_t sequence, bool keys)
|
||||
{
|
||||
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
JSValue arg_array = JS_GetPropertyStr(context, args, "args");
|
||||
JSValue arg = JS_GetPropertyUint32(context, arg_array, 0);
|
||||
if (JS_IsUndefined(arg))
|
||||
{
|
||||
tf_ssb_connection_rpc_send_error(connection, flags, -request_number, "Missing request.args in createHistoryStream.");
|
||||
}
|
||||
JSValue id = JS_GetPropertyStr(context, arg, "id");
|
||||
JSValue seq = JS_GetPropertyStr(context, arg, "seq");
|
||||
JSValue keys = JS_GetPropertyStr(context, arg, "keys");
|
||||
JSValue live = JS_GetPropertyStr(context, arg, "live");
|
||||
bool is_keys = JS_IsUndefined(keys) || JS_ToBool(context, keys) > 0;
|
||||
bool is_live = JS_ToBool(context, live) > 0;
|
||||
int64_t sequence = 0;
|
||||
JS_ToInt64(context, &sequence, seq);
|
||||
const char* author = JS_ToCString(context, id);
|
||||
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(db, "SELECT previous, author, id, sequence, timestamp, hash, content, signature, sequence_before_author FROM messages WHERE author = ?1 AND sequence >= ?2 ORDER BY sequence", -1, &statement, NULL) == SQLITE_OK)
|
||||
@ -719,7 +703,7 @@ static void _tf_ssb_rpc_createHistoryStream(tf_ssb_connection_t* connection, uin
|
||||
(const char*)sqlite3_column_text(statement, 6),
|
||||
(const char*)sqlite3_column_text(statement, 7),
|
||||
sqlite3_column_int(statement, 8));
|
||||
if (is_keys)
|
||||
if (keys)
|
||||
{
|
||||
message = JS_NewObject(context);
|
||||
JS_SetPropertyStr(context, message, "key", JS_NewString(context, (const char*)sqlite3_column_text(statement, 2)));
|
||||
@ -730,12 +714,35 @@ static void _tf_ssb_rpc_createHistoryStream(tf_ssb_connection_t* connection, uin
|
||||
{
|
||||
message = formatted;
|
||||
}
|
||||
tf_ssb_connection_rpc_send_json(connection, flags, -request_number, message, NULL, NULL, NULL);
|
||||
tf_ssb_connection_rpc_send_json(connection, k_ssb_rpc_flag_stream, request_number, message, NULL, NULL, NULL);
|
||||
JS_FreeValue(context, message);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_createHistoryStream(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
|
||||
{
|
||||
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
JSValue arg_array = JS_GetPropertyStr(context, args, "args");
|
||||
JSValue arg = JS_GetPropertyUint32(context, arg_array, 0);
|
||||
if (JS_IsUndefined(arg))
|
||||
{
|
||||
tf_ssb_connection_rpc_send_error(connection, flags, -request_number, "Missing request.args in createHistoryStream.");
|
||||
}
|
||||
JSValue id = JS_GetPropertyStr(context, arg, "id");
|
||||
JSValue seq = JS_GetPropertyStr(context, arg, "seq");
|
||||
JSValue keys = JS_GetPropertyStr(context, arg, "keys");
|
||||
JSValue live = JS_GetPropertyStr(context, arg, "live");
|
||||
bool is_keys = JS_IsUndefined(keys) || JS_ToBool(context, keys) > 0;
|
||||
bool is_live = JS_ToBool(context, live) > 0;
|
||||
int64_t sequence = 0;
|
||||
JS_ToInt64(context, &sequence, seq);
|
||||
const char* author = JS_ToCString(context, id);
|
||||
|
||||
_tf_ssb_connection_send_history_stream(connection, -request_number, author, sequence, is_keys);
|
||||
|
||||
if (is_live)
|
||||
{
|
||||
@ -750,6 +757,225 @@ static void _tf_ssb_rpc_createHistoryStream(tf_ssb_connection_t* connection, uin
|
||||
JS_FreeValue(context, arg_array);
|
||||
}
|
||||
|
||||
static bool _is_error(JSContext* context, JSValue message)
|
||||
{
|
||||
JSValue name = JS_GetPropertyStr(context, message, "name");
|
||||
const char* name_string = JS_ToCString(context, 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;
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_ebt_replicate_send_clock(tf_ssb_connection_t* connection, int32_t request_number, JSValue message)
|
||||
{
|
||||
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
JSValue full_clock = JS_NewObject(context);
|
||||
|
||||
/* Ask for every identity we know is being followed from local accounts. */
|
||||
const char** visible = tf_ssb_db_get_all_visible_identities(ssb, 2);
|
||||
for (int i = 0; visible[i]; i++)
|
||||
{
|
||||
int64_t sequence = 0;
|
||||
tf_ssb_db_get_latest_message_by_author(ssb, visible[i], &sequence, NULL, 0);
|
||||
JS_SetPropertyStr(context, full_clock, visible[i], JS_NewInt64(context, sequence));
|
||||
}
|
||||
|
||||
/* Ask about the incoming connection, too. */
|
||||
char id[k_id_base64_len] = "";
|
||||
if (tf_ssb_connection_get_id(connection, id, sizeof(id)))
|
||||
{
|
||||
JSValue in_clock = JS_GetPropertyStr(context, full_clock, id);
|
||||
if (JS_IsUndefined(in_clock))
|
||||
{
|
||||
int64_t sequence = 0;
|
||||
tf_ssb_db_get_latest_message_by_author(ssb, id, &sequence, NULL, 0);
|
||||
JS_SetPropertyStr(context, full_clock, id, JS_NewInt64(context, sequence));
|
||||
}
|
||||
JS_FreeValue(context, in_clock);
|
||||
}
|
||||
|
||||
/* Also respond with what we know about all requested identities. */
|
||||
if (!JS_IsUndefined(message))
|
||||
{
|
||||
JSPropertyEnum* ptab;
|
||||
uint32_t plen;
|
||||
JS_GetOwnPropertyNames(context, &ptab, &plen, message, JS_GPN_STRING_MASK);
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
{
|
||||
JSValue in_clock = JS_GetProperty(context, full_clock, ptab[i].atom);
|
||||
if (JS_IsUndefined(in_clock))
|
||||
{
|
||||
JSValue key = JS_AtomToString(context, ptab[i].atom);
|
||||
const char* key_string = JS_ToCString(context, key);
|
||||
if (key_string)
|
||||
{
|
||||
int64_t sequence = -1;
|
||||
tf_ssb_db_get_latest_message_by_author(ssb, key_string, &sequence, NULL, 0);
|
||||
JS_SetPropertyStr(context, full_clock, key_string, JS_NewInt64(context, sequence));
|
||||
}
|
||||
JS_FreeCString(context, key_string);
|
||||
JS_FreeValue(context, key);
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
{
|
||||
JS_FreeAtom(context, ptab[i].atom);
|
||||
}
|
||||
js_free(context, ptab);
|
||||
}
|
||||
|
||||
tf_free(visible);
|
||||
|
||||
/* TODO: Send it in bite-size chunks. */
|
||||
tf_ssb_connection_rpc_send_json(connection, k_ssb_rpc_flag_stream, -request_number, full_clock, NULL, NULL, NULL);
|
||||
JS_FreeValue(context, full_clock);
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_ebt_replicate_send_messages(tf_ssb_connection_t* connection, JSValue message)
|
||||
{
|
||||
if (JS_IsUndefined(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
JSPropertyEnum* ptab = NULL;
|
||||
uint32_t plen = 0;
|
||||
JS_GetOwnPropertyNames(context, &ptab, &plen, message, JS_GPN_STRING_MASK);
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
{
|
||||
JSValue in_clock = JS_GetProperty(context, message, ptab[i].atom);
|
||||
if (!JS_IsUndefined(in_clock))
|
||||
{
|
||||
JSValue key = JS_AtomToString(context, ptab[i].atom);
|
||||
int64_t sequence = -1;
|
||||
JS_ToInt64(context, &sequence, in_clock);
|
||||
const char* author = JS_ToCString(context, key);
|
||||
if (sequence >= 0 && (sequence & 1) == 0)
|
||||
{
|
||||
int32_t request_number = -tf_ssb_connection_get_ebt_request_number(connection);
|
||||
_tf_ssb_connection_send_history_stream(connection, request_number, author, sequence, false);
|
||||
tf_ssb_connection_add_new_message_request(connection, author, request_number, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_ssb_connection_remove_new_message_request(connection, author);
|
||||
}
|
||||
JS_FreeCString(context, author);
|
||||
JS_FreeValue(context, key);
|
||||
}
|
||||
JS_FreeValue(context, in_clock);
|
||||
}
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
{
|
||||
JS_FreeAtom(context, ptab[i].atom);
|
||||
}
|
||||
js_free(context, ptab);
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_ebt_replicate(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
|
||||
{
|
||||
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
if (_is_error(context, args))
|
||||
{
|
||||
/* TODO: Send createHistoryStream. */
|
||||
return;
|
||||
}
|
||||
|
||||
JSValue author = JS_GetPropertyStr(context, args, "author");
|
||||
JSValue name = JS_GetPropertyStr(context, args, "name");
|
||||
JSValue in_clock = JS_IsUndefined(name) ? args : JS_UNDEFINED;
|
||||
|
||||
if (!JS_IsUndefined(author))
|
||||
{
|
||||
/* Looks like a message. */
|
||||
tf_ssb_verify_strip_and_store_message(ssb, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* EBT clock. */
|
||||
tf_ssb_connection_set_ebt_send_clock(connection, args);
|
||||
if (!tf_ssb_connection_get_sent_clock(connection))
|
||||
{
|
||||
_tf_ssb_rpc_ebt_replicate_send_clock(connection, request_number, in_clock);
|
||||
tf_ssb_connection_set_sent_clock(connection, true);
|
||||
}
|
||||
_tf_ssb_rpc_ebt_replicate_send_messages(connection, in_clock);
|
||||
|
||||
}
|
||||
JS_FreeValue(context, name);
|
||||
JS_FreeValue(context, author);
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_ebt_replicate_client(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
|
||||
{
|
||||
_tf_ssb_rpc_ebt_replicate(connection, flags, request_number, args, message, size, user_data);
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_send_ebt_replicate(tf_ssb_connection_t* connection)
|
||||
{
|
||||
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
JSValue message = JS_NewObject(context);
|
||||
JSValue name = JS_NewArray(context);
|
||||
JS_SetPropertyUint32(context, name, 0, JS_NewString(context, "ebt"));
|
||||
JS_SetPropertyUint32(context, name, 1, JS_NewString(context, "replicate"));
|
||||
JS_SetPropertyStr(context, message, "name", name);
|
||||
JSValue arg = JS_NewObject(context);
|
||||
JS_SetPropertyStr(context, arg, "version", JS_NewInt32(context, 3));
|
||||
JS_SetPropertyStr(context, arg, "format", JS_NewString(context, "classic"));
|
||||
JSValue args = JS_NewArray(context);
|
||||
JS_SetPropertyUint32(context, args, 0, arg);
|
||||
JS_SetPropertyStr(context, message, "args", args);
|
||||
JS_SetPropertyStr(context, message, "type", JS_NewString(context, "duplex"));
|
||||
tf_ssb_connection_rpc_send_json(
|
||||
connection,
|
||||
0,
|
||||
tf_ssb_connection_next_request_number(connection),
|
||||
message,
|
||||
_tf_ssb_rpc_ebt_replicate_client,
|
||||
NULL,
|
||||
NULL);
|
||||
JS_FreeValue(context, message);
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_ebt_replicate_server(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
|
||||
{
|
||||
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
if (_is_error(context, args))
|
||||
{
|
||||
/* TODO: Send createHistoryStream. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tf_ssb_connection_get_ebt_request_number(connection))
|
||||
{
|
||||
tf_ssb_connection_set_ebt_request_number(connection, request_number);
|
||||
}
|
||||
|
||||
JSValue in_name = JS_GetPropertyStr(context, args, "name");
|
||||
if (!JS_IsUndefined(in_name))
|
||||
{
|
||||
/* This is the server receiving the initial ebt.replicate message. Respond. */
|
||||
if (!tf_ssb_connection_is_client(connection))
|
||||
{
|
||||
_tf_ssb_rpc_send_ebt_replicate(connection);
|
||||
}
|
||||
}
|
||||
JS_FreeValue(context, in_name);
|
||||
|
||||
_tf_ssb_rpc_ebt_replicate(connection, flags, request_number, args, message, size, user_data);
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_connections_changed_callback(tf_ssb_t* ssb, tf_ssb_change_t change, tf_ssb_connection_t* connection, void* user_data)
|
||||
{
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
@ -789,6 +1015,8 @@ static void _tf_ssb_rpc_connections_changed_callback(tf_ssb_t* ssb, tf_ssb_chang
|
||||
NULL,
|
||||
NULL);
|
||||
JS_FreeValue(context, message);
|
||||
|
||||
_tf_ssb_rpc_send_ebt_replicate(connection);
|
||||
}
|
||||
}
|
||||
else if (change == k_tf_ssb_change_remove)
|
||||
@ -833,4 +1061,5 @@ void tf_ssb_rpc_register(tf_ssb_t* ssb)
|
||||
tf_ssb_add_rpc_callback(ssb, (const char*[]) { "tunnel", "isRoom", NULL }, _tf_ssb_rpc_tunnel_is_room, NULL, NULL);
|
||||
tf_ssb_add_rpc_callback(ssb, (const char*[]) { "room", "attendants", NULL }, _tf_ssb_rpc_room_attendants, NULL, NULL);
|
||||
tf_ssb_add_rpc_callback(ssb, (const char*[]) { "createHistoryStream", NULL }, _tf_ssb_rpc_createHistoryStream, NULL, NULL);
|
||||
tf_ssb_add_rpc_callback(ssb, (const char*[]) { "ebt", "replicate", NULL }, _tf_ssb_rpc_ebt_replicate_server, NULL, NULL);
|
||||
}
|
||||
|
@ -1555,6 +1555,7 @@ void tf_task_activate(tf_task_t* task)
|
||||
JS_SetPropertyStr(context, global, "Socket", tf_socket_register(context));
|
||||
JS_SetPropertyStr(context, global, "TlsContext", tf_tls_context_register(context));
|
||||
tf_file_register(context);
|
||||
tf_database_register(context, task->_db);
|
||||
|
||||
task->_ssb = tf_ssb_create(&task->_loop, task->_context, task->_db);
|
||||
tf_ssb_set_trace(task->_ssb, task->_trace);
|
||||
|
Loading…
Reference in New Issue
Block a user