diff --git a/src/ssb.c b/src/ssb.c index 33e06d56..e8395cd2 100644 --- a/src/ssb.c +++ b/src/ssb.c @@ -2388,6 +2388,9 @@ void tf_ssb_connect(tf_ssb_t* ssb, const char* host, int port, const uint8_t* ke .port = port, .req.data = connect, }; + char id[k_id_base64_len] = { 0 }; + tf_ssb_id_bin_to_str(id, sizeof(id), key); + tf_ssb_connections_store(ssb->connections_tracker, host, port, id); snprintf(connect->host, sizeof(connect->host), "%s", host); memcpy(connect->key, key, k_id_bin_len); int r = uv_getaddrinfo(ssb->loop, &connect->req, _tf_on_connect_getaddrinfo, host, NULL, &(struct addrinfo) { .ai_family = AF_INET }); @@ -2639,9 +2642,8 @@ static void _tf_ssb_add_broadcast(tf_ssb_t* ssb, const tf_ssb_broadcast_t* broad char key[k_id_base64_len]; if (tf_ssb_id_bin_to_str(key, sizeof(key), broadcast->pub)) { - tf_ssb_connections_store(ssb->connections_tracker, broadcast->host, ntohs(broadcast->addr.sin_port), key); + printf("Received new broadcast: host=%s, pub=%s.\n", broadcast->host, key); } - printf("Received new broadcast: host=%s, pub=%s.\n", broadcast->host, key); } tf_ssb_broadcast_t* node = tf_malloc(sizeof(tf_ssb_broadcast_t)); diff --git a/src/ssb.connections.c b/src/ssb.connections.c index ce9fc48f..7dc67f4a 100644 --- a/src/ssb.connections.c +++ b/src/ssb.connections.c @@ -32,7 +32,6 @@ static void _tf_ssb_connections_changed_callback(tf_ssb_t* ssb, tf_ssb_change_t tf_ssb_connection_get_port(connection) && tf_ssb_connection_get_id(connection, key, sizeof(key))) { - tf_ssb_connections_store(connections, tf_ssb_connection_get_host(connection), tf_ssb_connection_get_port(connection), key); tf_ssb_connections_set_attempted(connections, tf_ssb_connection_get_host(connection), tf_ssb_connection_get_port(connection), key); } } diff --git a/src/ssb.db.c b/src/ssb.db.c index ea340498..ec5b3677 100644 --- a/src/ssb.db.c +++ b/src/ssb.db.c @@ -1245,3 +1245,47 @@ JSValue tf_ssb_db_get_message_by_id( tf_ssb_t* ssb, const char* id, bool is_keys } return result; } + +tf_ssb_db_stored_connection_t* tf_ssb_db_get_stored_connections(tf_ssb_t* ssb, int* out_count) +{ + sqlite3* db = tf_ssb_get_db(ssb); + tf_ssb_db_stored_connection_t* result = NULL; + int count = 0; + + sqlite3_stmt* statement; + if (sqlite3_prepare(db, "SELECT host, port, key FROM connections ORDER BY host, port, key", -1, &statement, NULL) == SQLITE_OK) + { + while (sqlite3_step(statement) == SQLITE_ROW) + { + result = tf_resize_vec(result, sizeof(tf_ssb_db_stored_connection_t) * (count + 1)); + result[count] = (tf_ssb_db_stored_connection_t) + { + .port = sqlite3_column_int(statement, 1), + }; + snprintf(result[count].address, sizeof(result[count].address), "%s", (const char*)sqlite3_column_text(statement, 0)); + snprintf(result[count].pubkey, sizeof(result[count].pubkey), "%s", (const char*)sqlite3_column_text(statement, 2)); + count++; + } + sqlite3_finalize(statement); + } + + *out_count = count; + return result; +} + +void tf_ssb_db_forget_stored_connection(tf_ssb_t* ssb, const char* address, int port, const char* pubkey) +{ + sqlite3* db = tf_ssb_get_db(ssb); + sqlite3_stmt* statement; + if (sqlite3_prepare(db, "DELETE FROM connections WHERE host = ? AND port = ? AND key = ?", -1, &statement, NULL) == SQLITE_OK) + { + if (sqlite3_bind_text(statement, 0, address, -1, NULL) != SQLITE_OK || + sqlite3_bind_int(statement, 1, port) != SQLITE_OK || + sqlite3_bind_text(statement, 2, pubkey, -1, NULL) != SQLITE_OK || + sqlite3_step(statement) != SQLITE_DONE) + { + printf("Delete stored connection: %s.\n", sqlite3_errmsg(db)); + } + sqlite3_finalize(statement); + } +} diff --git a/src/ssb.db.h b/src/ssb.db.h index d32a3dc3..ae75d972 100644 --- a/src/ssb.db.h +++ b/src/ssb.db.h @@ -1,5 +1,7 @@ #pragma once +#include "ssb.h" + #include #include @@ -41,3 +43,13 @@ const char** tf_ssb_db_following_deep(tf_ssb_t* ssb, const char** ids, int count const char** tf_ssb_db_get_all_visible_identities(tf_ssb_t* ssb, int depth); void tf_ssb_db_private(sqlite3* db); + +typedef struct _tf_ssb_db_stored_connection_t +{ + char address[256]; + int port; + char pubkey[k_id_base64_len]; +} tf_ssb_db_stored_connection_t; + +tf_ssb_db_stored_connection_t* tf_ssb_db_get_stored_connections(tf_ssb_t* ssb, int* out_count); +void tf_ssb_db_forget_stored_connection(tf_ssb_t* ssb, const char* address, int port, const char* pubkey); diff --git a/src/ssb.js.c b/src/ssb.js.c index 5b2ffd1b..bd092226 100644 --- a/src/ssb.js.c +++ b/src/ssb.js.c @@ -253,6 +253,28 @@ static JSValue _tf_ssb_connections(JSContext* context, JSValueConst this_val, in return result; } +static JSValue _tf_ssb_storedConnections(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + JSValue result = JS_NULL; + tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId); + if (ssb) + { + int count = 0; + tf_ssb_db_stored_connection_t* connections = tf_ssb_db_get_stored_connections(ssb, &count); + result = JS_NewArray(context); + for (int i = 0; i < count; i++) + { + JSValue connection = JS_NewObject(context); + JS_SetPropertyStr(context, connection, "address", JS_NewString(context, connections[i].address)); + JS_SetPropertyStr(context, connection, "port", JS_NewInt32(context, connections[i].port)); + JS_SetPropertyStr(context, connection, "pubkey", JS_NewString(context, connections[i].pubkey)); + JS_SetPropertyUint32(context, result, i, connection); + } + tf_free(connections); + } + return result; +} + static JSValue _tf_ssb_getConnection(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) { tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId); @@ -403,6 +425,32 @@ static JSValue _tf_ssb_connect(JSContext* context, JSValueConst this_val, int ar return JS_UNDEFINED; } +static JSValue _tf_ssb_forgetStoredConnection(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + JSValue args = argv[0]; + tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId); + if (ssb) + { + JSValue address = JS_GetPropertyStr(context, args, "address"); + JSValue port = JS_GetPropertyStr(context, args, "port"); + JSValue pubkey = JS_GetPropertyStr(context, args, "pubkey"); + const char* address_str = JS_ToCString(context, address); + int32_t port_int = 0; + JS_ToInt32(context, &port_int, port); + const char* pubkey_str = JS_ToCString(context, pubkey); + if (pubkey_str) + { + tf_ssb_db_forget_stored_connection(ssb, address_str, port_int, pubkey_str); + } + JS_FreeCString(context, pubkey_str); + JS_FreeCString(context, address_str); + JS_FreeValue(context, address); + JS_FreeValue(context, port); + JS_FreeValue(context, pubkey); + } + 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); @@ -802,8 +850,10 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb) JS_SetPropertyStr(context, object, "blobStore", JS_NewCFunction(context, _tf_ssb_blobStore, "blobStore", 2)); JS_SetPropertyStr(context, object, "messageContentGet", JS_NewCFunction(context, _tf_ssb_messageContentGet, "messageContentGet", 1)); JS_SetPropertyStr(context, object, "connections", JS_NewCFunction(context, _tf_ssb_connections, "connections", 0)); + JS_SetPropertyStr(context, object, "storedConnections", JS_NewCFunction(context, _tf_ssb_storedConnections, "storedConnections", 0)); JS_SetPropertyStr(context, object, "getConnection", JS_NewCFunction(context, _tf_ssb_getConnection, "getConnection", 1)); JS_SetPropertyStr(context, object, "closeConnection", JS_NewCFunction(context, _tf_ssb_closeConnection, "closeConnection", 1)); + JS_SetPropertyStr(context, object, "forgetStoredConnection", JS_NewCFunction(context, _tf_ssb_forgetStoredConnection, "forgetStoredConnection", 1)); JS_SetPropertyStr(context, object, "sqlStream", JS_NewCFunction(context, _tf_ssb_sqlStream, "sqlStream", 3)); JS_SetPropertyStr(context, object, "storeMessage", JS_NewCFunction(context, _tf_ssb_storeMessage, "storeMessage", 1)); JS_SetPropertyStr(context, object, "getBroadcasts", JS_NewCFunction(context, _tf_ssb_getBroadcasts, "getBroadcasts", 0));